commit 194a506da0e990204dab6f1edf8d67023eafa83e Author: starmen1265 Date: Sun Feb 15 00:37:39 2026 +0800 Initial commit: feishu + wecom extensions diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3c459389 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +*.log +.DS_Store diff --git a/feishu/LICENSE b/feishu/LICENSE new file mode 100644 index 00000000..192f19b9 --- /dev/null +++ b/feishu/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 m1heng + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/feishu/README.md b/feishu/README.md new file mode 100644 index 00000000..a1270084 --- /dev/null +++ b/feishu/README.md @@ -0,0 +1,635 @@ +# clawd-feishu + +Feishu/Lark (飞书) channel plugin for [OpenClaw](https://github.com/openclaw/openclaw). + +> **中文社区资料** - 配置教程、常见问题、使用技巧:[Wiki](https://github.com/m1heng/clawdbot-feishu/wiki) + +[English](#english) | [中文](#中文) + +--- + +## English + +### Installation + +```bash +openclaw plugins install @m1heng-clawd/feishu +``` + +### Upgrade + +```bash +openclaw plugins update feishu +``` + +### Configuration + +1. Create a self-built app on [Feishu Open Platform](https://open.feishu.cn) +2. Get your App ID and App Secret from the Credentials page +3. Enable required permissions (see below) +4. **Configure event subscriptions** (see below) ⚠️ Important +5. Configure the plugin: + +#### Required Permissions + +| Permission | Scope | Description | +|------------|-------|-------------| +| `im:message` | Messaging | Send and receive messages | +| `im:message.p2p_msg:readonly` | DM | Read direct messages to bot | +| `im:message.group_at_msg:readonly` | Group | Receive @mention messages in groups | +| `im:message:send_as_bot` | Send | Send messages as the bot | +| `im:resource` | Media | Upload and download images/files | + +#### Optional Permissions + +| Permission | Scope | Description | +|------------|-------|-------------| +| `contact:user.base:readonly` | User info | Get basic user info (required to resolve sender display names for speaker attribution) | +| `im:message.group_msg` | Group | Read all group messages (sensitive) | +| `im:message:readonly` | Read | Get message history | +| `im:message:update` | Edit | Update/edit sent messages | +| `im:message:recall` | Recall | Recall sent messages | +| `im:message.reactions:read` | Reactions | View message reactions | + +#### Tool Permissions + +**Read-only** (minimum required): + +| Permission | Tool | Description | +|------------|------|-------------| +| `docx:document:readonly` | `feishu_doc` | Read documents | +| `drive:drive:readonly` | `feishu_drive` | List folders, get file info | +| `wiki:wiki:readonly` | `feishu_wiki` | List spaces, list nodes, get node info, search | +| `bitable:app:readonly` | `feishu_bitable` | Read bitable records and fields | + +**Read-write** (optional, for create/edit/delete operations): + +| Permission | Tool | Description | +|------------|------|-------------| +| `docx:document` | `feishu_doc` | Create/edit documents | +| `docx:document.block:convert` | `feishu_doc` | Markdown to blocks conversion (required for write/append) | +| `drive:drive` | `feishu_doc`, `feishu_drive` | Upload images to documents, create folders, move/delete files | +| `wiki:wiki` | `feishu_wiki` | Create/move/rename wiki nodes | +| `bitable:app` | `feishu_bitable` | Create/update bitable records | + +#### Drive Access ⚠️ + +> **Important:** Bots don't have their own "My Space" (root folder). Bots can only access files/folders that have been **shared with them**. + +To let the bot manage files: +1. Create a folder in your Feishu Drive +2. Right-click the folder → **Share** → search for your bot name +3. Grant appropriate permission (view/edit) + +Without this step, `feishu_drive` operations like `create_folder` will fail because the bot has no root folder to create in. + +#### Wiki Space Access ⚠️ + +> **Important:** API permissions alone are not enough for wiki access. You must also add the bot to each wiki space. + +1. Open the wiki space you want the bot to access +2. Click **Settings** (gear icon) → **Members** +3. Click **Add Member** → search for your bot name +4. Select appropriate permission level (view/edit) + +Without this step, `feishu_wiki` will return empty results even with correct API permissions. + +Reference: [Wiki FAQ - How to add app to wiki](https://open.feishu.cn/document/server-docs/docs/wiki-v2/wiki-qa#a40ad4ca) + +#### Bitable Access ⚠️ + +> **Important:** Like other resources, the bot can only access bitables that have been **shared with it**. + +To let the bot access a bitable: +1. Open the bitable you want the bot to access +2. Click **Share** button → search for your bot name +3. Grant appropriate permission (view/edit) + +The `feishu_bitable` tools support both URL formats: +- `/base/XXX?table=YYY` - Standard bitable URL +- `/wiki/XXX?table=YYY` - Bitable embedded in wiki (auto-converts to app_token) + +#### Event Subscriptions ⚠️ + +> **This is the most commonly missed configuration!** If the bot can send messages but cannot receive them, check this section. + +In the Feishu Open Platform console, go to **Events & Callbacks**: + +1. **Event configuration**: Select the subscription mode matching your `connectionMode`: + - **Long connection** — for `connectionMode: "websocket"` (recommended, no public URL needed) + - **Request URL** — for `connectionMode: "webhook"` (requires a publicly accessible URL) +2. **Add event subscriptions**: + +| Event | Description | +|-------|-------------| +| `im.message.receive_v1` | Receive messages (required) | +| `im.message.message_read_v1` | Message read receipts | +| `im.chat.member.bot.added_v1` | Bot added to group | +| `im.chat.member.bot.deleted_v1` | Bot removed from group | + +3. Ensure the event permissions are approved + +```bash +openclaw config set channels.feishu.appId "cli_xxxxx" +openclaw config set channels.feishu.appSecret "your_app_secret" +openclaw config set channels.feishu.enabled true +``` + +### Configuration Options + +```yaml +channels: + feishu: + enabled: true + appId: "cli_xxxxx" + appSecret: "secret" + # Domain: "feishu" (China), "lark" (International), or custom URL + domain: "feishu" # or "https://open.xxx.cn" for private deployment + # Connection mode: "websocket" (recommended) or "webhook" + connectionMode: "websocket" + # DM policy: "pairing" | "open" | "allowlist" + dmPolicy: "pairing" + # Group policy: "open" | "allowlist" | "disabled" + groupPolicy: "allowlist" + # Require @mention in groups + requireMention: true + # Max media size in MB (default: 30) + mediaMaxMb: 30 + # Render mode for bot replies: "auto" | "raw" | "card" + renderMode: "auto" +``` + +#### Connection Mode + +Two connection modes are available for receiving events from Feishu: + +| Mode | Description | +|------|-------------| +| `websocket` | (Default, recommended) Long-polling WebSocket connection. No public URL required, works behind NAT/firewall. Best for local development and most deployments. | +| `webhook` | HTTP server that receives event callbacks. Requires a publicly accessible URL. Suitable for server deployments behind a reverse proxy (e.g. Nginx). | + +**WebSocket mode** (default, no extra config needed): + +```yaml +channels: + feishu: + connectionMode: "websocket" # or just omit this line +``` + +In Feishu console: Events & Callbacks → select **Long connection**. + +**Webhook mode**: + +```yaml +channels: + feishu: + connectionMode: "webhook" + webhookPort: 3000 # HTTP server port (default: 3000) + webhookPath: "/feishu/events" # Event callback path (default: "/feishu/events") + encryptKey: "your_encrypt_key" # From Feishu console → Events & Callbacks → Encrypt Key + verificationToken: "your_verify_token" # From Feishu console → Events & Callbacks → Verification Token +``` + +In Feishu console: Events & Callbacks → select **Request URL** → set the URL to: + +``` +https://your-domain.com/feishu/events +``` + +> **Note:** The Request URL must be HTTPS and publicly accessible. For local development, you can use tools like [ngrok](https://ngrok.com) to create a tunnel: `ngrok http 3000`, then use the generated URL. + +#### Render Mode + +| Mode | Description | +|------|-------------| +| `auto` | (Default) Automatically detect: use card for messages with code blocks or tables, plain text otherwise. | +| `raw` | Always send replies as plain text. Markdown tables are converted to ASCII. | +| `card` | Always send replies as interactive cards with full markdown rendering (syntax highlighting, tables, clickable links). | + +#### Dynamic Agent Creation (Multi-User Workspace Isolation) + +When enabled, each DM user automatically gets their own isolated agent instance with a dedicated workspace. This provides complete isolation including separate conversation history, memory (MEMORY.md), and workspace files. + +```yaml +channels: + feishu: + dmPolicy: "open" + allowFrom: ["*"] + dynamicAgentCreation: + enabled: true + # Template for workspace directory ({userId} = OpenID, {agentId} = generated agent ID) + workspaceTemplate: "~/workspaces/feishu-{agentId}" + # Template for agent config directory + agentDirTemplate: "~/.openclaw/agents/{agentId}/agent" + # Optional: limit total number of dynamic agents + maxAgents: 100 + +session: + # Also set dmScope for session isolation (conversation history) + dmScope: "per-peer" +``` + +| Option | Description | +|--------|-------------| +| `enabled` | Enable dynamic agent creation for DM users | +| `workspaceTemplate` | Template for workspace path. Supports `{userId}` (OpenID) and `{agentId}` (= `feishu-{openId}`) | +| `agentDirTemplate` | Template for agent directory path | +| `maxAgents` | Optional limit on number of dynamic agents | + +**How it works:** +1. When a new user sends a DM, the system creates a new agent entry in `openclaw.json` +2. A binding is created to route that user's DM to their dedicated agent +3. Workspace and agent directories are created automatically +4. Subsequent messages from that user go to their isolated agent + +**Difference from `dmScope: "per-peer"`:** +- `dmScope: "per-peer"` only isolates conversation history +- `dynamicAgentCreation` provides full isolation (workspace, memory, identity, tools) + +### Features + +- WebSocket and Webhook connection modes +- Direct messages and group chats +- Message replies and quoted message context +- **Inbound media support**: AI can see images, read files (PDF, Excel, etc.), and process rich text with embedded images +- Image and file uploads (outbound) +- Typing indicator (via emoji reactions) +- Pairing flow for DM approval +- User and group directory lookup +- **Card render mode**: Optional markdown rendering with syntax highlighting +- **Document tools**: Read, create, and write Feishu documents with markdown (tables not supported due to API limitations) +- **Wiki tools**: Navigate knowledge bases, list spaces, get node details, search, create/move/rename nodes +- **Drive tools**: List folders, get file info, create folders, move/delete files +- **Bitable tools**: Read/write bitable (多维表格) records, supports both `/base/` and `/wiki/` URLs +- **@mention forwarding**: When you @mention someone in your message, the bot's reply will automatically @mention them too +- **Permission error notification**: When the bot encounters a Feishu API permission error, it automatically notifies the user with the permission grant URL +- **Dynamic agent creation**: Each DM user can have their own isolated agent instance with dedicated workspace (optional) + +#### @Mention Forwarding + +When you want the bot to @mention someone in its reply, simply @mention them in your message: + +- **In DM**: `@张三 say hello` → Bot replies with `@张三 Hello!` +- **In Group**: `@bot @张三 say hello` → Bot replies with `@张三 Hello!` + +The bot automatically detects @mentions in your message and includes them in its reply. No extra permissions required beyond the standard messaging permissions. + +### FAQ + +#### Bot cannot receive messages + +Check the following: +1. Have you configured **event subscriptions**? (See Event Subscriptions section) +2. Does the event subscription mode match your `connectionMode`? + - `websocket` → **Long connection** in Feishu console + - `webhook` → **Request URL** in Feishu console (URL must be reachable) +3. Did you add the `im.message.receive_v1` event? +4. Are the permissions approved? +5. For webhook mode: is your server running and the URL publicly accessible? + +#### 403 error when sending messages + +Ensure `im:message:send_as_bot` permission is approved. + +#### How to clear history / start new conversation + +Send `/new` command in the chat. + +#### Why is the output not streaming + +Feishu API has rate limits. Streaming updates can easily trigger throttling. We use complete-then-send approach for stability. + +#### Windows install error `spawn npm ENOENT` + +If `openclaw plugins install` fails, install manually: + +```bash +# 1. Download the package +curl -O https://registry.npmjs.org/@m1heng-clawd/feishu/-/feishu-0.1.3.tgz + +# 2. Install from local file +openclaw plugins install ./feishu-0.1.3.tgz +``` + +#### Cannot find the bot in Feishu + +1. Ensure the app is published (at least to test version) +2. Search for the bot name in Feishu search box +3. Check if your account is in the app's availability scope + +--- + +## 中文 + +### 安装 + +```bash +openclaw plugins install @m1heng-clawd/feishu +``` + +### 升级 + +```bash +openclaw plugins update feishu +``` + +### 配置 + +1. 在 [飞书开放平台](https://open.feishu.cn) 创建自建应用 +2. 在凭证页面获取 App ID 和 App Secret +3. 开启所需权限(见下方) +4. **配置事件订阅**(见下方)⚠️ 重要 +5. 配置插件: + +#### 必需权限 + +| 权限 | 范围 | 说明 | +|------|------|------| +| `im:message` | 消息 | 发送和接收消息 | +| `im:message.p2p_msg:readonly` | 私聊 | 读取发给机器人的私聊消息 | +| `im:message.group_at_msg:readonly` | 群聊 | 接收群内 @机器人 的消息 | +| `im:message:send_as_bot` | 发送 | 以机器人身份发送消息 | +| `im:resource` | 媒体 | 上传和下载图片/文件 | + +#### 可选权限 + +| 权限 | 范围 | 说明 | +|------|------|------| +| `contact:user.base:readonly` | 用户信息 | 获取用户基本信息(用于解析发送者姓名,避免群聊/私聊把不同人当成同一说话者) | +| `im:message.group_msg` | 群聊 | 读取所有群消息(敏感) | +| `im:message:readonly` | 读取 | 获取历史消息 | +| `im:message:update` | 编辑 | 更新/编辑已发送消息 | +| `im:message:recall` | 撤回 | 撤回已发送消息 | +| `im:message.reactions:read` | 表情 | 查看消息表情回复 | + +#### 工具权限 + +**只读权限**(最低要求): + +| 权限 | 工具 | 说明 | +|------|------|------| +| `docx:document:readonly` | `feishu_doc` | 读取文档 | +| `drive:drive:readonly` | `feishu_drive` | 列出文件夹、获取文件信息 | +| `wiki:wiki:readonly` | `feishu_wiki` | 列出空间、列出节点、获取节点详情、搜索 | +| `bitable:app:readonly` | `feishu_bitable` | 读取多维表格记录和字段 | + +**读写权限**(可选,用于创建/编辑/删除操作): + +| 权限 | 工具 | 说明 | +|------|------|------| +| `docx:document` | `feishu_doc` | 创建/编辑文档 | +| `docx:document.block:convert` | `feishu_doc` | Markdown 转 blocks(write/append 必需) | +| `drive:drive` | `feishu_doc`, `feishu_drive` | 上传图片到文档、创建文件夹、移动/删除文件 | +| `wiki:wiki` | `feishu_wiki` | 创建/移动/重命名知识库节点 | +| `bitable:app` | `feishu_bitable` | 创建/更新多维表格记录 | + +#### 云空间访问权限 ⚠️ + +> **重要:** 机器人没有自己的"我的空间"(根目录)。机器人只能访问**被分享给它的文件/文件夹**。 + +要让机器人管理文件: +1. 在你的飞书云空间创建一个文件夹 +2. 右键文件夹 → **分享** → 搜索机器人名称 +3. 授予相应权限(查看/编辑) + +如果不做这一步,`feishu_drive` 的 `create_folder` 等操作会失败,因为机器人没有根目录可以创建文件夹。 + +#### 知识库空间权限 ⚠️ + +> **重要:** 仅有 API 权限不够,还需要将机器人添加到知识库空间。 + +1. 打开需要机器人访问的知识库空间 +2. 点击 **设置**(齿轮图标)→ **成员管理** +3. 点击 **添加成员** → 搜索机器人名称 +4. 选择权限级别(查看/编辑) + +如果不做这一步,即使 API 权限正确,`feishu_wiki` 也会返回空结果。 + +参考文档:[知识库常见问题 - 如何将应用添加为知识库成员](https://open.feishu.cn/document/server-docs/docs/wiki-v2/wiki-qa#a40ad4ca) + +#### 多维表格访问权限 ⚠️ + +> **重要:** 与其他资源一样,机器人只能访问**被分享给它的多维表格**。 + +要让机器人访问多维表格: +1. 打开需要机器人访问的多维表格 +2. 点击 **分享** 按钮 → 搜索机器人名称 +3. 授予相应权限(查看/编辑) + +`feishu_bitable` 工具支持两种 URL 格式: +- `/base/XXX?table=YYY` - 标准多维表格链接 +- `/wiki/XXX?table=YYY` - 嵌入在知识库中的多维表格(自动转换为 app_token) + +#### 事件订阅 ⚠️ + +> **这是最容易遗漏的配置!** 如果机器人能发消息但收不到消息,请检查此项。 + +在飞书开放平台的应用后台,进入 **事件与回调** 页面: + +1. **事件配置方式**:根据你的 `connectionMode` 选择对应的订阅方式: + - **使用长连接接收事件** — 对应 `connectionMode: "websocket"`(推荐,无需公网地址) + - **使用请求地址接收事件** — 对应 `connectionMode: "webhook"`(需要公网可访问的 URL) +2. **添加事件订阅**,勾选以下事件: + +| 事件 | 说明 | +|------|------| +| `im.message.receive_v1` | 接收消息(必需) | +| `im.message.message_read_v1` | 消息已读回执 | +| `im.chat.member.bot.added_v1` | 机器人进群 | +| `im.chat.member.bot.deleted_v1` | 机器人被移出群 | + +3. 确保事件订阅的权限已申请并通过审核 + +```bash +openclaw config set channels.feishu.appId "cli_xxxxx" +openclaw config set channels.feishu.appSecret "your_app_secret" +openclaw config set channels.feishu.enabled true +``` + +### 配置选项 + +```yaml +channels: + feishu: + enabled: true + appId: "cli_xxxxx" + appSecret: "secret" + # 域名: "feishu" (国内)、"lark" (国际) 或自定义 URL + domain: "feishu" # 私有化部署可用 "https://open.xxx.cn" + # 连接模式: "websocket" (推荐) 或 "webhook" + connectionMode: "websocket" + # 私聊策略: "pairing" | "open" | "allowlist" + dmPolicy: "pairing" + # 群聊策略: "open" | "allowlist" | "disabled" + groupPolicy: "allowlist" + # 群聊是否需要 @机器人 + requireMention: true + # 媒体文件最大大小 (MB, 默认 30) + mediaMaxMb: 30 + # 回复渲染模式: "auto" | "raw" | "card" + renderMode: "auto" +``` + +#### 连接模式 + +支持两种从飞书接收事件的连接模式: + +| 模式 | 说明 | +|------|------| +| `websocket` | (默认,推荐)长连接 WebSocket 模式。无需公网地址,可在 NAT/防火墙后使用。适合本地开发和大部分部署场景。 | +| `webhook` | HTTP 服务器接收事件回调。需要公网可访问的 URL。适合通过反向代理(如 Nginx)部署的服务器环境。 | + +**WebSocket 模式**(默认,无需额外配置): + +```yaml +channels: + feishu: + connectionMode: "websocket" # 或直接省略此行 +``` + +飞书控制台:事件与回调 → 选择 **使用长连接接收事件**。 + +**Webhook 模式**: + +```yaml +channels: + feishu: + connectionMode: "webhook" + webhookPort: 3000 # HTTP 服务端口(默认: 3000) + webhookPath: "/feishu/events" # 事件回调路径(默认: "/feishu/events") + encryptKey: "your_encrypt_key" # 飞书控制台 → 事件与回调 → Encrypt Key + verificationToken: "your_verify_token" # 飞书控制台 → 事件与回调 → Verification Token +``` + +飞书控制台:事件与回调 → 选择 **使用请求地址接收事件** → 填入请求地址: + +``` +https://your-domain.com/feishu/events +``` + +> **提示:** 请求地址必须是 HTTPS 且公网可访问。本地开发时,可使用 [ngrok](https://ngrok.com) 等工具创建隧道:`ngrok http 3000`,然后使用生成的地址。 + +#### 渲染模式 + +| 模式 | 说明 | +|------|------| +| `auto` | (默认)自动检测:有代码块或表格时用卡片,否则纯文本 | +| `raw` | 始终纯文本,表格转为 ASCII | +| `card` | 始终使用卡片,支持语法高亮、表格、链接等 | + +#### 动态 Agent 创建(多用户 Workspace 隔离) + +启用后,每个私聊用户会自动获得独立的 agent 实例和专属 workspace。这提供完整的隔离,包括独立的对话历史、记忆(MEMORY.md)和工作区文件。 + +```yaml +channels: + feishu: + dmPolicy: "open" + allowFrom: ["*"] + dynamicAgentCreation: + enabled: true + # workspace 目录模板 ({userId} = OpenID, {agentId} = 生成的 agent ID) + workspaceTemplate: "~/workspaces/feishu-{agentId}" + # agent 配置目录模板 + agentDirTemplate: "~/.openclaw/agents/{agentId}/agent" + # 可选:限制动态 agent 总数 + maxAgents: 100 + +session: + # 同时设置 dmScope 以隔离对话历史 + dmScope: "per-peer" +``` + +| 选项 | 说明 | +|------|------| +| `enabled` | 是否为私聊用户启用动态 agent 创建 | +| `workspaceTemplate` | workspace 路径模板,支持 `{userId}`(OpenID)和 `{agentId}`(= `feishu-{openId}`)| +| `agentDirTemplate` | agent 目录路径模板 | +| `maxAgents` | 可选,限制动态 agent 的最大数量 | + +**工作原理:** +1. 当新用户发送私聊时,系统在 `openclaw.json` 中创建新的 agent 条目 +2. 创建 binding 将该用户的私聊路由到专属 agent +3. 自动创建 workspace 和 agent 目录 +4. 该用户后续的消息都会路由到其隔离的 agent + +**与 `dmScope: "per-peer"` 的区别:** +- `dmScope: "per-peer"` 仅隔离对话历史 +- `dynamicAgentCreation` 提供完整隔离(workspace、记忆、身份、工具) + +### 功能 + +- WebSocket 和 Webhook 连接模式 +- 私聊和群聊 +- 消息回复和引用上下文 +- **入站媒体支持**:AI 可以看到图片、读取文件(PDF、Excel 等)、处理富文本中的嵌入图片 +- 图片和文件上传(出站) +- 输入指示器(通过表情回复实现) +- 私聊配对审批流程 +- 用户和群组目录查询 +- **卡片渲染模式**:支持语法高亮的 Markdown 渲染 +- **文档工具**:读取、创建、用 Markdown 写入飞书文档(表格因 API 限制不支持) +- **知识库工具**:浏览知识库、列出空间、获取节点详情、搜索、创建/移动/重命名节点 +- **云空间工具**:列出文件夹、获取文件信息、创建文件夹、移动/删除文件 +- **多维表格工具**:读写多维表格记录,支持 `/base/` 和 `/wiki/` 两种链接格式 +- **@ 转发功能**:在消息中 @ 某人,机器人的回复会自动 @ 该用户 +- **权限错误提示**:当机器人遇到飞书 API 权限错误时,会自动通知用户并提供权限授权链接 +- **动态 Agent 创建**:每个私聊用户可拥有独立的 agent 实例和专属 workspace(可选) + +#### @ 转发功能 + +如果你希望机器人的回复中 @ 某人,只需在你的消息中 @ 他们: + +- **私聊**:`@张三 跟他问好` → 机器人回复 `@张三 你好!` +- **群聊**:`@机器人 @张三 跟他问好` → 机器人回复 `@张三 你好!` + +机器人会自动检测消息中的 @ 并在回复时带上。无需额外权限。 + +### 常见问题 + +#### 机器人收不到消息 + +检查以下配置: +1. 是否配置了 **事件订阅**?(见上方事件订阅章节) +2. 事件订阅方式是否与 `connectionMode` 匹配? + - `websocket` → 飞书控制台选择 **使用长连接接收事件** + - `webhook` → 飞书控制台选择 **使用请求地址接收事件**(URL 必须可访问) +3. 是否添加了 `im.message.receive_v1` 事件? +4. 相关权限是否已申请并审核通过? +5. 如果使用 webhook 模式:服务是否正在运行?URL 是否公网可访问? + +#### 返回消息时 403 错误 + +确保已申请 `im:message:send_as_bot` 权限,并且权限已审核通过。 + +#### 如何清理历史会话 / 开启新对话 + +在聊天中发送 `/new` 命令即可开启新对话。 + +#### 消息为什么不是流式输出 + +飞书 API 有请求频率限制,流式更新消息很容易触发限流。当前采用完整回复后一次性发送的方式,以保证稳定性。 + +#### Windows 安装报错 `spawn npm ENOENT` + +如果 `openclaw plugins install` 失败,可以手动安装: + +```bash +# 1. 下载插件包 +curl -O https://registry.npmjs.org/@m1heng-clawd/feishu/-/feishu-0.1.3.tgz + +# 2. 从本地安装 +openclaw plugins install ./feishu-0.1.3.tgz +``` + +#### 在飞书里找不到机器人 + +1. 确保应用已发布(至少发布到测试版本) +2. 在飞书搜索框中搜索机器人名称 +3. 检查应用可用范围是否包含你的账号 + +--- + +## License + +MIT diff --git a/feishu/index.ts b/feishu/index.ts new file mode 100644 index 00000000..12eb911a --- /dev/null +++ b/feishu/index.ts @@ -0,0 +1,63 @@ +import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; +import { emptyPluginConfigSchema } from "openclaw/plugin-sdk"; +import { feishuPlugin } from "./src/channel.js"; +import { setFeishuRuntime } from "./src/runtime.js"; +import { registerFeishuDocTools } from "./src/docx.js"; +import { registerFeishuWikiTools } from "./src/wiki.js"; +import { registerFeishuDriveTools } from "./src/drive.js"; +import { registerFeishuPermTools } from "./src/perm.js"; +import { registerFeishuBitableTools } from "./src/bitable.js"; + +export { monitorFeishuProvider } from "./src/monitor.js"; +export { + sendMessageFeishu, + sendCardFeishu, + updateCardFeishu, + editMessageFeishu, + getMessageFeishu, +} from "./src/send.js"; +export { + uploadImageFeishu, + uploadFileFeishu, + sendImageFeishu, + sendFileFeishu, + sendMediaFeishu, +} from "./src/media.js"; +export { probeFeishu } from "./src/probe.js"; +export { + addReactionFeishu, + removeReactionFeishu, + listReactionsFeishu, + FeishuEmoji, +} from "./src/reactions.js"; +export { + extractMentionTargets, + extractMessageBody, + isMentionForwardRequest, + formatMentionForText, + formatMentionForCard, + formatMentionAllForText, + formatMentionAllForCard, + buildMentionedMessage, + buildMentionedCardContent, + type MentionTarget, +} from "./src/mention.js"; +export { feishuPlugin } from "./src/channel.js"; + +const plugin = { + id: "feishu", + name: "Feishu", + description: "Feishu/Lark channel plugin", + configSchema: emptyPluginConfigSchema(), + register(api: OpenClawPluginApi) { + setFeishuRuntime(api.runtime); + api.registerChannel({ plugin: feishuPlugin }); + registerFeishuDocTools(api); + registerFeishuWikiTools(api); + registerFeishuDriveTools(api); + registerFeishuPermTools(api); + registerFeishuBitableTools(api); + }, +}; + +export default plugin; diff --git a/feishu/openclaw.plugin.json b/feishu/openclaw.plugin.json new file mode 100644 index 00000000..90358d7e --- /dev/null +++ b/feishu/openclaw.plugin.json @@ -0,0 +1,10 @@ +{ + "id": "feishu", + "channels": ["feishu"], + "skills": ["./skills"], + "configSchema": { + "type": "object", + "additionalProperties": false, + "properties": {} + } +} diff --git a/feishu/package-lock.json b/feishu/package-lock.json new file mode 100644 index 00000000..f87e676e --- /dev/null +++ b/feishu/package-lock.json @@ -0,0 +1,10867 @@ +{ + "name": "@m1heng-clawd/feishu", + "version": "0.1.9", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@m1heng-clawd/feishu", + "version": "0.1.9", + "license": "MIT", + "dependencies": { + "@larksuiteoapi/node-sdk": "^1.58.0", + "@sinclair/typebox": "0.34.48", + "zod": "^4.3.6" + }, + "devDependencies": { + "@types/node": "^25.0.10", + "openclaw": "2026.1.29", + "tsx": "^4.21.0", + "typescript": "^5.7.0" + }, + "peerDependencies": { + "openclaw": ">=2026.1.29" + } + }, + "node_modules/@agentclientprotocol/sdk": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.13.1.tgz", + "integrity": "sha512-6byvu+F/xc96GBkdAx4hq6/tB3vT63DSBO4i3gYCz8nuyZMerVFna2Gkhm8EHNpZX0J9DjUxzZCW+rnHXUg0FA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.71.2", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.71.2.tgz", + "integrity": "sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock": { + "version": "3.988.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock/-/client-bedrock-3.988.0.tgz", + "integrity": "sha512-VQt+dHwg2SRCms9gN6MCV70ELWcoJ+cAJuvHiCAHVHUw822XdRL9OneaKTKO4Z1nU9FDpjLlUt5W9htSeiXyoQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/credential-provider-node": "^3.972.7", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.8", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/token-providers": "3.988.0", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.988.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.6", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.23.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.14", + "@smithy/middleware-retry": "^4.4.31", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.3", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.30", + "@smithy/util-defaults-mode-node": "^4.2.33", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.988.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.988.0.tgz", + "integrity": "sha512-NZlsQ8rjmAG0zRteqEiRakV97/nToIwDqT0zbye+j+HN60wiRSESAFCEozdwiiuVr0xl69NcoTiMg64xbh2I9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/credential-provider-node": "^3.972.7", + "@aws-sdk/eventstream-handler-node": "^3.972.5", + "@aws-sdk/middleware-eventstream": "^3.972.3", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.8", + "@aws-sdk/middleware-websocket": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/token-providers": "3.988.0", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.988.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.6", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.23.0", + "@smithy/eventstream-serde-browser": "^4.2.8", + "@smithy/eventstream-serde-config-resolver": "^4.3.8", + "@smithy/eventstream-serde-node": "^4.2.8", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.14", + "@smithy/middleware-retry": "^4.4.31", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.3", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.30", + "@smithy/util-defaults-mode-node": "^4.2.33", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-stream": "^4.5.12", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.988.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.988.0.tgz", + "integrity": "sha512-ThqQ7aF1k0Zz4yJRwegHw+T1rM3a7ZPvvEUSEdvn5Z8zTeWgJAbtqW/6ejPsMLmFOlHgNcwDQN/e69OvtEOoIQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.8", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.988.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.6", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.23.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.14", + "@smithy/middleware-retry": "^4.4.31", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.3", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.30", + "@smithy/util-defaults-mode-node": "^4.2.33", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.973.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.8.tgz", + "integrity": "sha512-WeYJ2sfvRLbbUIrjGMUXcEHGu5SJk53jz3K9F8vFP42zWyROzPJ2NB6lMu9vWl5hnMwzwabX7pJc9Euh3JyMGw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/xml-builder": "^3.972.4", + "@smithy/core": "^3.23.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/smithy-client": "^4.11.3", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.6.tgz", + "integrity": "sha512-+dYEBWgTqkQQHFUllvBL8SLyXyLKWdxLMD1LmKJRvmb0NMJuaJFG/qg78C+LE67eeGbipYcE+gJ48VlLBGHlMw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.8.tgz", + "integrity": "sha512-z3QkozMV8kOFisN2pgRag/f0zPDrw96mY+ejAM0xssV/+YQ2kklbylRNI/TcTQUDnGg0yPxNjyV6F2EM2zPTwg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/types": "^3.973.1", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.3", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.12", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.6.tgz", + "integrity": "sha512-6tkIYFv3sZH1XsjQq+veOmx8XWRnyqTZ5zx/sMtdu/xFRIzrJM1Y2wAXeCJL1rhYSB7uJSZ1PgALI2WVTj78ow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/credential-provider-env": "^3.972.6", + "@aws-sdk/credential-provider-http": "^3.972.8", + "@aws-sdk/credential-provider-login": "^3.972.6", + "@aws-sdk/credential-provider-process": "^3.972.6", + "@aws-sdk/credential-provider-sso": "^3.972.6", + "@aws-sdk/credential-provider-web-identity": "^3.972.6", + "@aws-sdk/nested-clients": "3.988.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.6.tgz", + "integrity": "sha512-LXsoBoaTSGHdRCQXlWSA0CHHh05KWncb592h9ElklnPus++8kYn1Ic6acBR4LKFQ0RjjMVgwe5ypUpmTSUOjPA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/nested-clients": "3.988.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.7.tgz", + "integrity": "sha512-PuJ1IkISG7ZDpBFYpGotaay6dYtmriBYuHJ/Oko4VHxh8YN5vfoWnMNYFEWuzOfyLmP7o9kDVW0BlYIpb3skvw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.6", + "@aws-sdk/credential-provider-http": "^3.972.8", + "@aws-sdk/credential-provider-ini": "^3.972.6", + "@aws-sdk/credential-provider-process": "^3.972.6", + "@aws-sdk/credential-provider-sso": "^3.972.6", + "@aws-sdk/credential-provider-web-identity": "^3.972.6", + "@aws-sdk/types": "^3.973.1", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.6.tgz", + "integrity": "sha512-Yf34cjIZJHVnD92jnVYy3tNjM+Q4WJtffLK2Ehn0nKpZfqd1m7SI0ra22Lym4C53ED76oZENVSS2wimoXJtChQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.6.tgz", + "integrity": "sha512-2+5UVwUYdD4BBOkLpKJ11MQ8wQeyJGDVMDRH5eWOULAh9d6HJq07R69M/mNNMC9NTjr3mB1T0KGDn4qyQh5jzg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.988.0", + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/token-providers": "3.988.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.6.tgz", + "integrity": "sha512-pdJzwKtlDxBnvZ04pWMqttijmkUIlwOsS0GcxCjzEVyUMpARysl0S0ks74+gs2Pdev3Ujz+BTAjOc1tQgAxGqA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/nested-clients": "3.988.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-handler-node": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.5.tgz", + "integrity": "sha512-xEmd3dnyn83K6t4AJxBJA63wpEoCD45ERFG0XMTViD2E/Ohls9TLxjOWPb1PAxR9/46cKy/TImez1GoqP6xVNQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/eventstream-codec": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-eventstream": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.3.tgz", + "integrity": "sha512-pbvZ6Ye/Ks6BAZPa3RhsNjHrvxU9li25PMhSdDpbX0jzdpKpAkIR65gXSNKmA/REnSdEMWSD4vKUW+5eMFzB6w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.3.tgz", + "integrity": "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.3.tgz", + "integrity": "sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.3.tgz", + "integrity": "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.8.tgz", + "integrity": "sha512-3PGL+Kvh1PhB0EeJeqNqOWQgipdqFheO4OUKc6aYiFwEpM5t9AyE5hjjxZ5X6iSj8JiduWFZLPwASzF6wQRgFg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.988.0", + "@smithy/core": "^3.23.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-websocket": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.6.tgz", + "integrity": "sha512-1DedO6N3m8zQ/vG6twNiHtsdwBgk773VdavLEbB3NXeKZDlzSK1BTviqWwvJdKx5UnIy4kGGP6WWpCEFEt/bhQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-format-url": "^3.972.3", + "@smithy/eventstream-codec": "^4.2.8", + "@smithy/eventstream-serde-browser": "^4.2.8", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.988.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.988.0.tgz", + "integrity": "sha512-OgYV9k1oBCQ6dOM+wWAMNNehXA8L4iwr7ydFV+JDHyuuu0Ko7tDXnLEtEmeQGYRcAFU3MGasmlBkMB8vf4POrg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.8", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.988.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.6", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.23.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.14", + "@smithy/middleware-retry": "^4.4.31", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.3", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.30", + "@smithy/util-defaults-mode-node": "^4.2.33", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.3.tgz", + "integrity": "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/config-resolver": "^4.4.6", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.988.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.988.0.tgz", + "integrity": "sha512-xvXVlRVKHnF2h6fgWBm64aPP5J+58aJyGfRrQa/uFh8a9mcK68mLfJOYq+ZSxQy/UN3McafJ2ILAy7IWzT9kRw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.8", + "@aws-sdk/nested-clients": "3.988.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.973.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.1.tgz", + "integrity": "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.988.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.988.0.tgz", + "integrity": "sha512-HuXu4boeUWU0DQiLslbgdvuQ4ZMCo4Lsk97w8BIUokql2o9MvjE5dwqI5pzGt0K7afO1FybjidUQVTMLuZNTOA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.972.3.tgz", + "integrity": "sha512-n7F2ycckcKFXa01vAsT/SJdjFHfKH9s96QHcs5gn8AaaigASICeME8WdUL9uBp8XV/OVwEt8+6gzn6KFUgQa8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.4.tgz", + "integrity": "sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.3.tgz", + "integrity": "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.6.tgz", + "integrity": "sha512-966xH8TPqkqOXP7EwnEThcKKz0SNP9kVJBKd9M8bNXE4GSqVouMKKnFBwYnzbWVKuLXubzX5seokcX4a0JLJIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "^3.972.8", + "@aws-sdk/types": "^3.973.1", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.4.tgz", + "integrity": "sha512-0zJ05ANfYqI6+rGqj8samZBFod0dPPousBjLEqg8WdxSgbMAkRgLyn81lP215Do0rFJ/17LIXwr7q0yK24mP6Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "fast-xml-parser": "5.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", + "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@borewit/text-codec": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.1.tgz", + "integrity": "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@buape/carbon": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@buape/carbon/-/carbon-0.14.0.tgz", + "integrity": "sha512-mavllPK2iVpRNRtC4C8JOUdJ1hdV0+LDelFW+pjpJaM31MBLMfIJ+f/LlYTIK5QrEcQsXOC+6lU2e0gmgjWhIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^25.0.9", + "discord-api-types": "0.38.37" + }, + "optionalDependencies": { + "@cloudflare/workers-types": "4.20260120.0", + "@discordjs/voice": "0.19.0", + "@hono/node-server": "1.19.9", + "@types/bun": "1.3.6", + "@types/ws": "8.18.1", + "ws": "8.19.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==", + "dev": true, + "license": "MIT", + "workspaces": [ + "scripts/actions/documentation" + ] + }, + "node_modules/@cacheable/memory": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.7.tgz", + "integrity": "sha512-RbxnxAMf89Tp1dLhXMS7ceft/PGsDl1Ip7T20z5nZ+pwIAsQ1p2izPjVG69oCLv/jfQ7HDPHTWK0c9rcAWXN3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.3.3", + "@keyv/bigmap": "^1.3.0", + "hookified": "^1.14.0", + "keyv": "^5.5.5" + } + }, + "node_modules/@cacheable/node-cache": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@cacheable/node-cache/-/node-cache-1.7.6.tgz", + "integrity": "sha512-6Omk2SgNnjtxB5f/E6bTIWIt5xhdpx39fGNRQgU9lojvRxU68v+qY+SXXLsp3ZGukqoPjsK21wZ6XABFr/Ge3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cacheable": "^2.3.1", + "hookified": "^1.14.0", + "keyv": "^5.5.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.3.4.tgz", + "integrity": "sha512-knwKUJEYgIfwShABS1BX6JyJJTglAFcEU7EXqzTdiGCXur4voqkiJkdgZIQtWNFhynzDWERcTYv/sETMu3uJWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.3.0", + "keyv": "^5.6.0" + } + }, + "node_modules/@clack/core": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.5.0.tgz", + "integrity": "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "sisteransi": "^1.0.5" + } + }, + "node_modules/@clack/prompts": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.11.0.tgz", + "integrity": "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@clack/core": "0.5.0", + "picocolors": "^1.0.0", + "sisteransi": "^1.0.5" + } + }, + "node_modules/@cloudflare/workers-types": { + "version": "4.20260120.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260120.0.tgz", + "integrity": "sha512-B8pueG+a5S+mdK3z8oKu1ShcxloZ7qWb68IEyLLaepvdryIbNC7JVPcY0bWsjS56UQVKc5fnyRge3yZIwc9bxw==", + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true + }, + "node_modules/@discordjs/voice": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.19.0.tgz", + "integrity": "sha512-UyX6rGEXzVyPzb1yvjHtPfTlnLvB5jX/stAMdiytHhfoydX+98hfympdOwsnTktzr+IRvphxTbdErgYDJkEsvw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@types/ws": "^8.18.1", + "discord-api-types": "^0.38.16", + "prism-media": "^1.3.5", + "tslib": "^2.8.1", + "ws": "^8.18.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@google/genai": { + "version": "1.34.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.34.0.tgz", + "integrity": "sha512-vu53UMPvjmb7PGzlYu6Tzxso8Dfhn+a7eQFaS2uNemVtDZKwzSpJ5+ikqBbXplF7RGB1STcVDqCkPvquiwb2sw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.24.0" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@grammyjs/runner": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@grammyjs/runner/-/runner-2.0.3.tgz", + "integrity": "sha512-nckmTs1dPWfVQteK9cxqxzE+0m1VRvluLWB8UgFzsjg62w3qthPJt0TYtJBEdG7OedvfQq4vnFAyE6iaMkR42A==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0" + }, + "engines": { + "node": ">=12.20.0 || >=14.13.1" + }, + "peerDependencies": { + "grammy": "^1.13.1" + } + }, + "node_modules/@grammyjs/transformer-throttler": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@grammyjs/transformer-throttler/-/transformer-throttler-1.2.1.tgz", + "integrity": "sha512-CpWB0F3rJdUiKsq7826QhQsxbZi4wqfz1ccKX+fr+AOC+o8K7ZvS+wqX0suSu1QCsyUq2MDpNiKhyL2ZOJUS4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bottleneck": "^2.0.0" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + }, + "peerDependencies": { + "grammy": "^1.0.0" + } + }, + "node_modules/@grammyjs/types": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/@grammyjs/types/-/types-3.24.0.tgz", + "integrity": "sha512-qQIEs4lN5WqUdr4aT8MeU6UFpMbGYAvcvYSW1A4OO1PABGJQHz/KLON6qvpf+5RxaNDQBxiY2k2otIhg/AG7RQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@hapi/boom": { + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.4.tgz", + "integrity": "sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "9.x.x" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@homebridge/ciao": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.3.5.tgz", + "integrity": "sha512-f7MAw7YuoEYgJEQ1VyRcLHGuVmCpmXi65GVR8CAtPWPqIZf/HFr4vHzVpOfQMpEQw9Pt5uh07guuLt5HE8ruog==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "fast-deep-equal": "^3.1.3", + "source-map-support": "^0.5.21", + "tslib": "^2.8.1" + }, + "bin": { + "ciao-bcs": "lib/bonjour-conformance-testing.js" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@huggingface/jinja": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.5.tgz", + "integrity": "sha512-xRlzazC+QZwr6z4ixEqYHo9fgwhTZ3xNSdljlKfUFGZSdlvt166DljRELFUfFytlYOYvo3vTisA/AFOuOAzFQQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "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" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "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" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "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" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", + "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@larksuiteoapi/node-sdk": { + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@larksuiteoapi/node-sdk/-/node-sdk-1.59.0.tgz", + "integrity": "sha512-sBpkruTvZDOxnVtoTbepWKRX0j1Y1ZElQYu0x7+v088sI9pcpbVp6ZzCGn62dhrKPatzNyCJyzYCPXPYQWccrA==", + "license": "MIT", + "dependencies": { + "axios": "~1.13.3", + "lodash.identity": "^3.0.0", + "lodash.merge": "^4.6.2", + "lodash.pickby": "^4.6.0", + "protobufjs": "^7.2.6", + "qs": "^6.14.2", + "ws": "^8.19.0" + } + }, + "node_modules/@line/bot-sdk": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/@line/bot-sdk/-/bot-sdk-10.6.0.tgz", + "integrity": "sha512-4hSpglL/G/cW2JCcohaYz/BS0uOSJNV9IEYdMm0EiPEvDLayoI2hGq2D86uYPQFD2gvgkyhmzdShpWLG3P5r3w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^24.0.0" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "axios": "^1.7.4" + } + }, + "node_modules/@line/bot-sdk/node_modules/@types/node": { + "version": "24.10.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.13.tgz", + "integrity": "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@lydell/node-pty": { + "version": "1.2.0-beta.3", + "resolved": "https://registry.npmjs.org/@lydell/node-pty/-/node-pty-1.2.0-beta.3.tgz", + "integrity": "sha512-ngGAItlRhmJXrhspxt8kX13n1dVFqzETOq0m/+gqSkO8NJBvNMwP7FZckMwps2UFySdr4yxCXNGu/bumg5at6A==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "@lydell/node-pty-darwin-arm64": "1.2.0-beta.3", + "@lydell/node-pty-darwin-x64": "1.2.0-beta.3", + "@lydell/node-pty-linux-arm64": "1.2.0-beta.3", + "@lydell/node-pty-linux-x64": "1.2.0-beta.3", + "@lydell/node-pty-win32-arm64": "1.2.0-beta.3", + "@lydell/node-pty-win32-x64": "1.2.0-beta.3" + } + }, + "node_modules/@lydell/node-pty-darwin-arm64": { + "version": "1.2.0-beta.3", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lydell/node-pty-darwin-x64": { + "version": "1.2.0-beta.3", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lydell/node-pty-linux-arm64": { + "version": "1.2.0-beta.3", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lydell/node-pty-linux-x64": { + "version": "1.2.0-beta.3", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lydell/node-pty-win32-arm64": { + "version": "1.2.0-beta.3", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@lydell/node-pty-win32-x64": { + "version": "1.2.0-beta.3", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@mariozechner/clipboard": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.2.tgz", + "integrity": "sha512-IHQpksNjo7EAtGuHFU+tbWDp5LarH3HU/8WiB9O70ZEoBPHOg0/6afwSLK0QyNMMmx4Bpi/zl6+DcBXe95nWYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@mariozechner/clipboard-darwin-arm64": "0.3.2", + "@mariozechner/clipboard-darwin-universal": "0.3.2", + "@mariozechner/clipboard-darwin-x64": "0.3.2", + "@mariozechner/clipboard-linux-arm64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-arm64-musl": "0.3.2", + "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-x64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-x64-musl": "0.3.2", + "@mariozechner/clipboard-win32-arm64-msvc": "0.3.2", + "@mariozechner/clipboard-win32-x64-msvc": "0.3.2" + } + }, + "node_modules/@mariozechner/clipboard-darwin-arm64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-arm64/-/clipboard-darwin-arm64-0.3.2.tgz", + "integrity": "sha512-uBf6K7Je1ihsgvmWxA8UCGCeI+nbRVRXoarZdLjl6slz94Zs1tNKFZqx7aCI5O1i3e0B6ja82zZ06BWrl0MCVw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-darwin-universal": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.2.tgz", + "integrity": "sha512-mxSheKTW2U9LsBdXy0SdmdCAE5HqNS9QUmpNHLnfJ+SsbFKALjEZc5oRrVMXxGQSirDvYf5bjmRyT0QYYonnlg==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-darwin-x64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-x64/-/clipboard-darwin-x64-0.3.2.tgz", + "integrity": "sha512-U1BcVEoidvwIp95+HJswSW+xr28EQiHR7rZjH6pn8Sja5yO4Yoe3yCN0Zm8Lo72BbSOK/fTSq0je7CJpaPCspg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-arm64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-gnu/-/clipboard-linux-arm64-gnu-0.3.2.tgz", + "integrity": "sha512-BsinwG3yWTIjdgNCxsFlip7LkfwPk+ruw/aFCXHUg/fb5XC/Ksp+YMQ7u0LUtiKzIv/7LMXgZInJQH6gxbAaqQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-arm64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-musl/-/clipboard-linux-arm64-musl-0.3.2.tgz", + "integrity": "sha512-0/Gi5Xq2V6goXBop19ePoHvXsmJD9SzFlO3S+d6+T2b+BlPcpOu3Oa0wTjl+cZrLAAEzA86aPNBI+VVAFDFPKw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-riscv64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-riscv64-gnu/-/clipboard-linux-riscv64-gnu-0.3.2.tgz", + "integrity": "sha512-2AFFiXB24qf0zOZsxI1GJGb9wQGlOJyN6UwoXqmKS3dpQi/l6ix30IzDDA4c4ZcCcx4D+9HLYXhC1w7Sov8pXA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-x64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.2.tgz", + "integrity": "sha512-v6fVnsn7WMGg73Dab8QMwyFce7tzGfgEixKgzLP8f1GJqkJZi5zO4k4FOHzSgUufgLil63gnxvMpjWkgfeQN7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-x64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.2.tgz", + "integrity": "sha512-xVUtnoMQ8v2JVyfJLKKXACA6avdnchdbBkTsZs8BgJQo29qwCp5NIHAUO8gbJ40iaEGToW5RlmVk2M9V0HsHEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-win32-arm64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-arm64-msvc/-/clipboard-win32-arm64-msvc-0.3.2.tgz", + "integrity": "sha512-AEgg95TNi8TGgak2wSXZkXKCvAUTjWoU1Pqb0ON7JHrX78p616XUFNTJohtIon3e0w6k0pYPZeCuqRCza/Tqeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-win32-x64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-x64-msvc/-/clipboard-win32-x64-msvc-0.3.2.tgz", + "integrity": "sha512-tGRuYpZwDOD7HBrCpyRuhGnHHSCknELvqwKKUG4JSfSB7JIU7LKRh6zx6fMUOQd8uISK35TjFg5UcNih+vJhFA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/jiti": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@mariozechner/jiti/-/jiti-2.6.5.tgz", + "integrity": "sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "std-env": "^3.10.0", + "yoctocolors": "^2.1.2" + }, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/@mariozechner/pi-agent-core": { + "version": "0.49.3", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.49.3.tgz", + "integrity": "sha512-YL3PrLA8//Cklx58GJBUyNBCVLIOtK+wpAgqimuR03EgToaGPkSM7B/1S4CP4pFkr7b3DTIsC6t++tK7LgfjQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mariozechner/pi-ai": "^0.49.3", + "@mariozechner/pi-tui": "^0.49.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-ai": { + "version": "0.49.3", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.49.3.tgz", + "integrity": "sha512-FYck4TPrF7ps3WBKxLnBQdda9OXUWN6rukni0LgK8m/GpMAXGienHouDrWPn0XIgTwrz5r7SGI3sfsEYslCICA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "0.71.2", + "@aws-sdk/client-bedrock-runtime": "^3.966.0", + "@google/genai": "1.34.0", + "@mistralai/mistralai": "1.10.0", + "@sinclair/typebox": "^0.34.41", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "chalk": "^5.6.2", + "openai": "6.10.0", + "partial-json": "^0.1.7", + "zod-to-json-schema": "^3.24.6" + }, + "bin": { + "pi-ai": "dist/cli.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-coding-agent": { + "version": "0.49.3", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-coding-agent/-/pi-coding-agent-0.49.3.tgz", + "integrity": "sha512-V/Fsq0PeYB5svmw5lZsbD/glmkXofegQcPSKecK2ab3VihKwQp/MQfYjyK6nY4b/B1HIugatcDWaH5WvPKIKwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mariozechner/clipboard": "^0.3.0", + "@mariozechner/jiti": "^2.6.2", + "@mariozechner/pi-agent-core": "^0.49.3", + "@mariozechner/pi-ai": "^0.49.3", + "@mariozechner/pi-tui": "^0.49.3", + "@silvia-odwyer/photon-node": "^0.3.4", + "chalk": "^5.5.0", + "cli-highlight": "^2.1.11", + "diff": "^8.0.2", + "file-type": "^21.1.1", + "glob": "^11.0.3", + "marked": "^15.0.12", + "minimatch": "^10.1.1", + "proper-lockfile": "^4.1.2", + "yaml": "^2.8.2" + }, + "bin": { + "pi": "dist/cli.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-tui": { + "version": "0.49.3", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.49.3.tgz", + "integrity": "sha512-SyBtQ0B9A/8V4eX7z3l9Q7sEVAnSueNJ1gC6+nRakDBfBOSxuqA61vz6tic0C7Jj46NERRuvJKdQSmk1VP5sUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime-types": "^2.1.4", + "chalk": "^5.5.0", + "get-east-asian-width": "^1.3.0", + "marked": "^15.0.12", + "mime-types": "^3.0.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-tui/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mariozechner/pi-tui/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@mistralai/mistralai": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.10.0.tgz", + "integrity": "sha512-tdIgWs4Le8vpvPiUEWne6tK0qbVc+jMenujnvTqOjogrJUsCSQhus0tHTU1avDDh5//Rq2dFgP9mWRAdIEoBqg==", + "dev": true, + "dependencies": { + "zod": "^3.20.0", + "zod-to-json-schema": "^3.24.1" + } + }, + "node_modules/@mistralai/mistralai/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@mozilla/readability": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.6.0.tgz", + "integrity": "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@napi-rs/canvas": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.92.tgz", + "integrity": "sha512-q7ZaUCJkEU5BeOdE7fBx1XWRd2T5Ady65nxq4brMf5L4cE1VV/ACq5w9Z5b/IVJs8CwSSIwc30nlthH0gFo4Ig==", + "dev": true, + "license": "MIT", + "optional": true, + "workspaces": [ + "e2e/*" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.92", + "@napi-rs/canvas-darwin-arm64": "0.1.92", + "@napi-rs/canvas-darwin-x64": "0.1.92", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.92", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.92", + "@napi-rs/canvas-linux-arm64-musl": "0.1.92", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.92", + "@napi-rs/canvas-linux-x64-gnu": "0.1.92", + "@napi-rs/canvas-linux-x64-musl": "0.1.92", + "@napi-rs/canvas-win32-arm64-msvc": "0.1.92", + "@napi-rs/canvas-win32-x64-msvc": "0.1.92" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.92.tgz", + "integrity": "sha512-rDOtq53ujfOuevD5taxAuIFALuf1QsQWZe1yS/N4MtT+tNiDBEdjufvQRPWZ11FubL2uwgP8ApYU3YOaNu1ZsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.92.tgz", + "integrity": "sha512-4PT6GRGCr7yMRehp42x0LJb1V0IEy1cDZDDayv7eKbFUIGbPFkV7CRC9Bee5MPkjg1EB4ZPXXUyy3gjQm7mR8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.92.tgz", + "integrity": "sha512-5e/3ZapP7CqPtDcZPtmowCsjoyQwuNMMD7c0GKPtZQ8pgQhLkeq/3fmk0HqNSD1i227FyJN/9pDrhw/UMTkaWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.92.tgz", + "integrity": "sha512-j6KaLL9iir68lwpzzY+aBGag1PZp3+gJE2mQ3ar4VJVmyLRVOh+1qsdNK1gfWoAVy5w6U7OEYFrLzN2vOFUSng==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.92.tgz", + "integrity": "sha512-s3NlnJMHOSotUYVoTCoC1OcomaChFdKmZg0VsHFeIkeHbwX0uPHP4eCX1irjSfMykyvsGHTQDfBAtGYuqxCxhQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.92.tgz", + "integrity": "sha512-xV0GQnukYq5qY+ebkAwHjnP2OrSGBxS3vSi1zQNQj0bkXU6Ou+Tw7JjCM7pZcQ28MUyEBS1yKfo7rc7ip2IPFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.92.tgz", + "integrity": "sha512-+GKvIFbQ74eB/TopEdH6XIXcvOGcuKvCITLGXy7WLJAyNp3Kdn1ncjxg91ihatBaPR+t63QOE99yHuIWn3UQ9w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.92.tgz", + "integrity": "sha512-tFd6MwbEhZ1g64iVY2asV+dOJC+GT3Yd6UH4G3Hp0/VHQ6qikB+nvXEULskFYZ0+wFqlGPtXjG1Jmv7sJy+3Ww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.92.tgz", + "integrity": "sha512-uSuqeSveB/ZGd72VfNbHCSXO9sArpZTvznMVsb42nqPP7gBGEH6NJQ0+hmF+w24unEmxBhPYakP/Wiosm16KkA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-arm64-msvc": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.92.tgz", + "integrity": "sha512-20SK5AU/OUNz9ZuoAPj5ekWai45EIBDh/XsdrVZ8le/pJVlhjFU3olbumSQUXRFn7lBRS+qwM8kA//uLaDx6iQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.92.tgz", + "integrity": "sha512-KEhyZLzq1MXCNlXybz4k25MJmHFp+uK1SIb8yJB0xfrQjz5aogAMhyseSzewo+XxAq3OAOdyKvfHGNzT3w1RPg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@node-llama-cpp/linux-arm64": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-arm64/-/linux-arm64-3.15.0.tgz", + "integrity": "sha512-IaHIllWlj6tGjhhCtyp1w6xA7AHaGJiVaXAZ+78hDs8X1SL9ySBN2Qceju8AQJALePtynbAfjgjTqjQ7Hyk+IQ==", + "cpu": [ + "arm64", + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/linux-armv7l": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-armv7l/-/linux-armv7l-3.15.0.tgz", + "integrity": "sha512-ZuZ3q6mejQnEP4o22la7zBv7jNR+IZfgItDm3KjAl04HUXTKJ43HpNwjnf9GyYYd+dEgtoX0MESvWz4RnGH8Jw==", + "cpu": [ + "arm", + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/linux-x64": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64/-/linux-x64-3.15.0.tgz", + "integrity": "sha512-etUuTqSyNefRObqc5+JZviNTkuef2XEtHcQLaamEIWwjI1dj7nTD2YMZPBP7H3M3E55HSIY82vqCQ1bp6ZILiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/linux-x64-cuda": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64-cuda/-/linux-x64-cuda-3.15.0.tgz", + "integrity": "sha512-mDjyVulCTRYilm9Emm3lDMx7dbI1vzGqk28Pj28shartjERTUu8aUNDYOmVKNMLpUKS1akw7vy0lMF8t4qswxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/linux-x64-cuda-ext": { + "version": "3.15.0", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/linux-x64-vulkan": { + "version": "3.15.0", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/mac-arm64-metal": { + "version": "3.15.0", + "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", + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/mac-x64": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/mac-x64/-/mac-x64-3.15.0.tgz", + "integrity": "sha512-BUrmLu0ySveEYv2YzFIjqnWWAqjTZfRHuzoFLaZwqIJ86Jzycm9tzxJub4wfJCj6ixeuWyI1sUdNGIw4/2E03Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/win-arm64": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-arm64/-/win-arm64-3.15.0.tgz", + "integrity": "sha512-GwhqaPNpbtGDmw0Ex13hwq4jqzSZr7hw5QpRWhSKB1dHiYj6C1NLM1Vz5xiDZX+69WI/ndb+FEqGiIYFQpfmiQ==", + "cpu": [ + "arm64", + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/win-x64": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64/-/win-x64-3.15.0.tgz", + "integrity": "sha512-gWhtc8l3HOry5guO46YfFohLQnF0NfL4On0GAO8E27JiYYxHO9nHSCfFif4+U03+FfHquZXL0znJ1qPVOiwOPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/win-x64-cuda": { + "version": "3.15.0", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/win-x64-cuda-ext": { + "version": "3.15.0", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/win-x64-vulkan": { + "version": "3.15.0", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@octokit/app": { + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/@octokit/app/-/app-16.1.2.tgz", + "integrity": "sha512-8j7sEpUYVj18dxvh0KWj6W/l6uAiVRBl1JBDVRqH1VHKAO/G5eRVl4yEoYACjakWers1DjUkcCHyJNQK47JqyQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/auth-app": "^8.1.2", + "@octokit/auth-unauthenticated": "^7.0.3", + "@octokit/core": "^7.0.6", + "@octokit/oauth-app": "^8.0.3", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/types": "^16.0.0", + "@octokit/webhooks": "^14.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-app": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-8.2.0.tgz", + "integrity": "sha512-vVjdtQQwomrZ4V46B9LaCsxsySxGoHsyw6IYBov/TqJVROrlYdyNgw5q6tQbB7KZt53v1l1W53RiqTvpzL907g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/auth-oauth-app": "^9.0.3", + "@octokit/auth-oauth-user": "^6.0.2", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "toad-cache": "^3.7.0", + "universal-github-app-jwt": "^2.2.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-oauth-app": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-9.0.3.tgz", + "integrity": "sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/auth-oauth-device": "^8.0.3", + "@octokit/auth-oauth-user": "^6.0.2", + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-oauth-device": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-8.0.3.tgz", + "integrity": "sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/oauth-methods": "^6.0.2", + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-oauth-user": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-6.0.2.tgz", + "integrity": "sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/auth-oauth-device": "^8.0.3", + "@octokit/oauth-methods": "^6.0.2", + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-unauthenticated": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-7.0.3.tgz", + "integrity": "sha512-8Jb1mtUdmBHL7lGmop9mU9ArMRUTRhg8vp0T1VtZ4yd9vEm3zcLwmjQkhNEduKawOOORie61xhtYIhTDN+ZQ3g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0" + }, + "engines": { + "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==", + "dev": true, + "license": "MIT", + "optional": 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://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz", + "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/graphql": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/oauth-app": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-8.0.3.tgz", + "integrity": "sha512-jnAjvTsPepyUaMu9e69hYBuozEPgYqP4Z3UnpmvoIzHDpf8EXDGvTY1l1jK0RsZ194oRd+k6Hm13oRU8EoDFwg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/auth-oauth-app": "^9.0.2", + "@octokit/auth-oauth-user": "^6.0.1", + "@octokit/auth-unauthenticated": "^7.0.2", + "@octokit/core": "^7.0.5", + "@octokit/oauth-authorization-url": "^8.0.0", + "@octokit/oauth-methods": "^6.0.1", + "@types/aws-lambda": "^8.10.83", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/oauth-authorization-url": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-8.0.0.tgz", + "integrity": "sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/oauth-methods": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-6.0.2.tgz", + "integrity": "sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/oauth-authorization-url": "^8.0.0", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@octokit/openapi-webhooks-types": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-12.1.0.tgz", + "integrity": "sha512-WiuzhOsiOvb7W3Pvmhf8d2C6qaLHXrWiLBP4nJ/4kydu+wpagV5Fkz9RfQwV2afYzv3PB+3xYgp4mAdNGjDprA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@octokit/plugin-paginate-graphql": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-6.0.0.tgz", + "integrity": "sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "17.0.0", + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.0.3.tgz", + "integrity": "sha512-vKGx1i3MC0za53IzYBSBXcrhmd+daQDzuZfYDd52X5S0M2otf3kVZTVP8bLA3EkU0lTvd1WEC2OlNNa4G+dohA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=7" + } + }, + "node_modules/@octokit/plugin-throttling": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-11.0.3.tgz", + "integrity": "sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/types": "^16.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": "^7.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz", + "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/endpoint": "^11.0.2", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "fast-content-type-parse": "^3.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/request-error": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/types": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/openapi-types": "^27.0.0" + } + }, + "node_modules/@octokit/webhooks": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-14.2.0.tgz", + "integrity": "sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/openapi-webhooks-types": "12.1.0", + "@octokit/request-error": "^7.0.0", + "@octokit/webhooks-methods": "^6.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/webhooks-methods": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-6.0.0.tgz", + "integrity": "sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@reflink/reflink": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink/-/reflink-0.1.19.tgz", + "integrity": "sha512-DmCG8GzysnCZ15bres3N5AHCmwBwYgp0As6xjhQ47rAUTUXxJiK+lLUxaGsX3hd/30qUpVElh05PbGuxRPgJwA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@reflink/reflink-darwin-arm64": "0.1.19", + "@reflink/reflink-darwin-x64": "0.1.19", + "@reflink/reflink-linux-arm64-gnu": "0.1.19", + "@reflink/reflink-linux-arm64-musl": "0.1.19", + "@reflink/reflink-linux-x64-gnu": "0.1.19", + "@reflink/reflink-linux-x64-musl": "0.1.19", + "@reflink/reflink-win32-arm64-msvc": "0.1.19", + "@reflink/reflink-win32-x64-msvc": "0.1.19" + } + }, + "node_modules/@reflink/reflink-darwin-arm64": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink-darwin-arm64/-/reflink-darwin-arm64-0.1.19.tgz", + "integrity": "sha512-ruy44Lpepdk1FqDz38vExBY/PVUsjxZA+chd9wozjUH9JjuDT/HEaQYA6wYN9mf041l0yLVar6BCZuWABJvHSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-darwin-x64": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink-darwin-x64/-/reflink-darwin-x64-0.1.19.tgz", + "integrity": "sha512-By85MSWrMZa+c26TcnAy8SDk0sTUkYlNnwknSchkhHpGXOtjNDUOxJE9oByBnGbeuIE1PiQsxDG3Ud+IVV9yuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-arm64-gnu": { + "version": "0.1.19", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-arm64-musl": { + "version": "0.1.19", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-x64-gnu": { + "version": "0.1.19", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-x64-musl": { + "version": "0.1.19", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-win32-arm64-msvc": { + "version": "0.1.19", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-win32-x64-msvc": { + "version": "0.1.19", + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@silvia-odwyer/photon-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", + "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", + "license": "MIT" + }, + "node_modules/@slack/bolt": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@slack/bolt/-/bolt-4.6.0.tgz", + "integrity": "sha512-xPgfUs2+OXSugz54Ky07pA890+Qydk22SYToi8uGpXeHSt1JWwFJkRyd/9Vlg5I1AdfdpGXExDpwnbuN9Q/2dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@slack/logger": "^4.0.0", + "@slack/oauth": "^3.0.4", + "@slack/socket-mode": "^2.0.5", + "@slack/types": "^2.18.0", + "@slack/web-api": "^7.12.0", + "axios": "^1.12.0", + "express": "^5.0.0", + "path-to-regexp": "^8.1.0", + "raw-body": "^3", + "tsscmp": "^1.0.6" + }, + "engines": { + "node": ">=18", + "npm": ">=8.6.0" + }, + "peerDependencies": { + "@types/express": "^5.0.0" + } + }, + "node_modules/@slack/logger": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-4.0.0.tgz", + "integrity": "sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": ">=18.0.0" + }, + "engines": { + "node": ">= 18", + "npm": ">= 8.6.0" + } + }, + "node_modules/@slack/oauth": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@slack/oauth/-/oauth-3.0.4.tgz", + "integrity": "sha512-+8H0g7mbrHndEUbYCP7uYyBCbwqmm3E6Mo3nfsDvZZW74zKk1ochfH/fWSvGInYNCVvaBUbg3RZBbTp0j8yJCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@slack/logger": "^4", + "@slack/web-api": "^7.10.0", + "@types/jsonwebtoken": "^9", + "@types/node": ">=18", + "jsonwebtoken": "^9" + }, + "engines": { + "node": ">=18", + "npm": ">=8.6.0" + } + }, + "node_modules/@slack/socket-mode": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@slack/socket-mode/-/socket-mode-2.0.5.tgz", + "integrity": "sha512-VaapvmrAifeFLAFaDPfGhEwwunTKsI6bQhYzxRXw7BSujZUae5sANO76WqlVsLXuhVtCVrBWPiS2snAQR2RHJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@slack/logger": "^4", + "@slack/web-api": "^7.10.0", + "@types/node": ">=18", + "@types/ws": "^8", + "eventemitter3": "^5", + "ws": "^8" + }, + "engines": { + "node": ">= 18", + "npm": ">= 8.6.0" + } + }, + "node_modules/@slack/types": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.20.0.tgz", + "integrity": "sha512-PVF6P6nxzDMrzPC8fSCsnwaI+kF8YfEpxf3MqXmdyjyWTYsZQURpkK7WWUWvP5QpH55pB7zyYL9Qem/xSgc5VA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0", + "npm": ">= 6.12.0" + } + }, + "node_modules/@slack/web-api": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-7.14.0.tgz", + "integrity": "sha512-VtMK63RmtMYXqTirsIjjPOP1GpK9Nws5rUr6myZK7N6ABdff84Z8KUfoBsJx0QBEL43ANSQr3ANZPjmeKBXUCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@slack/logger": "^4.0.0", + "@slack/types": "^2.20.0", + "@types/node": ">=18.0.0", + "@types/retry": "0.12.0", + "axios": "^1.11.0", + "eventemitter3": "^5.0.1", + "form-data": "^4.0.4", + "is-electron": "2.2.2", + "is-stream": "^2", + "p-queue": "^6", + "p-retry": "^4", + "retry": "^0.13.1" + }, + "engines": { + "node": ">= 18", + "npm": ">= 8.6.0" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz", + "integrity": "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz", + "integrity": "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.0.tgz", + "integrity": "sha512-Yq4UPVoQICM9zHnByLmG8632t2M0+yap4T7ANVw482J0W7HW0pOuxwVmeOwzJqX2Q89fkXz0Vybz55Wj2Xzrsg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.2.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.12", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz", + "integrity": "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.8.tgz", + "integrity": "sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.12.0", + "@smithy/util-hex-encoding": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.8.tgz", + "integrity": "sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.8", + "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==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.8.tgz", + "integrity": "sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.8.tgz", + "integrity": "sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz", + "integrity": "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.8.tgz", + "integrity": "sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz", + "integrity": "sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz", + "integrity": "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.14.tgz", + "integrity": "sha512-FUFNE5KVeaY6U/GL0nzAAHkaCHzXLZcY1EhtQnsAqhD8Du13oPKtMB9/0WK4/LK6a/T5OZ24wPoSShff5iI6Ag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.0", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.31", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.31.tgz", + "integrity": "sha512-RXBzLpMkIrxBPe4C8OmEOHvS8aH9RUuCOH++Acb5jZDEblxDjyg6un72X9IcbrGTJoiUwmI7hLypNfuDACypbg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/service-error-classification": "^4.2.8", + "@smithy/smithy-client": "^4.11.3", + "@smithy/types": "^4.12.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz", + "integrity": "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz", + "integrity": "sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz", + "integrity": "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.10.tgz", + "integrity": "sha512-u4YeUwOWRZaHbWaebvrs3UhwQwj+2VNmcVCwXcYTvPIuVyM7Ex1ftAj+fdbG/P4AkBwLq/+SKn+ydOI4ZJE9PA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz", + "integrity": "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz", + "integrity": "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz", + "integrity": "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz", + "integrity": "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz", + "integrity": "sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.3", + "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==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.8.tgz", + "integrity": "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.11.3.tgz", + "integrity": "sha512-Q7kY5sDau8OoE6Y9zJoRGgje8P4/UY0WzH8R2ok0PDh+iJ+ZnEKowhjEqYafVcubkbYxQVaqwm3iufktzhprGg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.0", + "@smithy/middleware-endpoint": "^4.4.14", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.12", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz", + "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz", + "integrity": "sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.30", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.30.tgz", + "integrity": "sha512-cMni0uVU27zxOiU8TuC8pQLC1pYeZ/xEMxvchSK/ILwleRd1ugobOcIRr5vXtcRqKd4aBLWlpeBoDPJJ91LQng==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.11.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.33", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.33.tgz", + "integrity": "sha512-LEb2aq5F4oZUSzWBG7S53d4UytZSkOEJPXcBq/xbG2/TmK9EW5naUZ8lKu1BEyWMzdHIzEVN16M3k8oxDq+DJA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.6", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.11.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz", + "integrity": "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "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==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz", + "integrity": "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz", + "integrity": "sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.12", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.12.tgz", + "integrity": "sha512-D8tgkrmhAX/UNeCZbqbEO3uqyghUnEmmoO9YEvRuwxjlkKKUE7FOgCJnqpTlQPe9MApdWPky58mNQQHbnCzoNg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "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==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@tinyhttp/content-disposition": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-disposition/-/content-disposition-2.2.4.tgz", + "integrity": "sha512-5Kc5CM2Ysn3vTTArBs2vESUt0AQiWZA86yc1TI3B+lxXmtEq133C1nxXNOgnzhrivdPZIh3zLj5gDnZjoLL5GA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.17.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.160", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.160.tgz", + "integrity": "sha512-uoO4QVQNWFPJMh26pXtmtrRfGshPUSpMZGUyUQY20FhfHEElEBOPKgVmFs1z+kbpyBsRs2JnoOPT7++Z4GA9pA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bun": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.6.tgz", + "integrity": "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bun-types": "1.3.6" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime-types": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@whiskeysockets/baileys": { + "version": "7.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@whiskeysockets/baileys/-/baileys-7.0.0-rc.9.tgz", + "integrity": "sha512-YFm5gKXfDP9byCXCW3OPHKXLzrAKzolzgVUlRosHHgwbnf2YOO3XknkMm6J7+F0ns8OA0uuSBhgkRHTDtqkacw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@cacheable/node-cache": "^1.4.0", + "@hapi/boom": "^9.1.3", + "async-mutex": "^0.5.0", + "libsignal": "git+https://github.com/whiskeysockets/libsignal-node.git", + "lru-cache": "^11.1.0", + "music-metadata": "^11.7.0", + "p-queue": "^9.0.0", + "pino": "^9.6", + "protobufjs": "^7.2.4", + "ws": "^8.13.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "audio-decode": "^2.1.3", + "jimp": "^1.6.0", + "link-preview-js": "^3.0.0", + "sharp": "*" + }, + "peerDependenciesMeta": { + "audio-decode": { + "optional": true + }, + "jimp": { + "optional": true + }, + "link-preview-js": { + "optional": true + } + } + }, + "node_modules/@whiskeysockets/baileys/node_modules/p-queue": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", + "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@whiskeysockets/baileys/node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "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.", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/axios": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bun-types": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.6.tgz", + "integrity": "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.2.tgz", + "integrity": "sha512-w+ZuRNmex9c1TR9RcsxbfTKCjSL0rh1WA5SABbrWprIHeNBdmyQLSYonlDy9gpD+63XT8DgZ/wNh1Smvc9WnJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/memory": "^2.0.7", + "@cacheable/utils": "^2.3.3", + "hookified": "^1.15.0", + "keyv": "^5.5.5", + "qified": "^0.6.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "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", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chmodrp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chmodrp/-/chmodrp-1.0.2.tgz", + "integrity": "sha512-TdngOlFV1FLTzU0o1w8MB6/BFywhtLC0SzRTGJU7T9lmdjlCWeMRt1iVo0Ki+ldwNk0BqNiKoc8xpLZEQ8mY1w==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chromium-bidi": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-13.0.1.tgz", + "integrity": "sha512-c+RLxH0Vg2x2syS9wPw378oJgiJNXtYXUvnVAldUlt5uaHekn0CCU7gPksNgHjrH1qFhmjVXQj4esvuthuC7OQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/chromium-bidi/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cmake-js": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.4.0.tgz", + "integrity": "sha512-Lw0JxEHrmk+qNj1n9W9d4IvkDdYTBn7l2BW6XmtLj7WPpIo2shvxUy+YokfjMxAAOELNonQwX3stkPhM5xSC2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "axios": "^1.6.5", + "debug": "^4", + "fs-extra": "^11.2.0", + "memory-stream": "^1.0.0", + "node-api-headers": "^1.1.0", + "npmlog": "^6.0.2", + "rc": "^1.2.7", + "semver": "^7.5.4", + "tar": "^6.2.0", + "url-join": "^4.0.1", + "which": "^2.0.2", + "yargs": "^17.7.2" + }, + "bin": { + "cmake-js": "bin/cmake-js" + }, + "engines": { + "node": ">= 14.15.0" + } + }, + "node_modules/cmake-js/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cmake-js/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/cmake-js/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cmake-js/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cmake-js/node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cmake-js/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cmake-js/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cmake-js/node_modules/tar": { + "version": "6.2.1", + "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 exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cmake-js/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/cmake-js/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cmake-js/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/croner": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/croner/-/croner-9.1.0.tgz", + "integrity": "sha512-p9nwwR4qyT5W996vBZhdvBCnMhicY5ytZkR4D1Xj0wuTDEiMnjwR57Q3RXYY/s0EpX6Ay3vgIcfaR+ewGHsi+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/curve25519-js": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/curve25519-js/-/curve25519-js-0.0.4.tgz", + "integrity": "sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1583009", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1583009.tgz", + "integrity": "sha512-tAHclTnaBGpXAapKdmJ+Coy2Ih2FQRlWx0SCBK9JBY8KqI72O3B0hIzMZRYFcNW6MCgbAeGIjXNPw3VjU2V82g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/diff": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/discord-api-types": { + "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==", + "dev": true, + "license": "MIT", + "workspaces": [ + "scripts/actions/documentation" + ] + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "17.2.4", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.4.tgz", + "integrity": "sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-var": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/env-var/-/env-var-7.5.0.tgz", + "integrity": "sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz", + "integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-type": { + "version": "21.3.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.0.tgz", + "integrity": "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/filename-reserved-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", + "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/filenamify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-6.0.0.tgz", + "integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "filename-reserved-regex": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", + "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob 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 exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/google-auth-library": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz", + "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^8.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/grammy": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/grammy/-/grammy-1.40.0.tgz", + "integrity": "sha512-ssuE7fc1AwqlUxHr931OCVW3fU+oFDjHZGgvIedPKXfTdjXvzP19xifvVGCnPtYVUig1Kz+gwxe4A9M5WdkT4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@grammyjs/types": "3.24.0", + "abort-controller": "^3.0.0", + "debug": "^4.4.3", + "node-fetch": "^2.7.0" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + } + }, + "node_modules/grammy/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/hashery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.4.0.tgz", + "integrity": "sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.14.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/hono": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz", + "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ipull": { + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/ipull/-/ipull-3.9.3.tgz", + "integrity": "sha512-ZMkxaopfwKHwmEuGDYx7giNBdLxbHbRCWcQVA1D2eqE4crUguupfxej6s7UqbidYEwT69dkyumYkY8DPHIxF9g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tinyhttp/content-disposition": "^2.2.0", + "async-retry": "^1.3.3", + "chalk": "^5.3.0", + "ci-info": "^4.0.0", + "cli-spinners": "^2.9.2", + "commander": "^10.0.0", + "eventemitter3": "^5.0.1", + "filenamify": "^6.0.0", + "fs-extra": "^11.1.1", + "is-unicode-supported": "^2.0.0", + "lifecycle-utils": "^2.0.1", + "lodash.debounce": "^4.0.8", + "lowdb": "^7.0.1", + "pretty-bytes": "^6.1.0", + "pretty-ms": "^8.0.0", + "sleep-promise": "^9.1.0", + "slice-ansi": "^7.1.0", + "stdout-update": "^4.0.1", + "strip-ansi": "^7.1.0" + }, + "bin": { + "ipull": "dist/cli/cli.js" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/ido-pluto/ipull?sponsor=1" + }, + "optionalDependencies": { + "@reflink/reflink": "^0.1.16" + } + }, + "node_modules/ipull/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/ipull/node_modules/lifecycle-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lifecycle-utils/-/lifecycle-utils-2.1.0.tgz", + "integrity": "sha512-AnrXnE2/OF9PHCyFg0RSqsnQTzV991XaZA/buhFDoc58xU7rhSCDgCz/09Lqpsn4MpoPHt7TRAXV1kWZypFVsA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/ipull/node_modules/parse-ms": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-3.0.0.tgz", + "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ipull/node_modules/pretty-ms": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz", + "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "parse-ms": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-electron": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", + "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/libsignal": { + "name": "@whiskeysockets/libsignal-node", + "version": "2.0.1", + "resolved": "git+ssh://git@github.com/whiskeysockets/libsignal-node.git#1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67", + "dev": true, + "license": "GPL-3.0", + "dependencies": { + "curve25519-js": "^0.0.4", + "protobufjs": "6.8.8" + } + }, + "node_modules/libsignal/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/libsignal/node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/libsignal/node_modules/protobufjs": { + "version": "6.8.8", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", + "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.0", + "@types/node": "^10.1.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lifecycle-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lifecycle-utils/-/lifecycle-utils-3.1.0.tgz", + "integrity": "sha512-kVvegv+r/icjIo1dkHv1hznVQi4FzEVglJD2IU4w07HzevIyH3BAYsFZzEIbBk/nNZjXHGgclJ5g9rz9QdBCLw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/linkedom": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.12.tgz", + "integrity": "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "css-select": "^5.1.0", + "cssom": "^0.5.0", + "html-escaper": "^3.0.3", + "htmlparser2": "^10.0.0", + "uhyphen": "^0.2.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "canvas": ">= 2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/lodash.identity": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash.identity/-/lodash.identity-3.0.0.tgz", + "integrity": "sha512-AupTIzdLQxJS5wIYUQlgGyk2XRTfGXA+MCghDHqZk0pzUNYvd3EESS6dkChNauNYVIutcb0dfHw1ri9Q1yPV8Q==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.pickby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", + "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", + "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-unicode-supported": "^2.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lowdb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz", + "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "steno": "^4.0.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/memory-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-1.0.0.tgz", + "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "readable-stream": "^3.4.0" + } + }, + "node_modules/memory-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", + "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/music-metadata": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-11.12.0.tgz", + "integrity": "sha512-9ChYnmVmyHvFxR2g0MWFSHmJfbssRy07457G4gbb4LA9WYvyZea/8EMbqvg5dcv4oXNCNL01m8HXtymLlhhkYg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "file-type": "^21.3.0", + "media-typer": "^1.1.0", + "strtok3": "^10.3.4", + "token-types": "^6.1.2", + "uint8array-extras": "^1.5.0", + "win-guid": "^0.2.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "optional": true, + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-api-headers": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-1.8.0.tgz", + "integrity": "sha512-jfnmiKWjRAGbdD1yQS28bknFM1tbHC1oucyuMPjmkEs+kpiu76aRs40WlTmBmyEgzDM76ge1DQ7XJ3R5deiVjQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "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", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-edge-tts": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/node-edge-tts/-/node-edge-tts-1.2.10.tgz", + "integrity": "sha512-bV2i4XU54D45+US0Zm1HcJRkifuB3W438dWyuJEHLQdKxnuqlI1kim2MOvR6Q3XUQZvfF9PoDyR1Rt7aeXhPdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "https-proxy-agent": "^7.0.1", + "ws": "^8.13.0", + "yargs": "^17.7.2" + }, + "bin": { + "node-edge-tts": "bin.js" + } + }, + "node_modules/node-edge-tts/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/node-edge-tts/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/node-edge-tts/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-edge-tts/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/node-edge-tts/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-llama-cpp": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/node-llama-cpp/-/node-llama-cpp-3.15.0.tgz", + "integrity": "sha512-xQKl+MvKiA5QNi/CTwqLKMos7hefhRVyzJuNIAEwl7zvOoF+gNMOXEsR4Ojwl7qvgpcjsVeGKWSK3Rb6zoUP1w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@huggingface/jinja": "^0.5.3", + "async-retry": "^1.3.3", + "bytes": "^3.1.2", + "chalk": "^5.4.1", + "chmodrp": "^1.0.2", + "cmake-js": "^7.4.0", + "cross-spawn": "^7.0.6", + "env-var": "^7.5.0", + "filenamify": "^6.0.0", + "fs-extra": "^11.3.0", + "ignore": "^7.0.4", + "ipull": "^3.9.2", + "is-unicode-supported": "^2.1.0", + "lifecycle-utils": "^3.0.1", + "log-symbols": "^7.0.0", + "nanoid": "^5.1.5", + "node-addon-api": "^8.3.1", + "octokit": "^5.0.3", + "ora": "^8.2.0", + "pretty-ms": "^9.2.0", + "proper-lockfile": "^4.1.2", + "semver": "^7.7.1", + "simple-git": "^3.27.0", + "slice-ansi": "^7.1.0", + "stdout-update": "^4.0.1", + "strip-ansi": "^7.1.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "nlc": "dist/cli/cli.js", + "node-llama-cpp": "dist/cli/cli.js" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/giladgd" + }, + "optionalDependencies": { + "@node-llama-cpp/linux-arm64": "3.15.0", + "@node-llama-cpp/linux-armv7l": "3.15.0", + "@node-llama-cpp/linux-x64": "3.15.0", + "@node-llama-cpp/linux-x64-cuda": "3.15.0", + "@node-llama-cpp/linux-x64-cuda-ext": "3.15.0", + "@node-llama-cpp/linux-x64-vulkan": "3.15.0", + "@node-llama-cpp/mac-arm64-metal": "3.15.0", + "@node-llama-cpp/mac-x64": "3.15.0", + "@node-llama-cpp/win-arm64": "3.15.0", + "@node-llama-cpp/win-x64": "3.15.0", + "@node-llama-cpp/win-x64-cuda": "3.15.0", + "@node-llama-cpp/win-x64-cuda-ext": "3.15.0", + "@node-llama-cpp/win-x64-vulkan": "3.15.0" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/node-llama-cpp/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-llama-cpp/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/node-llama-cpp/node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-llama-cpp/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-llama-cpp/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-llama-cpp/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/node-llama-cpp/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/node-readable-to-web-readable-stream": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/node-readable-to-web-readable-stream/-/node-readable-to-web-readable-stream-0.4.2.tgz", + "integrity": "sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/npmlog": { + "version": "6.0.2", + "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.", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/octokit": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/octokit/-/octokit-5.0.5.tgz", + "integrity": "sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@octokit/app": "^16.1.2", + "@octokit/core": "^7.0.6", + "@octokit/oauth-app": "^8.0.3", + "@octokit/plugin-paginate-graphql": "^6.0.0", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0", + "@octokit/plugin-retry": "^8.0.3", + "@octokit/plugin-throttling": "^11.0.3", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "@octokit/webhooks": "^14.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openai": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.10.0.tgz", + "integrity": "sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openclaw": { + "version": "2026.1.29", + "resolved": "https://registry.npmjs.org/openclaw/-/openclaw-2026.1.29.tgz", + "integrity": "sha512-CVUOAH83BCrlnD56I1JahGVpg0mjp4fjJPdJg9q6Pijn/YdOopsfdInDqSeIEZwGtu/+0w69/uJccY23C9I+vg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "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": { + "openclaw": "openclaw.mjs" + }, + "engines": { + "node": ">=22.12.0" + }, + "optionalDependencies": { + "@napi-rs/canvas": "^0.1.88", + "node-llama-cpp": "3.15.0" + } + }, + "node_modules/openclaw/node_modules/@sinclair/typebox": { + "version": "0.34.47", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", + "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/osc-progress": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/osc-progress/-/osc-progress-0.3.0.tgz", + "integrity": "sha512-4/8JfsetakdeEa4vAYV45FW20aY+B/+K8NEXp5Eiar3wR8726whgHrbSg5Ar/ZY1FLJ/AGtUqV7W2IVF+Gvp9A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pdfjs-dist": { + "version": "5.4.624", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.624.tgz", + "integrity": "sha512-sm6TxKTtWv1Oh6n3C6J6a8odejb5uO4A4zo/2dgkHuC0iu8ZMAXOezEODkVaoVp8nX1Xzr+0WxFJJmUr45hQzg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=20.16.0 || >=22.3.0" + }, + "optionalDependencies": { + "@napi-rs/canvas": "^0.1.88", + "node-readable-to-web-readable-stream": "^0.4.2" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pino": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "dev": true, + "license": "MIT" + }, + "node_modules/playwright-core": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.0.tgz", + "integrity": "sha512-aaoB1RWrdNi3//rOeKuMiS65UCcgOVljU46At6eFcOFPFHWtd2weHRRow6z/n+Lec0Lvu0k9ZPKJSjPugikirw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prism-media": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.5.tgz", + "integrity": "sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peerDependencies": { + "@discordjs/opus": ">=0.8.0 <1.0.0", + "ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0", + "node-opus": "^0.3.3", + "opusscript": "^0.0.8" + }, + "peerDependenciesMeta": { + "@discordjs/opus": { + "optional": true + }, + "ffmpeg-static": { + "optional": true + }, + "node-opus": { + "optional": true + }, + "opusscript": { + "optional": true + } + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qified": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.6.0.tgz", + "integrity": "sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.14.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/qrcode-terminal": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", + "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", + "dev": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "dev": true, + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/rimraf/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob 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 exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rimraf/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/send/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-git": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.30.0.tgz", + "integrity": "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sleep-promise": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/sleep-promise/-/sleep-promise-9.1.0.tgz", + "integrity": "sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sqlite-vec": { + "version": "0.1.7-alpha.2", + "resolved": "https://registry.npmjs.org/sqlite-vec/-/sqlite-vec-0.1.7-alpha.2.tgz", + "integrity": "sha512-rNgRCv+4V4Ed3yc33Qr+nNmjhtrMnnHzXfLVPeGb28Dx5mmDL3Ngw/Wk8vhCGjj76+oC6gnkmMG8y73BZWGBwQ==", + "dev": true, + "license": "MIT OR Apache", + "optionalDependencies": { + "sqlite-vec-darwin-arm64": "0.1.7-alpha.2", + "sqlite-vec-darwin-x64": "0.1.7-alpha.2", + "sqlite-vec-linux-arm64": "0.1.7-alpha.2", + "sqlite-vec-linux-x64": "0.1.7-alpha.2", + "sqlite-vec-windows-x64": "0.1.7-alpha.2" + } + }, + "node_modules/sqlite-vec-darwin-arm64": { + "version": "0.1.7-alpha.2", + "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" + ], + "dev": true, + "license": "MIT OR Apache", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/sqlite-vec-darwin-x64": { + "version": "0.1.7-alpha.2", + "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" + ], + "dev": true, + "license": "MIT OR Apache", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/sqlite-vec-linux-arm64": { + "version": "0.1.7-alpha.2", + "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" + ], + "dev": true, + "license": "MIT OR Apache", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/sqlite-vec-linux-x64": { + "version": "0.1.7-alpha.2", + "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" + ], + "dev": true, + "license": "MIT OR Apache", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/sqlite-vec-windows-x64": { + "version": "0.1.7-alpha.2", + "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" + ], + "dev": true, + "license": "MIT OR Apache", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stdout-update": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/stdout-update/-/stdout-update-4.0.1.tgz", + "integrity": "sha512-wiS21Jthlvl1to+oorePvcyrIkiG/6M3D3VTmDUlJm7Cy6SbFhKkAvX+YBuHLxck/tO3mrdpC/cNesigQc3+UQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-escapes": "^6.2.0", + "ansi-styles": "^6.2.1", + "string-width": "^7.1.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/stdout-update/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/stdout-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/steno": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz", + "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strnum": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/strtok3": { + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", + "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "7.5.4", + "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 exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tslog": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/tslog/-/tslog-4.10.2.tgz", + "integrity": "sha512-XuELoRpMR+sq8fuWwX7P0bcj+PRNiicOKDEb3fGNURhxWVyykCi9BNq7c4uVz7h7P0sj8qgBsr5SWS6yBClq3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/fullstack-build/tslog?sponsor=1" + } + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.x" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/uhyphen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", + "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", + "dev": true, + "license": "ISC" + }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.21.0.tgz", + "integrity": "sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/universal-github-app-jwt": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz", + "integrity": "sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-name": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", + "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/win-guid": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/win-guid/-/win-guid-0.2.1.tgz", + "integrity": "sha512-gEIQU4mkgl2OPeoNrWflcJFJ3Ae2BPd4eCsHHA/XikslkIVms/nHhvnvzIZV7VLmBvtFlDOzLt9rrZT+n6D67A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + } + } +} diff --git a/feishu/package.json b/feishu/package.json new file mode 100644 index 00000000..98e4d931 --- /dev/null +++ b/feishu/package.json @@ -0,0 +1,62 @@ +{ + "name": "@m1heng-clawd/feishu", + "version": "0.1.9", + "type": "module", + "description": "OpenClaw Feishu/Lark channel plugin", + "license": "MIT", + "files": [ + "index.ts", + "src", + "skills", + "openclaw.plugin.json" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/m1heng/clawdbot-feishu.git" + }, + "keywords": [ + "openclaw", + "feishu", + "lark", + "飞书", + "chatbot", + "ai", + "claude" + ], + "openclaw": { + "extensions": [ + "./index.ts" + ], + "channel": { + "id": "feishu", + "label": "Feishu", + "selectionLabel": "Feishu/Lark (飞书)", + "docsPath": "/channels/feishu", + "docsLabel": "feishu", + "blurb": "飞书/Lark enterprise messaging.", + "aliases": [ + "lark" + ], + "order": 70 + }, + "install": { + "npmSpec": "@m1heng-clawd/feishu", + "localPath": ".", + "defaultChoice": "npm" + } + }, + "dependencies": { + "@larksuiteoapi/node-sdk": "^1.58.0", + "@sinclair/typebox": "0.34.48", + "zod": "^4.3.6" + }, + "devDependencies": { + "@types/node": "^25.0.10", + "openclaw": "2026.1.29", + "tsx": "^4.21.0", + "typescript": "^5.7.0" + }, + "peerDependencies": { + "openclaw": ">=2026.1.29" + } +} diff --git a/feishu/skills/feishu-doc/SKILL.md b/feishu/skills/feishu-doc/SKILL.md new file mode 100644 index 00000000..0d92c92c --- /dev/null +++ b/feishu/skills/feishu-doc/SKILL.md @@ -0,0 +1,99 @@ +--- +name: feishu-doc +description: | + Feishu document read/write operations. Activate when user mentions Feishu docs, cloud docs, or docx links. +--- + +# Feishu Document Tool + +Single tool `feishu_doc` with action parameter for all document operations. + +## Token Extraction + +From URL `https://xxx.feishu.cn/docx/ABC123def` → `doc_token` = `ABC123def` + +## Actions + +### Read Document + +```json +{ "action": "read", "doc_token": "ABC123def" } +``` + +Returns: title, plain text content, block statistics. Check `hint` field - if present, structured content (tables, images) exists that requires `list_blocks`. + +### Write Document (Replace All) + +```json +{ "action": "write", "doc_token": "ABC123def", "content": "# Title\n\nMarkdown content..." } +``` + +Replaces entire document with markdown content. Supports: headings, lists, code blocks, quotes, links, images (`![](url)` auto-uploaded), bold/italic/strikethrough. + +**Limitation:** Markdown tables are NOT supported. + +### Append Content + +```json +{ "action": "append", "doc_token": "ABC123def", "content": "Additional content" } +``` + +Appends markdown to end of document. + +### Create Document + +```json +{ "action": "create", "title": "New Document" } +``` + +With folder: +```json +{ "action": "create", "title": "New Document", "folder_token": "fldcnXXX" } +``` + +### List Blocks + +```json +{ "action": "list_blocks", "doc_token": "ABC123def" } +``` + +Returns full block data including tables, images. Use this to read structured content. + +### Get Single Block + +```json +{ "action": "get_block", "doc_token": "ABC123def", "block_id": "doxcnXXX" } +``` + +### Update Block Text + +```json +{ "action": "update_block", "doc_token": "ABC123def", "block_id": "doxcnXXX", "content": "New text" } +``` + +### Delete Block + +```json +{ "action": "delete_block", "doc_token": "ABC123def", "block_id": "doxcnXXX" } +``` + +## Reading Workflow + +1. Start with `action: "read"` - get plain text + statistics +2. Check `block_types` in response for Table, Image, Code, etc. +3. If structured content exists, use `action: "list_blocks"` for full data + +## Configuration + +```yaml +channels: + feishu: + tools: + doc: true # default: true +``` + +**Note:** `feishu_wiki` depends on this tool - wiki page content is read/written via `feishu_doc`. + +## Permissions + +Required: `docx:document`, `docx:document:readonly`, `docx:document.block:convert`, `drive:drive` diff --git a/feishu/skills/feishu-doc/references/block-types.md b/feishu/skills/feishu-doc/references/block-types.md new file mode 100644 index 00000000..a089a908 --- /dev/null +++ b/feishu/skills/feishu-doc/references/block-types.md @@ -0,0 +1,102 @@ +# Feishu Block Types Reference + +Complete reference for Feishu document block types. Use with `feishu_doc_list_blocks`, `feishu_doc_update_block`, and `feishu_doc_delete_block`. + +## Block Type Table + +| block_type | Name | Description | Editable | +|------------|------|-------------|----------| +| 1 | Page | Document root (contains title) | No | +| 2 | Text | Plain text paragraph | Yes | +| 3 | Heading1 | H1 heading | Yes | +| 4 | Heading2 | H2 heading | Yes | +| 5 | Heading3 | H3 heading | Yes | +| 6 | Heading4 | H4 heading | Yes | +| 7 | Heading5 | H5 heading | Yes | +| 8 | Heading6 | H6 heading | Yes | +| 9 | Heading7 | H7 heading | Yes | +| 10 | Heading8 | H8 heading | Yes | +| 11 | Heading9 | H9 heading | Yes | +| 12 | Bullet | Unordered list item | Yes | +| 13 | Ordered | Ordered list item | Yes | +| 14 | Code | Code block | Yes | +| 15 | Quote | Blockquote | Yes | +| 16 | Equation | LaTeX equation | Partial | +| 17 | Todo | Checkbox / task item | Yes | +| 18 | Bitable | Multi-dimensional table | No | +| 19 | Callout | Highlight block | Yes | +| 20 | ChatCard | Chat card embed | No | +| 21 | Diagram | Diagram embed | No | +| 22 | Divider | Horizontal rule | No | +| 23 | File | File attachment | No | +| 24 | Grid | Grid layout container | No | +| 25 | GridColumn | Grid column | No | +| 26 | Iframe | Embedded iframe | No | +| 27 | Image | Image | Partial | +| 28 | ISV | Third-party widget | No | +| 29 | MindnoteBlock | Mindmap embed | No | +| 30 | Sheet | Spreadsheet embed | No | +| 31 | Table | Table | Partial | +| 32 | TableCell | Table cell | Yes | +| 33 | View | View embed | No | +| 34 | Undefined | Unknown type | No | +| 35 | QuoteContainer | Quote container | No | +| 36 | Task | Lark Tasks integration | No | +| 37 | OKR | OKR integration | No | +| 38 | OKRObjective | OKR objective | No | +| 39 | OKRKeyResult | OKR key result | No | +| 40 | OKRProgress | OKR progress | No | +| 41 | AddOns | Add-ons block | No | +| 42 | JiraIssue | Jira issue embed | No | +| 43 | WikiCatalog | Wiki catalog | No | +| 44 | Board | Board embed | No | +| 45 | Agenda | Agenda block | No | +| 46 | AgendaItem | Agenda item | No | +| 47 | AgendaItemTitle | Agenda item title | No | +| 48 | SyncedBlock | Synced block reference | No | + +## Editing Guidelines + +### Text-based blocks (2-17, 19) + +Update text content using `feishu_doc_update_block`: + +```json +{ + "doc_token": "ABC123", + "block_id": "block_xxx", + "content": "New text content" +} +``` + +### Image blocks (27) + +Images cannot be updated directly via `update_block`. Use `feishu_doc_write` or `feishu_doc_append` with markdown to add new images. + +### Table blocks (31) + +**Important:** Table blocks CANNOT be created via the `documentBlockChildren.create` API (error 1770029). This affects `feishu_doc_write` and `feishu_doc_append` - markdown tables will be skipped with a warning. + +Tables can only be read (via `list_blocks`) and individual cells (type 32) can be updated, but new tables cannot be inserted programmatically via markdown. + +### Container blocks (24, 25, 35) + +Grid and QuoteContainer are layout containers. Edit their child blocks instead. + +## Common Patterns + +### Replace specific paragraph + +1. `feishu_doc_list_blocks` - find the block_id +2. `feishu_doc_update_block` - update its content + +### Insert content at specific location + +Currently, the API only supports appending to document end. For insertion at specific positions, consider: +1. Read existing content +2. Delete affected blocks +3. Rewrite with new content in desired order + +### Delete multiple blocks + +Blocks must be deleted one at a time. Delete child blocks before parent containers. diff --git a/feishu/skills/feishu-drive/SKILL.md b/feishu/skills/feishu-drive/SKILL.md new file mode 100644 index 00000000..b4a957a4 --- /dev/null +++ b/feishu/skills/feishu-drive/SKILL.md @@ -0,0 +1,96 @@ +--- +name: feishu-drive +description: | + Feishu cloud storage file management. Activate when user mentions cloud space, folders, drive. +--- + +# Feishu Drive Tool + +Single tool `feishu_drive` for cloud storage operations. + +## Token Extraction + +From URL `https://xxx.feishu.cn/drive/folder/ABC123` → `folder_token` = `ABC123` + +## Actions + +### List Folder Contents + +```json +{ "action": "list" } +``` + +Root directory (no folder_token). + +```json +{ "action": "list", "folder_token": "fldcnXXX" } +``` + +Returns: files with token, name, type, url, timestamps. + +### Get File Info + +```json +{ "action": "info", "file_token": "ABC123", "type": "docx" } +``` + +Searches for the file in the root directory. Note: file must be in root or use `list` to browse folders first. + +`type`: `doc`, `docx`, `sheet`, `bitable`, `folder`, `file`, `mindnote`, `shortcut` + +### Create Folder + +```json +{ "action": "create_folder", "name": "New Folder" } +``` + +In parent folder: +```json +{ "action": "create_folder", "name": "New Folder", "folder_token": "fldcnXXX" } +``` + +### Move File + +```json +{ "action": "move", "file_token": "ABC123", "type": "docx", "folder_token": "fldcnXXX" } +``` + +### Delete File + +```json +{ "action": "delete", "file_token": "ABC123", "type": "docx" } +``` + +## File Types + +| Type | Description | +|------|-------------| +| `doc` | Old format document | +| `docx` | New format document | +| `sheet` | Spreadsheet | +| `bitable` | Multi-dimensional table | +| `folder` | Folder | +| `file` | Uploaded file | +| `mindnote` | Mind map | +| `shortcut` | Shortcut | + +## Configuration + +```yaml +channels: + feishu: + tools: + drive: true # default: true +``` + +## Permissions + +- `drive:drive` - Full access (create, move, delete) +- `drive:drive:readonly` - Read only (list, info) + +## Known Limitations + +- **Bots have no root folder**: Feishu bots use `tenant_access_token` and don't have their own "My Space". The root folder concept only exists for user accounts. This means: + - `create_folder` without `folder_token` will fail (400 error) + - Bot can only access files/folders that have been **shared with it** + - **Workaround**: User must first create a folder manually and share it with the bot, then bot can create subfolders inside it diff --git a/feishu/skills/feishu-perm/SKILL.md b/feishu/skills/feishu-perm/SKILL.md new file mode 100644 index 00000000..cade6b17 --- /dev/null +++ b/feishu/skills/feishu-perm/SKILL.md @@ -0,0 +1,90 @@ +--- +name: feishu-perm +description: | + Feishu permission management for documents and files. Activate when user mentions sharing, permissions, collaborators. +--- + +# Feishu Permission Tool + +Single tool `feishu_perm` for managing file/document permissions. + +## Actions + +### List Collaborators + +```json +{ "action": "list", "token": "ABC123", "type": "docx" } +``` + +Returns: members with member_type, member_id, perm, name. + +### Add Collaborator + +```json +{ "action": "add", "token": "ABC123", "type": "docx", "member_type": "email", "member_id": "user@example.com", "perm": "edit" } +``` + +### Remove Collaborator + +```json +{ "action": "remove", "token": "ABC123", "type": "docx", "member_type": "email", "member_id": "user@example.com" } +``` + +## Token Types + +| Type | Description | +|------|-------------| +| `doc` | Old format document | +| `docx` | New format document | +| `sheet` | Spreadsheet | +| `bitable` | Multi-dimensional table | +| `folder` | Folder | +| `file` | Uploaded file | +| `wiki` | Wiki node | +| `mindnote` | Mind map | + +## Member Types + +| Type | Description | +|------|-------------| +| `email` | Email address | +| `openid` | User open_id | +| `userid` | User user_id | +| `unionid` | User union_id | +| `openchat` | Group chat open_id | +| `opendepartmentid` | Department open_id | + +## Permission Levels + +| Perm | Description | +|------|-------------| +| `view` | View only | +| `edit` | Can edit | +| `full_access` | Full access (can manage permissions) | + +## Examples + +Share document with email: +```json +{ "action": "add", "token": "doxcnXXX", "type": "docx", "member_type": "email", "member_id": "alice@company.com", "perm": "edit" } +``` + +Share folder with group: +```json +{ "action": "add", "token": "fldcnXXX", "type": "folder", "member_type": "openchat", "member_id": "oc_xxx", "perm": "view" } +``` + +## Configuration + +```yaml +channels: + feishu: + tools: + perm: true # default: false (disabled) +``` + +**Note:** This tool is disabled by default because permission management is a sensitive operation. Enable explicitly if needed. + +## Permissions + +Required: `drive:permission` diff --git a/feishu/skills/feishu-wiki/SKILL.md b/feishu/skills/feishu-wiki/SKILL.md new file mode 100644 index 00000000..b2fe919d --- /dev/null +++ b/feishu/skills/feishu-wiki/SKILL.md @@ -0,0 +1,96 @@ +--- +name: feishu-wiki +description: | + Feishu knowledge base navigation. Activate when user mentions knowledge base, wiki, or wiki links. +--- + +# Feishu Wiki Tool + +Single tool `feishu_wiki` for knowledge base operations. + +## Token Extraction + +From URL `https://xxx.feishu.cn/wiki/ABC123def` → `token` = `ABC123def` + +## Actions + +### List Knowledge Spaces + +```json +{ "action": "spaces" } +``` + +Returns all accessible wiki spaces. + +### List Nodes + +```json +{ "action": "nodes", "space_id": "7xxx" } +``` + +With parent: +```json +{ "action": "nodes", "space_id": "7xxx", "parent_node_token": "wikcnXXX" } +``` + +### Get Node Details + +```json +{ "action": "get", "token": "ABC123def" } +``` + +Returns: `node_token`, `obj_token`, `obj_type`, etc. Use `obj_token` with `feishu_doc` to read/write the document. + +### Create Node + +```json +{ "action": "create", "space_id": "7xxx", "title": "New Page" } +``` + +With type and parent: +```json +{ "action": "create", "space_id": "7xxx", "title": "Sheet", "obj_type": "sheet", "parent_node_token": "wikcnXXX" } +``` + +`obj_type`: `docx` (default), `sheet`, `bitable`, `mindnote`, `file`, `doc`, `slides` + +### Move Node + +```json +{ "action": "move", "space_id": "7xxx", "node_token": "wikcnXXX" } +``` + +To different location: +```json +{ "action": "move", "space_id": "7xxx", "node_token": "wikcnXXX", "target_space_id": "7yyy", "target_parent_token": "wikcnYYY" } +``` + +### Rename Node + +```json +{ "action": "rename", "space_id": "7xxx", "node_token": "wikcnXXX", "title": "New Title" } +``` + +## Wiki-Doc Workflow + +To edit a wiki page: + +1. Get node: `{ "action": "get", "token": "wiki_token" }` → returns `obj_token` +2. Read doc: `feishu_doc { "action": "read", "doc_token": "obj_token" }` +3. Write doc: `feishu_doc { "action": "write", "doc_token": "obj_token", "content": "..." }` + +## Configuration + +```yaml +channels: + feishu: + tools: + wiki: true # default: true + doc: true # required - wiki content uses feishu_doc +``` + +**Dependency:** This tool requires `feishu_doc` to be enabled. Wiki pages are documents - use `feishu_wiki` to navigate, then `feishu_doc` to read/edit content. + +## Permissions + +Required: `wiki:wiki` or `wiki:wiki:readonly` diff --git a/feishu/src/accounts.ts b/feishu/src/accounts.ts new file mode 100644 index 00000000..e4d33a5c --- /dev/null +++ b/feishu/src/accounts.ts @@ -0,0 +1,140 @@ +import type { ClawdbotConfig } from "openclaw/plugin-sdk"; +import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk"; +import type { FeishuConfig, FeishuAccountConfig, FeishuDomain, ResolvedFeishuAccount } from "./types.js"; + +/** + * List all configured account IDs from the accounts field. + */ +function listConfiguredAccountIds(cfg: ClawdbotConfig): string[] { + const accounts = (cfg.channels?.feishu as FeishuConfig)?.accounts; + if (!accounts || typeof accounts !== "object") { + return []; + } + return Object.keys(accounts).filter(Boolean); +} + +/** + * List all Feishu account IDs. + * If no accounts are configured, returns [DEFAULT_ACCOUNT_ID] for backward compatibility. + */ +export function listFeishuAccountIds(cfg: ClawdbotConfig): string[] { + const ids = listConfiguredAccountIds(cfg); + if (ids.length === 0) { + // Backward compatibility: no accounts configured, use default + return [DEFAULT_ACCOUNT_ID]; + } + return [...ids].sort((a, b) => a.localeCompare(b)); +} + +/** + * Resolve the default account ID. + */ +export function resolveDefaultFeishuAccountId(cfg: ClawdbotConfig): string { + const ids = listFeishuAccountIds(cfg); + if (ids.includes(DEFAULT_ACCOUNT_ID)) { + return DEFAULT_ACCOUNT_ID; + } + return ids[0] ?? DEFAULT_ACCOUNT_ID; +} + +/** + * Get the raw account-specific config. + */ +function resolveAccountConfig( + cfg: ClawdbotConfig, + accountId: string, +): FeishuAccountConfig | undefined { + const accounts = (cfg.channels?.feishu as FeishuConfig)?.accounts; + if (!accounts || typeof accounts !== "object") { + return undefined; + } + return accounts[accountId]; +} + +/** + * Merge top-level config with account-specific config. + * Account-specific fields override top-level fields. + */ +function mergeFeishuAccountConfig( + cfg: ClawdbotConfig, + accountId: string, +): FeishuConfig { + const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined; + + // Extract base config (exclude accounts field to avoid recursion) + const { accounts: _ignored, ...base } = feishuCfg ?? {}; + + // Get account-specific overrides + const account = resolveAccountConfig(cfg, accountId) ?? {}; + + // Merge: account config overrides base config + return { ...base, ...account } as FeishuConfig; +} + +/** + * Resolve Feishu credentials from a config. + */ +export function resolveFeishuCredentials(cfg?: FeishuConfig): { + appId: string; + appSecret: string; + encryptKey?: string; + verificationToken?: string; + domain: FeishuDomain; +} | null { + const appId = cfg?.appId?.trim(); + const appSecret = cfg?.appSecret?.trim(); + if (!appId || !appSecret) return null; + return { + appId, + appSecret, + encryptKey: cfg?.encryptKey?.trim() || undefined, + verificationToken: cfg?.verificationToken?.trim() || undefined, + domain: cfg?.domain ?? "feishu", + }; +} + +/** + * Resolve a complete Feishu account with merged config. + */ +export function resolveFeishuAccount(params: { + cfg: ClawdbotConfig; + accountId?: string | null; +}): ResolvedFeishuAccount { + const accountId = normalizeAccountId(params.accountId); + const feishuCfg = params.cfg.channels?.feishu as FeishuConfig | undefined; + + // Base enabled state (top-level) + const baseEnabled = feishuCfg?.enabled !== false; + + // Merge configs + const merged = mergeFeishuAccountConfig(params.cfg, accountId); + + // Account-level enabled state + const accountEnabled = merged.enabled !== false; + const enabled = baseEnabled && accountEnabled; + + // Resolve credentials from merged config + const creds = resolveFeishuCredentials(merged); + + return { + accountId, + enabled, + configured: Boolean(creds), + name: (merged as FeishuAccountConfig).name?.trim() || undefined, + appId: creds?.appId, + appSecret: creds?.appSecret, + encryptKey: creds?.encryptKey, + verificationToken: creds?.verificationToken, + domain: creds?.domain ?? "feishu", + config: merged, + }; +} + +/** + * List all enabled and configured accounts. + */ +export function listEnabledFeishuAccounts(cfg: ClawdbotConfig): ResolvedFeishuAccount[] { + return listFeishuAccountIds(cfg) + .map((accountId) => resolveFeishuAccount({ cfg, accountId })) + .filter((account) => account.enabled && account.configured); +} diff --git a/feishu/src/bitable.ts b/feishu/src/bitable.ts new file mode 100644 index 00000000..c5b201b2 --- /dev/null +++ b/feishu/src/bitable.ts @@ -0,0 +1,441 @@ +import { Type } from "@sinclair/typebox"; +import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; +import { createFeishuClient } from "./client.js"; +import type { FeishuConfig } from "./types.js"; + +// ============ Helpers ============ + +function json(data: unknown) { + return { + content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }], + details: data, + }; +} + +/** Field type ID to human-readable name */ +const FIELD_TYPE_NAMES: Record = { + 1: "Text", + 2: "Number", + 3: "SingleSelect", + 4: "MultiSelect", + 5: "DateTime", + 7: "Checkbox", + 11: "User", + 13: "Phone", + 15: "URL", + 17: "Attachment", + 18: "SingleLink", + 19: "Lookup", + 20: "Formula", + 21: "DuplexLink", + 22: "Location", + 23: "GroupChat", + 1001: "CreatedTime", + 1002: "ModifiedTime", + 1003: "CreatedUser", + 1004: "ModifiedUser", + 1005: "AutoNumber", +}; + +// ============ Core Functions ============ + +/** Parse bitable URL and extract tokens */ +function parseBitableUrl(url: string): { token: string; tableId?: string; isWiki: boolean } | null { + try { + const u = new URL(url); + const tableId = u.searchParams.get("table") ?? undefined; + + // Wiki format: /wiki/XXXXX?table=YYY + const wikiMatch = u.pathname.match(/\/wiki\/([A-Za-z0-9]+)/); + if (wikiMatch) { + return { token: wikiMatch[1], tableId, isWiki: true }; + } + + // Base format: /base/XXXXX?table=YYY + const baseMatch = u.pathname.match(/\/base\/([A-Za-z0-9]+)/); + if (baseMatch) { + return { token: baseMatch[1], tableId, isWiki: false }; + } + + return null; + } catch { + return null; + } +} + +/** Get app_token from wiki node_token */ +async function getAppTokenFromWiki( + client: ReturnType, + nodeToken: string, +): Promise { + const res = await client.wiki.space.getNode({ + params: { token: nodeToken }, + }); + if (res.code !== 0) throw new Error(res.msg); + + const node = res.data?.node; + if (!node) throw new Error("Node not found"); + if (node.obj_type !== "bitable") { + throw new Error(`Node is not a bitable (type: ${node.obj_type})`); + } + + return node.obj_token!; +} + +/** Get bitable metadata from URL (handles both /base/ and /wiki/ URLs) */ +async function getBitableMeta( + client: ReturnType, + url: string, +) { + const parsed = parseBitableUrl(url); + if (!parsed) { + throw new Error("Invalid URL format. Expected /base/XXX or /wiki/XXX URL"); + } + + let appToken: string; + if (parsed.isWiki) { + appToken = await getAppTokenFromWiki(client, parsed.token); + } else { + appToken = parsed.token; + } + + // Get bitable app info + const res = await client.bitable.app.get({ + path: { app_token: appToken }, + }); + if (res.code !== 0) throw new Error(res.msg); + + // List tables if no table_id specified + let tables: { table_id: string; name: string }[] = []; + if (!parsed.tableId) { + const tablesRes = await client.bitable.appTable.list({ + path: { app_token: appToken }, + }); + if (tablesRes.code === 0) { + tables = (tablesRes.data?.items ?? []).map((t) => ({ + table_id: t.table_id!, + name: t.name!, + })); + } + } + + return { + app_token: appToken, + table_id: parsed.tableId, + name: res.data?.app?.name, + url_type: parsed.isWiki ? "wiki" : "base", + ...(tables.length > 0 && { tables }), + hint: parsed.tableId + ? `Use app_token="${appToken}" and table_id="${parsed.tableId}" for other bitable tools` + : `Use app_token="${appToken}" for other bitable tools. Select a table_id from the tables list.`, + }; +} + +async function listFields( + client: ReturnType, + appToken: string, + tableId: string, +) { + const res = await client.bitable.appTableField.list({ + path: { app_token: appToken, table_id: tableId }, + }); + if (res.code !== 0) throw new Error(res.msg); + + const fields = res.data?.items ?? []; + return { + fields: fields.map((f) => ({ + field_id: f.field_id, + field_name: f.field_name, + type: f.type, + type_name: FIELD_TYPE_NAMES[f.type ?? 0] || `type_${f.type}`, + is_primary: f.is_primary, + ...(f.property && { property: f.property }), + })), + total: fields.length, + }; +} + +async function listRecords( + client: ReturnType, + appToken: string, + tableId: string, + pageSize?: number, + pageToken?: string, +) { + const res = await client.bitable.appTableRecord.list({ + path: { app_token: appToken, table_id: tableId }, + params: { + page_size: pageSize ?? 100, + ...(pageToken && { page_token: pageToken }), + }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + records: res.data?.items ?? [], + has_more: res.data?.has_more ?? false, + page_token: res.data?.page_token, + total: res.data?.total, + }; +} + +async function getRecord( + client: ReturnType, + appToken: string, + tableId: string, + recordId: string, +) { + const res = await client.bitable.appTableRecord.get({ + path: { app_token: appToken, table_id: tableId, record_id: recordId }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + record: res.data?.record, + }; +} + +async function createRecord( + client: ReturnType, + appToken: string, + tableId: string, + fields: Record, +) { + const res = await client.bitable.appTableRecord.create({ + path: { app_token: appToken, table_id: tableId }, + data: { fields }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + record: res.data?.record, + }; +} + +async function updateRecord( + client: ReturnType, + appToken: string, + tableId: string, + recordId: string, + fields: Record, +) { + const res = await client.bitable.appTableRecord.update({ + path: { app_token: appToken, table_id: tableId, record_id: recordId }, + data: { fields }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + record: res.data?.record, + }; +} + +// ============ Schemas ============ + +const GetMetaSchema = Type.Object({ + url: Type.String({ + description: + "Bitable URL. Supports both formats: /base/XXX?table=YYY or /wiki/XXX?table=YYY", + }), +}); + +const ListFieldsSchema = Type.Object({ + app_token: Type.String({ + description: "Bitable app token (use feishu_bitable_get_meta to get from URL)", + }), + table_id: Type.String({ description: "Table ID (from URL: ?table=YYY)" }), +}); + +const ListRecordsSchema = Type.Object({ + app_token: Type.String({ + description: "Bitable app token (use feishu_bitable_get_meta to get from URL)", + }), + table_id: Type.String({ description: "Table ID (from URL: ?table=YYY)" }), + page_size: Type.Optional( + Type.Number({ description: "Number of records per page (1-500, default 100)", minimum: 1, maximum: 500 }), + ), + page_token: Type.Optional(Type.String({ description: "Pagination token from previous response" })), +}); + +const GetRecordSchema = Type.Object({ + app_token: Type.String({ + description: "Bitable app token (use feishu_bitable_get_meta to get from URL)", + }), + table_id: Type.String({ description: "Table ID (from URL: ?table=YYY)" }), + record_id: Type.String({ description: "Record ID to retrieve" }), +}); + +const CreateRecordSchema = Type.Object({ + app_token: Type.String({ + description: "Bitable app token (use feishu_bitable_get_meta to get from URL)", + }), + table_id: Type.String({ description: "Table ID (from URL: ?table=YYY)" }), + fields: Type.Record(Type.String(), Type.Any(), { + description: + "Field values keyed by field name. Format by type: Text='string', Number=123, SingleSelect='Option', MultiSelect=['A','B'], DateTime=timestamp_ms, User=[{id:'ou_xxx'}], URL={text:'Display',link:'https://...'}", + }), +}); + +const UpdateRecordSchema = Type.Object({ + app_token: Type.String({ + description: "Bitable app token (use feishu_bitable_get_meta to get from URL)", + }), + table_id: Type.String({ description: "Table ID (from URL: ?table=YYY)" }), + record_id: Type.String({ description: "Record ID to update" }), + fields: Type.Record(Type.String(), Type.Any(), { + description: "Field values to update (same format as create_record)", + }), +}); + +// ============ Tool Registration ============ + +export function registerFeishuBitableTools(api: OpenClawPluginApi) { + const feishuCfg = api.config?.channels?.feishu as FeishuConfig | undefined; + if (!feishuCfg?.appId || !feishuCfg?.appSecret) { + api.logger.debug?.("feishu_bitable: Feishu credentials not configured, skipping bitable tools"); + return; + } + + const getClient = () => createFeishuClient(feishuCfg); + + // Tool 0: feishu_bitable_get_meta (helper to parse URLs) + api.registerTool( + { + name: "feishu_bitable_get_meta", + label: "Feishu Bitable Get Meta", + description: + "Parse a Bitable URL and get app_token, table_id, and table list. Use this first when given a /wiki/ or /base/ URL.", + parameters: GetMetaSchema, + async execute(_toolCallId, params) { + const { url } = params as { url: string }; + try { + const result = await getBitableMeta(getClient(), url); + return json(result); + } catch (err) { + return json({ error: err instanceof Error ? err.message : String(err) }); + } + }, + }, + { name: "feishu_bitable_get_meta" }, + ); + + // Tool 1: feishu_bitable_list_fields + api.registerTool( + { + name: "feishu_bitable_list_fields", + label: "Feishu Bitable List Fields", + description: "List all fields (columns) in a Bitable table with their types and properties", + parameters: ListFieldsSchema, + async execute(_toolCallId, params) { + const { app_token, table_id } = params as { app_token: string; table_id: string }; + try { + const result = await listFields(getClient(), app_token, table_id); + return json(result); + } catch (err) { + return json({ error: err instanceof Error ? err.message : String(err) }); + } + }, + }, + { name: "feishu_bitable_list_fields" }, + ); + + // Tool 2: feishu_bitable_list_records + api.registerTool( + { + name: "feishu_bitable_list_records", + label: "Feishu Bitable List Records", + description: "List records (rows) from a Bitable table with pagination support", + parameters: ListRecordsSchema, + async execute(_toolCallId, params) { + const { app_token, table_id, page_size, page_token } = params as { + app_token: string; + table_id: string; + page_size?: number; + page_token?: string; + }; + try { + const result = await listRecords(getClient(), app_token, table_id, page_size, page_token); + return json(result); + } catch (err) { + return json({ error: err instanceof Error ? err.message : String(err) }); + } + }, + }, + { name: "feishu_bitable_list_records" }, + ); + + // Tool 3: feishu_bitable_get_record + api.registerTool( + { + name: "feishu_bitable_get_record", + label: "Feishu Bitable Get Record", + description: "Get a single record by ID from a Bitable table", + parameters: GetRecordSchema, + async execute(_toolCallId, params) { + const { app_token, table_id, record_id } = params as { + app_token: string; + table_id: string; + record_id: string; + }; + try { + const result = await getRecord(getClient(), app_token, table_id, record_id); + return json(result); + } catch (err) { + return json({ error: err instanceof Error ? err.message : String(err) }); + } + }, + }, + { name: "feishu_bitable_get_record" }, + ); + + // Tool 4: feishu_bitable_create_record + api.registerTool( + { + name: "feishu_bitable_create_record", + label: "Feishu Bitable Create Record", + description: "Create a new record (row) in a Bitable table", + parameters: CreateRecordSchema, + async execute(_toolCallId, params) { + const { app_token, table_id, fields } = params as { + app_token: string; + table_id: string; + fields: Record; + }; + try { + const result = await createRecord(getClient(), app_token, table_id, fields); + return json(result); + } catch (err) { + return json({ error: err instanceof Error ? err.message : String(err) }); + } + }, + }, + { name: "feishu_bitable_create_record" }, + ); + + // Tool 5: feishu_bitable_update_record + api.registerTool( + { + name: "feishu_bitable_update_record", + label: "Feishu Bitable Update Record", + description: "Update an existing record (row) in a Bitable table", + parameters: UpdateRecordSchema, + async execute(_toolCallId, params) { + const { app_token, table_id, record_id, fields } = params as { + app_token: string; + table_id: string; + record_id: string; + fields: Record; + }; + try { + const result = await updateRecord(getClient(), app_token, table_id, record_id, fields); + return json(result); + } catch (err) { + return json({ error: err instanceof Error ? err.message : String(err) }); + } + }, + }, + { name: "feishu_bitable_update_record" }, + ); + + api.logger.info?.(`feishu_bitable: Registered 6 bitable tools`); +} diff --git a/feishu/src/bot.ts b/feishu/src/bot.ts new file mode 100644 index 00000000..8ecae6e4 --- /dev/null +++ b/feishu/src/bot.ts @@ -0,0 +1,919 @@ +import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk"; +import { + buildPendingHistoryContextFromMap, + recordPendingHistoryEntryIfEnabled, + clearHistoryEntriesIfEnabled, + DEFAULT_GROUP_HISTORY_LIMIT, + type HistoryEntry, +} from "openclaw/plugin-sdk"; +import type { FeishuConfig, FeishuMessageContext, FeishuMediaInfo, ResolvedFeishuAccount } from "./types.js"; +import { getFeishuRuntime } from "./runtime.js"; +import { createFeishuClient } from "./client.js"; +import { resolveFeishuAccount } from "./accounts.js"; +import { + resolveFeishuGroupConfig, + resolveFeishuReplyPolicy, + resolveFeishuAllowlistMatch, + isFeishuGroupAllowed, +} from "./policy.js"; +import { createFeishuReplyDispatcher } from "./reply-dispatcher.js"; +import { getMessageFeishu } from "./send.js"; +import { downloadImageFeishu, downloadMessageResourceFeishu } from "./media.js"; +import { + extractMentionTargets, + extractMessageBody, + isMentionForwardRequest, +} from "./mention.js"; +import { maybeCreateDynamicAgent } from "./dynamic-agent.js"; +import type { DynamicAgentCreationConfig } from "./types.js"; + +// --- Message deduplication --- +// Prevent duplicate processing when WebSocket reconnects or Feishu redelivers messages. +const DEDUP_TTL_MS = 30 * 60 * 1000; // 30 minutes +const DEDUP_MAX_SIZE = 1_000; +const DEDUP_CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // cleanup every 5 minutes +const processedMessageIds = new Map(); // messageId -> timestamp +let lastCleanupTime = Date.now(); + +function tryRecordMessage(messageId: string): boolean { + const now = Date.now(); + + // Throttled cleanup: evict expired entries at most once per interval + if (now - lastCleanupTime > DEDUP_CLEANUP_INTERVAL_MS) { + for (const [id, ts] of processedMessageIds) { + if (now - ts > DEDUP_TTL_MS) processedMessageIds.delete(id); + } + lastCleanupTime = now; + } + + if (processedMessageIds.has(messageId)) return false; + + // Evict oldest entries if cache is full + if (processedMessageIds.size >= DEDUP_MAX_SIZE) { + const first = processedMessageIds.keys().next().value!; + processedMessageIds.delete(first); + } + + processedMessageIds.set(messageId, now); + return true; +} + +// --- Permission error extraction --- +// Extract permission grant URL from Feishu API error response. +type PermissionError = { + code: number; + message: string; + grantUrl?: string; +}; + +function extractPermissionError(err: unknown): PermissionError | null { + if (!err || typeof err !== "object") return null; + + // Axios error structure: err.response.data contains the Feishu error + const axiosErr = err as { response?: { data?: unknown } }; + const data = axiosErr.response?.data; + if (!data || typeof data !== "object") return null; + + const feishuErr = data as { + code?: number; + msg?: string; + error?: { permission_violations?: Array<{ uri?: string }> }; + }; + + // Feishu permission error code: 99991672 + if (feishuErr.code !== 99991672) return null; + + // Extract the grant URL from the error message (contains the direct link) + const msg = feishuErr.msg ?? ""; + const urlMatch = msg.match(/https:\/\/[^\s,]+\/app\/[^\s,]+/); + const grantUrl = urlMatch?.[0]; + + return { + code: feishuErr.code, + message: msg, + grantUrl, + }; +} + +// --- Sender name resolution (so the agent can distinguish who is speaking in group chats) --- +// Cache display names by open_id to avoid an API call on every message. +const SENDER_NAME_TTL_MS = 10 * 60 * 1000; +const senderNameCache = new Map(); + +// Cache permission errors to avoid spamming the user with repeated notifications. +// Key: appId or "default", Value: timestamp of last notification +const permissionErrorNotifiedAt = new Map(); +const PERMISSION_ERROR_COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes + +type SenderNameResult = { + name?: string; + permissionError?: PermissionError; +}; + +async function resolveFeishuSenderName(params: { + account: ResolvedFeishuAccount; + senderOpenId: string; + log: (...args: any[]) => void; +}): Promise { + const { account, senderOpenId, log } = params; + if (!account.configured) return {}; + if (!senderOpenId) return {}; + + const cached = senderNameCache.get(senderOpenId); + const now = Date.now(); + if (cached && cached.expireAt > now) return { name: cached.name }; + + try { + const client = createFeishuClient(account); + + // contact/v3/users/:user_id?user_id_type=open_id + const res: any = await client.contact.user.get({ + path: { user_id: senderOpenId }, + params: { user_id_type: "open_id" }, + }); + + const name: string | undefined = + res?.data?.user?.name || + res?.data?.user?.display_name || + res?.data?.user?.nickname || + res?.data?.user?.en_name; + + if (name && typeof name === "string") { + senderNameCache.set(senderOpenId, { name, expireAt: now + SENDER_NAME_TTL_MS }); + return { name }; + } + + return {}; + } catch (err) { + // Check if this is a permission error + const permErr = extractPermissionError(err); + if (permErr) { + log(`feishu: permission error resolving sender name: code=${permErr.code}`); + return { permissionError: permErr }; + } + + // Best-effort. Don't fail message handling if name lookup fails. + log(`feishu: failed to resolve sender name for ${senderOpenId}: ${String(err)}`); + return {}; + } +} + +export type FeishuMessageEvent = { + sender: { + sender_id: { + open_id?: string; + user_id?: string; + union_id?: string; + }; + sender_type?: string; + tenant_key?: string; + }; + message: { + message_id: string; + root_id?: string; + parent_id?: string; + chat_id: string; + chat_type: "p2p" | "group"; + message_type: string; + content: string; + mentions?: Array<{ + key: string; + id: { + open_id?: string; + user_id?: string; + union_id?: string; + }; + name: string; + tenant_key?: string; + }>; + }; +}; + +export type FeishuBotAddedEvent = { + chat_id: string; + operator_id: { + open_id?: string; + user_id?: string; + union_id?: string; + }; + external: boolean; + operator_tenant_key?: string; +}; + +function parseMessageContent(content: string, messageType: string): string { + try { + const parsed = JSON.parse(content); + if (messageType === "text") { + return parsed.text || ""; + } + if (messageType === "post") { + // Extract text content from rich text post + const { textContent } = parsePostContent(content); + return textContent; + } + return content; + } catch { + return content; + } +} + +function checkBotMentioned(event: FeishuMessageEvent, botOpenId?: string): boolean { + const mentions = event.message.mentions ?? []; + if (mentions.length === 0) return false; + if (!botOpenId) return mentions.length > 0; + return mentions.some((m) => m.id.open_id === botOpenId); +} + +function stripBotMention(text: string, mentions?: FeishuMessageEvent["message"]["mentions"]): string { + if (!mentions || mentions.length === 0) return text; + let result = text; + for (const mention of mentions) { + result = result.replace(new RegExp(`@${mention.name}\\s*`, "g"), "").trim(); + result = result.replace(new RegExp(mention.key, "g"), "").trim(); + } + return result; +} + +/** + * Parse media keys from message content based on message type. + */ +function parseMediaKeys( + content: string, + messageType: string, +): { + imageKey?: string; + fileKey?: string; + fileName?: string; +} { + try { + const parsed = JSON.parse(content); + switch (messageType) { + case "image": + return { imageKey: parsed.image_key }; + case "file": + return { fileKey: parsed.file_key, fileName: parsed.file_name }; + case "audio": + return { fileKey: parsed.file_key }; + case "video": + // Video has both file_key (video) and image_key (thumbnail) + return { fileKey: parsed.file_key, imageKey: parsed.image_key }; + case "sticker": + return { fileKey: parsed.file_key }; + default: + return {}; + } + } catch { + return {}; + } +} + +/** + * Parse post (rich text) content and extract embedded image keys. + * Post structure: { title?: string, content: [[{ tag, text?, image_key?, ... }]] } + */ +function parsePostContent(content: string): { + textContent: string; + imageKeys: string[]; +} { + try { + const parsed = JSON.parse(content); + const title = parsed.title || ""; + const contentBlocks = parsed.content || []; + let textContent = title ? `${title}\n\n` : ""; + const imageKeys: string[] = []; + + for (const paragraph of contentBlocks) { + if (Array.isArray(paragraph)) { + for (const element of paragraph) { + if (element.tag === "text") { + textContent += element.text || ""; + } else if (element.tag === "a") { + // Link: show text or href + textContent += element.text || element.href || ""; + } else if (element.tag === "at") { + // Mention: @username + textContent += `@${element.user_name || element.user_id || ""}`; + } else if (element.tag === "img" && element.image_key) { + // Embedded image + imageKeys.push(element.image_key); + } + } + textContent += "\n"; + } + } + + return { + textContent: textContent.trim() || "[富文本消息]", + imageKeys, + }; + } catch { + return { textContent: "[富文本消息]", imageKeys: [] }; + } +} + +/** + * Infer placeholder text based on message type. + */ +function inferPlaceholder(messageType: string): string { + switch (messageType) { + case "image": + return ""; + case "file": + return ""; + case "audio": + return ""; + case "video": + return ""; + case "sticker": + return ""; + default: + return ""; + } +} + +/** + * Resolve media from a Feishu message, downloading and saving to disk. + * Similar to Discord's resolveMediaList(). + */ +async function resolveFeishuMediaList(params: { + cfg: ClawdbotConfig; + messageId: string; + messageType: string; + content: string; + maxBytes: number; + log?: (msg: string) => void; + accountId?: string; +}): Promise { + const { cfg, messageId, messageType, content, maxBytes, log, accountId } = params; + + // Only process media message types (including post for embedded images) + const mediaTypes = ["image", "file", "audio", "video", "sticker", "post"]; + if (!mediaTypes.includes(messageType)) { + return []; + } + + const out: FeishuMediaInfo[] = []; + const core = getFeishuRuntime(); + + // Handle post (rich text) messages with embedded images + if (messageType === "post") { + const { imageKeys } = parsePostContent(content); + if (imageKeys.length === 0) { + return []; + } + + log?.(`feishu: post message contains ${imageKeys.length} embedded image(s)`); + + for (const imageKey of imageKeys) { + try { + // Embedded images in post use messageResource API with image_key as file_key + const result = await downloadMessageResourceFeishu({ + cfg, + messageId, + fileKey: imageKey, + type: "image", + accountId, + }); + + let contentType = result.contentType; + if (!contentType) { + contentType = await core.media.detectMime({ buffer: result.buffer }); + } + + const saved = await core.channel.media.saveMediaBuffer( + result.buffer, + contentType, + "inbound", + maxBytes, + ); + + out.push({ + path: saved.path, + contentType: saved.contentType, + placeholder: "", + }); + + log?.(`feishu: downloaded embedded image ${imageKey}, saved to ${saved.path}`); + } catch (err) { + log?.(`feishu: failed to download embedded image ${imageKey}: ${String(err)}`); + } + } + + return out; + } + + // Handle other media types + const mediaKeys = parseMediaKeys(content, messageType); + if (!mediaKeys.imageKey && !mediaKeys.fileKey) { + return []; + } + + try { + let buffer: Buffer; + let contentType: string | undefined; + let fileName: string | undefined; + + // For message media, always use messageResource API + // The image.get API is only for images uploaded via im/v1/images, not for message attachments + const fileKey = mediaKeys.imageKey || mediaKeys.fileKey; + if (!fileKey) { + return []; + } + + const resourceType = messageType === "image" ? "image" : "file"; + const result = await downloadMessageResourceFeishu({ + cfg, + messageId, + fileKey, + type: resourceType, + accountId, + }); + buffer = result.buffer; + contentType = result.contentType; + fileName = result.fileName || mediaKeys.fileName; + + // Detect mime type if not provided + if (!contentType) { + contentType = await core.media.detectMime({ buffer }); + } + + // Save to disk using core's saveMediaBuffer + const saved = await core.channel.media.saveMediaBuffer( + buffer, + contentType, + "inbound", + maxBytes, + fileName, + ); + + out.push({ + path: saved.path, + contentType: saved.contentType, + placeholder: inferPlaceholder(messageType), + }); + + log?.(`feishu: downloaded ${messageType} media, saved to ${saved.path}`); + } catch (err) { + log?.(`feishu: failed to download ${messageType} media: ${String(err)}`); + } + + return out; +} + +/** + * Build media payload for inbound context. + * Similar to Discord's buildDiscordMediaPayload(). + */ +function buildFeishuMediaPayload( + mediaList: FeishuMediaInfo[], +): { + MediaPath?: string; + MediaType?: string; + MediaUrl?: string; + MediaPaths?: string[]; + MediaUrls?: string[]; + MediaTypes?: string[]; +} { + const first = mediaList[0]; + const mediaPaths = mediaList.map((media) => media.path); + const mediaTypes = mediaList.map((media) => media.contentType).filter(Boolean) as string[]; + return { + MediaPath: first?.path, + MediaType: first?.contentType, + MediaUrl: first?.path, + MediaPaths: mediaPaths.length > 0 ? mediaPaths : undefined, + MediaUrls: mediaPaths.length > 0 ? mediaPaths : undefined, + MediaTypes: mediaTypes.length > 0 ? mediaTypes : undefined, + }; +} + +export function parseFeishuMessageEvent( + event: FeishuMessageEvent, + botOpenId?: string, +): FeishuMessageContext { + const rawContent = parseMessageContent(event.message.content, event.message.message_type); + const mentionedBot = checkBotMentioned(event, botOpenId); + const content = stripBotMention(rawContent, event.message.mentions); + + const ctx: FeishuMessageContext = { + chatId: event.message.chat_id, + messageId: event.message.message_id, + senderId: event.sender.sender_id.user_id || event.sender.sender_id.open_id || "", + senderOpenId: event.sender.sender_id.open_id || "", + chatType: event.message.chat_type, + mentionedBot, + rootId: event.message.root_id || undefined, + parentId: event.message.parent_id || undefined, + content, + contentType: event.message.message_type, + }; + + // Detect mention forward request: message mentions bot + at least one other user + if (isMentionForwardRequest(event, botOpenId)) { + const mentionTargets = extractMentionTargets(event, botOpenId); + if (mentionTargets.length > 0) { + ctx.mentionTargets = mentionTargets; + // Extract message body (remove all @ placeholders) + const allMentionKeys = (event.message.mentions ?? []).map((m) => m.key); + ctx.mentionMessageBody = extractMessageBody(content, allMentionKeys); + } + } + + return ctx; +} + +export async function handleFeishuMessage(params: { + cfg: ClawdbotConfig; + event: FeishuMessageEvent; + botOpenId?: string; + runtime?: RuntimeEnv; + chatHistories?: Map; + accountId?: string; +}): Promise { + const { cfg, event, botOpenId, runtime, chatHistories, accountId } = params; + + // Resolve account with merged config + const account = resolveFeishuAccount({ cfg, accountId }); + const feishuCfg = account.config; + + const log = runtime?.log ?? console.log; + const error = runtime?.error ?? console.error; + + // Dedup check: skip if this message was already processed + const messageId = event.message.message_id; + if (!tryRecordMessage(messageId)) { + log(`feishu: skipping duplicate message ${messageId}`); + return; + } + + let ctx = parseFeishuMessageEvent(event, botOpenId); + const isGroup = ctx.chatType === "group"; + + // Resolve sender display name (best-effort) so the agent can attribute messages correctly. + const senderResult = await resolveFeishuSenderName({ + account, + senderOpenId: ctx.senderOpenId, + log, + }); + if (senderResult.name) ctx = { ...ctx, senderName: senderResult.name }; + + // Track permission error to inform agent later (with cooldown to avoid repetition) + let permissionErrorForAgent: PermissionError | undefined; + if (senderResult.permissionError) { + const appKey = account.appId ?? "default"; + const now = Date.now(); + const lastNotified = permissionErrorNotifiedAt.get(appKey) ?? 0; + + if (now - lastNotified > PERMISSION_ERROR_COOLDOWN_MS) { + permissionErrorNotifiedAt.set(appKey, now); + permissionErrorForAgent = senderResult.permissionError; + } + } + + log(`feishu[${account.accountId}]: received message from ${ctx.senderOpenId} in ${ctx.chatId} (${ctx.chatType})`); + + // Log mention targets if detected + if (ctx.mentionTargets && ctx.mentionTargets.length > 0) { + const names = ctx.mentionTargets.map((t) => t.name).join(", "); + log(`feishu[${account.accountId}]: detected @ forward request, targets: [${names}]`); + } + + const historyLimit = Math.max( + 0, + feishuCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT, + ); + + if (isGroup) { + const groupPolicy = feishuCfg?.groupPolicy ?? "open"; + const groupAllowFrom = feishuCfg?.groupAllowFrom ?? []; + // DEBUG: log(`feishu[${account.accountId}]: groupPolicy=${groupPolicy}`); + const groupConfig = resolveFeishuGroupConfig({ cfg: feishuCfg, groupId: ctx.chatId }); + + // Check if this GROUP is allowed (groupAllowFrom contains group IDs like oc_xxx, not user IDs) + const groupAllowed = isFeishuGroupAllowed({ + groupPolicy, + allowFrom: groupAllowFrom, + senderId: ctx.chatId, // Check group ID, not sender ID + senderName: undefined, + }); + + if (!groupAllowed) { + log(`feishu[${account.accountId}]: sender ${ctx.senderOpenId} not in group allowlist`); + return; + } + + // Additional sender-level allowlist check if group has specific allowFrom config + const senderAllowFrom = groupConfig?.allowFrom ?? []; + if (senderAllowFrom.length > 0) { + const senderAllowed = isFeishuGroupAllowed({ + groupPolicy: "allowlist", + allowFrom: senderAllowFrom, + senderId: ctx.senderOpenId, + senderName: ctx.senderName, + }); + if (!senderAllowed) { + log(`feishu: sender ${ctx.senderOpenId} not in group ${ctx.chatId} sender allowlist`); + return; + } + } + + const { requireMention } = resolveFeishuReplyPolicy({ + isDirectMessage: false, + globalConfig: feishuCfg, + groupConfig, + }); + + if (requireMention && !ctx.mentionedBot) { + log(`feishu[${account.accountId}]: message in group ${ctx.chatId} did not mention bot, recording to history`); + if (chatHistories) { + recordPendingHistoryEntryIfEnabled({ + historyMap: chatHistories, + historyKey: ctx.chatId, + limit: historyLimit, + entry: { + sender: ctx.senderOpenId, + body: `${ctx.senderName ?? ctx.senderOpenId}: ${ctx.content}`, + timestamp: Date.now(), + messageId: ctx.messageId, + }, + }); + } + return; + } + } else { + const dmPolicy = feishuCfg?.dmPolicy ?? "pairing"; + const allowFrom = feishuCfg?.allowFrom ?? []; + + if (dmPolicy === "allowlist") { + const match = resolveFeishuAllowlistMatch({ + allowFrom, + senderId: ctx.senderOpenId, + }); + if (!match.allowed) { + log(`feishu[${account.accountId}]: sender ${ctx.senderOpenId} not in DM allowlist`); + return; + } + } + } + + try { + const core = getFeishuRuntime(); + + // In group chats, the session is scoped to the group, but the *speaker* is the sender. + // Using a group-scoped From causes the agent to treat different users as the same person. + const feishuFrom = `feishu:${ctx.senderOpenId}`; + const feishuTo = isGroup ? `chat:${ctx.chatId}` : `user:${ctx.senderOpenId}`; + + // Resolve peer ID for session routing + // When topicSessionMode is enabled, messages within a topic (identified by root_id) + // get a separate session from the main group chat. + let peerId = isGroup ? ctx.chatId : ctx.senderOpenId; + if (isGroup && ctx.rootId) { + const groupConfig = resolveFeishuGroupConfig({ cfg: feishuCfg, groupId: ctx.chatId }); + const topicSessionMode = groupConfig?.topicSessionMode ?? feishuCfg?.topicSessionMode ?? "disabled"; + if (topicSessionMode === "enabled") { + // Use chatId:topic:rootId as peer ID for topic-scoped sessions + peerId = `${ctx.chatId}:topic:${ctx.rootId}`; + log(`feishu[${account.accountId}]: topic session isolation enabled, peer=${peerId}`); + } + } + + let route = core.channel.routing.resolveAgentRoute({ + cfg, + channel: "feishu", + accountId: account.accountId, + peer: { + kind: isGroup ? "group" : "direct", + id: peerId, + }, + }); + + // Dynamic agent creation for DM users + // When enabled, creates a unique agent instance with its own workspace for each DM user. + let effectiveCfg = cfg; + if (!isGroup && route.matchedBy === "default") { + const dynamicCfg = feishuCfg?.dynamicAgentCreation as DynamicAgentCreationConfig | undefined; + if (dynamicCfg?.enabled) { + const runtime = getFeishuRuntime(); + const result = await maybeCreateDynamicAgent({ + cfg, + runtime, + senderOpenId: ctx.senderOpenId, + dynamicCfg, + log: (msg) => log(msg), + }); + if (result.created) { + effectiveCfg = result.updatedCfg; + // Re-resolve route with updated config + route = core.channel.routing.resolveAgentRoute({ + cfg: result.updatedCfg, + channel: "feishu", + accountId: account.accountId, + peer: { kind: "dm", id: ctx.senderOpenId }, + }); + log(`feishu[${account.accountId}]: dynamic agent created, new route: ${route.sessionKey}`); + } + } + } + + const preview = ctx.content.replace(/\s+/g, " ").slice(0, 160); + const inboundLabel = isGroup + ? `Feishu[${account.accountId}] message in group ${ctx.chatId}` + : `Feishu[${account.accountId}] DM from ${ctx.senderOpenId}`; + + core.system.enqueueSystemEvent(`${inboundLabel}: ${preview}`, { + sessionKey: route.sessionKey, + contextKey: `feishu:message:${ctx.chatId}:${ctx.messageId}`, + }); + + // Resolve media from message + const mediaMaxBytes = (feishuCfg?.mediaMaxMb ?? 30) * 1024 * 1024; // 30MB default + const mediaList = await resolveFeishuMediaList({ + cfg, + messageId: ctx.messageId, + messageType: event.message.message_type, + content: event.message.content, + maxBytes: mediaMaxBytes, + log, + accountId: account.accountId, + }); + const mediaPayload = buildFeishuMediaPayload(mediaList); + + // Fetch quoted/replied message content if parentId exists + let quotedContent: string | undefined; + if (ctx.parentId) { + try { + const quotedMsg = await getMessageFeishu({ cfg, messageId: ctx.parentId, accountId: account.accountId }); + if (quotedMsg) { + quotedContent = quotedMsg.content; + log(`feishu[${account.accountId}]: fetched quoted message: ${quotedContent?.slice(0, 100)}`); + } + } catch (err) { + log(`feishu[${account.accountId}]: failed to fetch quoted message: ${String(err)}`); + } + } + + const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg); + + // Build message body with quoted content if available + let messageBody = ctx.content; + if (quotedContent) { + messageBody = `[Replying to: "${quotedContent}"]\n\n${ctx.content}`; + } + + // Include a readable speaker label so the model can attribute instructions. + // (DMs already have per-sender sessions, but the prefix is still useful for clarity.) + const speaker = ctx.senderName ?? ctx.senderOpenId; + messageBody = `${speaker}: ${messageBody}`; + + // If there are mention targets, inform the agent that replies will auto-mention them + if (ctx.mentionTargets && ctx.mentionTargets.length > 0) { + const targetNames = ctx.mentionTargets.map((t) => t.name).join(", "); + messageBody += `\n\n[System: Your reply will automatically @mention: ${targetNames}. Do not write @xxx yourself.]`; + } + + const envelopeFrom = isGroup ? `${ctx.chatId}:${ctx.senderOpenId}` : ctx.senderOpenId; + + // If there's a permission error, dispatch a separate notification first + if (permissionErrorForAgent) { + const grantUrl = permissionErrorForAgent.grantUrl ?? ""; + const permissionNotifyBody = `[System: The bot encountered a Feishu API permission error. Please inform the user about this issue and provide the permission grant URL for the admin to authorize. Permission grant URL: ${grantUrl}]`; + + const permissionBody = core.channel.reply.formatAgentEnvelope({ + channel: "Feishu", + from: envelopeFrom, + timestamp: new Date(), + envelope: envelopeOptions, + body: permissionNotifyBody, + }); + + const permissionCtx = core.channel.reply.finalizeInboundContext({ + Body: permissionBody, + RawBody: permissionNotifyBody, + CommandBody: permissionNotifyBody, + From: feishuFrom, + To: feishuTo, + SessionKey: route.sessionKey, + AccountId: route.accountId, + ChatType: isGroup ? "group" : "direct", + GroupSubject: isGroup ? ctx.chatId : undefined, + SenderName: "system", + SenderId: "system", + Provider: "feishu" as const, + Surface: "feishu" as const, + MessageSid: `${ctx.messageId}:permission-error`, + Timestamp: Date.now(), + WasMentioned: false, + CommandAuthorized: true, + OriginatingChannel: "feishu" as const, + OriginatingTo: feishuTo, + }); + + const { dispatcher: permDispatcher, replyOptions: permReplyOptions, markDispatchIdle: markPermIdle } = + createFeishuReplyDispatcher({ + cfg, + agentId: route.agentId, + runtime: runtime as RuntimeEnv, + chatId: ctx.chatId, + replyToMessageId: ctx.messageId, + accountId: account.accountId, + }); + + log(`feishu[${account.accountId}]: dispatching permission error notification to agent`); + + await core.channel.reply.dispatchReplyFromConfig({ + ctx: permissionCtx, + cfg, + dispatcher: permDispatcher, + replyOptions: permReplyOptions, + }); + + markPermIdle(); + } + + const body = core.channel.reply.formatAgentEnvelope({ + channel: "Feishu", + from: envelopeFrom, + timestamp: new Date(), + envelope: envelopeOptions, + body: messageBody, + }); + + let combinedBody = body; + const historyKey = isGroup ? ctx.chatId : undefined; + + if (isGroup && historyKey && chatHistories) { + combinedBody = buildPendingHistoryContextFromMap({ + historyMap: chatHistories, + historyKey, + limit: historyLimit, + currentMessage: combinedBody, + formatEntry: (entry) => + core.channel.reply.formatAgentEnvelope({ + channel: "Feishu", + // Preserve speaker identity in group history as well. + from: `${ctx.chatId}:${entry.sender}`, + timestamp: entry.timestamp, + body: entry.body, + envelope: envelopeOptions, + }), + }); + } + + const ctxPayload = core.channel.reply.finalizeInboundContext({ + Body: combinedBody, + RawBody: ctx.content, + CommandBody: ctx.content, + From: feishuFrom, + To: feishuTo, + SessionKey: route.sessionKey, + AccountId: route.accountId, + ChatType: isGroup ? "group" : "direct", + GroupSubject: isGroup ? ctx.chatId : undefined, + SenderName: ctx.senderName ?? ctx.senderOpenId, + SenderId: ctx.senderOpenId, + Provider: "feishu" as const, + Surface: "feishu" as const, + MessageSid: ctx.messageId, + Timestamp: Date.now(), + WasMentioned: ctx.mentionedBot, + CommandAuthorized: true, + OriginatingChannel: "feishu" as const, + OriginatingTo: feishuTo, + ...mediaPayload, + }); + + const { dispatcher, replyOptions, markDispatchIdle } = createFeishuReplyDispatcher({ + cfg, + agentId: route.agentId, + runtime: runtime as RuntimeEnv, + chatId: ctx.chatId, + replyToMessageId: ctx.messageId, + mentionTargets: ctx.mentionTargets, + accountId: account.accountId, + }); + + log(`feishu[${account.accountId}]: dispatching to agent (session=${route.sessionKey})`); + + const { queuedFinal, counts } = await core.channel.reply.dispatchReplyFromConfig({ + ctx: ctxPayload, + cfg, + dispatcher, + replyOptions, + }); + + markDispatchIdle(); + + if (isGroup && historyKey && chatHistories) { + clearHistoryEntriesIfEnabled({ + historyMap: chatHistories, + historyKey, + limit: historyLimit, + }); + } + + log(`feishu[${account.accountId}]: dispatch complete (queuedFinal=${queuedFinal}, replies=${counts.final})`); + } catch (err) { + error(`feishu[${account.accountId}]: failed to dispatch message: ${String(err)}`); + } +} diff --git a/feishu/src/channel.ts b/feishu/src/channel.ts new file mode 100644 index 00000000..93bea26c --- /dev/null +++ b/feishu/src/channel.ts @@ -0,0 +1,334 @@ +import type { ChannelPlugin, ClawdbotConfig } from "openclaw/plugin-sdk"; +import { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk"; +import type { ResolvedFeishuAccount, FeishuConfig } from "./types.js"; +import { + resolveFeishuAccount, + resolveFeishuCredentials, + listFeishuAccountIds, + resolveDefaultFeishuAccountId, +} from "./accounts.js"; +import { feishuOutbound } from "./outbound.js"; +import { probeFeishu } from "./probe.js"; +import { resolveFeishuGroupToolPolicy } from "./policy.js"; +import { normalizeFeishuTarget, looksLikeFeishuId, formatFeishuTarget } from "./targets.js"; +import { sendMessageFeishu } from "./send.js"; +import { + listFeishuDirectoryPeers, + listFeishuDirectoryGroups, + listFeishuDirectoryPeersLive, + listFeishuDirectoryGroupsLive, +} from "./directory.js"; +import { feishuOnboardingAdapter } from "./onboarding.js"; + +const meta = { + id: "feishu", + label: "Feishu", + selectionLabel: "Feishu/Lark (飞书)", + docsPath: "/channels/feishu", + docsLabel: "feishu", + blurb: "飞书/Lark enterprise messaging.", + aliases: ["lark"], + order: 70, +} as const; + +export const feishuPlugin: ChannelPlugin = { + id: "feishu", + meta: { + ...meta, + }, + pairing: { + idLabel: "feishuUserId", + normalizeAllowEntry: (entry) => entry.replace(/^(feishu|user|open_id):/i, ""), + notifyApproval: async ({ cfg, id, accountId }) => { + await sendMessageFeishu({ + cfg, + to: id, + text: PAIRING_APPROVED_MESSAGE, + accountId, + }); + }, + }, + capabilities: { + chatTypes: ["direct", "channel"], + polls: false, + threads: true, + media: true, + reactions: true, + edit: true, + reply: true, + }, + agentPrompt: { + messageToolHints: () => [ + "- Feishu targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `user:open_id` or `chat:chat_id`.", + "- Feishu supports interactive cards for rich messages.", + ], + }, + groups: { + resolveToolPolicy: resolveFeishuGroupToolPolicy, + }, + reload: { configPrefixes: ["channels.feishu"] }, + configSchema: { + schema: { + type: "object", + additionalProperties: false, + properties: { + enabled: { type: "boolean" }, + appId: { type: "string" }, + appSecret: { type: "string" }, + encryptKey: { type: "string" }, + verificationToken: { type: "string" }, + domain: { + oneOf: [ + { type: "string", enum: ["feishu", "lark"] }, + { type: "string", format: "uri", pattern: "^https://" }, + ], + }, + connectionMode: { type: "string", enum: ["websocket", "webhook"] }, + webhookPath: { type: "string" }, + webhookPort: { type: "integer", minimum: 1 }, + dmPolicy: { type: "string", enum: ["open", "pairing", "allowlist"] }, + allowFrom: { type: "array", items: { oneOf: [{ type: "string" }, { type: "number" }] } }, + groupPolicy: { type: "string", enum: ["open", "allowlist", "disabled"] }, + groupAllowFrom: { type: "array", items: { oneOf: [{ type: "string" }, { type: "number" }] } }, + requireMention: { type: "boolean" }, + topicSessionMode: { type: "string", enum: ["disabled", "enabled"] }, + historyLimit: { type: "integer", minimum: 0 }, + dmHistoryLimit: { type: "integer", minimum: 0 }, + textChunkLimit: { type: "integer", minimum: 1 }, + chunkMode: { type: "string", enum: ["length", "newline"] }, + mediaMaxMb: { type: "number", minimum: 0 }, + renderMode: { type: "string", enum: ["auto", "raw", "card"] }, + accounts: { + type: "object", + additionalProperties: { + type: "object", + properties: { + enabled: { type: "boolean" }, + name: { type: "string" }, + appId: { type: "string" }, + appSecret: { type: "string" }, + encryptKey: { type: "string" }, + verificationToken: { type: "string" }, + domain: { type: "string", enum: ["feishu", "lark"] }, + connectionMode: { type: "string", enum: ["websocket", "webhook"] }, + }, + }, + }, + }, + }, + }, + config: { + listAccountIds: (cfg) => listFeishuAccountIds(cfg), + resolveAccount: (cfg, accountId) => resolveFeishuAccount({ cfg, accountId }), + defaultAccountId: (cfg) => resolveDefaultFeishuAccountId(cfg), + setAccountEnabled: ({ cfg, accountId, enabled }) => { + const account = resolveFeishuAccount({ cfg, accountId }); + const isDefault = accountId === DEFAULT_ACCOUNT_ID; + + if (isDefault) { + // For default account, set top-level enabled + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...cfg.channels?.feishu, + enabled, + }, + }, + }; + } + + // For named accounts, set enabled in accounts[accountId] + const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined; + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...feishuCfg, + accounts: { + ...feishuCfg?.accounts, + [accountId]: { + ...feishuCfg?.accounts?.[accountId], + enabled, + }, + }, + }, + }, + }; + }, + deleteAccount: ({ cfg, accountId }) => { + const isDefault = accountId === DEFAULT_ACCOUNT_ID; + + if (isDefault) { + // Delete entire feishu config + const next = { ...cfg } as ClawdbotConfig; + const nextChannels = { ...cfg.channels }; + delete (nextChannels as Record).feishu; + if (Object.keys(nextChannels).length > 0) { + next.channels = nextChannels; + } else { + delete next.channels; + } + return next; + } + + // Delete specific account from accounts + const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined; + const accounts = { ...feishuCfg?.accounts }; + delete accounts[accountId]; + + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...feishuCfg, + accounts: Object.keys(accounts).length > 0 ? accounts : undefined, + }, + }, + }; + }, + isConfigured: (account) => account.configured, + describeAccount: (account) => ({ + accountId: account.accountId, + enabled: account.enabled, + configured: account.configured, + name: account.name, + appId: account.appId, + domain: account.domain, + }), + resolveAllowFrom: ({ cfg, accountId }) => { + const account = resolveFeishuAccount({ cfg, accountId }); + return account.config?.allowFrom ?? []; + }, + formatAllowFrom: ({ allowFrom }) => + allowFrom + .map((entry) => String(entry).trim()) + .filter(Boolean) + .map((entry) => entry.toLowerCase()), + }, + security: { + collectWarnings: ({ cfg, accountId }) => { + const account = resolveFeishuAccount({ cfg, accountId }); + const feishuCfg = account.config; + const defaultGroupPolicy = (cfg.channels as Record | undefined)?.defaults?.groupPolicy; + const groupPolicy = feishuCfg?.groupPolicy ?? defaultGroupPolicy ?? "allowlist"; + if (groupPolicy !== "open") return []; + return [ + `- Feishu[${account.accountId}] groups: groupPolicy="open" allows any member to trigger (mention-gated). Set channels.feishu.groupPolicy="allowlist" + channels.feishu.groupAllowFrom to restrict senders.`, + ]; + }, + }, + setup: { + resolveAccountId: () => DEFAULT_ACCOUNT_ID, + applyAccountConfig: ({ cfg, accountId }) => { + const isDefault = !accountId || accountId === DEFAULT_ACCOUNT_ID; + + if (isDefault) { + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...cfg.channels?.feishu, + enabled: true, + }, + }, + }; + } + + const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined; + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...feishuCfg, + accounts: { + ...feishuCfg?.accounts, + [accountId]: { + ...feishuCfg?.accounts?.[accountId], + enabled: true, + }, + }, + }, + }, + }; + }, + }, + onboarding: feishuOnboardingAdapter, + messaging: { + normalizeTarget: normalizeFeishuTarget, + targetResolver: { + looksLikeId: looksLikeFeishuId, + hint: "", + }, + }, + directory: { + self: async () => null, + listPeers: async ({ cfg, query, limit, accountId }) => + listFeishuDirectoryPeers({ cfg, query, limit, accountId }), + listGroups: async ({ cfg, query, limit, accountId }) => + listFeishuDirectoryGroups({ cfg, query, limit, accountId }), + listPeersLive: async ({ cfg, query, limit, accountId }) => + listFeishuDirectoryPeersLive({ cfg, query, limit, accountId }), + listGroupsLive: async ({ cfg, query, limit, accountId }) => + listFeishuDirectoryGroupsLive({ cfg, query, limit, accountId }), + }, + outbound: feishuOutbound, + status: { + defaultRuntime: { + accountId: DEFAULT_ACCOUNT_ID, + running: false, + lastStartAt: null, + lastStopAt: null, + lastError: null, + port: null, + }, + buildChannelSummary: ({ snapshot }) => ({ + configured: snapshot.configured ?? false, + running: snapshot.running ?? false, + lastStartAt: snapshot.lastStartAt ?? null, + lastStopAt: snapshot.lastStopAt ?? null, + lastError: snapshot.lastError ?? null, + port: snapshot.port ?? null, + probe: snapshot.probe, + lastProbeAt: snapshot.lastProbeAt ?? null, + }), + probeAccount: async ({ cfg, accountId }) => { + const account = resolveFeishuAccount({ cfg, accountId }); + return await probeFeishu(account); + }, + buildAccountSnapshot: ({ account, runtime, probe }) => ({ + accountId: account.accountId, + enabled: account.enabled, + configured: account.configured, + name: account.name, + appId: account.appId, + domain: account.domain, + running: runtime?.running ?? false, + lastStartAt: runtime?.lastStartAt ?? null, + lastStopAt: runtime?.lastStopAt ?? null, + lastError: runtime?.lastError ?? null, + port: runtime?.port ?? null, + probe, + }), + }, + gateway: { + startAccount: async (ctx) => { + const { monitorFeishuProvider } = await import("./monitor.js"); + const account = resolveFeishuAccount({ cfg: ctx.cfg, accountId: ctx.accountId }); + const port = account.config?.webhookPort ?? null; + ctx.setStatus({ accountId: ctx.accountId, port }); + ctx.log?.info(`starting feishu[${ctx.accountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`); + return monitorFeishuProvider({ + config: ctx.cfg, + runtime: ctx.runtime, + abortSignal: ctx.abortSignal, + accountId: ctx.accountId, + }); + }, + }, +}; diff --git a/feishu/src/client.ts b/feishu/src/client.ts new file mode 100644 index 00000000..60df8978 --- /dev/null +++ b/feishu/src/client.ts @@ -0,0 +1,114 @@ +import * as Lark from "@larksuiteoapi/node-sdk"; +import type { FeishuDomain, ResolvedFeishuAccount } from "./types.js"; + +// Multi-account client cache +const clientCache = new Map< + string, + { + client: Lark.Client; + config: { appId: string; appSecret: string; domain?: FeishuDomain }; + } +>(); + +function resolveDomain(domain: FeishuDomain | undefined): Lark.Domain | string { + if (domain === "lark") return Lark.Domain.Lark; + if (domain === "feishu" || !domain) return Lark.Domain.Feishu; + return domain.replace(/\/+$/, ""); // Custom URL for private deployment +} + +/** + * Credentials needed to create a Feishu client. + * Both FeishuConfig and ResolvedFeishuAccount satisfy this interface. + */ +export type FeishuClientCredentials = { + accountId?: string; + appId?: string; + appSecret?: string; + domain?: FeishuDomain; +}; + +/** + * Create or get a cached Feishu client for an account. + * Accepts any object with appId, appSecret, and optional domain/accountId. + */ +export function createFeishuClient(creds: FeishuClientCredentials): Lark.Client { + const { accountId = "default", appId, appSecret, domain } = creds; + + if (!appId || !appSecret) { + throw new Error(`Feishu credentials not configured for account "${accountId}"`); + } + + // Check cache + const cached = clientCache.get(accountId); + if ( + cached && + cached.config.appId === appId && + cached.config.appSecret === appSecret && + cached.config.domain === domain + ) { + return cached.client; + } + + // Create new client + const client = new Lark.Client({ + appId, + appSecret, + appType: Lark.AppType.SelfBuild, + domain: resolveDomain(domain), + }); + + // Cache it + clientCache.set(accountId, { + client, + config: { appId, appSecret, domain }, + }); + + return client; +} + +/** + * Create a Feishu WebSocket client for an account. + * Note: WSClient is not cached since each call creates a new connection. + */ +export function createFeishuWSClient(account: ResolvedFeishuAccount): Lark.WSClient { + const { accountId, appId, appSecret, domain } = account; + + if (!appId || !appSecret) { + throw new Error(`Feishu credentials not configured for account "${accountId}"`); + } + + return new Lark.WSClient({ + appId, + appSecret, + domain: resolveDomain(domain), + loggerLevel: Lark.LoggerLevel.info, + }); +} + +/** + * Create an event dispatcher for an account. + */ +export function createEventDispatcher(account: ResolvedFeishuAccount): Lark.EventDispatcher { + return new Lark.EventDispatcher({ + encryptKey: account.encryptKey, + verificationToken: account.verificationToken, + }); +} + +/** + * Get a cached client for an account (if exists). + */ +export function getFeishuClient(accountId: string): Lark.Client | null { + return clientCache.get(accountId)?.client ?? null; +} + +/** + * Clear client cache for a specific account or all accounts. + */ +export function clearClientCache(accountId?: string): void { + if (accountId) { + clientCache.delete(accountId); + } else { + clientCache.clear(); + } +} diff --git a/feishu/src/config-schema.ts b/feishu/src/config-schema.ts new file mode 100644 index 00000000..89b25639 --- /dev/null +++ b/feishu/src/config-schema.ts @@ -0,0 +1,199 @@ +import { z } from "zod"; +export { z }; + +const DmPolicySchema = z.enum(["open", "pairing", "allowlist"]); +const GroupPolicySchema = z.enum(["open", "allowlist", "disabled"]); +const FeishuDomainSchema = z.union([ + z.enum(["feishu", "lark"]), + z.string().url().startsWith("https://"), +]); +const FeishuConnectionModeSchema = z.enum(["websocket", "webhook"]); + +const ToolPolicySchema = z + .object({ + allow: z.array(z.string()).optional(), + deny: z.array(z.string()).optional(), + }) + .strict() + .optional(); + +const DmConfigSchema = z + .object({ + enabled: z.boolean().optional(), + systemPrompt: z.string().optional(), + }) + .strict() + .optional(); + +const MarkdownConfigSchema = z + .object({ + mode: z.enum(["native", "escape", "strip"]).optional(), + tableMode: z.enum(["native", "ascii", "simple"]).optional(), + }) + .strict() + .optional(); + +// Message render mode: auto (default) = detect markdown, raw = plain text, card = always card +const RenderModeSchema = z.enum(["auto", "raw", "card"]).optional(); + +const BlockStreamingCoalesceSchema = z + .object({ + enabled: z.boolean().optional(), + minDelayMs: z.number().int().positive().optional(), + maxDelayMs: z.number().int().positive().optional(), + }) + .strict() + .optional(); + +const ChannelHeartbeatVisibilitySchema = z + .object({ + visibility: z.enum(["visible", "hidden"]).optional(), + intervalMs: z.number().int().positive().optional(), + }) + .strict() + .optional(); + +/** + * Dynamic agent creation configuration. + * When enabled, a new agent is created for each unique DM user. + */ +const DynamicAgentCreationSchema = z + .object({ + enabled: z.boolean().optional(), + workspaceTemplate: z.string().optional(), + agentDirTemplate: z.string().optional(), + maxAgents: z.number().int().positive().optional(), + }) + .strict() + .optional(); + +/** + * Feishu tools configuration. + * Controls which tool categories are enabled. + * + * Dependencies: + * - wiki requires doc (wiki content is edited via doc tools) + * - perm can work independently but is typically used with drive + */ +const FeishuToolsConfigSchema = z + .object({ + doc: z.boolean().optional(), // Document operations (default: true) + wiki: z.boolean().optional(), // Knowledge base operations (default: true, requires doc) + drive: z.boolean().optional(), // Cloud storage operations (default: true) + perm: z.boolean().optional(), // Permission management (default: false, sensitive) + scopes: z.boolean().optional(), // App scopes diagnostic (default: true) + }) + .strict() + .optional(); + +/** + * Topic session isolation mode for group chats. + * - "disabled" (default): All messages in a group share one session + * - "enabled": Messages in different topics get separate sessions + * + * When enabled, the session key becomes `chat:{chatId}:topic:{rootId}` + * for messages within a topic thread, allowing isolated conversations. + */ +const TopicSessionModeSchema = z.enum(["disabled", "enabled"]).optional(); + +export const FeishuGroupSchema = z + .object({ + requireMention: z.boolean().optional(), + tools: ToolPolicySchema, + skills: z.array(z.string()).optional(), + enabled: z.boolean().optional(), + allowFrom: z.array(z.union([z.string(), z.number()])).optional(), + systemPrompt: z.string().optional(), + topicSessionMode: TopicSessionModeSchema, + }) + .strict(); + +/** + * Per-account configuration. + * All fields are optional - missing fields inherit from top-level config. + */ +export const FeishuAccountConfigSchema = z + .object({ + enabled: z.boolean().optional(), + name: z.string().optional(), // Display name for this account + appId: z.string().optional(), + appSecret: z.string().optional(), + encryptKey: z.string().optional(), + verificationToken: z.string().optional(), + domain: FeishuDomainSchema.optional(), + connectionMode: FeishuConnectionModeSchema.optional(), + webhookPath: z.string().optional(), + webhookPort: z.number().int().positive().optional(), + capabilities: z.array(z.string()).optional(), + markdown: MarkdownConfigSchema, + configWrites: z.boolean().optional(), + dmPolicy: DmPolicySchema.optional(), + allowFrom: z.array(z.union([z.string(), z.number()])).optional(), + groupPolicy: GroupPolicySchema.optional(), + groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(), + requireMention: z.boolean().optional(), + groups: z.record(z.string(), FeishuGroupSchema.optional()).optional(), + historyLimit: z.number().int().min(0).optional(), + dmHistoryLimit: z.number().int().min(0).optional(), + dms: z.record(z.string(), DmConfigSchema).optional(), + textChunkLimit: z.number().int().positive().optional(), + chunkMode: z.enum(["length", "newline"]).optional(), + blockStreamingCoalesce: BlockStreamingCoalesceSchema, + mediaMaxMb: z.number().positive().optional(), + heartbeat: ChannelHeartbeatVisibilitySchema, + renderMode: RenderModeSchema, + tools: FeishuToolsConfigSchema, + }) + .strict(); + +export const FeishuConfigSchema = z + .object({ + enabled: z.boolean().optional(), + // Top-level credentials (backward compatible for single-account mode) + appId: z.string().optional(), + appSecret: z.string().optional(), + encryptKey: z.string().optional(), + verificationToken: z.string().optional(), + domain: FeishuDomainSchema.optional().default("feishu"), + connectionMode: FeishuConnectionModeSchema.optional().default("websocket"), + webhookPath: z.string().optional().default("/feishu/events"), + webhookPort: z.number().int().positive().optional(), + capabilities: z.array(z.string()).optional(), + markdown: MarkdownConfigSchema, + configWrites: z.boolean().optional(), + dmPolicy: DmPolicySchema.optional().default("pairing"), + allowFrom: z.array(z.union([z.string(), z.number()])).optional(), + groupPolicy: GroupPolicySchema.optional().default("allowlist"), + groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(), + requireMention: z.boolean().optional().default(true), + groups: z.record(z.string(), FeishuGroupSchema.optional()).optional(), + topicSessionMode: TopicSessionModeSchema, + historyLimit: z.number().int().min(0).optional(), + dmHistoryLimit: z.number().int().min(0).optional(), + dms: z.record(z.string(), DmConfigSchema).optional(), + textChunkLimit: z.number().int().positive().optional(), + chunkMode: z.enum(["length", "newline"]).optional(), + blockStreamingCoalesce: BlockStreamingCoalesceSchema, + mediaMaxMb: z.number().positive().optional(), + heartbeat: ChannelHeartbeatVisibilitySchema, + renderMode: RenderModeSchema, // raw = plain text (default), card = interactive card with markdown + tools: FeishuToolsConfigSchema, + // Dynamic agent creation for DM users + dynamicAgentCreation: DynamicAgentCreationSchema, + // Multi-account configuration + accounts: z.record(z.string(), FeishuAccountConfigSchema.optional()).optional(), + }) + .strict() + .superRefine((value, ctx) => { + if (value.dmPolicy === "open") { + const allowFrom = value.allowFrom ?? []; + const hasWildcard = allowFrom.some((entry) => String(entry).trim() === "*"); + if (!hasWildcard) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["allowFrom"], + message: 'channels.feishu.dmPolicy="open" requires channels.feishu.allowFrom to include "*"', + }); + } + } + }); diff --git a/feishu/src/directory.ts b/feishu/src/directory.ts new file mode 100644 index 00000000..96b92e85 --- /dev/null +++ b/feishu/src/directory.ts @@ -0,0 +1,165 @@ +import type { ClawdbotConfig } from "openclaw/plugin-sdk"; +import { createFeishuClient } from "./client.js"; +import { resolveFeishuAccount } from "./accounts.js"; +import { normalizeFeishuTarget } from "./targets.js"; + +export type FeishuDirectoryPeer = { + kind: "user"; + id: string; + name?: string; +}; + +export type FeishuDirectoryGroup = { + kind: "group"; + id: string; + name?: string; +}; + +export async function listFeishuDirectoryPeers(params: { + cfg: ClawdbotConfig; + query?: string; + limit?: number; + accountId?: string; +}): Promise { + const account = resolveFeishuAccount({ cfg: params.cfg, accountId: params.accountId }); + const feishuCfg = account.config; + const q = params.query?.trim().toLowerCase() || ""; + const ids = new Set(); + + for (const entry of feishuCfg?.allowFrom ?? []) { + const trimmed = String(entry).trim(); + if (trimmed && trimmed !== "*") ids.add(trimmed); + } + + for (const userId of Object.keys(feishuCfg?.dms ?? {})) { + const trimmed = userId.trim(); + if (trimmed) ids.add(trimmed); + } + + return Array.from(ids) + .map((raw) => raw.trim()) + .filter(Boolean) + .map((raw) => normalizeFeishuTarget(raw) ?? raw) + .filter((id) => (q ? id.toLowerCase().includes(q) : true)) + .slice(0, params.limit && params.limit > 0 ? params.limit : undefined) + .map((id) => ({ kind: "user" as const, id })); +} + +export async function listFeishuDirectoryGroups(params: { + cfg: ClawdbotConfig; + query?: string; + limit?: number; + accountId?: string; +}): Promise { + const account = resolveFeishuAccount({ cfg: params.cfg, accountId: params.accountId }); + const feishuCfg = account.config; + const q = params.query?.trim().toLowerCase() || ""; + const ids = new Set(); + + for (const groupId of Object.keys(feishuCfg?.groups ?? {})) { + const trimmed = groupId.trim(); + if (trimmed && trimmed !== "*") ids.add(trimmed); + } + + for (const entry of feishuCfg?.groupAllowFrom ?? []) { + const trimmed = String(entry).trim(); + if (trimmed && trimmed !== "*") ids.add(trimmed); + } + + return Array.from(ids) + .map((raw) => raw.trim()) + .filter(Boolean) + .filter((id) => (q ? id.toLowerCase().includes(q) : true)) + .slice(0, params.limit && params.limit > 0 ? params.limit : undefined) + .map((id) => ({ kind: "group" as const, id })); +} + +export async function listFeishuDirectoryPeersLive(params: { + cfg: ClawdbotConfig; + query?: string; + limit?: number; + accountId?: string; +}): Promise { + const account = resolveFeishuAccount({ cfg: params.cfg, accountId: params.accountId }); + if (!account.configured) { + return listFeishuDirectoryPeers(params); + } + + try { + const client = createFeishuClient(account); + const peers: FeishuDirectoryPeer[] = []; + const limit = params.limit ?? 50; + + const response = await client.contact.user.list({ + params: { + page_size: Math.min(limit, 50), + }, + }); + + if (response.code === 0 && response.data?.items) { + for (const user of response.data.items) { + if (user.open_id) { + const q = params.query?.trim().toLowerCase() || ""; + const name = user.name || ""; + if (!q || user.open_id.toLowerCase().includes(q) || name.toLowerCase().includes(q)) { + peers.push({ + kind: "user", + id: user.open_id, + name: name || undefined, + }); + } + } + if (peers.length >= limit) break; + } + } + + return peers; + } catch { + return listFeishuDirectoryPeers(params); + } +} + +export async function listFeishuDirectoryGroupsLive(params: { + cfg: ClawdbotConfig; + query?: string; + limit?: number; + accountId?: string; +}): Promise { + const account = resolveFeishuAccount({ cfg: params.cfg, accountId: params.accountId }); + if (!account.configured) { + return listFeishuDirectoryGroups(params); + } + + try { + const client = createFeishuClient(account); + const groups: FeishuDirectoryGroup[] = []; + const limit = params.limit ?? 50; + + const response = await client.im.chat.list({ + params: { + page_size: Math.min(limit, 100), + }, + }); + + if (response.code === 0 && response.data?.items) { + for (const chat of response.data.items) { + if (chat.chat_id) { + const q = params.query?.trim().toLowerCase() || ""; + const name = chat.name || ""; + if (!q || chat.chat_id.toLowerCase().includes(q) || name.toLowerCase().includes(q)) { + groups.push({ + kind: "group", + id: chat.chat_id, + name: name || undefined, + }); + } + } + if (groups.length >= limit) break; + } + } + + return groups; + } catch { + return listFeishuDirectoryGroups(params); + } +} diff --git a/feishu/src/doc-schema.ts b/feishu/src/doc-schema.ts new file mode 100644 index 00000000..811835f7 --- /dev/null +++ b/feishu/src/doc-schema.ts @@ -0,0 +1,47 @@ +import { Type, type Static } from "@sinclair/typebox"; + +export const FeishuDocSchema = Type.Union([ + Type.Object({ + action: Type.Literal("read"), + doc_token: Type.String({ description: "Document token (extract from URL /docx/XXX)" }), + }), + Type.Object({ + action: Type.Literal("write"), + doc_token: Type.String({ description: "Document token" }), + content: Type.String({ + description: "Markdown content to write (replaces entire document content)", + }), + }), + Type.Object({ + action: Type.Literal("append"), + doc_token: Type.String({ description: "Document token" }), + content: Type.String({ description: "Markdown content to append to end of document" }), + }), + Type.Object({ + action: Type.Literal("create"), + title: Type.String({ description: "Document title" }), + folder_token: Type.Optional(Type.String({ description: "Target folder token (optional)" })), + }), + Type.Object({ + action: Type.Literal("list_blocks"), + doc_token: Type.String({ description: "Document token" }), + }), + Type.Object({ + action: Type.Literal("get_block"), + doc_token: Type.String({ description: "Document token" }), + block_id: Type.String({ description: "Block ID (from list_blocks)" }), + }), + Type.Object({ + action: Type.Literal("update_block"), + doc_token: Type.String({ description: "Document token" }), + block_id: Type.String({ description: "Block ID (from list_blocks)" }), + content: Type.String({ description: "New text content" }), + }), + Type.Object({ + action: Type.Literal("delete_block"), + doc_token: Type.String({ description: "Document token" }), + block_id: Type.String({ description: "Block ID" }), + }), +]); + +export type FeishuDocParams = Static; diff --git a/feishu/src/docx.ts b/feishu/src/docx.ts new file mode 100644 index 00000000..dc38e32e --- /dev/null +++ b/feishu/src/docx.ts @@ -0,0 +1,480 @@ +import { Type } from "@sinclair/typebox"; +import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; +import { createFeishuClient } from "./client.js"; +import { resolveFeishuAccount, listEnabledFeishuAccounts } from "./accounts.js"; +import type * as Lark from "@larksuiteoapi/node-sdk"; +import { Readable } from "stream"; +import { FeishuDocSchema, type FeishuDocParams } from "./doc-schema.js"; +import { resolveToolsConfig } from "./tools-config.js"; + +// ============ Helpers ============ + +function json(data: unknown) { + return { + content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }], + details: data, + }; +} + +/** Extract image URLs from markdown content */ +function extractImageUrls(markdown: string): string[] { + const regex = /!\[[^\]]*\]\(([^)]+)\)/g; + const urls: string[] = []; + let match; + while ((match = regex.exec(markdown)) !== null) { + const url = match[1].trim(); + if (url.startsWith("http://") || url.startsWith("https://")) { + urls.push(url); + } + } + return urls; +} + +const BLOCK_TYPE_NAMES: Record = { + 1: "Page", + 2: "Text", + 3: "Heading1", + 4: "Heading2", + 5: "Heading3", + 12: "Bullet", + 13: "Ordered", + 14: "Code", + 15: "Quote", + 17: "Todo", + 18: "Bitable", + 21: "Diagram", + 22: "Divider", + 23: "File", + 27: "Image", + 30: "Sheet", + 31: "Table", + 32: "TableCell", +}; + +// Block types that cannot be created via documentBlockChildren.create API +const UNSUPPORTED_CREATE_TYPES = new Set([31, 32]); + +/** Clean blocks for insertion (remove unsupported types and read-only fields) */ +function cleanBlocksForInsert(blocks: any[]): { cleaned: any[]; skipped: string[] } { + const skipped: string[] = []; + const cleaned = blocks + .filter((block) => { + if (UNSUPPORTED_CREATE_TYPES.has(block.block_type)) { + const typeName = BLOCK_TYPE_NAMES[block.block_type] || `type_${block.block_type}`; + skipped.push(typeName); + return false; + } + return true; + }) + .map((block) => { + if (block.block_type === 31 && block.table?.merge_info) { + const { merge_info, ...tableRest } = block.table; + return { ...block, table: tableRest }; + } + return block; + }); + return { cleaned, skipped }; +} + +// ============ Core Functions ============ + +async function convertMarkdown(client: Lark.Client, markdown: string) { + const res = await client.docx.document.convert({ + data: { content_type: "markdown", content: markdown }, + }); + if (res.code !== 0) throw new Error(res.msg); + return { + blocks: res.data?.blocks ?? [], + firstLevelBlockIds: res.data?.first_level_block_ids ?? [], + }; +} + +async function insertBlocks( + client: Lark.Client, + docToken: string, + blocks: any[], + parentBlockId?: string, +): Promise<{ children: any[]; skipped: string[] }> { + const { cleaned, skipped } = cleanBlocksForInsert(blocks); + const blockId = parentBlockId ?? docToken; + + if (cleaned.length === 0) { + return { children: [], skipped }; + } + + const res = await client.docx.documentBlockChildren.create({ + path: { document_id: docToken, block_id: blockId }, + data: { children: cleaned }, + }); + if (res.code !== 0) throw new Error(res.msg); + return { children: res.data?.children ?? [], skipped }; +} + +async function clearDocumentContent(client: Lark.Client, docToken: string) { + const existing = await client.docx.documentBlock.list({ + path: { document_id: docToken }, + }); + if (existing.code !== 0) throw new Error(existing.msg); + + const childIds = + existing.data?.items + ?.filter((b) => b.parent_id === docToken && b.block_type !== 1) + .map((b) => b.block_id) ?? []; + + if (childIds.length > 0) { + const res = await client.docx.documentBlockChildren.batchDelete({ + path: { document_id: docToken, block_id: docToken }, + data: { start_index: 0, end_index: childIds.length }, + }); + if (res.code !== 0) throw new Error(res.msg); + } + + return childIds.length; +} + +async function uploadImageToDocx( + client: Lark.Client, + blockId: string, + imageBuffer: Buffer, + fileName: string, +): Promise { + const res = await client.drive.media.uploadAll({ + data: { + file_name: fileName, + parent_type: "docx_image", + parent_node: blockId, + size: imageBuffer.length, + file: Readable.from(imageBuffer) as any, + }, + }); + + const fileToken = res?.file_token; + if (!fileToken) { + throw new Error("Image upload failed: no file_token returned"); + } + return fileToken; +} + +async function downloadImage(url: string): Promise { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to download image: ${response.status} ${response.statusText}`); + } + return Buffer.from(await response.arrayBuffer()); +} + +async function processImages( + client: Lark.Client, + docToken: string, + markdown: string, + insertedBlocks: any[], +): Promise { + const imageUrls = extractImageUrls(markdown); + if (imageUrls.length === 0) return 0; + + const imageBlocks = insertedBlocks.filter((b) => b.block_type === 27); + + let processed = 0; + for (let i = 0; i < Math.min(imageUrls.length, imageBlocks.length); i++) { + const url = imageUrls[i]; + const blockId = imageBlocks[i].block_id; + + try { + const buffer = await downloadImage(url); + const urlPath = new URL(url).pathname; + const fileName = urlPath.split("/").pop() || `image_${i}.png`; + const fileToken = await uploadImageToDocx(client, blockId, buffer, fileName); + + await client.docx.documentBlock.patch({ + path: { document_id: docToken, block_id: blockId }, + data: { + replace_image: { token: fileToken }, + }, + }); + + processed++; + } catch (err) { + console.error(`Failed to process image ${url}:`, err); + } + } + + return processed; +} + +// ============ Actions ============ + +const STRUCTURED_BLOCK_TYPES = new Set([14, 18, 21, 23, 27, 30, 31, 32]); + +async function readDoc(client: Lark.Client, docToken: string) { + const [contentRes, infoRes, blocksRes] = await Promise.all([ + client.docx.document.rawContent({ path: { document_id: docToken } }), + client.docx.document.get({ path: { document_id: docToken } }), + client.docx.documentBlock.list({ path: { document_id: docToken } }), + ]); + + if (contentRes.code !== 0) throw new Error(contentRes.msg); + + const blocks = blocksRes.data?.items ?? []; + const blockCounts: Record = {}; + const structuredTypes: string[] = []; + + for (const b of blocks) { + const type = b.block_type ?? 0; + const name = BLOCK_TYPE_NAMES[type] || `type_${type}`; + blockCounts[name] = (blockCounts[name] || 0) + 1; + + if (STRUCTURED_BLOCK_TYPES.has(type) && !structuredTypes.includes(name)) { + structuredTypes.push(name); + } + } + + let hint: string | undefined; + if (structuredTypes.length > 0) { + hint = `This document contains ${structuredTypes.join(", ")} which are NOT included in the plain text above. Use feishu_doc with action: "list_blocks" to get full content.`; + } + + return { + title: infoRes.data?.document?.title, + content: contentRes.data?.content, + revision_id: infoRes.data?.document?.revision_id, + block_count: blocks.length, + block_types: blockCounts, + ...(hint && { hint }), + }; +} + +async function createDoc(client: Lark.Client, title: string, folderToken?: string) { + const res = await client.docx.document.create({ + data: { title, folder_token: folderToken }, + }); + if (res.code !== 0) throw new Error(res.msg); + const doc = res.data?.document; + return { + document_id: doc?.document_id, + title: doc?.title, + url: `https://feishu.cn/docx/${doc?.document_id}`, + }; +} + +async function writeDoc(client: Lark.Client, docToken: string, markdown: string) { + const deleted = await clearDocumentContent(client, docToken); + + const { blocks } = await convertMarkdown(client, markdown); + if (blocks.length === 0) { + return { success: true, blocks_deleted: deleted, blocks_added: 0, images_processed: 0 }; + } + + const { children: inserted, skipped } = await insertBlocks(client, docToken, blocks); + const imagesProcessed = await processImages(client, docToken, markdown, inserted); + + return { + success: true, + blocks_deleted: deleted, + blocks_added: inserted.length, + images_processed: imagesProcessed, + ...(skipped.length > 0 && { + warning: `Skipped unsupported block types: ${skipped.join(", ")}. Tables are not supported via this API.`, + }), + }; +} + +async function appendDoc(client: Lark.Client, docToken: string, markdown: string) { + const { blocks } = await convertMarkdown(client, markdown); + if (blocks.length === 0) { + throw new Error("Content is empty"); + } + + const { children: inserted, skipped } = await insertBlocks(client, docToken, blocks); + const imagesProcessed = await processImages(client, docToken, markdown, inserted); + + return { + success: true, + blocks_added: inserted.length, + images_processed: imagesProcessed, + block_ids: inserted.map((b: any) => b.block_id), + ...(skipped.length > 0 && { + warning: `Skipped unsupported block types: ${skipped.join(", ")}. Tables are not supported via this API.`, + }), + }; +} + +async function updateBlock( + client: Lark.Client, + docToken: string, + blockId: string, + content: string, +) { + const blockInfo = await client.docx.documentBlock.get({ + path: { document_id: docToken, block_id: blockId }, + }); + if (blockInfo.code !== 0) throw new Error(blockInfo.msg); + + const res = await client.docx.documentBlock.patch({ + path: { document_id: docToken, block_id: blockId }, + data: { + update_text_elements: { + elements: [{ text_run: { content } }], + }, + }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { success: true, block_id: blockId }; +} + +async function deleteBlock(client: Lark.Client, docToken: string, blockId: string) { + const blockInfo = await client.docx.documentBlock.get({ + path: { document_id: docToken, block_id: blockId }, + }); + if (blockInfo.code !== 0) throw new Error(blockInfo.msg); + + const parentId = blockInfo.data?.block?.parent_id ?? docToken; + + const children = await client.docx.documentBlockChildren.get({ + path: { document_id: docToken, block_id: parentId }, + }); + if (children.code !== 0) throw new Error(children.msg); + + const items = children.data?.items ?? []; + const index = items.findIndex((item: any) => item.block_id === blockId); + if (index === -1) throw new Error("Block not found"); + + const res = await client.docx.documentBlockChildren.batchDelete({ + path: { document_id: docToken, block_id: parentId }, + data: { start_index: index, end_index: index + 1 }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { success: true, deleted_block_id: blockId }; +} + +async function listBlocks(client: Lark.Client, docToken: string) { + const res = await client.docx.documentBlock.list({ + path: { document_id: docToken }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + blocks: res.data?.items ?? [], + }; +} + +async function getBlock(client: Lark.Client, docToken: string, blockId: string) { + const res = await client.docx.documentBlock.get({ + path: { document_id: docToken, block_id: blockId }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + block: res.data?.block, + }; +} + +async function listAppScopes(client: Lark.Client) { + const res = await client.application.scope.list({}); + if (res.code !== 0) throw new Error(res.msg); + + const scopes = res.data?.scopes ?? []; + const granted = scopes.filter((s) => s.grant_status === 1); + const pending = scopes.filter((s) => s.grant_status !== 1); + + return { + granted: granted.map((s) => ({ name: s.scope_name, type: s.scope_type })), + pending: pending.map((s) => ({ name: s.scope_name, type: s.scope_type })), + summary: `${granted.length} granted, ${pending.length} pending`, + }; +} + +// ============ Tool Registration ============ + +export function registerFeishuDocTools(api: OpenClawPluginApi) { + if (!api.config) { + api.logger.debug?.("feishu_doc: No config available, skipping doc tools"); + return; + } + + // Check if any account is configured + const accounts = listEnabledFeishuAccounts(api.config); + if (accounts.length === 0) { + api.logger.debug?.("feishu_doc: No Feishu accounts configured, skipping doc tools"); + return; + } + + // Use first account's config for tools configuration + const firstAccount = accounts[0]; + const toolsCfg = resolveToolsConfig(firstAccount.config.tools); + + // Helper to get client for the default account + const getClient = () => createFeishuClient(firstAccount); + const registered: string[] = []; + + // Main document tool with action-based dispatch + if (toolsCfg.doc) { + api.registerTool( + { + name: "feishu_doc", + label: "Feishu Doc", + description: + "Feishu document operations. Actions: read, write, append, create, list_blocks, get_block, update_block, delete_block", + parameters: FeishuDocSchema, + async execute(_toolCallId, params) { + const p = params as FeishuDocParams; + try { + const client = getClient(); + switch (p.action) { + case "read": + return json(await readDoc(client, p.doc_token)); + case "write": + return json(await writeDoc(client, p.doc_token, p.content)); + case "append": + return json(await appendDoc(client, p.doc_token, p.content)); + case "create": + return json(await createDoc(client, p.title, p.folder_token)); + case "list_blocks": + return json(await listBlocks(client, p.doc_token)); + case "get_block": + return json(await getBlock(client, p.doc_token, p.block_id)); + case "update_block": + return json(await updateBlock(client, p.doc_token, p.block_id, p.content)); + case "delete_block": + return json(await deleteBlock(client, p.doc_token, p.block_id)); + default: + return json({ error: `Unknown action: ${(p as any).action}` }); + } + } catch (err) { + return json({ error: err instanceof Error ? err.message : String(err) }); + } + }, + }, + { name: "feishu_doc" }, + ); + registered.push("feishu_doc"); + } + + // Keep feishu_app_scopes as independent tool + if (toolsCfg.scopes) { + api.registerTool( + { + name: "feishu_app_scopes", + label: "Feishu App Scopes", + description: + "List current app permissions (scopes). Use to debug permission issues or check available capabilities.", + parameters: Type.Object({}), + async execute() { + try { + const result = await listAppScopes(getClient()); + return json(result); + } catch (err) { + return json({ error: err instanceof Error ? err.message : String(err) }); + } + }, + }, + { name: "feishu_app_scopes" }, + ); + registered.push("feishu_app_scopes"); + } + + if (registered.length > 0) { + api.logger.info?.(`feishu_doc: Registered ${registered.join(", ")}`); + } +} diff --git a/feishu/src/drive-schema.ts b/feishu/src/drive-schema.ts new file mode 100644 index 00000000..4642aad8 --- /dev/null +++ b/feishu/src/drive-schema.ts @@ -0,0 +1,46 @@ +import { Type, type Static } from "@sinclair/typebox"; + +const FileType = Type.Union([ + Type.Literal("doc"), + Type.Literal("docx"), + Type.Literal("sheet"), + Type.Literal("bitable"), + Type.Literal("folder"), + Type.Literal("file"), + Type.Literal("mindnote"), + Type.Literal("shortcut"), +]); + +export const FeishuDriveSchema = Type.Union([ + Type.Object({ + action: Type.Literal("list"), + folder_token: Type.Optional( + Type.String({ description: "Folder token (optional, omit for root directory)" }), + ), + }), + Type.Object({ + action: Type.Literal("info"), + file_token: Type.String({ description: "File or folder token" }), + type: FileType, + }), + Type.Object({ + action: Type.Literal("create_folder"), + name: Type.String({ description: "Folder name" }), + folder_token: Type.Optional( + Type.String({ description: "Parent folder token (optional, omit for root)" }), + ), + }), + Type.Object({ + action: Type.Literal("move"), + file_token: Type.String({ description: "File token to move" }), + type: FileType, + folder_token: Type.String({ description: "Target folder token" }), + }), + Type.Object({ + action: Type.Literal("delete"), + file_token: Type.String({ description: "File token to delete" }), + type: FileType, + }), +]); + +export type FeishuDriveParams = Static; diff --git a/feishu/src/drive.ts b/feishu/src/drive.ts new file mode 100644 index 00000000..2b04a026 --- /dev/null +++ b/feishu/src/drive.ts @@ -0,0 +1,207 @@ +import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; +import { createFeishuClient } from "./client.js"; +import { listEnabledFeishuAccounts } from "./accounts.js"; +import type * as Lark from "@larksuiteoapi/node-sdk"; +import { FeishuDriveSchema, type FeishuDriveParams } from "./drive-schema.js"; +import { resolveToolsConfig } from "./tools-config.js"; + +// ============ Helpers ============ + +function json(data: unknown) { + return { + content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }], + details: data, + }; +} + +// ============ Actions ============ + +async function getRootFolderToken(client: Lark.Client): Promise { + // Use generic HTTP client to call the root folder meta API + // as it's not directly exposed in the SDK + const domain = (client as any).domain ?? "https://open.feishu.cn"; + const res = (await (client as any).httpInstance.get( + `${domain}/open-apis/drive/explorer/v2/root_folder/meta`, + )) as { code: number; msg?: string; data?: { token?: string } }; + if (res.code !== 0) throw new Error(res.msg ?? "Failed to get root folder"); + const token = res.data?.token; + if (!token) throw new Error("Root folder token not found"); + return token; +} + +async function listFolder(client: Lark.Client, folderToken?: string) { + // Filter out invalid folder_token values (empty, "0", etc.) + const validFolderToken = folderToken && folderToken !== "0" ? folderToken : undefined; + const res = await client.drive.file.list({ + params: validFolderToken ? { folder_token: validFolderToken } : {}, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + files: + res.data?.files?.map((f) => ({ + token: f.token, + name: f.name, + type: f.type, + url: f.url, + created_time: f.created_time, + modified_time: f.modified_time, + owner_id: f.owner_id, + })) ?? [], + next_page_token: res.data?.next_page_token, + }; +} + +async function getFileInfo(client: Lark.Client, fileToken: string, folderToken?: string) { + // Use list with folder_token to find file info + const res = await client.drive.file.list({ + params: folderToken ? { folder_token: folderToken } : {}, + }); + if (res.code !== 0) throw new Error(res.msg); + + const file = res.data?.files?.find((f) => f.token === fileToken); + if (!file) { + throw new Error(`File not found: ${fileToken}`); + } + + return { + token: file.token, + name: file.name, + type: file.type, + url: file.url, + created_time: file.created_time, + modified_time: file.modified_time, + owner_id: file.owner_id, + }; +} + +async function createFolder(client: Lark.Client, name: string, folderToken?: string) { + // Feishu supports using folder_token="0" as the root folder. + // We *try* to resolve the real root token (explorer API), but fall back to "0" + // because some tenants/apps return 400 for that explorer endpoint. + let effectiveToken = folderToken && folderToken !== "0" ? folderToken : "0"; + if (effectiveToken === "0") { + try { + effectiveToken = await getRootFolderToken(client); + } catch { + // ignore and keep "0" + } + } + + const res = await client.drive.file.createFolder({ + data: { + name, + folder_token: effectiveToken, + }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + token: res.data?.token, + url: res.data?.url, + }; +} + +async function moveFile( + client: Lark.Client, + fileToken: string, + type: string, + folderToken: string, +) { + const res = await client.drive.file.move({ + path: { file_token: fileToken }, + data: { + type: type as "doc" | "docx" | "sheet" | "bitable" | "folder" | "file" | "mindnote" | "slides", + folder_token: folderToken, + }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + success: true, + task_id: res.data?.task_id, + }; +} + +async function deleteFile(client: Lark.Client, fileToken: string, type: string) { + const res = await client.drive.file.delete({ + path: { file_token: fileToken }, + params: { + type: type as + | "doc" + | "docx" + | "sheet" + | "bitable" + | "folder" + | "file" + | "mindnote" + | "slides" + | "shortcut", + }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + success: true, + task_id: res.data?.task_id, + }; +} + +// ============ Tool Registration ============ + +export function registerFeishuDriveTools(api: OpenClawPluginApi) { + if (!api.config) { + api.logger.debug?.("feishu_drive: No config available, skipping drive tools"); + return; + } + + const accounts = listEnabledFeishuAccounts(api.config); + if (accounts.length === 0) { + api.logger.debug?.("feishu_drive: No Feishu accounts configured, skipping drive tools"); + return; + } + + const firstAccount = accounts[0]; + const toolsCfg = resolveToolsConfig(firstAccount.config.tools); + if (!toolsCfg.drive) { + api.logger.debug?.("feishu_drive: drive tool disabled in config"); + return; + } + + const getClient = () => createFeishuClient(firstAccount); + + api.registerTool( + { + name: "feishu_drive", + label: "Feishu Drive", + description: + "Feishu cloud storage operations. Actions: list, info, create_folder, move, delete", + parameters: FeishuDriveSchema, + async execute(_toolCallId, params) { + const p = params as FeishuDriveParams; + try { + const client = getClient(); + switch (p.action) { + case "list": + return json(await listFolder(client, p.folder_token)); + case "info": + return json(await getFileInfo(client, p.file_token)); + case "create_folder": + return json(await createFolder(client, p.name, p.folder_token)); + case "move": + return json(await moveFile(client, p.file_token, p.type, p.folder_token)); + case "delete": + return json(await deleteFile(client, p.file_token, p.type)); + default: + return json({ error: `Unknown action: ${(p as any).action}` }); + } + } catch (err) { + return json({ error: err instanceof Error ? err.message : String(err) }); + } + }, + }, + { name: "feishu_drive" }, + ); + + api.logger.info?.(`feishu_drive: Registered feishu_drive tool`); +} diff --git a/feishu/src/dynamic-agent.ts b/feishu/src/dynamic-agent.ts new file mode 100644 index 00000000..619e822a --- /dev/null +++ b/feishu/src/dynamic-agent.ts @@ -0,0 +1,131 @@ +import fs from "node:fs"; +import path from "node:path"; +import os from "node:os"; +import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk"; +import type { DynamicAgentCreationConfig } from "./types.js"; + +export type MaybeCreateDynamicAgentResult = { + created: boolean; + updatedCfg: OpenClawConfig; + agentId?: string; +}; + +/** + * Check if a dynamic agent should be created for a DM user and create it if needed. + * This creates a unique agent instance with its own workspace for each DM user. + */ +export async function maybeCreateDynamicAgent(params: { + cfg: OpenClawConfig; + runtime: PluginRuntime; + senderOpenId: string; + dynamicCfg: DynamicAgentCreationConfig; + log: (msg: string) => void; +}): Promise { + const { cfg, runtime, senderOpenId, dynamicCfg, log } = params; + + // Check if there's already a binding for this user + const existingBindings = cfg.bindings ?? []; + const hasBinding = existingBindings.some( + (b) => + b.match?.channel === "feishu" && + b.match?.peer?.kind === "dm" && + b.match?.peer?.id === senderOpenId, + ); + + if (hasBinding) { + return { created: false, updatedCfg: cfg }; + } + + // Check maxAgents limit if configured + if (dynamicCfg.maxAgents !== undefined) { + const feishuAgentCount = (cfg.agents?.list ?? []).filter((a) => + a.id.startsWith("feishu-"), + ).length; + if (feishuAgentCount >= dynamicCfg.maxAgents) { + log( + `feishu: maxAgents limit (${dynamicCfg.maxAgents}) reached, not creating agent for ${senderOpenId}`, + ); + return { created: false, updatedCfg: cfg }; + } + } + + // Use full OpenID as agent ID suffix (OpenID format: ou_xxx is already filesystem-safe) + const agentId = `feishu-${senderOpenId}`; + + // Check if agent already exists (but binding was missing) + const existingAgent = (cfg.agents?.list ?? []).find((a) => a.id === agentId); + if (existingAgent) { + // Agent exists but binding doesn't - just add the binding + log(`feishu: agent "${agentId}" exists, adding missing binding for ${senderOpenId}`); + + const updatedCfg: OpenClawConfig = { + ...cfg, + bindings: [ + ...existingBindings, + { + agentId, + match: { + channel: "feishu", + peer: { kind: "dm", id: senderOpenId }, + }, + }, + ], + }; + + await runtime.config.writeConfigFile(updatedCfg); + return { created: true, updatedCfg, agentId }; + } + + // Resolve path templates with substitutions + const workspaceTemplate = dynamicCfg.workspaceTemplate ?? "~/.openclaw/workspace-{agentId}"; + const agentDirTemplate = dynamicCfg.agentDirTemplate ?? "~/.openclaw/agents/{agentId}/agent"; + + const workspace = resolveUserPath( + workspaceTemplate.replace("{userId}", senderOpenId).replace("{agentId}", agentId), + ); + const agentDir = resolveUserPath( + agentDirTemplate.replace("{userId}", senderOpenId).replace("{agentId}", agentId), + ); + + log(`feishu: creating dynamic agent "${agentId}" for user ${senderOpenId}`); + log(` workspace: ${workspace}`); + log(` agentDir: ${agentDir}`); + + // Create directories + await fs.promises.mkdir(workspace, { recursive: true }); + await fs.promises.mkdir(agentDir, { recursive: true }); + + // Update configuration with new agent and binding + const updatedCfg: OpenClawConfig = { + ...cfg, + agents: { + ...cfg.agents, + list: [...(cfg.agents?.list ?? []), { id: agentId, workspace, agentDir }], + }, + bindings: [ + ...existingBindings, + { + agentId, + match: { + channel: "feishu", + peer: { kind: "dm", id: senderOpenId }, + }, + }, + ], + }; + + // Write updated config using PluginRuntime API + await runtime.config.writeConfigFile(updatedCfg); + + return { created: true, updatedCfg, agentId }; +} + +/** + * Resolve a path that may start with ~ to the user's home directory. + */ +function resolveUserPath(p: string): string { + if (p.startsWith("~/")) { + return path.join(os.homedir(), p.slice(2)); + } + return p; +} diff --git a/feishu/src/media.ts b/feishu/src/media.ts new file mode 100644 index 00000000..397a3fd7 --- /dev/null +++ b/feishu/src/media.ts @@ -0,0 +1,523 @@ +import type { ClawdbotConfig } from "openclaw/plugin-sdk"; +import { createFeishuClient } from "./client.js"; +import { resolveFeishuAccount } from "./accounts.js"; +import { resolveReceiveIdType, normalizeFeishuTarget } from "./targets.js"; +import fs from "fs"; +import path from "path"; +import os from "os"; +import { Readable } from "stream"; + +export type DownloadImageResult = { + buffer: Buffer; + contentType?: string; +}; + +export type DownloadMessageResourceResult = { + buffer: Buffer; + contentType?: string; + fileName?: string; +}; + +/** + * Download an image from Feishu using image_key. + * Used for downloading images sent in messages. + */ +export async function downloadImageFeishu(params: { + cfg: ClawdbotConfig; + imageKey: string; + accountId?: string; +}): Promise { + const { cfg, imageKey, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + + const response = await client.im.image.get({ + path: { image_key: imageKey }, + }); + + const responseAny = response as any; + if (responseAny.code !== undefined && responseAny.code !== 0) { + throw new Error(`Feishu image download failed: ${responseAny.msg || `code ${responseAny.code}`}`); + } + + // Handle various response formats from Feishu SDK + let buffer: Buffer; + + if (Buffer.isBuffer(response)) { + buffer = response; + } else if (response instanceof ArrayBuffer) { + buffer = Buffer.from(response); + } else if (responseAny.data && Buffer.isBuffer(responseAny.data)) { + buffer = responseAny.data; + } else if (responseAny.data instanceof ArrayBuffer) { + buffer = Buffer.from(responseAny.data); + } else if (typeof responseAny.getReadableStream === "function") { + // SDK provides getReadableStream method + const stream = responseAny.getReadableStream(); + const chunks: Buffer[] = []; + for await (const chunk of stream) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + } + buffer = Buffer.concat(chunks); + } else if (typeof responseAny.writeFile === "function") { + // SDK provides writeFile method - use a temp file + const tmpPath = path.join(os.tmpdir(), `feishu_img_${Date.now()}_${imageKey}`); + await responseAny.writeFile(tmpPath); + buffer = await fs.promises.readFile(tmpPath); + await fs.promises.unlink(tmpPath).catch(() => {}); // cleanup + } else if (typeof responseAny[Symbol.asyncIterator] === "function") { + // Response is an async iterable + const chunks: Buffer[] = []; + for await (const chunk of responseAny) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + } + buffer = Buffer.concat(chunks); + } else if (typeof responseAny.read === "function") { + // Response is a Readable stream + const chunks: Buffer[] = []; + for await (const chunk of responseAny as Readable) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + } + buffer = Buffer.concat(chunks); + } else { + // Debug: log what we actually received + const keys = Object.keys(responseAny); + const types = keys.map(k => `${k}: ${typeof responseAny[k]}`).join(", "); + throw new Error( + `Feishu image download failed: unexpected response format. Keys: [${types}]`, + ); + } + + return { buffer }; +} + +/** + * Download a message resource (file/image/audio/video) from Feishu. + * Used for downloading files, audio, and video from messages. + */ +export async function downloadMessageResourceFeishu(params: { + cfg: ClawdbotConfig; + messageId: string; + fileKey: string; + type: "image" | "file"; + accountId?: string; +}): Promise { + const { cfg, messageId, fileKey, type, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + + const response = await client.im.messageResource.get({ + path: { message_id: messageId, file_key: fileKey }, + params: { type }, + }); + + const responseAny = response as any; + if (responseAny.code !== undefined && responseAny.code !== 0) { + throw new Error( + `Feishu message resource download failed: ${responseAny.msg || `code ${responseAny.code}`}`, + ); + } + + // Handle various response formats from Feishu SDK + let buffer: Buffer; + + if (Buffer.isBuffer(response)) { + buffer = response; + } else if (response instanceof ArrayBuffer) { + buffer = Buffer.from(response); + } else if (responseAny.data && Buffer.isBuffer(responseAny.data)) { + buffer = responseAny.data; + } else if (responseAny.data instanceof ArrayBuffer) { + buffer = Buffer.from(responseAny.data); + } else if (typeof responseAny.getReadableStream === "function") { + // SDK provides getReadableStream method + const stream = responseAny.getReadableStream(); + const chunks: Buffer[] = []; + for await (const chunk of stream) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + } + buffer = Buffer.concat(chunks); + } else if (typeof responseAny.writeFile === "function") { + // SDK provides writeFile method - use a temp file + const tmpPath = path.join(os.tmpdir(), `feishu_${Date.now()}_${fileKey}`); + await responseAny.writeFile(tmpPath); + buffer = await fs.promises.readFile(tmpPath); + await fs.promises.unlink(tmpPath).catch(() => {}); // cleanup + } else if (typeof responseAny[Symbol.asyncIterator] === "function") { + // Response is an async iterable + const chunks: Buffer[] = []; + for await (const chunk of responseAny) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + } + buffer = Buffer.concat(chunks); + } else if (typeof responseAny.read === "function") { + // Response is a Readable stream + const chunks: Buffer[] = []; + for await (const chunk of responseAny as Readable) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + } + buffer = Buffer.concat(chunks); + } else { + // Debug: log what we actually received + const keys = Object.keys(responseAny); + const types = keys.map(k => `${k}: ${typeof responseAny[k]}`).join(", "); + throw new Error( + `Feishu message resource download failed: unexpected response format. Keys: [${types}]`, + ); + } + + return { buffer }; +} + +export type UploadImageResult = { + imageKey: string; +}; + +export type UploadFileResult = { + fileKey: string; +}; + +export type SendMediaResult = { + messageId: string; + chatId: string; +}; + +/** + * Upload an image to Feishu and get an image_key for sending. + * Supports: JPEG, PNG, WEBP, GIF, TIFF, BMP, ICO + */ +export async function uploadImageFeishu(params: { + cfg: ClawdbotConfig; + image: Buffer | string; // Buffer or file path + imageType?: "message" | "avatar"; + accountId?: string; +}): Promise { + const { cfg, image, imageType = "message", accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + + // SDK expects a Readable stream, not a Buffer + // Use type assertion since SDK actually accepts any Readable at runtime + const imageStream = + typeof image === "string" ? fs.createReadStream(image) : Readable.from(image); + + const response = await client.im.image.create({ + data: { + image_type: imageType, + image: imageStream as any, + }, + }); + + // SDK v1.30+ returns data directly without code wrapper on success + // On error, it throws or returns { code, msg } + const responseAny = response as any; + if (responseAny.code !== undefined && responseAny.code !== 0) { + throw new Error(`Feishu image upload failed: ${responseAny.msg || `code ${responseAny.code}`}`); + } + + const imageKey = responseAny.image_key ?? responseAny.data?.image_key; + if (!imageKey) { + throw new Error("Feishu image upload failed: no image_key returned"); + } + + return { imageKey }; +} + +/** + * Upload a file to Feishu and get a file_key for sending. + * Max file size: 30MB + */ +export async function uploadFileFeishu(params: { + cfg: ClawdbotConfig; + file: Buffer | string; // Buffer or file path + fileName: string; + fileType: "opus" | "mp4" | "pdf" | "doc" | "xls" | "ppt" | "stream"; + duration?: number; // Required for audio/video files, in milliseconds + accountId?: string; +}): Promise { + const { cfg, file, fileName, fileType, duration, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + + // SDK expects a Readable stream, not a Buffer + // Use type assertion since SDK actually accepts any Readable at runtime + const fileStream = + typeof file === "string" ? fs.createReadStream(file) : Readable.from(file); + + const response = await client.im.file.create({ + data: { + file_type: fileType, + file_name: fileName, + file: fileStream as any, + ...(duration !== undefined && { duration }), + }, + }); + + // SDK v1.30+ returns data directly without code wrapper on success + const responseAny = response as any; + if (responseAny.code !== undefined && responseAny.code !== 0) { + throw new Error(`Feishu file upload failed: ${responseAny.msg || `code ${responseAny.code}`}`); + } + + const fileKey = responseAny.file_key ?? responseAny.data?.file_key; + if (!fileKey) { + throw new Error("Feishu file upload failed: no file_key returned"); + } + + return { fileKey }; +} + +/** + * Send an image message using an image_key + */ +export async function sendImageFeishu(params: { + cfg: ClawdbotConfig; + to: string; + imageKey: string; + replyToMessageId?: string; + accountId?: string; +}): Promise { + const { cfg, to, imageKey, replyToMessageId, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + const receiveId = normalizeFeishuTarget(to); + if (!receiveId) { + throw new Error(`Invalid Feishu target: ${to}`); + } + + const receiveIdType = resolveReceiveIdType(receiveId); + const content = JSON.stringify({ image_key: imageKey }); + + if (replyToMessageId) { + const response = await client.im.message.reply({ + path: { message_id: replyToMessageId }, + data: { + content, + msg_type: "image", + }, + }); + + if (response.code !== 0) { + throw new Error(`Feishu image reply failed: ${response.msg || `code ${response.code}`}`); + } + + return { + messageId: response.data?.message_id ?? "unknown", + chatId: receiveId, + }; + } + + const response = await client.im.message.create({ + params: { receive_id_type: receiveIdType }, + data: { + receive_id: receiveId, + content, + msg_type: "image", + }, + }); + + if (response.code !== 0) { + throw new Error(`Feishu image send failed: ${response.msg || `code ${response.code}`}`); + } + + return { + messageId: response.data?.message_id ?? "unknown", + chatId: receiveId, + }; +} + +/** + * Send a file message using a file_key + */ +export async function sendFileFeishu(params: { + cfg: ClawdbotConfig; + to: string; + fileKey: string; + replyToMessageId?: string; + accountId?: string; +}): Promise { + const { cfg, to, fileKey, replyToMessageId, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + const receiveId = normalizeFeishuTarget(to); + if (!receiveId) { + throw new Error(`Invalid Feishu target: ${to}`); + } + + const receiveIdType = resolveReceiveIdType(receiveId); + const content = JSON.stringify({ file_key: fileKey }); + + if (replyToMessageId) { + const response = await client.im.message.reply({ + path: { message_id: replyToMessageId }, + data: { + content, + msg_type: "file", + }, + }); + + if (response.code !== 0) { + throw new Error(`Feishu file reply failed: ${response.msg || `code ${response.code}`}`); + } + + return { + messageId: response.data?.message_id ?? "unknown", + chatId: receiveId, + }; + } + + const response = await client.im.message.create({ + params: { receive_id_type: receiveIdType }, + data: { + receive_id: receiveId, + content, + msg_type: "file", + }, + }); + + if (response.code !== 0) { + throw new Error(`Feishu file send failed: ${response.msg || `code ${response.code}`}`); + } + + return { + messageId: response.data?.message_id ?? "unknown", + chatId: receiveId, + }; +} + +/** + * Helper to detect file type from extension + */ +export function detectFileType( + fileName: string, +): "opus" | "mp4" | "pdf" | "doc" | "xls" | "ppt" | "stream" { + const ext = path.extname(fileName).toLowerCase(); + switch (ext) { + case ".opus": + case ".ogg": + return "opus"; + case ".mp4": + case ".mov": + case ".avi": + return "mp4"; + case ".pdf": + return "pdf"; + case ".doc": + case ".docx": + return "doc"; + case ".xls": + case ".xlsx": + return "xls"; + case ".ppt": + case ".pptx": + return "ppt"; + default: + return "stream"; + } +} + +/** + * Check if a string is a local file path (not a URL) + */ +function isLocalPath(urlOrPath: string): boolean { + // Starts with / or ~ or drive letter (Windows) + if (urlOrPath.startsWith("/") || urlOrPath.startsWith("~") || /^[a-zA-Z]:/.test(urlOrPath)) { + return true; + } + // Try to parse as URL - if it fails or has no protocol, it's likely a local path + try { + const url = new URL(urlOrPath); + return url.protocol === "file:"; + } catch { + return true; // Not a valid URL, treat as local path + } +} + +/** + * Upload and send media (image or file) from URL, local path, or buffer + */ +export async function sendMediaFeishu(params: { + cfg: ClawdbotConfig; + to: string; + mediaUrl?: string; + mediaBuffer?: Buffer; + fileName?: string; + replyToMessageId?: string; + accountId?: string; +}): Promise { + const { cfg, to, mediaUrl, mediaBuffer, fileName, replyToMessageId, accountId } = params; + + let buffer: Buffer; + let name: string; + + if (mediaBuffer) { + buffer = mediaBuffer; + name = fileName ?? "file"; + } else if (mediaUrl) { + if (isLocalPath(mediaUrl)) { + // Local file path - read directly + const filePath = mediaUrl.startsWith("~") + ? mediaUrl.replace("~", process.env.HOME ?? "") + : mediaUrl.replace("file://", ""); + + if (!fs.existsSync(filePath)) { + throw new Error(`Local file not found: ${filePath}`); + } + buffer = fs.readFileSync(filePath); + name = fileName ?? path.basename(filePath); + } else { + // Remote URL - fetch + const response = await fetch(mediaUrl); + if (!response.ok) { + throw new Error(`Failed to fetch media from URL: ${response.status}`); + } + buffer = Buffer.from(await response.arrayBuffer()); + name = fileName ?? (path.basename(new URL(mediaUrl).pathname) || "file"); + } + } else { + throw new Error("Either mediaUrl or mediaBuffer must be provided"); + } + + // Determine if it's an image based on extension + const ext = path.extname(name).toLowerCase(); + const isImage = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".ico", ".tiff"].includes(ext); + + if (isImage) { + const { imageKey } = await uploadImageFeishu({ cfg, image: buffer, accountId }); + return sendImageFeishu({ cfg, to, imageKey, replyToMessageId, accountId }); + } else { + const fileType = detectFileType(name); + const { fileKey } = await uploadFileFeishu({ + cfg, + file: buffer, + fileName: name, + fileType, + accountId, + }); + return sendFileFeishu({ cfg, to, fileKey, replyToMessageId, accountId }); + } +} diff --git a/feishu/src/mention.ts b/feishu/src/mention.ts new file mode 100644 index 00000000..ef912604 --- /dev/null +++ b/feishu/src/mention.ts @@ -0,0 +1,121 @@ +import type { FeishuMessageEvent } from "./bot.js"; + +/** + * Mention target user info + */ +export type MentionTarget = { + openId: string; + name: string; + key: string; // Placeholder in original message, e.g. @_user_1 +}; + +/** + * Extract mention targets from message event (excluding the bot itself) + */ +export function extractMentionTargets( + event: FeishuMessageEvent, + botOpenId?: string, +): MentionTarget[] { + const mentions = event.message.mentions ?? []; + + return mentions + .filter((m) => { + // Exclude the bot itself + if (botOpenId && m.id.open_id === botOpenId) return false; + // Must have open_id + return !!m.id.open_id; + }) + .map((m) => ({ + openId: m.id.open_id!, + name: m.name, + key: m.key, + })); +} + +/** + * Check if message is a mention forward request + * Rules: + * - Group: message mentions bot + at least one other user + * - DM: message mentions any user (no need to mention bot) + */ +export function isMentionForwardRequest( + event: FeishuMessageEvent, + botOpenId?: string, +): boolean { + const mentions = event.message.mentions ?? []; + if (mentions.length === 0) return false; + + const isDirectMessage = event.message.chat_type === "p2p"; + const hasOtherMention = mentions.some((m) => m.id.open_id !== botOpenId); + + if (isDirectMessage) { + // DM: trigger if any non-bot user is mentioned + return hasOtherMention; + } else { + // Group: need to mention both bot and other users + const hasBotMention = mentions.some((m) => m.id.open_id === botOpenId); + return hasBotMention && hasOtherMention; + } +} + +/** + * Extract message body from text (remove @ placeholders) + */ +export function extractMessageBody(text: string, allMentionKeys: string[]): string { + let result = text; + + // Remove all @ placeholders + for (const key of allMentionKeys) { + result = result.replace(new RegExp(key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), ""); + } + + return result.replace(/\s+/g, " ").trim(); +} + +/** + * Format @mention for text message + */ +export function formatMentionForText(target: MentionTarget): string { + return `${target.name}`; +} + +/** + * Format @everyone for text message + */ +export function formatMentionAllForText(): string { + return `Everyone`; +} + +/** + * Format @mention for card message (lark_md) + */ +export function formatMentionForCard(target: MentionTarget): string { + return ``; +} + +/** + * Format @everyone for card message + */ +export function formatMentionAllForCard(): string { + return ``; +} + +/** + * Build complete message with @mentions (text format) + */ +export function buildMentionedMessage(targets: MentionTarget[], message: string): string { + if (targets.length === 0) return message; + + const mentionParts = targets.map((t) => formatMentionForText(t)); + return `${mentionParts.join(" ")} ${message}`; +} + +/** + * Build card content with @mentions (Markdown format) + */ +export function buildMentionedCardContent(targets: MentionTarget[], message: string): string { + if (targets.length === 0) return message; + + const mentionParts = targets.map((t) => formatMentionForCard(t)); + return `${mentionParts.join(" ")} ${message}`; +} diff --git a/feishu/src/monitor.ts b/feishu/src/monitor.ts new file mode 100644 index 00000000..dce8e747 --- /dev/null +++ b/feishu/src/monitor.ts @@ -0,0 +1,296 @@ +import * as Lark from "@larksuiteoapi/node-sdk"; +import * as http from "http"; +import type { ClawdbotConfig, RuntimeEnv, HistoryEntry } from "openclaw/plugin-sdk"; +import type { ResolvedFeishuAccount } from "./types.js"; +import { createFeishuWSClient, createEventDispatcher } from "./client.js"; +import { resolveFeishuAccount, listEnabledFeishuAccounts } from "./accounts.js"; +import { handleFeishuMessage, type FeishuMessageEvent, type FeishuBotAddedEvent } from "./bot.js"; +import { probeFeishu } from "./probe.js"; + +export type MonitorFeishuOpts = { + config?: ClawdbotConfig; + runtime?: RuntimeEnv; + abortSignal?: AbortSignal; + accountId?: string; +}; + +// Per-account WebSocket clients, HTTP servers, and bot info +const wsClients = new Map(); +const httpServers = new Map(); +const botOpenIds = new Map(); + +async function fetchBotOpenId( + account: ResolvedFeishuAccount, +): Promise { + try { + const result = await probeFeishu(account); + return result.ok ? result.botOpenId : undefined; + } catch { + return undefined; + } +} + +/** + * Register common event handlers on an EventDispatcher. + * When fireAndForget is true (webhook mode), message handling is not awaited + * to avoid blocking the HTTP response (Lark requires <3s response). + */ +function registerEventHandlers( + eventDispatcher: Lark.EventDispatcher, + context: { + cfg: ClawdbotConfig; + accountId: string; + runtime?: RuntimeEnv; + chatHistories: Map; + fireAndForget?: boolean; + }, +) { + const { cfg, accountId, runtime, chatHistories, fireAndForget } = context; + const log = runtime?.log ?? console.log; + const error = runtime?.error ?? console.error; + + eventDispatcher.register({ + "im.message.receive_v1": async (data) => { + try { + const event = data as unknown as FeishuMessageEvent; + const promise = handleFeishuMessage({ + cfg, + event, + botOpenId: botOpenIds.get(accountId), + runtime, + chatHistories, + accountId, + }); + if (fireAndForget) { + promise.catch((err) => { + error(`feishu[${accountId}]: error handling message: ${String(err)}`); + }); + } else { + await promise; + } + } catch (err) { + error(`feishu[${accountId}]: error handling message: ${String(err)}`); + } + }, + "im.message.message_read_v1": async () => { + // Ignore read receipts + }, + "im.chat.member.bot.added_v1": async (data) => { + try { + const event = data as unknown as FeishuBotAddedEvent; + log(`feishu[${accountId}]: bot added to chat ${event.chat_id}`); + } catch (err) { + error(`feishu[${accountId}]: error handling bot added event: ${String(err)}`); + } + }, + "im.chat.member.bot.deleted_v1": async (data) => { + try { + const event = data as unknown as { chat_id: string }; + log(`feishu[${accountId}]: bot removed from chat ${event.chat_id}`); + } catch (err) { + error(`feishu[${accountId}]: error handling bot removed event: ${String(err)}`); + } + }, + }); +} + +type MonitorAccountParams = { + cfg: ClawdbotConfig; + account: ResolvedFeishuAccount; + runtime?: RuntimeEnv; + abortSignal?: AbortSignal; +}; + +/** + * Monitor a single Feishu account. + */ +async function monitorSingleAccount(params: MonitorAccountParams): Promise { + const { cfg, account, runtime, abortSignal } = params; + const { accountId } = account; + const log = runtime?.log ?? console.log; + + // Fetch bot open_id + const botOpenId = await fetchBotOpenId(account); + botOpenIds.set(accountId, botOpenId ?? ""); + log(`feishu[${accountId}]: bot open_id resolved: ${botOpenId ?? "unknown"}`); + + const connectionMode = account.config.connectionMode ?? "websocket"; + const eventDispatcher = createEventDispatcher(account); + const chatHistories = new Map(); + + registerEventHandlers(eventDispatcher, { + cfg, + accountId, + runtime, + chatHistories, + fireAndForget: connectionMode === "webhook", + }); + + if (connectionMode === "webhook") { + return monitorWebhook({ params, accountId, eventDispatcher }); + } + + return monitorWebSocket({ params, accountId, eventDispatcher }); +} + +type ConnectionParams = { + params: MonitorAccountParams; + accountId: string; + eventDispatcher: Lark.EventDispatcher; +}; + +async function monitorWebSocket({ params, accountId, eventDispatcher }: ConnectionParams): Promise { + const { account, runtime, abortSignal } = params; + const log = runtime?.log ?? console.log; + const error = runtime?.error ?? console.error; + + log(`feishu[${accountId}]: starting WebSocket connection...`); + + const wsClient = createFeishuWSClient(account); + wsClients.set(accountId, wsClient); + + return new Promise((resolve, reject) => { + const cleanup = () => { + wsClients.delete(accountId); + botOpenIds.delete(accountId); + }; + + const handleAbort = () => { + log(`feishu[${accountId}]: abort signal received, stopping`); + cleanup(); + resolve(); + }; + + if (abortSignal?.aborted) { + cleanup(); + resolve(); + return; + } + + abortSignal?.addEventListener("abort", handleAbort, { once: true }); + + try { + wsClient.start({ eventDispatcher }); + log(`feishu[${accountId}]: WebSocket client started`); + } catch (err) { + cleanup(); + abortSignal?.removeEventListener("abort", handleAbort); + reject(err); + } + }); +} + +async function monitorWebhook({ params, accountId, eventDispatcher }: ConnectionParams): Promise { + const { account, runtime, abortSignal } = params; + const log = runtime?.log ?? console.log; + const error = runtime?.error ?? console.error; + + const port = account.config.webhookPort ?? 3000; + const path = account.config.webhookPath ?? "/feishu/events"; + + log(`feishu[${accountId}]: starting Webhook server on port ${port}, path ${path}...`); + + const server = http.createServer(); + server.on("request", Lark.adaptDefault(path, eventDispatcher, { autoChallenge: true })); + httpServers.set(accountId, server); + + return new Promise((resolve, reject) => { + const cleanup = () => { + server.close(); + httpServers.delete(accountId); + botOpenIds.delete(accountId); + }; + + const handleAbort = () => { + log(`feishu[${accountId}]: abort signal received, stopping Webhook server`); + cleanup(); + resolve(); + }; + + if (abortSignal?.aborted) { + cleanup(); + resolve(); + return; + } + + abortSignal?.addEventListener("abort", handleAbort, { once: true }); + + server.listen(port, () => { + log(`feishu[${accountId}]: Webhook server listening on port ${port}`); + }); + + server.on("error", (err) => { + error(`feishu[${accountId}]: Webhook server error: ${err}`); + abortSignal?.removeEventListener("abort", handleAbort); + reject(err); + }); + }); +} + +/** + * Main entry: start monitoring for all enabled accounts. + */ +export async function monitorFeishuProvider(opts: MonitorFeishuOpts = {}): Promise { + const cfg = opts.config; + if (!cfg) { + throw new Error("Config is required for Feishu monitor"); + } + + const log = opts.runtime?.log ?? console.log; + + // If accountId is specified, only monitor that account + if (opts.accountId) { + const account = resolveFeishuAccount({ cfg, accountId: opts.accountId }); + if (!account.enabled || !account.configured) { + throw new Error(`Feishu account "${opts.accountId}" not configured or disabled`); + } + return monitorSingleAccount({ + cfg, + account, + runtime: opts.runtime, + abortSignal: opts.abortSignal, + }); + } + + // Otherwise, start all enabled accounts + const accounts = listEnabledFeishuAccounts(cfg); + if (accounts.length === 0) { + throw new Error("No enabled Feishu accounts configured"); + } + + log(`feishu: starting ${accounts.length} account(s): ${accounts.map((a) => a.accountId).join(", ")}`); + + // Start all accounts in parallel + await Promise.all( + accounts.map((account) => + monitorSingleAccount({ + cfg, + account, + runtime: opts.runtime, + abortSignal: opts.abortSignal, + }), + ), + ); +} + +/** + * Stop monitoring for a specific account or all accounts. + */ +export function stopFeishuMonitor(accountId?: string): void { + if (accountId) { + wsClients.delete(accountId); + const server = httpServers.get(accountId); + if (server) { + server.close(); + httpServers.delete(accountId); + } + botOpenIds.delete(accountId); + } else { + wsClients.clear(); + for (const server of httpServers.values()) { + server.close(); + } + httpServers.clear(); + botOpenIds.clear(); + } +} diff --git a/feishu/src/onboarding.ts b/feishu/src/onboarding.ts new file mode 100644 index 00000000..eb013bce --- /dev/null +++ b/feishu/src/onboarding.ts @@ -0,0 +1,358 @@ +import type { + ChannelOnboardingAdapter, + ChannelOnboardingDmPolicy, + ClawdbotConfig, + DmPolicy, + WizardPrompter, +} from "openclaw/plugin-sdk"; +import { addWildcardAllowFrom, DEFAULT_ACCOUNT_ID, formatDocsLink } from "openclaw/plugin-sdk"; + +import { resolveFeishuCredentials } from "./accounts.js"; +import { probeFeishu } from "./probe.js"; +import type { FeishuConfig } from "./types.js"; + +const channel = "feishu" as const; + +function setFeishuDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy): ClawdbotConfig { + const allowFrom = + dmPolicy === "open" + ? addWildcardAllowFrom(cfg.channels?.feishu?.allowFrom)?.map((entry) => String(entry)) + : undefined; + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...cfg.channels?.feishu, + dmPolicy, + ...(allowFrom ? { allowFrom } : {}), + }, + }, + }; +} + +function setFeishuAllowFrom(cfg: ClawdbotConfig, allowFrom: string[]): ClawdbotConfig { + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...cfg.channels?.feishu, + allowFrom, + }, + }, + }; +} + +function parseAllowFromInput(raw: string): string[] { + return raw + .split(/[\n,;]+/g) + .map((entry) => entry.trim()) + .filter(Boolean); +} + +async function promptFeishuAllowFrom(params: { + cfg: ClawdbotConfig; + prompter: WizardPrompter; +}): Promise { + const existing = params.cfg.channels?.feishu?.allowFrom ?? []; + await params.prompter.note( + [ + "Allowlist Feishu DMs by open_id or user_id.", + "You can find user open_id in Feishu admin console or via API.", + "Examples:", + "- ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "- on_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + ].join("\n"), + "Feishu allowlist", + ); + + while (true) { + const entry = await params.prompter.text({ + message: "Feishu allowFrom (user open_ids)", + placeholder: "ou_xxxxx, ou_yyyyy", + initialValue: existing[0] ? String(existing[0]) : undefined, + validate: (value) => (String(value ?? "").trim() ? undefined : "Required"), + }); + const parts = parseAllowFromInput(String(entry)); + if (parts.length === 0) { + await params.prompter.note("Enter at least one user.", "Feishu allowlist"); + continue; + } + + const unique = [ + ...new Set([...existing.map((v) => String(v).trim()).filter(Boolean), ...parts]), + ]; + return setFeishuAllowFrom(params.cfg, unique); + } +} + +async function noteFeishuCredentialHelp(prompter: WizardPrompter): Promise { + await prompter.note( + [ + "1) Go to Feishu Open Platform (open.feishu.cn)", + "2) Create a self-built app", + "3) Get App ID and App Secret from Credentials page", + "4) Enable required permissions: im:message, im:chat, contact:user.base:readonly", + "5) Publish the app or add it to a test group", + "Tip: you can also set FEISHU_APP_ID / FEISHU_APP_SECRET env vars.", + `Docs: ${formatDocsLink("/channels/feishu", "feishu")}`, + ].join("\n"), + "Feishu credentials", + ); +} + +function setFeishuGroupPolicy( + cfg: ClawdbotConfig, + groupPolicy: "open" | "allowlist" | "disabled", +): ClawdbotConfig { + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...cfg.channels?.feishu, + enabled: true, + groupPolicy, + }, + }, + }; +} + +function setFeishuGroupAllowFrom(cfg: ClawdbotConfig, groupAllowFrom: string[]): ClawdbotConfig { + return { + ...cfg, + channels: { + ...cfg.channels, + feishu: { + ...cfg.channels?.feishu, + groupAllowFrom, + }, + }, + }; +} + +const dmPolicy: ChannelOnboardingDmPolicy = { + label: "Feishu", + channel, + policyKey: "channels.feishu.dmPolicy", + allowFromKey: "channels.feishu.allowFrom", + getCurrent: (cfg) => (cfg.channels?.feishu as FeishuConfig | undefined)?.dmPolicy ?? "pairing", + setPolicy: (cfg, policy) => setFeishuDmPolicy(cfg, policy), + promptAllowFrom: promptFeishuAllowFrom, +}; + +export const feishuOnboardingAdapter: ChannelOnboardingAdapter = { + channel, + getStatus: async ({ cfg }) => { + const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined; + const configured = Boolean(resolveFeishuCredentials(feishuCfg)); + + // Try to probe if configured + let probeResult = null; + if (configured && feishuCfg) { + try { + probeResult = await probeFeishu(feishuCfg); + } catch { + // Ignore probe errors + } + } + + const statusLines: string[] = []; + if (!configured) { + statusLines.push("Feishu: needs app credentials"); + } else if (probeResult?.ok) { + statusLines.push(`Feishu: connected as ${probeResult.botName ?? probeResult.botOpenId ?? "bot"}`); + } else { + statusLines.push("Feishu: configured (connection not verified)"); + } + + return { + channel, + configured, + statusLines, + selectionHint: configured ? "configured" : "needs app creds", + quickstartScore: configured ? 2 : 0, + }; + }, + + configure: async ({ cfg, prompter }) => { + const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined; + const resolved = resolveFeishuCredentials(feishuCfg); + const hasConfigCreds = Boolean(feishuCfg?.appId?.trim() && feishuCfg?.appSecret?.trim()); + const canUseEnv = Boolean( + !hasConfigCreds && + process.env.FEISHU_APP_ID?.trim() && + process.env.FEISHU_APP_SECRET?.trim(), + ); + + let next = cfg; + let appId: string | null = null; + let appSecret: string | null = null; + + if (!resolved) { + await noteFeishuCredentialHelp(prompter); + } + + if (canUseEnv) { + const keepEnv = await prompter.confirm({ + message: "FEISHU_APP_ID + FEISHU_APP_SECRET detected. Use env vars?", + initialValue: true, + }); + if (keepEnv) { + next = { + ...next, + channels: { + ...next.channels, + feishu: { ...next.channels?.feishu, enabled: true }, + }, + }; + } else { + appId = String( + await prompter.text({ + message: "Enter Feishu App ID", + validate: (value) => (value?.trim() ? undefined : "Required"), + }), + ).trim(); + appSecret = String( + await prompter.text({ + message: "Enter Feishu App Secret", + validate: (value) => (value?.trim() ? undefined : "Required"), + }), + ).trim(); + } + } else if (hasConfigCreds) { + const keep = await prompter.confirm({ + message: "Feishu credentials already configured. Keep them?", + initialValue: true, + }); + if (!keep) { + appId = String( + await prompter.text({ + message: "Enter Feishu App ID", + validate: (value) => (value?.trim() ? undefined : "Required"), + }), + ).trim(); + appSecret = String( + await prompter.text({ + message: "Enter Feishu App Secret", + validate: (value) => (value?.trim() ? undefined : "Required"), + }), + ).trim(); + } + } else { + appId = String( + await prompter.text({ + message: "Enter Feishu App ID", + validate: (value) => (value?.trim() ? undefined : "Required"), + }), + ).trim(); + appSecret = String( + await prompter.text({ + message: "Enter Feishu App Secret", + validate: (value) => (value?.trim() ? undefined : "Required"), + }), + ).trim(); + } + + if (appId && appSecret) { + next = { + ...next, + channels: { + ...next.channels, + feishu: { + ...next.channels?.feishu, + enabled: true, + appId, + appSecret, + }, + }, + }; + + // Test connection + const testCfg = next.channels?.feishu as FeishuConfig; + try { + const probe = await probeFeishu(testCfg); + if (probe.ok) { + await prompter.note( + `Connected as ${probe.botName ?? probe.botOpenId ?? "bot"}`, + "Feishu connection test", + ); + } else { + await prompter.note( + `Connection failed: ${probe.error ?? "unknown error"}`, + "Feishu connection test", + ); + } + } catch (err) { + await prompter.note(`Connection test failed: ${String(err)}`, "Feishu connection test"); + } + } + + // Domain selection + const currentDomain = (next.channels?.feishu as FeishuConfig | undefined)?.domain ?? "feishu"; + const domain = await prompter.select({ + message: "Which Feishu domain?", + options: [ + { value: "feishu", label: "Feishu (feishu.cn) - China" }, + { value: "lark", label: "Lark (larksuite.com) - International" }, + ], + initialValue: currentDomain, + }); + if (domain) { + next = { + ...next, + channels: { + ...next.channels, + feishu: { + ...next.channels?.feishu, + domain: domain as "feishu" | "lark", + }, + }, + }; + } + + // Group policy + const groupPolicy = await prompter.select({ + message: "Group chat policy", + options: [ + { value: "allowlist", label: "Allowlist - only respond in specific groups" }, + { value: "open", label: "Open - respond in all groups (requires mention)" }, + { value: "disabled", label: "Disabled - don't respond in groups" }, + ], + initialValue: + (next.channels?.feishu as FeishuConfig | undefined)?.groupPolicy ?? "allowlist", + }); + if (groupPolicy) { + next = setFeishuGroupPolicy(next, groupPolicy as "open" | "allowlist" | "disabled"); + } + + // Group allowlist if needed + if (groupPolicy === "allowlist") { + const existing = (next.channels?.feishu as FeishuConfig | undefined)?.groupAllowFrom ?? []; + const entry = await prompter.text({ + message: "Group chat allowlist (chat_ids)", + placeholder: "oc_xxxxx, oc_yyyyy", + initialValue: existing.length > 0 ? existing.map(String).join(", ") : undefined, + }); + if (entry) { + const parts = parseAllowFromInput(String(entry)); + if (parts.length > 0) { + next = setFeishuGroupAllowFrom(next, parts); + } + } + } + + return { cfg: next, accountId: DEFAULT_ACCOUNT_ID }; + }, + + dmPolicy, + + disable: (cfg) => ({ + ...cfg, + channels: { + ...cfg.channels, + feishu: { ...cfg.channels?.feishu, enabled: false }, + }, + }), +}; diff --git a/feishu/src/outbound.ts b/feishu/src/outbound.ts new file mode 100644 index 00000000..3a241365 --- /dev/null +++ b/feishu/src/outbound.ts @@ -0,0 +1,40 @@ +import type { ChannelOutboundAdapter } from "openclaw/plugin-sdk"; +import { getFeishuRuntime } from "./runtime.js"; +import { sendMessageFeishu } from "./send.js"; +import { sendMediaFeishu } from "./media.js"; + +export const feishuOutbound: ChannelOutboundAdapter = { + deliveryMode: "direct", + chunker: (text, limit) => getFeishuRuntime().channel.text.chunkMarkdownText(text, limit), + chunkerMode: "markdown", + textChunkLimit: 4000, + sendText: async ({ cfg, to, text, accountId }) => { + const result = await sendMessageFeishu({ cfg, to, text, accountId }); + return { channel: "feishu", ...result }; + }, + sendMedia: async ({ cfg, to, text, mediaUrl, accountId }) => { + // Send text first if provided + if (text?.trim()) { + await sendMessageFeishu({ cfg, to, text, accountId }); + } + + // Upload and send media if URL provided + if (mediaUrl) { + try { + const result = await sendMediaFeishu({ cfg, to, mediaUrl, accountId }); + return { channel: "feishu", ...result }; + } catch (err) { + // Log the error for debugging + console.error(`[feishu] sendMediaFeishu failed:`, err); + // Fallback to URL link if upload fails + const fallbackText = `📎 ${mediaUrl}`; + const result = await sendMessageFeishu({ cfg, to, text: fallbackText, accountId }); + return { channel: "feishu", ...result }; + } + } + + // No media URL, just return text result + const result = await sendMessageFeishu({ cfg, to, text: text ?? "", accountId }); + return { channel: "feishu", ...result }; + }, +}; diff --git a/feishu/src/perm-schema.ts b/feishu/src/perm-schema.ts new file mode 100644 index 00000000..ac645389 --- /dev/null +++ b/feishu/src/perm-schema.ts @@ -0,0 +1,52 @@ +import { Type, type Static } from "@sinclair/typebox"; + +const TokenType = Type.Union([ + Type.Literal("doc"), + Type.Literal("docx"), + Type.Literal("sheet"), + Type.Literal("bitable"), + Type.Literal("folder"), + Type.Literal("file"), + Type.Literal("wiki"), + Type.Literal("mindnote"), +]); + +const MemberType = Type.Union([ + Type.Literal("email"), + Type.Literal("openid"), + Type.Literal("userid"), + Type.Literal("unionid"), + Type.Literal("openchat"), + Type.Literal("opendepartmentid"), +]); + +const Permission = Type.Union([ + Type.Literal("view"), + Type.Literal("edit"), + Type.Literal("full_access"), +]); + +export const FeishuPermSchema = Type.Union([ + Type.Object({ + action: Type.Literal("list"), + token: Type.String({ description: "File token" }), + type: TokenType, + }), + Type.Object({ + action: Type.Literal("add"), + token: Type.String({ description: "File token" }), + type: TokenType, + member_type: MemberType, + member_id: Type.String({ description: "Member ID (email, open_id, user_id, etc.)" }), + perm: Permission, + }), + Type.Object({ + action: Type.Literal("remove"), + token: Type.String({ description: "File token" }), + type: TokenType, + member_type: MemberType, + member_id: Type.String({ description: "Member ID to remove" }), + }), +]); + +export type FeishuPermParams = Static; diff --git a/feishu/src/perm.ts b/feishu/src/perm.ts new file mode 100644 index 00000000..66071cbf --- /dev/null +++ b/feishu/src/perm.ts @@ -0,0 +1,166 @@ +import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; +import { createFeishuClient } from "./client.js"; +import { listEnabledFeishuAccounts } from "./accounts.js"; +import type * as Lark from "@larksuiteoapi/node-sdk"; +import { FeishuPermSchema, type FeishuPermParams } from "./perm-schema.js"; +import { resolveToolsConfig } from "./tools-config.js"; + +// ============ Helpers ============ + +function json(data: unknown) { + return { + content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }], + details: data, + }; +} + +type ListTokenType = + | "doc" + | "sheet" + | "file" + | "wiki" + | "bitable" + | "docx" + | "mindnote" + | "minutes" + | "slides"; +type CreateTokenType = + | "doc" + | "sheet" + | "file" + | "wiki" + | "bitable" + | "docx" + | "folder" + | "mindnote" + | "minutes" + | "slides"; +type MemberType = + | "email" + | "openid" + | "unionid" + | "openchat" + | "opendepartmentid" + | "userid" + | "groupid" + | "wikispaceid"; +type PermType = "view" | "edit" | "full_access"; + +// ============ Actions ============ + +async function listMembers(client: Lark.Client, token: string, type: string) { + const res = await client.drive.permissionMember.list({ + path: { token }, + params: { type: type as ListTokenType }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + members: + res.data?.items?.map((m) => ({ + member_type: m.member_type, + member_id: m.member_id, + perm: m.perm, + name: m.name, + })) ?? [], + }; +} + +async function addMember( + client: Lark.Client, + token: string, + type: string, + memberType: string, + memberId: string, + perm: string, +) { + const res = await client.drive.permissionMember.create({ + path: { token }, + params: { type: type as CreateTokenType, need_notification: false }, + data: { + member_type: memberType as MemberType, + member_id: memberId, + perm: perm as PermType, + }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + success: true, + member: res.data?.member, + }; +} + +async function removeMember( + client: Lark.Client, + token: string, + type: string, + memberType: string, + memberId: string, +) { + const res = await client.drive.permissionMember.delete({ + path: { token, member_id: memberId }, + params: { type: type as CreateTokenType, member_type: memberType as MemberType }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + success: true, + }; +} + +// ============ Tool Registration ============ + +export function registerFeishuPermTools(api: OpenClawPluginApi) { + if (!api.config) { + api.logger.debug?.("feishu_perm: No config available, skipping perm tools"); + return; + } + + const accounts = listEnabledFeishuAccounts(api.config); + if (accounts.length === 0) { + api.logger.debug?.("feishu_perm: No Feishu accounts configured, skipping perm tools"); + return; + } + + const firstAccount = accounts[0]; + const toolsCfg = resolveToolsConfig(firstAccount.config.tools); + if (!toolsCfg.perm) { + api.logger.debug?.("feishu_perm: perm tool disabled in config (default: false)"); + return; + } + + const getClient = () => createFeishuClient(firstAccount); + + api.registerTool( + { + name: "feishu_perm", + label: "Feishu Perm", + description: "Feishu permission management. Actions: list, add, remove", + parameters: FeishuPermSchema, + async execute(_toolCallId, params) { + const p = params as FeishuPermParams; + try { + const client = getClient(); + switch (p.action) { + case "list": + return json(await listMembers(client, p.token, p.type)); + case "add": + return json( + await addMember(client, p.token, p.type, p.member_type, p.member_id, p.perm), + ); + case "remove": + return json(await removeMember(client, p.token, p.type, p.member_type, p.member_id)); + default: + return json({ error: `Unknown action: ${(p as any).action}` }); + } + } catch (err) { + return json({ error: err instanceof Error ? err.message : String(err) }); + } + }, + }, + { name: "feishu_perm" }, + ); + + api.logger.info?.(`feishu_perm: Registered feishu_perm tool`); +} diff --git a/feishu/src/policy.ts b/feishu/src/policy.ts new file mode 100644 index 00000000..a0e1a0d8 --- /dev/null +++ b/feishu/src/policy.ts @@ -0,0 +1,92 @@ +import type { ChannelGroupContext, GroupToolPolicyConfig } from "openclaw/plugin-sdk"; +import type { FeishuConfig, FeishuGroupConfig } from "./types.js"; + +export type FeishuAllowlistMatch = { + allowed: boolean; + matchKey?: string; + matchSource?: "wildcard" | "id" | "name"; +}; + +export function resolveFeishuAllowlistMatch(params: { + allowFrom: Array; + senderId: string; + senderName?: string | null; +}): FeishuAllowlistMatch { + const allowFrom = params.allowFrom + .map((entry) => String(entry).trim().toLowerCase()) + .filter(Boolean); + + if (allowFrom.length === 0) return { allowed: false }; + if (allowFrom.includes("*")) { + return { allowed: true, matchKey: "*", matchSource: "wildcard" }; + } + + const senderId = params.senderId.toLowerCase(); + if (allowFrom.includes(senderId)) { + return { allowed: true, matchKey: senderId, matchSource: "id" }; + } + + const senderName = params.senderName?.toLowerCase(); + if (senderName && allowFrom.includes(senderName)) { + return { allowed: true, matchKey: senderName, matchSource: "name" }; + } + + return { allowed: false }; +} + +export function resolveFeishuGroupConfig(params: { + cfg?: FeishuConfig; + groupId?: string | null; +}): FeishuGroupConfig | undefined { + const groups = params.cfg?.groups ?? {}; + const groupId = params.groupId?.trim(); + if (!groupId) return undefined; + + const direct = groups[groupId] as FeishuGroupConfig | undefined; + if (direct) return direct; + + const lowered = groupId.toLowerCase(); + const matchKey = Object.keys(groups).find((key) => key.toLowerCase() === lowered); + return matchKey ? (groups[matchKey] as FeishuGroupConfig | undefined) : undefined; +} + +export function resolveFeishuGroupToolPolicy( + params: ChannelGroupContext, +): GroupToolPolicyConfig | undefined { + const cfg = params.cfg.channels?.feishu as FeishuConfig | undefined; + if (!cfg) return undefined; + + const groupConfig = resolveFeishuGroupConfig({ + cfg, + groupId: params.groupId, + }); + + return groupConfig?.tools; +} + +export function isFeishuGroupAllowed(params: { + groupPolicy: "open" | "allowlist" | "disabled"; + allowFrom: Array; + senderId: string; + senderName?: string | null; +}): boolean { + const { groupPolicy } = params; + if (groupPolicy === "disabled") return false; + if (groupPolicy === "open") return true; + return resolveFeishuAllowlistMatch(params).allowed; +} + +export function resolveFeishuReplyPolicy(params: { + isDirectMessage: boolean; + globalConfig?: FeishuConfig; + groupConfig?: FeishuGroupConfig; +}): { requireMention: boolean } { + if (params.isDirectMessage) { + return { requireMention: false }; + } + + const requireMention = + params.groupConfig?.requireMention ?? params.globalConfig?.requireMention ?? true; + + return { requireMention }; +} diff --git a/feishu/src/probe.ts b/feishu/src/probe.ts new file mode 100644 index 00000000..c738cd03 --- /dev/null +++ b/feishu/src/probe.ts @@ -0,0 +1,70 @@ +import type { FeishuProbeResult } from "./types.js"; +import { createFeishuClient, type FeishuClientCredentials } from "./client.js"; + + +const SUCCESS_CACHE_TTL = 60 * 60 * 1000 * 12; // 12 hour - bot info rarely changes +const FAILURE_CACHE_TTL = 60 * 1000; // 1 minute - avoid rapid retry on errors + +export async function probeFeishu(creds?: FeishuClientCredentials): Promise { + if (!creds?.appId || !creds?.appSecret) { + return { + ok: false, + error: "missing credentials (appId, appSecret)", + }; + } + + // Check cache first + const cached = probeCache.get(creds.appId); + if (cached && Date.now() < cached.expiry) { + return cached.result; + } + + try { + const client = createFeishuClient(creds); + // Use bot/v3/info API to get bot information + const response = await (client as any).request({ + method: "GET", + url: "/open-apis/bot/v3/info", + data: {}, + }); + + let result: FeishuProbeResult; + + if (response.code !== 0) { + result = { + ok: false, + appId: creds.appId, + error: `API error: ${response.msg || `code ${response.code}`}`, + }; + }else{ + const bot = response.bot || response.data?.bot; + result = { + ok: true, + appId: creds.appId, + botName: bot?.bot_name, + botOpenId: bot?.open_id, + }; + } + + // Cache the result with appropriate TTL + const ttl = result.ok ? SUCCESS_CACHE_TTL : FAILURE_CACHE_TTL; + probeCache.set(creds.appId, { + result, + expiry: Date.now() + ttl, + }); + + return result; + } catch (err) { + const result: FeishuProbeResult = { + ok: false, + appId: creds.appId, + error: err instanceof Error ? err.message : String(err), + }; + // Cache error results to avoid rapid retries on errors + probeCache.set(creds.appId, { + result, + expiry: Date.now() + FAILURE_CACHE_TTL, + }); + return result; + } +} diff --git a/feishu/src/reactions.ts b/feishu/src/reactions.ts new file mode 100644 index 00000000..ae2d2085 --- /dev/null +++ b/feishu/src/reactions.ts @@ -0,0 +1,160 @@ +import type { ClawdbotConfig } from "openclaw/plugin-sdk"; +import { createFeishuClient } from "./client.js"; +import { resolveFeishuAccount } from "./accounts.js"; + +export type FeishuReaction = { + reactionId: string; + emojiType: string; + operatorType: "app" | "user"; + operatorId: string; +}; + +/** + * Add a reaction (emoji) to a message. + * @param emojiType - Feishu emoji type, e.g., "SMILE", "THUMBSUP", "HEART" + * @see https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce + */ +export async function addReactionFeishu(params: { + cfg: ClawdbotConfig; + messageId: string; + emojiType: string; + accountId?: string; +}): Promise<{ reactionId: string }> { + const { cfg, messageId, emojiType, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + + const response = (await client.im.messageReaction.create({ + path: { message_id: messageId }, + data: { + reaction_type: { + emoji_type: emojiType, + }, + }, + })) as { + code?: number; + msg?: string; + data?: { reaction_id?: string }; + }; + + if (response.code !== 0) { + throw new Error(`Feishu add reaction failed: ${response.msg || `code ${response.code}`}`); + } + + const reactionId = response.data?.reaction_id; + if (!reactionId) { + throw new Error("Feishu add reaction failed: no reaction_id returned"); + } + + return { reactionId }; +} + +/** + * Remove a reaction from a message. + */ +export async function removeReactionFeishu(params: { + cfg: ClawdbotConfig; + messageId: string; + reactionId: string; + accountId?: string; +}): Promise { + const { cfg, messageId, reactionId, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + + const response = (await client.im.messageReaction.delete({ + path: { + message_id: messageId, + reaction_id: reactionId, + }, + })) as { code?: number; msg?: string }; + + if (response.code !== 0) { + throw new Error(`Feishu remove reaction failed: ${response.msg || `code ${response.code}`}`); + } +} + +/** + * List all reactions for a message. + */ +export async function listReactionsFeishu(params: { + cfg: ClawdbotConfig; + messageId: string; + emojiType?: string; + accountId?: string; +}): Promise { + const { cfg, messageId, emojiType, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + + const response = (await client.im.messageReaction.list({ + path: { message_id: messageId }, + params: emojiType ? { reaction_type: emojiType } : undefined, + })) as { + code?: number; + msg?: string; + data?: { + items?: Array<{ + reaction_id?: string; + reaction_type?: { emoji_type?: string }; + operator_type?: string; + operator_id?: { open_id?: string; user_id?: string; union_id?: string }; + }>; + }; + }; + + if (response.code !== 0) { + throw new Error(`Feishu list reactions failed: ${response.msg || `code ${response.code}`}`); + } + + const items = response.data?.items ?? []; + return items.map((item) => ({ + reactionId: item.reaction_id ?? "", + emojiType: item.reaction_type?.emoji_type ?? "", + operatorType: item.operator_type === "app" ? "app" : "user", + operatorId: + item.operator_id?.open_id ?? item.operator_id?.user_id ?? item.operator_id?.union_id ?? "", + })); +} + +/** + * Common Feishu emoji types for convenience. + * @see https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce + */ +export const FeishuEmoji = { + // Common reactions + THUMBSUP: "THUMBSUP", + THUMBSDOWN: "THUMBSDOWN", + HEART: "HEART", + SMILE: "SMILE", + GRINNING: "GRINNING", + LAUGHING: "LAUGHING", + CRY: "CRY", + ANGRY: "ANGRY", + SURPRISED: "SURPRISED", + THINKING: "THINKING", + CLAP: "CLAP", + OK: "OK", + FIST: "FIST", + PRAY: "PRAY", + FIRE: "FIRE", + PARTY: "PARTY", + CHECK: "CHECK", + CROSS: "CROSS", + QUESTION: "QUESTION", + EXCLAMATION: "EXCLAMATION", +} as const; + +export type FeishuEmojiType = (typeof FeishuEmoji)[keyof typeof FeishuEmoji]; diff --git a/feishu/src/reply-dispatcher.ts b/feishu/src/reply-dispatcher.ts new file mode 100644 index 00000000..75514f12 --- /dev/null +++ b/feishu/src/reply-dispatcher.ts @@ -0,0 +1,174 @@ +import { + createReplyPrefixContext, + createTypingCallbacks, + logTypingFailure, + type ClawdbotConfig, + type RuntimeEnv, + type ReplyPayload, +} from "openclaw/plugin-sdk"; +import { getFeishuRuntime } from "./runtime.js"; +import { sendMessageFeishu, sendMarkdownCardFeishu } from "./send.js"; +import type { FeishuConfig } from "./types.js"; +import type { MentionTarget } from "./mention.js"; +import { resolveFeishuAccount } from "./accounts.js"; +import { + addTypingIndicator, + removeTypingIndicator, + type TypingIndicatorState, +} from "./typing.js"; + +/** + * Detect if text contains markdown elements that benefit from card rendering. + * Used by auto render mode. + */ +function shouldUseCard(text: string): boolean { + // Code blocks (fenced) + if (/```[\s\S]*?```/.test(text)) return true; + // Tables (at least header + separator row with |) + if (/\|.+\|[\r\n]+\|[-:| ]+\|/.test(text)) return true; + return false; +} + +export type CreateFeishuReplyDispatcherParams = { + cfg: ClawdbotConfig; + agentId: string; + runtime: RuntimeEnv; + chatId: string; + replyToMessageId?: string; + /** Mention targets, will be auto-included in replies */ + mentionTargets?: MentionTarget[]; + /** Account ID for multi-account support */ + accountId?: string; +}; + +export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherParams) { + const core = getFeishuRuntime(); + const { cfg, agentId, chatId, replyToMessageId, mentionTargets, accountId } = params; + + // Resolve account for config access + const account = resolveFeishuAccount({ cfg, accountId }); + + const prefixContext = createReplyPrefixContext({ + cfg, + agentId, + }); + + // Feishu doesn't have a native typing indicator API. + // We use message reactions as a typing indicator substitute. + let typingState: TypingIndicatorState | null = null; + + const typingCallbacks = createTypingCallbacks({ + start: async () => { + if (!replyToMessageId) return; + typingState = await addTypingIndicator({ cfg, messageId: replyToMessageId, accountId }); + params.runtime.log?.(`feishu[${account.accountId}]: added typing indicator reaction`); + }, + stop: async () => { + if (!typingState) return; + await removeTypingIndicator({ cfg, state: typingState, accountId }); + typingState = null; + params.runtime.log?.(`feishu[${account.accountId}]: removed typing indicator reaction`); + }, + onStartError: (err) => { + logTypingFailure({ + log: (message) => params.runtime.log?.(message), + channel: "feishu", + action: "start", + error: err, + }); + }, + onStopError: (err) => { + logTypingFailure({ + log: (message) => params.runtime.log?.(message), + channel: "feishu", + action: "stop", + error: err, + }); + }, + }); + + const textChunkLimit = core.channel.text.resolveTextChunkLimit({ + cfg, + channel: "feishu", + defaultLimit: 4000, + }); + const chunkMode = core.channel.text.resolveChunkMode(cfg, "feishu"); + const tableMode = core.channel.text.resolveMarkdownTableMode({ + cfg, + channel: "feishu", + }); + + const { dispatcher, replyOptions, markDispatchIdle } = + core.channel.reply.createReplyDispatcherWithTyping({ + responsePrefix: prefixContext.responsePrefix, + responsePrefixContextProvider: prefixContext.responsePrefixContextProvider, + humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, agentId), + onReplyStart: typingCallbacks.onReplyStart, + deliver: async (payload: ReplyPayload) => { + params.runtime.log?.(`feishu[${account.accountId}] deliver called: text=${payload.text?.slice(0, 100)}`); + const text = payload.text ?? ""; + if (!text.trim()) { + params.runtime.log?.(`feishu[${account.accountId}] deliver: empty text, skipping`); + return; + } + + // Check render mode: auto (default), raw, or card + const feishuCfg = account.config; + const renderMode = feishuCfg?.renderMode ?? "auto"; + + // Determine if we should use card for this message + const useCard = + renderMode === "card" || (renderMode === "auto" && shouldUseCard(text)); + + // Only include @mentions in the first chunk (avoid duplicate @s) + let isFirstChunk = true; + + if (useCard) { + // Card mode: send as interactive card with markdown rendering + const chunks = core.channel.text.chunkTextWithMode(text, textChunkLimit, chunkMode); + params.runtime.log?.(`feishu[${account.accountId}] deliver: sending ${chunks.length} card chunks to ${chatId}`); + for (const chunk of chunks) { + await sendMarkdownCardFeishu({ + cfg, + to: chatId, + text: chunk, + replyToMessageId, + mentions: isFirstChunk ? mentionTargets : undefined, + accountId, + }); + isFirstChunk = false; + } + } else { + // Raw mode: send as plain text with table conversion + const converted = core.channel.text.convertMarkdownTables(text, tableMode); + const chunks = core.channel.text.chunkTextWithMode(converted, textChunkLimit, chunkMode); + params.runtime.log?.(`feishu[${account.accountId}] deliver: sending ${chunks.length} text chunks to ${chatId}`); + for (const chunk of chunks) { + await sendMessageFeishu({ + cfg, + to: chatId, + text: chunk, + replyToMessageId, + mentions: isFirstChunk ? mentionTargets : undefined, + accountId, + }); + isFirstChunk = false; + } + } + }, + onError: (err, info) => { + params.runtime.error?.(`feishu[${account.accountId}] ${info.kind} reply failed: ${String(err)}`); + typingCallbacks.onIdle?.(); + }, + onIdle: typingCallbacks.onIdle, + }); + + return { + dispatcher, + replyOptions: { + ...replyOptions, + onModelSelected: prefixContext.onModelSelected, + }, + markDispatchIdle, + }; +} diff --git a/feishu/src/runtime.ts b/feishu/src/runtime.ts new file mode 100644 index 00000000..f1148c5e --- /dev/null +++ b/feishu/src/runtime.ts @@ -0,0 +1,14 @@ +import type { PluginRuntime } from "openclaw/plugin-sdk"; + +let runtime: PluginRuntime | null = null; + +export function setFeishuRuntime(next: PluginRuntime) { + runtime = next; +} + +export function getFeishuRuntime(): PluginRuntime { + if (!runtime) { + throw new Error("Feishu runtime not initialized"); + } + return runtime; +} diff --git a/feishu/src/send.ts b/feishu/src/send.ts new file mode 100644 index 00000000..96341f40 --- /dev/null +++ b/feishu/src/send.ts @@ -0,0 +1,360 @@ +import type { ClawdbotConfig } from "openclaw/plugin-sdk"; +import type { FeishuSendResult, ResolvedFeishuAccount } from "./types.js"; +import type { MentionTarget } from "./mention.js"; +import { buildMentionedMessage, buildMentionedCardContent } from "./mention.js"; +import { createFeishuClient } from "./client.js"; +import { resolveReceiveIdType, normalizeFeishuTarget } from "./targets.js"; +import { getFeishuRuntime } from "./runtime.js"; +import { resolveFeishuAccount } from "./accounts.js"; + +export type FeishuMessageInfo = { + messageId: string; + chatId: string; + senderId?: string; + senderOpenId?: string; + content: string; + contentType: string; + createTime?: number; +}; + +/** + * Get a message by its ID. + * Useful for fetching quoted/replied message content. + */ +export async function getMessageFeishu(params: { + cfg: ClawdbotConfig; + messageId: string; + accountId?: string; +}): Promise { + const { cfg, messageId, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + + try { + const response = (await client.im.message.get({ + path: { message_id: messageId }, + })) as { + code?: number; + msg?: string; + data?: { + items?: Array<{ + message_id?: string; + chat_id?: string; + msg_type?: string; + body?: { content?: string }; + sender?: { + id?: string; + id_type?: string; + sender_type?: string; + }; + create_time?: string; + }>; + }; + }; + + if (response.code !== 0) { + return null; + } + + const item = response.data?.items?.[0]; + if (!item) { + return null; + } + + // Parse content based on message type + let content = item.body?.content ?? ""; + try { + const parsed = JSON.parse(content); + if (item.msg_type === "text" && parsed.text) { + content = parsed.text; + } + } catch { + // Keep raw content if parsing fails + } + + return { + messageId: item.message_id ?? messageId, + chatId: item.chat_id ?? "", + senderId: item.sender?.id, + senderOpenId: item.sender?.id_type === "open_id" ? item.sender?.id : undefined, + content, + contentType: item.msg_type ?? "text", + createTime: item.create_time ? parseInt(item.create_time, 10) : undefined, + }; + } catch { + return null; + } +} + +export type SendFeishuMessageParams = { + cfg: ClawdbotConfig; + to: string; + text: string; + replyToMessageId?: string; + /** Mention target users */ + mentions?: MentionTarget[]; + /** Account ID (optional, uses default if not specified) */ + accountId?: string; +}; + +function buildFeishuPostMessagePayload(params: { messageText: string }): { + content: string; + msgType: string; +} { + const { messageText } = params; + return { + content: JSON.stringify({ + zh_cn: { + content: [ + [ + { + tag: "md", + text: messageText, + }, + ], + ], + }, + }), + msgType: "post", + }; +} + +export async function sendMessageFeishu(params: SendFeishuMessageParams): Promise { + const { cfg, to, text, replyToMessageId, mentions, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + const receiveId = normalizeFeishuTarget(to); + if (!receiveId) { + throw new Error(`Invalid Feishu target: ${to}`); + } + + const receiveIdType = resolveReceiveIdType(receiveId); + const tableMode = getFeishuRuntime().channel.text.resolveMarkdownTableMode({ + cfg, + channel: "feishu", + }); + + // Build message content (with @mention support) + let rawText = text ?? ""; + if (mentions && mentions.length > 0) { + rawText = buildMentionedMessage(mentions, rawText); + } + const messageText = getFeishuRuntime().channel.text.convertMarkdownTables(rawText, tableMode); + + const { content, msgType } = buildFeishuPostMessagePayload({ messageText }); + + if (replyToMessageId) { + const response = await client.im.message.reply({ + path: { message_id: replyToMessageId }, + data: { + content, + msg_type: msgType, + }, + }); + + if (response.code !== 0) { + throw new Error(`Feishu reply failed: ${response.msg || `code ${response.code}`}`); + } + + return { + messageId: response.data?.message_id ?? "unknown", + chatId: receiveId, + }; + } + + const response = await client.im.message.create({ + params: { receive_id_type: receiveIdType }, + data: { + receive_id: receiveId, + content, + msg_type: msgType, + }, + }); + + if (response.code !== 0) { + throw new Error(`Feishu send failed: ${response.msg || `code ${response.code}`}`); + } + + return { + messageId: response.data?.message_id ?? "unknown", + chatId: receiveId, + }; +} + +export type SendFeishuCardParams = { + cfg: ClawdbotConfig; + to: string; + card: Record; + replyToMessageId?: string; + accountId?: string; +}; + +export async function sendCardFeishu(params: SendFeishuCardParams): Promise { + const { cfg, to, card, replyToMessageId, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + const receiveId = normalizeFeishuTarget(to); + if (!receiveId) { + throw new Error(`Invalid Feishu target: ${to}`); + } + + const receiveIdType = resolveReceiveIdType(receiveId); + const content = JSON.stringify(card); + + if (replyToMessageId) { + const response = await client.im.message.reply({ + path: { message_id: replyToMessageId }, + data: { + content, + msg_type: "interactive", + }, + }); + + if (response.code !== 0) { + throw new Error(`Feishu card reply failed: ${response.msg || `code ${response.code}`}`); + } + + return { + messageId: response.data?.message_id ?? "unknown", + chatId: receiveId, + }; + } + + const response = await client.im.message.create({ + params: { receive_id_type: receiveIdType }, + data: { + receive_id: receiveId, + content, + msg_type: "interactive", + }, + }); + + if (response.code !== 0) { + throw new Error(`Feishu card send failed: ${response.msg || `code ${response.code}`}`); + } + + return { + messageId: response.data?.message_id ?? "unknown", + chatId: receiveId, + }; +} + +export async function updateCardFeishu(params: { + cfg: ClawdbotConfig; + messageId: string; + card: Record; + accountId?: string; +}): Promise { + const { cfg, messageId, card, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + const content = JSON.stringify(card); + + const response = await client.im.message.patch({ + path: { message_id: messageId }, + data: { content }, + }); + + if (response.code !== 0) { + throw new Error(`Feishu card update failed: ${response.msg || `code ${response.code}`}`); + } +} + +/** + * Build a Feishu interactive card with markdown content. + * Cards render markdown properly (code blocks, tables, links, etc.) + * Uses schema 2.0 format for proper markdown rendering. + */ +export function buildMarkdownCard(text: string): Record { + return { + schema: "2.0", + config: { + wide_screen_mode: true, + }, + body: { + elements: [ + { + tag: "markdown", + content: text, + }, + ], + }, + }; +} + +/** + * Send a message as a markdown card (interactive message). + * This renders markdown properly in Feishu (code blocks, tables, bold/italic, etc.) + */ +export async function sendMarkdownCardFeishu(params: { + cfg: ClawdbotConfig; + to: string; + text: string; + replyToMessageId?: string; + /** Mention target users */ + mentions?: MentionTarget[]; + accountId?: string; +}): Promise { + const { cfg, to, text, replyToMessageId, mentions, accountId } = params; + // Build message content (with @mention support) + let cardText = text; + if (mentions && mentions.length > 0) { + cardText = buildMentionedCardContent(mentions, text); + } + const card = buildMarkdownCard(cardText); + return sendCardFeishu({ cfg, to, card, replyToMessageId, accountId }); +} + +/** + * Edit an existing text message. + * Note: Feishu only allows editing messages within 24 hours. + */ +export async function editMessageFeishu(params: { + cfg: ClawdbotConfig; + messageId: string; + text: string; + accountId?: string; +}): Promise { + const { cfg, messageId, text, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + throw new Error(`Feishu account "${account.accountId}" not configured`); + } + + const client = createFeishuClient(account); + const tableMode = getFeishuRuntime().channel.text.resolveMarkdownTableMode({ + cfg, + channel: "feishu", + }); + const messageText = getFeishuRuntime().channel.text.convertMarkdownTables(text ?? "", tableMode); + + const { content, msgType } = buildFeishuPostMessagePayload({ messageText }); + + const response = await client.im.message.update({ + path: { message_id: messageId }, + data: { + msg_type: msgType, + content, + }, + }); + + if (response.code !== 0) { + throw new Error(`Feishu message edit failed: ${response.msg || `code ${response.code}`}`); + } +} diff --git a/feishu/src/targets.ts b/feishu/src/targets.ts new file mode 100644 index 00000000..16d3e99b --- /dev/null +++ b/feishu/src/targets.ts @@ -0,0 +1,58 @@ +import type { FeishuIdType } from "./types.js"; + +const CHAT_ID_PREFIX = "oc_"; +const OPEN_ID_PREFIX = "ou_"; +const USER_ID_REGEX = /^[a-zA-Z0-9_-]+$/; + +export function detectIdType(id: string): FeishuIdType | null { + const trimmed = id.trim(); + if (trimmed.startsWith(CHAT_ID_PREFIX)) return "chat_id"; + if (trimmed.startsWith(OPEN_ID_PREFIX)) return "open_id"; + if (USER_ID_REGEX.test(trimmed)) return "user_id"; + return null; +} + +export function normalizeFeishuTarget(raw: string): string | null { + const trimmed = raw.trim(); + if (!trimmed) return null; + + const lowered = trimmed.toLowerCase(); + if (lowered.startsWith("chat:")) { + return trimmed.slice("chat:".length).trim() || null; + } + if (lowered.startsWith("user:")) { + return trimmed.slice("user:".length).trim() || null; + } + if (lowered.startsWith("open_id:")) { + return trimmed.slice("open_id:".length).trim() || null; + } + + return trimmed; +} + +export function formatFeishuTarget(id: string, type?: FeishuIdType): string { + const trimmed = id.trim(); + if (type === "chat_id" || trimmed.startsWith(CHAT_ID_PREFIX)) { + return `chat:${trimmed}`; + } + if (type === "open_id" || trimmed.startsWith(OPEN_ID_PREFIX)) { + return `user:${trimmed}`; + } + return trimmed; +} + +export function resolveReceiveIdType(id: string): "chat_id" | "open_id" | "user_id" { + const trimmed = id.trim(); + if (trimmed.startsWith(CHAT_ID_PREFIX)) return "chat_id"; + if (trimmed.startsWith(OPEN_ID_PREFIX)) return "open_id"; + return "open_id"; +} + +export function looksLikeFeishuId(raw: string): boolean { + const trimmed = raw.trim(); + if (!trimmed) return false; + if (/^(chat|user|open_id):/i.test(trimmed)) return true; + if (trimmed.startsWith(CHAT_ID_PREFIX)) return true; + if (trimmed.startsWith(OPEN_ID_PREFIX)) return true; + return false; +} diff --git a/feishu/src/tools-config.ts b/feishu/src/tools-config.ts new file mode 100644 index 00000000..1c1321ee --- /dev/null +++ b/feishu/src/tools-config.ts @@ -0,0 +1,21 @@ +import type { FeishuToolsConfig } from "./types.js"; + +/** + * Default tool configuration. + * - doc, wiki, drive, scopes: enabled by default + * - perm: disabled by default (sensitive operation) + */ +export const DEFAULT_TOOLS_CONFIG: Required = { + doc: true, + wiki: true, + drive: true, + perm: false, + scopes: true, +}; + +/** + * Resolve tools config with defaults. + */ +export function resolveToolsConfig(cfg?: FeishuToolsConfig): Required { + return { ...DEFAULT_TOOLS_CONFIG, ...cfg }; +} diff --git a/feishu/src/types.ts b/feishu/src/types.ts new file mode 100644 index 00000000..3d73edb9 --- /dev/null +++ b/feishu/src/types.ts @@ -0,0 +1,77 @@ +import type { FeishuConfigSchema, FeishuGroupSchema, FeishuAccountConfigSchema, z } from "./config-schema.js"; +import type { MentionTarget } from "./mention.js"; + +export type FeishuConfig = z.infer; +export type FeishuGroupConfig = z.infer; +export type FeishuAccountConfig = z.infer; + +export type FeishuDomain = "feishu" | "lark" | (string & {}); +export type FeishuConnectionMode = "websocket" | "webhook"; + +export type ResolvedFeishuAccount = { + accountId: string; + enabled: boolean; + configured: boolean; + name?: string; + appId?: string; + appSecret?: string; + encryptKey?: string; + verificationToken?: string; + domain: FeishuDomain; + /** Merged config (top-level defaults + account-specific overrides) */ + config: FeishuConfig; +}; + +export type FeishuIdType = "open_id" | "user_id" | "union_id" | "chat_id"; + +export type FeishuMessageContext = { + chatId: string; + messageId: string; + senderId: string; + senderOpenId: string; + senderName?: string; + chatType: "p2p" | "group"; + mentionedBot: boolean; + rootId?: string; + parentId?: string; + content: string; + contentType: string; + /** Mention forward targets (excluding the bot itself) */ + mentionTargets?: MentionTarget[]; + /** Extracted message body (after removing @ placeholders) */ + mentionMessageBody?: string; +}; + +export type FeishuSendResult = { + messageId: string; + chatId: string; +}; + +export type FeishuProbeResult = { + ok: boolean; + error?: string; + appId?: string; + botName?: string; + botOpenId?: string; +}; + +export type FeishuMediaInfo = { + path: string; + contentType?: string; + placeholder: string; +}; + +export type FeishuToolsConfig = { + doc?: boolean; + wiki?: boolean; + drive?: boolean; + perm?: boolean; + scopes?: boolean; +}; + +export type DynamicAgentCreationConfig = { + enabled?: boolean; + workspaceTemplate?: string; + agentDirTemplate?: string; + maxAgents?: number; +}; diff --git a/feishu/src/typing.ts b/feishu/src/typing.ts new file mode 100644 index 00000000..217daa80 --- /dev/null +++ b/feishu/src/typing.ts @@ -0,0 +1,75 @@ +import type { ClawdbotConfig } from "openclaw/plugin-sdk"; +import { createFeishuClient } from "./client.js"; +import { resolveFeishuAccount } from "./accounts.js"; + +// Feishu emoji types for typing indicator +// See: https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce +// Full list: https://github.com/go-lark/lark/blob/main/emoji.go +const TYPING_EMOJI = "Typing"; // Typing indicator emoji + +export type TypingIndicatorState = { + messageId: string; + reactionId: string | null; +}; + +/** + * Add a typing indicator (reaction) to a message + */ +export async function addTypingIndicator(params: { + cfg: ClawdbotConfig; + messageId: string; + accountId?: string; +}): Promise { + const { cfg, messageId, accountId } = params; + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) { + return { messageId, reactionId: null }; + } + + const client = createFeishuClient(account); + + try { + const response = await client.im.messageReaction.create({ + path: { message_id: messageId }, + data: { + reaction_type: { emoji_type: TYPING_EMOJI }, + }, + }); + + const reactionId = (response as any)?.data?.reaction_id ?? null; + return { messageId, reactionId }; + } catch (err) { + // Silently fail - typing indicator is not critical + console.log(`[feishu] failed to add typing indicator: ${err}`); + return { messageId, reactionId: null }; + } +} + +/** + * Remove a typing indicator (reaction) from a message + */ +export async function removeTypingIndicator(params: { + cfg: ClawdbotConfig; + state: TypingIndicatorState; + accountId?: string; +}): Promise { + const { cfg, state, accountId } = params; + if (!state.reactionId) return; + + const account = resolveFeishuAccount({ cfg, accountId }); + if (!account.configured) return; + + const client = createFeishuClient(account); + + try { + await client.im.messageReaction.delete({ + path: { + message_id: state.messageId, + reaction_id: state.reactionId, + }, + }); + } catch (err) { + // Silently fail - cleanup is not critical + console.log(`[feishu] failed to remove typing indicator: ${err}`); + } +} diff --git a/feishu/src/wiki-schema.ts b/feishu/src/wiki-schema.ts new file mode 100644 index 00000000..006cc2da --- /dev/null +++ b/feishu/src/wiki-schema.ts @@ -0,0 +1,55 @@ +import { Type, type Static } from "@sinclair/typebox"; + +export const FeishuWikiSchema = Type.Union([ + Type.Object({ + action: Type.Literal("spaces"), + }), + Type.Object({ + action: Type.Literal("nodes"), + space_id: Type.String({ description: "Knowledge space ID" }), + parent_node_token: Type.Optional( + Type.String({ description: "Parent node token (optional, omit for root)" }), + ), + }), + Type.Object({ + action: Type.Literal("get"), + token: Type.String({ description: "Wiki node token (from URL /wiki/XXX)" }), + }), + Type.Object({ + action: Type.Literal("search"), + query: Type.String({ description: "Search query" }), + space_id: Type.Optional(Type.String({ description: "Limit search to this space (optional)" })), + }), + Type.Object({ + action: Type.Literal("create"), + space_id: Type.String({ description: "Knowledge space ID" }), + title: Type.String({ description: "Node title" }), + obj_type: Type.Optional( + Type.Union([Type.Literal("docx"), Type.Literal("sheet"), Type.Literal("bitable")], { + description: "Object type (default: docx)", + }), + ), + parent_node_token: Type.Optional( + Type.String({ description: "Parent node token (optional, omit for root)" }), + ), + }), + Type.Object({ + action: Type.Literal("move"), + space_id: Type.String({ description: "Source knowledge space ID" }), + node_token: Type.String({ description: "Node token to move" }), + target_space_id: Type.Optional( + Type.String({ description: "Target space ID (optional, same space if omitted)" }), + ), + target_parent_token: Type.Optional( + Type.String({ description: "Target parent node token (optional, root if omitted)" }), + ), + }), + Type.Object({ + action: Type.Literal("rename"), + space_id: Type.String({ description: "Knowledge space ID" }), + node_token: Type.String({ description: "Node token to rename" }), + title: Type.String({ description: "New title" }), + }), +]); + +export type FeishuWikiParams = Static; diff --git a/feishu/src/wiki.ts b/feishu/src/wiki.ts new file mode 100644 index 00000000..c9ae3e54 --- /dev/null +++ b/feishu/src/wiki.ts @@ -0,0 +1,224 @@ +import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; +import { createFeishuClient } from "./client.js"; +import { listEnabledFeishuAccounts } from "./accounts.js"; +import type * as Lark from "@larksuiteoapi/node-sdk"; +import { FeishuWikiSchema, type FeishuWikiParams } from "./wiki-schema.js"; +import { resolveToolsConfig } from "./tools-config.js"; + +// ============ Helpers ============ + +function json(data: unknown) { + return { + content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }], + details: data, + }; +} + +type ObjType = "doc" | "sheet" | "mindnote" | "bitable" | "file" | "docx" | "slides"; + +// ============ Actions ============ + +const WIKI_ACCESS_HINT = + "To grant wiki access: Open wiki space → Settings → Members → Add the bot. " + + "See: https://open.feishu.cn/document/server-docs/docs/wiki-v2/wiki-qa#a40ad4ca"; + +async function listSpaces(client: Lark.Client) { + const res = await client.wiki.space.list({}); + if (res.code !== 0) throw new Error(res.msg); + + const spaces = + res.data?.items?.map((s) => ({ + space_id: s.space_id, + name: s.name, + description: s.description, + visibility: s.visibility, + })) ?? []; + + return { + spaces, + ...(spaces.length === 0 && { hint: WIKI_ACCESS_HINT }), + }; +} + +async function listNodes(client: Lark.Client, spaceId: string, parentNodeToken?: string) { + const res = await client.wiki.spaceNode.list({ + path: { space_id: spaceId }, + params: { parent_node_token: parentNodeToken }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + nodes: + res.data?.items?.map((n) => ({ + node_token: n.node_token, + obj_token: n.obj_token, + obj_type: n.obj_type, + title: n.title, + has_child: n.has_child, + })) ?? [], + }; +} + +async function getNode(client: Lark.Client, token: string) { + const res = await client.wiki.space.getNode({ + params: { token }, + }); + if (res.code !== 0) throw new Error(res.msg); + + const node = res.data?.node; + return { + node_token: node?.node_token, + space_id: node?.space_id, + obj_token: node?.obj_token, + obj_type: node?.obj_type, + title: node?.title, + parent_node_token: node?.parent_node_token, + has_child: node?.has_child, + creator: node?.creator, + create_time: node?.node_create_time, + }; +} + +async function createNode( + client: Lark.Client, + spaceId: string, + title: string, + objType?: string, + parentNodeToken?: string, +) { + const res = await client.wiki.spaceNode.create({ + path: { space_id: spaceId }, + data: { + obj_type: (objType as ObjType) || "docx", + node_type: "origin" as const, + title, + parent_node_token: parentNodeToken, + }, + }); + if (res.code !== 0) throw new Error(res.msg); + + const node = res.data?.node; + return { + node_token: node?.node_token, + obj_token: node?.obj_token, + obj_type: node?.obj_type, + title: node?.title, + }; +} + +async function moveNode( + client: Lark.Client, + spaceId: string, + nodeToken: string, + targetSpaceId?: string, + targetParentToken?: string, +) { + const res = await client.wiki.spaceNode.move({ + path: { space_id: spaceId, node_token: nodeToken }, + data: { + target_space_id: targetSpaceId || spaceId, + target_parent_token: targetParentToken, + }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + success: true, + node_token: res.data?.node?.node_token, + }; +} + +async function renameNode( + client: Lark.Client, + spaceId: string, + nodeToken: string, + title: string, +) { + const res = await client.wiki.spaceNode.updateTitle({ + path: { space_id: spaceId, node_token: nodeToken }, + data: { title }, + }); + if (res.code !== 0) throw new Error(res.msg); + + return { + success: true, + node_token: nodeToken, + title, + }; +} + +// ============ Tool Registration ============ + +export function registerFeishuWikiTools(api: OpenClawPluginApi) { + if (!api.config) { + api.logger.debug?.("feishu_wiki: No config available, skipping wiki tools"); + return; + } + + const accounts = listEnabledFeishuAccounts(api.config); + if (accounts.length === 0) { + api.logger.debug?.("feishu_wiki: No Feishu accounts configured, skipping wiki tools"); + return; + } + + const firstAccount = accounts[0]; + const toolsCfg = resolveToolsConfig(firstAccount.config.tools); + if (!toolsCfg.wiki) { + api.logger.debug?.("feishu_wiki: wiki tool disabled in config"); + return; + } + + const getClient = () => createFeishuClient(firstAccount); + + api.registerTool( + { + name: "feishu_wiki", + label: "Feishu Wiki", + description: + "Feishu knowledge base operations. Actions: spaces, nodes, get, create, move, rename", + parameters: FeishuWikiSchema, + async execute(_toolCallId, params) { + const p = params as FeishuWikiParams; + try { + const client = getClient(); + switch (p.action) { + case "spaces": + return json(await listSpaces(client)); + case "nodes": + return json(await listNodes(client, p.space_id, p.parent_node_token)); + case "get": + return json(await getNode(client, p.token)); + case "search": + return json({ + error: + "Search is not available. Use feishu_wiki with action: 'nodes' to browse or action: 'get' to lookup by token.", + }); + case "create": + return json( + await createNode(client, p.space_id, p.title, p.obj_type, p.parent_node_token), + ); + case "move": + return json( + await moveNode( + client, + p.space_id, + p.node_token, + p.target_space_id, + p.target_parent_token, + ), + ); + case "rename": + return json(await renameNode(client, p.space_id, p.node_token, p.title)); + default: + return json({ error: `Unknown action: ${(p as any).action}` }); + } + } catch (err) { + return json({ error: err instanceof Error ? err.message : String(err) }); + } + }, + }, + { name: "feishu_wiki" }, + ); + + api.logger.info?.(`feishu_wiki: Registered feishu_wiki tool`); +} diff --git a/wecom.tar.gz b/wecom.tar.gz new file mode 100644 index 00000000..5607e167 Binary files /dev/null and b/wecom.tar.gz differ diff --git a/wecom/.idea/CopilotChatHistory.xml b/wecom/.idea/CopilotChatHistory.xml new file mode 100644 index 00000000..70d81483 --- /dev/null +++ b/wecom/.idea/CopilotChatHistory.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/wecom/.idea/CopilotChatTabs.xml b/wecom/.idea/CopilotChatTabs.xml new file mode 100644 index 00000000..181b4681 --- /dev/null +++ b/wecom/.idea/CopilotChatTabs.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/wecom/.idea/inspectionProfiles/Project_Default.xml b/wecom/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..f69dedef --- /dev/null +++ b/wecom/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,44 @@ + + + + \ No newline at end of file diff --git a/wecom/.idea/inspectionProfiles/profiles_settings.xml b/wecom/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/wecom/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/wecom/.idea/misc.xml b/wecom/.idea/misc.xml new file mode 100644 index 00000000..812ab5a6 --- /dev/null +++ b/wecom/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/wecom/.idea/modules.xml b/wecom/.idea/modules.xml new file mode 100644 index 00000000..fea2e66b --- /dev/null +++ b/wecom/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/wecom/.idea/vcs.xml b/wecom/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/wecom/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/wecom/.idea/wecom.iml b/wecom/.idea/wecom.iml new file mode 100644 index 00000000..d0876a78 --- /dev/null +++ b/wecom/.idea/wecom.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/wecom/LICENSE b/wecom/LICENSE new file mode 100644 index 00000000..6806bcdd --- /dev/null +++ b/wecom/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 xl370869-art + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/wecom/README.md b/wecom/README.md new file mode 100644 index 00000000..3d6ac9a6 --- /dev/null +++ b/wecom/README.md @@ -0,0 +1,360 @@ +# OpenClaw 企业微信(WeCom)Channel 插件 + +

+ 🚀 企业级双模式 AI 助手接入方案 +

+ +

+ 功能亮点 • + 模式对比 • + 快速开始 • + 配置说明 • + 联系我 +

+ +--- + +## 💡 核心价值:为什么选择本插件? + +### 🏗 独创架构:Bot + Agent 双模融合 + +传统的企微插件通常只能在 "只能聊天的机器人 (Bot)" 和 "只能推送的自建应用 (Agent)" 之间二选一。 +本插件采用 **双模并行架构**,同时压榨两种模式的极限能力: + +* **Bot 通道 (智能体)**:负责 **实时对话**。提供毫秒级流式响应(打字机效果),零延迟交互。 +* **Agent 通道 (自建应用)**:负责 **能力兜底**。当需要发送图片/文件、进行全员广播、或 Bot 对话超时(>6分钟)时,无缝切换到 Agent 通道接管。 + +### 🧩 功能特性全景 + +#### 1. 🗣 **沉浸式交互 (Immersive Interaction)** +* **原生流式 (Stream)**:基于 HTTP 分块传输,拒绝 "转圈等待",体验如 ChatGPT 网页版般丝滑。 +* **交互式卡片 (Card)**:支持 Button/Menu 交互回传,可构建审批、查询等复杂业务流 (Agent模式)。 + +#### 2. 📎 **全模态支持 (Multi-Modal)** +* **发什么都能看**:支持接收图片、文件 (PDF/Doc/Zip)、语音 (自动转文字)、视频。 +* **要什么都能给**:AI 生成的图表、代码文件、语音回复,均可自动上传并推送到企微。 + +#### 3. 📢 **企业级触达 (Enterprise Reach)** +* **精准广播**:支持向 **部门 (Party)**、**标签 (Tag)** 或 **外部群** 批量推送消息。 +* **Cronjob 集成**:通过简单的 JSON 配置实现早报推送、日报提醒、服务器报警。 + +#### 4. 🛡 **生产级稳定 (Production Ready)** +* **容灾切换**:Bot 模式 6 分钟超时自动熔断,切换 Agent 私信送达,防止长任务回答丢失。 +* **Token 自动运维**:内置 AccessToken 守护进程,自动缓存、提前刷新、过期重试。 + +--- + + +## 📊 模式能力对比 + +| 能力维度 | 🤖 Bot 模式 | 🧩 Agent 模式 | ✨ **本插件 (双模)** | +|:---|:---|:---|:---| +| **接收消息 (单聊)** | ✅ 文本/图片/语音/文件 | ✅ 文本/图片/语音/视频/位置/链接 | **✅ 全能互补** (覆盖所有类型) | +| **接收消息 (群聊)** | ✅ 文本/引用 | ❌ 不支持 (无回调) | **✅ 文本/引用** | +| **发送消息** | ❌ 仅支持文本/图片/Markdown | ✅ **全格式支持** (文本/图片/视频/文件等) | **✅ 智能路由** (自动切换) | +| **流式响应** | ✅ **支持** (打字机效果) | ❌ 不支持 | **✅ 完美支持** | +| **主动推送** | ❌ 仅被动回复 | ✅ **支持** (指定用户/部门/标签) | **✅ 完整 API** | + +--- + +## 快速开始 + +### 1. 安装插件 + +```bash +openclaw plugins install @openclaw/wecom +openclaw plugins enable wecom +``` + +也可以通过命令行向导快速配置: + +```bash +openclaw config --section channels +``` + +### 2. 配置 Bot 模式(智能体) + +```bash +openclaw config set channels.wecom.enabled true +openclaw config set channels.wecom.bot.token "YOUR_BOT_TOKEN" +openclaw config set channels.wecom.bot.encodingAESKey "YOUR_BOT_AES_KEY" +openclaw config set channels.wecom.bot.receiveId "" +openclaw config set channels.wecom.bot.streamPlaceholderContent "正在思考..." +openclaw config set channels.wecom.bot.welcomeText "你好!我是 AI 助手" + +# DM 门禁(推荐显式设置 policy) +# - open: 默认放开(所有人可用) +# - disabled: 全部禁用 +# - allowlist: 仅 allowFrom 允许的人可用 +openclaw config set channels.wecom.bot.dm.policy "open" +# policy=allowlist 时生效(例如只允许某些 userid;"*" 表示允许所有人) +openclaw config set channels.wecom.bot.dm.allowFrom '["*"]' +``` + +### 3. 配置 Agent 模式(自建应用,可选) + +```bash +openclaw config set channels.wecom.enabled true +openclaw config set channels.wecom.agent.corpId "YOUR_CORP_ID" +openclaw config set channels.wecom.agent.corpSecret "YOUR_CORP_SECRET" +openclaw config set channels.wecom.agent.agentId 1000001 +openclaw config set channels.wecom.agent.token "YOUR_CALLBACK_TOKEN" +openclaw config set channels.wecom.agent.encodingAESKey "YOUR_CALLBACK_AES_KEY" +openclaw config set channels.wecom.agent.welcomeText "欢迎使用智能助手" +openclaw config set channels.wecom.agent.dm.policy "open" +openclaw config set channels.wecom.agent.dm.allowFrom '["*"]' +``` + +### 4. 高级网络配置 (公网出口代理) +如果您的服务器使用 **动态 IP** (如家庭宽带、内网穿透) 或 **无公网 IP**,企业微信 API 会因 IP 变动报错 `60020 not allow to access from your ip`。 +此时需配置一个**固定 IP 的正向代理** (如 Squid),让插件通过该代理访问企微 API。 + +```bash +openclaw config set channels.wecom.network.egressProxyUrl "http://proxy.company.local:3128" +``` + +### 5. 验证 + +```bash +openclaw gateway restart +openclaw channels status +``` + +--- + +## 配置说明 + +### 完整配置结构 + +```jsonc +{ + "channels": { + "wecom": { + "enabled": true, + + // Bot 模式配置(智能体) + "bot": { + "token": "YOUR_BOT_TOKEN", + "encodingAESKey": "YOUR_BOT_AES_KEY", + "receiveId": "", // 可选,用于解密校验 + "streamPlaceholderContent": "正在思考...", + "welcomeText": "你好!我是 AI 助手", + "dm": { "allowFrom": [] } // 私聊限制 + }, + + // Agent 模式配置(自建应用) + "agent": { + "corpId": "YOUR_CORP_ID", + "corpSecret": "YOUR_CORP_SECRET", + "agentId": 1000001, + "token": "YOUR_CALLBACK_TOKEN", // 企微后台「设置API接收」 + "encodingAESKey": "YOUR_CALLBACK_AES_KEY", + "welcomeText": "欢迎使用智能助手", + "dm": { "allowFrom": [] } + }, + + // 网络配置(可选) + "network": { + "egressProxyUrl": "http://proxy.company.local:3128" + } + } + } +} +``` + +### Webhook 路径(固定) + +| 模式 | 路径 | 说明 | +|:---|:---|:---| +| Bot | `/wecom/bot` | 智能体回调 | +| Agent | `/wecom/agent` | 自建应用回调 | + +### DM 策略 + +- **不配置 `dm.allowFrom`** → 所有人可用(默认) +- **配置 `dm.allowFrom: ["user1", "user2"]`** → 白名单模式,仅列表内用户可私聊 + +### 常用指令 + +| 指令 | 说明 | 示例 | +|:---|:---|:---| +| `/new` | 🆕 开启新会话 (重置上下文) | `/new` 或 `/new GPT-4` | +| `/reset` | 🔄 重置会话 (同 /new) | `/reset` | + +--- + +## 企业微信接入指南 + +### Bot 模式(智能机器人) + +1. 登录 [企业微信管理后台](https://work.weixin.qq.com/wework_admin/frame#/manageTools) +2. 进入「安全与管理」→「管理工具」→「智能机器人」 +3. 创建机器人,选择 **API 模式** +4. 填写回调 URL:`https://your-domain.com/wecom/bot` +5. 记录 Token 和 EncodingAESKey + +### Agent 模式(自建应用) + +1. 登录 [企业微信管理后台](https://work.weixin.qq.com/wework_admin/frame#/apps) +2. 进入「应用管理」→「自建」→ 创建应用 +3. 获取 AgentId、CorpId、Secret +4. **重要:** 进入「企业可信IP」→「配置」→ 添加你服务器的 IP 地址 + - 如果你使用内网穿透/动态 IP,建议配置 `channels.wecom.network.egressProxyUrl` 走固定出口代理,否则可能出现:`60020 not allow to access from your ip` +5. 在应用详情中设置「接收消息 - 设置API接收」 +6. 填写回调 URL:`https://your-domain.com/wecom/agent` +7. 记录回调 Token 和 EncodingAESKey + +--- + +## 高级功能 + +### A2UI 交互卡片 + +Agent 输出 `{"template_card": ...}` 时自动渲染为交互卡片: + +- ✅ 单聊场景:发送真实交互卡片 +- ✅ 按钮点击:触发 `template_card_event` 回调 +- ✅ 自动去重:基于 `msgid` 避免重复处理 +- ⚠️ 群聊降级:自动转为文本描述 + + + +### ⏰ Cronjob 企业级定时推送 + +本插件深度集成了 OpenClaw 的 Cronjob 调度能力,配合 Agent 强大的广播 API,轻松实现企业级通知服务。 + +> **核心场景**:早报推送、服务器报警、日报提醒、节日祝福。 + +#### 1. 目标配置 (Target) +无需遍历用户列表,直接利用 Agent 强大的组织架构触达能力: + +| 目标类型 | 格式示例 | 推送范围 | 典型场景 | +|:---|:---|:---|:---| +| **部门 (Party)** | `party:1` (或 `1`) | 📢 **全员广播** | 全员通知、技术部周报 | +| **标签 (Tag)** | `tag:Ops` | 🎯 **精准分组** | 运维报警、管理层汇报 | +| **外部群 (Group)** | `group:wr...` | 💬 **群聊推送** | 项目组群日报 (需由Agent建群) | +| **用户 (User)** | `user:zhangsan` | 👤 **即时私信** | 个人待办提醒 | + +#### 2. 配置示例 (`schedule.json`) + +只需在工作区根目录创建 `schedule.json` 即可生效: + +```json +{ + "tasks": [ + { + "cron": "0 9 * * 1-5", // 每周一至周五 早上9:00 + "action": "reply.send", + "params": { + "channel": "wecom", + "to": "party:1", // 一键发送给根部门所有人! + "text": "🌞 早安!请查收[今日行业简报](https://example.com/daily)。" + } + }, + { + "cron": "0 18 * * 5", + "action": "reply.send", + "params": { + "channel": "wecom", + "to": "tag:Ops", // 仅发送给运维组 + "text": "🔒 周五封网提醒:请检查服务器状态。" + } + } + ] +} +``` + + + +--- + +## 📖 详细行为说明 (Behavior Detail) + +### 1. 企业微信群聊交付规则 + +* **默认 (Bot 回复)**:群聊里 @Bot,默认由 Bot 在群内直接回复(优先文本/图片/Markdown)。 +* **例外 (文件兜底)**:如果回复内容包含**非图片文件**(如 PDF/Word/表格/压缩包等),由于企微 Bot 接口不支持,插件会自动: + 1. Bot 在群里提示:"由于格式限制,文件将通过私信发送给您"。 + 2. 无缝切换到 **自建应用 (Agent)** 通道,将文件私信发送给触发者。 +* **提示**:若未配置 Agent,Bot 会明确提示“需要管理员配置自建应用通道”。 + +### 2. 长任务可靠性保障 + +* **超时熔断**:企业微信限制 Bot 流式回复窗口约为 6 分钟。 +* **自动接力**:当对话时长接近此阈值时,Monitor 会自动截断 Bot 流,提示 "剩余内容将私信发送",并立即启动 Agent 通道私信发送完整结果。这彻底解决了长思考任务(如深度推理、代码生成)因超时导致用户收不到结果的问题。 + +### 3. 主动发送安全机制 + +* **群发保护**:Agent 主动发送接口不再尝试向普通群 `chatid` (wr/wc...) 发消息(该路径常因权限与归属产生的隐蔽错误)。 +* **引导提示**:系统会明确拦截并通过日志提示中文错误:"请使用 Bot 群内交付或改为私信目标(userid/部门/标签)",帮助管理员快速排查配置。 + +### 4. 管理员友好 + +* 所有兜底逻辑(Fallback)触发时,如果因配置缺失导致失败,Bot 都会给出清晰的**中文提示**,而不是沉默或报代码错误,极大降低了排查难度。 + +--- + +## 🙋 社区问答 (FAQ) + +针对社区反馈的高频问题,我们已在 v2.2.4 版本中全部解决: + +**Q1: 同时使用 Bot 和 Agent 会导致消息重复吗?** +> **A:** 不会。本插件采用“Bot 优先”策略。用户在哪个通道发消息,就从哪个通道回。只有在 Bot 无法处理(如发文件)时才会智能切换到 Agent 通道作为补充。 + +**Q2: 使用内网穿透时,企业微信报错 60020 (IP 不白名单) 怎么办?** +> **A:** 新增了 `config.network.egressProxyUrl` 配置。您可以配置一个拥有固定公网 IP 的代理服务器(如 Squid),让插件通过该代理与企微 API 通信,从而绕过动态 IP 限制。 + +**Q3: 原生 Bot 模式支持图片,为什么 Agent 模式不行?** +> **A:** Agent 模式之前确实存在此短板。但在 v2.2.4 中,我们完整实现了 Agent 端的 XML 媒体解析与 `media_id` 下载逻辑,现在 Agent 模式也能完美看图、听语音了。 + +**Q4: 群里 @机器人 发送文件失败?** +> **A:** 因为企业微信 Bot 接口本身不支持发送非图片文件。我们的解决方案是:自动检测到文件发送需求后,改为通过 Agent 私信该用户发送文件,并在群里给出 "文件已私信发给您" 的提示。 + +**Q5: 为什么在 Agent 模式下发送文件(如 PDF、Word)给机器人没有反应?** +> **A:** 这是由于企业微信官方接口限制。自建应用(Agent)的消息回调接口仅支持:文本、图片、语音、视频、位置和链接信息。**不支持**通用文件(File)类型的回调,因此插件无法感知您发送的文件。 + +**Q6: Cronjob 定时任务怎么发给群?** +> **A:** Cronjob 必须走 Agent 通道(Bot 无法主动发消息)。您只需在配置中指定 `to: "party:1"` (部门) 或 `to: "group:wr123..."` (外部群),即可实现定时推送到群。 + +**Q7: 为什么发视频给 Bot 没反应?** +> **A:** 官方 Bot 接口**不支持接收视频**。如果您需要处理视频内容,请配置并使用 Agent 模式(Agent 支持接收视频)。 + +--- + +--- + +## 更新日志 + +### 2026.2.5 + +- 🛠 **体验优化**:WeCom 媒体(图片/语音/视频/文件)处理的默认大小上限提升到 25MB,减少大文件因超限导致的“下载/保存失败”。 +- 📌 **可配置提示**:若仍遇到 Media exceeds ... limit,日志/回复会提示通过 channels.wecom.media.maxBytes 调整上限,并给出可直接执行的 openclaw config set 示例命令。 + +### 2026.2.4 + +- 🚀 **架构升级**:实施 "Bot 优先 + Agent 兜底" 策略,兼顾流式体验与长任务稳定性(6分钟切换)。 +- ✨ **全模态支持**:Agent 模式完整支持接收图片/语音/视频(文件仅支持发送)。 +- ✨ **Cronjob 增强**:支持向部门 (`party:ID`) 和标签 (`tag:ID`) 广播消息。 +- 🛠 **Monitor 重构**:统一的消息防抖与流状态管理,提升并发稳定性。 +- 🛠 **体验优化**:修复企微重试导致的重复回复(Bot/Agent 均做 `msgid` 去重);优化 Bot 连续多条消息的排队/合并回执,避免“重复同一答案”或“消息失败提示”。 +- 🐞 **修复**:Outbound ID 解析逻辑及 API 客户端参数缺失问题。 + +### 2026.2.3 + +- 🎉 **重大更新**:新增 Agent 模式(自建应用)支持 +- ✨ 双模式并行:Bot + Agent 可同时运行 +- ✨ **多模态支持**:Agent 模式支持图片/语音/文件/视频的接收与自动下载 +- ✨ AccessToken 自动管理:缓存 + 智能刷新 +- ✨ Agent 主动推送:脱离回调限制 +- ✨ XML 加解密:完整 Agent 回调支持 +- 📁 代码重构:模块化解耦设计 + +### 2026.1.31 + +- 文档:补充入模与测试截图说明 +- 新增文件支持 +- 新增卡片支持 + +### 2026.1.30 + +- 项目更名:Clawdbot → OpenClaw diff --git a/wecom/clawdbot.plugin.json b/wecom/clawdbot.plugin.json new file mode 100644 index 00000000..0f83de0c --- /dev/null +++ b/wecom/clawdbot.plugin.json @@ -0,0 +1,10 @@ +{ + "id": "wecom", + "channels": ["wecom"], + "configSchema": { + "type": "object", + "additionalProperties": false, + "properties": {} + } +} + diff --git a/wecom/index.ts b/wecom/index.ts new file mode 100644 index 00000000..6dab8d03 --- /dev/null +++ b/wecom/index.ts @@ -0,0 +1,28 @@ +import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; +import { emptyPluginConfigSchema } from "openclaw/plugin-sdk"; + +import { handleWecomWebhookRequest } from "./src/monitor.js"; +import { setWecomRuntime } from "./src/runtime.js"; +import { wecomPlugin } from "./src/channel.js"; + +const plugin = { + id: "wecom", + name: "WeCom", + description: "OpenClaw WeCom (WeChat Work) intelligent bot channel plugin", + configSchema: emptyPluginConfigSchema(), + /** + * **register (注册插件)** + * + * OpenClaw 插件入口点。 + * 1. 注入 Runtime 环境 (api.runtime)。 + * 2. 注册 WeCom 渠道插件 (ChannelPlugin)。 + * 3. 注册 Webhook HTTP 处理器 (handleWecomWebhookRequest)。 + */ + register(api: OpenClawPluginApi) { + setWecomRuntime(api.runtime); + api.registerChannel({ plugin: wecomPlugin }); + api.registerHttpHandler(handleWecomWebhookRequest); + }, +}; + +export default plugin; diff --git a/wecom/openclaw.plugin.json b/wecom/openclaw.plugin.json new file mode 100644 index 00000000..0f83de0c --- /dev/null +++ b/wecom/openclaw.plugin.json @@ -0,0 +1,10 @@ +{ + "id": "wecom", + "channels": ["wecom"], + "configSchema": { + "type": "object", + "additionalProperties": false, + "properties": {} + } +} + diff --git a/wecom/package-lock.json b/wecom/package-lock.json new file mode 100644 index 00000000..eeabfab8 --- /dev/null +++ b/wecom/package-lock.json @@ -0,0 +1,10469 @@ +{ + "name": "wecom", + "version": "2026.2.5", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "wecom", + "version": "2026.2.5", + "dependencies": { + "fast-xml-parser": "5.3.4", + "undici": "^7.20.0", + "zod": "^4.3.6" + }, + "devDependencies": { + "@types/node": "^25.2.0", + "typescript": "^5.9.3" + }, + "peerDependencies": { + "openclaw": ">=2026.1.26" + } + }, + "node_modules/@agentclientprotocol/sdk": { + "version": "0.13.1", + "resolved": "http://mirrors.tencentyun.com/npm/@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": "http://mirrors.tencentyun.com/npm/@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" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock": { + "version": "3.984.0", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/client-bedrock/-/client-bedrock-3.984.0.tgz", + "integrity": "sha512-thcdcQhHWEtDAePgN9snjCwInNvaDGMF4H9YoCfM/wxG8G9XHunaWuWj/n48XO+5tOh936IPgN4GujovTx5myg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-node": "^3.972.5", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/token-providers": "3.984.0", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.984.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.984.0", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.984.0.tgz", + "integrity": "sha512-iFrdkDXdo+ELZ5qD8ZYw9MHoOhcXyVutO8z7csnYpJO0rbET/X6B8cQlOCMsqJHxkyMwW21J4vt9S5k2/FgPCg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-node": "^3.972.5", + "@aws-sdk/eventstream-handler-node": "^3.972.5", + "@aws-sdk/middleware-eventstream": "^3.972.3", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/middleware-websocket": "^3.972.5", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/token-providers": "3.984.0", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.984.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/eventstream-serde-browser": "^4.2.8", + "@smithy/eventstream-serde-config-resolver": "^4.3.8", + "@smithy/eventstream-serde-node": "^4.2.8", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-stream": "^4.5.10", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.982.0", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/client-sso/-/client-sso-3.982.0.tgz", + "integrity": "sha512-qJrIiivmvujdGqJ0ldSUvhN3k3N7GtPesoOI1BSt0fNXovVnMz4C/JmnkhZihU7hJhDvxJaBROLYTU+lpild4w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.982.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-endpoints": { + "version": "3.982.0", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/util-endpoints/-/util-endpoints-3.982.0.tgz", + "integrity": "sha512-M27u8FJP7O0Of9hMWX5dipp//8iglmV9jr7R8SR8RveU+Z50/8TqH68Tu6wUWBGMfXjzbVwn1INIAO5lZrlxXQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.973.6", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/core/-/core-3.973.6.tgz", + "integrity": "sha512-pz4ZOw3BLG0NdF25HoB9ymSYyPbMiIjwQJ2aROXRhAzt+b+EOxStfFv8s5iZyP6Kiw7aYhyWxj5G3NhmkoOTKw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/xml-builder": "^3.972.4", + "@smithy/core": "^3.22.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.4", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.4.tgz", + "integrity": "sha512-/8dnc7+XNMmViEom2xsNdArQxQPSgy4Z/lm6qaFPTrMFesT1bV3PsBhb19n09nmxHdrtQskYmViddUIjUQElXg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.6", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.6.tgz", + "integrity": "sha512-5ERWqRljiZv44AIdvIRQ3k+EAV0Sq2WeJHvXuK7gL7bovSxOf8Al7MLH7Eh3rdovH4KHFnlIty7J71mzvQBl5Q==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/types": "^3.973.1", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.10", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.4", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.4.tgz", + "integrity": "sha512-eRUg+3HaUKuXWn/lEMirdiA5HOKmEl8hEHVuszIDt2MMBUKgVX5XNGmb3XmbgU17h6DZ+RtjbxQpjhz3SbTjZg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-env": "^3.972.4", + "@aws-sdk/credential-provider-http": "^3.972.6", + "@aws-sdk/credential-provider-login": "^3.972.4", + "@aws-sdk/credential-provider-process": "^3.972.4", + "@aws-sdk/credential-provider-sso": "^3.972.4", + "@aws-sdk/credential-provider-web-identity": "^3.972.4", + "@aws-sdk/nested-clients": "3.982.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.4", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.4.tgz", + "integrity": "sha512-nLGjXuvWWDlQAp505xIONI7Gam0vw2p7Qu3P6on/W2q7rjJXtYjtpHbcsaOjJ/pAju3eTvEQuSuRedcRHVQIAQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/nested-clients": "3.982.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.5", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.5.tgz", + "integrity": "sha512-VWXKgSISQCI2GKN3zakTNHSiZ0+mux7v6YHmmbLQp/o3fvYUQJmKGcLZZzg2GFA+tGGBStplra9VFNf/WwxpYg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.4", + "@aws-sdk/credential-provider-http": "^3.972.6", + "@aws-sdk/credential-provider-ini": "^3.972.4", + "@aws-sdk/credential-provider-process": "^3.972.4", + "@aws-sdk/credential-provider-sso": "^3.972.4", + "@aws-sdk/credential-provider-web-identity": "^3.972.4", + "@aws-sdk/types": "^3.973.1", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.4", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.4.tgz", + "integrity": "sha512-TCZpWUnBQN1YPk6grvd5x419OfXjHvhj5Oj44GYb84dOVChpg/+2VoEj+YVA4F4E/6huQPNnX7UYbTtxJqgihw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.4", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.4.tgz", + "integrity": "sha512-wzsGwv9mKlwJ3vHLyembBvGE/5nPUIwRR2I51B1cBV4Cb4ql9nIIfpmHzm050XYTY5fqTOKJQnhLj7zj89VG8g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/client-sso": "3.982.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/token-providers": "3.982.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { + "version": "3.982.0", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/token-providers/-/token-providers-3.982.0.tgz", + "integrity": "sha512-v3M0KYp2TVHYHNBT7jHD9lLTWAdS9CaWJ2jboRKt0WAB65bA7iUEpR+k4VqKYtpQN4+8kKSc4w+K6kUNZkHKQw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/nested-clients": "3.982.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.4", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.4.tgz", + "integrity": "sha512-hIzw2XzrG8jzsUSEatehmpkd5rWzASg5IHUfA+m01k/RtvfAML7ZJVVohuKdhAYx+wV2AThLiQJVzqn7F0khrw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/nested-clients": "3.982.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-handler-node": { + "version": "3.972.5", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.5.tgz", + "integrity": "sha512-xEmd3dnyn83K6t4AJxBJA63wpEoCD45ERFG0XMTViD2E/Ohls9TLxjOWPb1PAxR9/46cKy/TImez1GoqP6xVNQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/eventstream-codec": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-eventstream": { + "version": "3.972.3", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.3.tgz", + "integrity": "sha512-pbvZ6Ye/Ks6BAZPa3RhsNjHrvxU9li25PMhSdDpbX0jzdpKpAkIR65gXSNKmA/REnSdEMWSD4vKUW+5eMFzB6w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.3", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.3.tgz", + "integrity": "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.3", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/middleware-logger/-/middleware-logger-3.972.3.tgz", + "integrity": "sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.3", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.3.tgz", + "integrity": "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.972.6", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.6.tgz", + "integrity": "sha512-TehLN8W/kivl0U9HcS+keryElEWORROpghDXZBLfnb40DXM7hx/i+7OOjkogXQOF3QtUraJVRkHQ07bPhrWKlw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.982.0", + "@smithy/core": "^3.22.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent/node_modules/@aws-sdk/util-endpoints": { + "version": "3.982.0", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/util-endpoints/-/util-endpoints-3.982.0.tgz", + "integrity": "sha512-M27u8FJP7O0Of9hMWX5dipp//8iglmV9jr7R8SR8RveU+Z50/8TqH68Tu6wUWBGMfXjzbVwn1INIAO5lZrlxXQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-websocket": { + "version": "3.972.5", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.5.tgz", + "integrity": "sha512-BN4A9K71WRIlpQ3+IYGdBC2wVyobZ95g6ZomodmJ8Te772GWo0iDk2Mv6JIHdr842tOTgi1b3npLIFDUS4hl4g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-format-url": "^3.972.3", + "@smithy/eventstream-codec": "^4.2.8", + "@smithy/eventstream-serde-browser": "^4.2.8", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.982.0", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/nested-clients/-/nested-clients-3.982.0.tgz", + "integrity": "sha512-VVkaH27digrJfdVrT64rjkllvOp4oRiZuuJvrylLXAKl18ujToJR7AqpDldL/LS63RVne3QWIpkygIymxFtliQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.982.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-endpoints": { + "version": "3.982.0", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/util-endpoints/-/util-endpoints-3.982.0.tgz", + "integrity": "sha512-M27u8FJP7O0Of9hMWX5dipp//8iglmV9jr7R8SR8RveU+Z50/8TqH68Tu6wUWBGMfXjzbVwn1INIAO5lZrlxXQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.972.3", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.3.tgz", + "integrity": "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/config-resolver": "^4.4.6", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.984.0", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/token-providers/-/token-providers-3.984.0.tgz", + "integrity": "sha512-UJ/+OzZv+4nAQ1bSspCSb4JlYbMB2Adn8CK7hySpKX5sjhRu1bm6w1PqQq59U67LZEKsPdhl1rzcZ7ybK8YQxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/nested-clients": "3.984.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers/node_modules/@aws-sdk/nested-clients": { + "version": "3.984.0", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/nested-clients/-/nested-clients-3.984.0.tgz", + "integrity": "sha512-E9Os+U9NWFoEJXbTVT8sCi+HMnzmsMA8cuCkvlUUfin/oWewUTnCkB/OwFwiUQ2N7v1oBk+i4ZSsI1PiuOy8/w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.984.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.973.1", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.984.0", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/util-endpoints/-/util-endpoints-3.984.0.tgz", + "integrity": "sha512-9ebjLA0hMKHeVvXEtTDCCOBtwjb0bOXiuUV06HNeVdgAjH6gj4x4Zwt4IBti83TiyTGOCl5YfZqGx4ehVsasbQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.972.3", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/util-format-url/-/util-format-url-3.972.3.tgz", + "integrity": "sha512-n7F2ycckcKFXa01vAsT/SJdjFHfKH9s96QHcs5gn8AaaigASICeME8WdUL9uBp8XV/OVwEt8+6gzn6KFUgQa8g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.4", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.972.3", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.3.tgz", + "integrity": "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.972.4", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.4.tgz", + "integrity": "sha512-3WFCBLiM8QiHDfosQq3Py+lIMgWlFWwFQliUHUqwEiRqLnKyhgbU3AKa7AWJF7lW2Oc/2kFNY4MlAYVnVc0i8A==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/types": "^3.973.1", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.4", + "resolved": "http://mirrors.tencentyun.com/npm/@aws-sdk/xml-builder/-/xml-builder-3.972.4.tgz", + "integrity": "sha512-0zJ05ANfYqI6+rGqj8samZBFod0dPPousBjLEqg8WdxSgbMAkRgLyn81lP215Do0rFJ/17LIXwr7q0yK24mP6Q==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.12.0", + "fast-xml-parser": "5.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.3", + "resolved": "http://mirrors.tencentyun.com/npm/@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": "http://mirrors.tencentyun.com/npm/@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": "http://mirrors.tencentyun.com/npm/@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" + } + }, + "node_modules/@buape/carbon": { + "version": "0.14.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "optionalDependencies": { + "@cloudflare/workers-types": "4.20260120.0", + "@discordjs/voice": "0.19.0", + "@hono/node-server": "1.19.9", + "@types/bun": "1.3.6", + "@types/ws": "8.18.1", + "ws": "8.19.0" + } + }, + "node_modules/@buape/carbon/node_modules/discord-api-types": { + "version": "0.38.37", + "resolved": "http://mirrors.tencentyun.com/npm/discord-api-types/-/discord-api-types-0.38.37.tgz", + "integrity": "sha512-Cv47jzY1jkGkh5sv0bfHYqGgKOWO1peOrGMkDFM4UmaGMOTgOW8QSexhvixa9sVOiz8MnVOBryWYyw/CEVhj7w==", + "license": "MIT", + "peer": true, + "workspaces": [ + "scripts/actions/documentation" + ] + }, + "node_modules/@cacheable/memory": { + "version": "2.0.7", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "hookified": "^1.14.0", + "keyv": "^5.5.5" + } + }, + "node_modules/@cacheable/node-cache": { + "version": "1.7.6", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "keyv": "^5.5.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.3.3", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + } + }, + "node_modules/@clack/core": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@clack/core/-/core-1.0.0.tgz", + "integrity": "sha512-Orf9Ltr5NeiEuVJS8Rk2XTw3IxNC2Bic3ash7GgYeA8LJ/zmSNpSQ/m5UAhe03lA6KFgklzZ5KTHs4OAMA/SAQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "picocolors": "^1.0.0", + "sisteransi": "^1.0.5" + } + }, + "node_modules/@clack/prompts": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@clack/prompts/-/prompts-1.0.0.tgz", + "integrity": "sha512-rWPXg9UaCFqErJVQ+MecOaWsozjaxol4yjnmYcGNipAWzdaWa2x+VJmKfGq7L0APwBohQOYdHC+9RO4qRXej+A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@clack/core": "1.0.0", + "picocolors": "^1.0.0", + "sisteransi": "^1.0.5" + } + }, + "node_modules/@cloudflare/workers-types": { + "version": "4.20260120.0", + "resolved": "http://mirrors.tencentyun.com/npm/@cloudflare/workers-types/-/workers-types-4.20260120.0.tgz", + "integrity": "sha512-B8pueG+a5S+mdK3z8oKu1ShcxloZ7qWb68IEyLLaepvdryIbNC7JVPcY0bWsjS56UQVKc5fnyRge3yZIwc9bxw==", + "license": "MIT OR Apache-2.0", + "optional": true, + "peer": true + }, + "node_modules/@discordjs/voice": { + "version": "0.19.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "prism-media": "^1.3.5", + "tslib": "^2.8.1", + "ws": "^8.18.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "http://mirrors.tencentyun.com/npm/@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": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.24.0" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@grammyjs/runner": { + "version": "2.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/@grammyjs/runner/-/runner-2.0.3.tgz", + "integrity": "sha512-nckmTs1dPWfVQteK9cxqxzE+0m1VRvluLWB8UgFzsjg62w3qthPJt0TYtJBEdG7OedvfQq4vnFAyE6iaMkR42A==", + "license": "MIT", + "peer": true, + "dependencies": { + "abort-controller": "^3.0.0" + }, + "engines": { + "node": ">=12.20.0 || >=14.13.1" + }, + "peerDependencies": { + "grammy": "^1.13.1" + } + }, + "node_modules/@grammyjs/transformer-throttler": { + "version": "1.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + }, + "peerDependencies": { + "grammy": "^1.0.0" + } + }, + "node_modules/@grammyjs/types": { + "version": "3.23.0", + "resolved": "http://mirrors.tencentyun.com/npm/@grammyjs/types/-/types-3.23.0.tgz", + "integrity": "sha512-D3jQ4UWERPsyR3op/YFudMMIPNTU47vy7L51uO9/73tMELmjO/+LX5N36/Y0CG5IQfIsz43MxiHI5rgsK0/k+g==", + "license": "MIT", + "peer": true + }, + "node_modules/@hapi/boom": { + "version": "9.1.4", + "resolved": "http://mirrors.tencentyun.com/npm/@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": "http://mirrors.tencentyun.com/npm/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@homebridge/ciao": { + "version": "1.3.4", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "source-map-support": "^0.5.21", + "tslib": "^2.8.1" + }, + "bin": { + "ciao-bcs": "lib/bonjour-conformance-testing.js" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@huggingface/jinja": { + "version": "0.5.5", + "resolved": "http://mirrors.tencentyun.com/npm/@huggingface/jinja/-/jinja-0.5.5.tgz", + "integrity": "sha512-xRlzazC+QZwr6z4ixEqYHo9fgwhTZ3xNSdljlKfUFGZSdlvt166DljRELFUfFytlYOYvo3vTisA/AFOuOAzFQQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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": "http://mirrors.tencentyun.com/npm/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/@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.1", + "resolved": "http://mirrors.tencentyun.com/npm/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", + "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "license": "BlueOak-1.0.0", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "peer": true, + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "http://mirrors.tencentyun.com/npm/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "license": "MIT", + "peer": true + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "license": "MIT", + "peer": true + }, + "node_modules/@larksuiteoapi/node-sdk": { + "version": "1.58.0", + "resolved": "http://mirrors.tencentyun.com/npm/@larksuiteoapi/node-sdk/-/node-sdk-1.58.0.tgz", + "integrity": "sha512-NcQNHdGuHOxOWY3bRGS9WldwpbR6+k7Fi0H1IJXDNNmbSrEB/8rLwqHRC8tAbbj/Mp8TWH/v1O+p487m6xskxw==", + "license": "MIT", + "peer": true, + "dependencies": { + "axios": "~1.13.3", + "lodash.identity": "^3.0.0", + "lodash.merge": "^4.6.2", + "lodash.pickby": "^4.6.0", + "protobufjs": "^7.2.6", + "qs": "^6.13.0", + "ws": "^8.16.0" + } + }, + "node_modules/@line/bot-sdk": { + "version": "10.6.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "axios": "^1.7.4" + } + }, + "node_modules/@line/bot-sdk/node_modules/@types/node": { + "version": "24.10.11", + "resolved": "http://mirrors.tencentyun.com/npm/@types/node/-/node-24.10.11.tgz", + "integrity": "sha512-/Af7O8r1frCVgOz0I62jWUtMohJ0/ZQU/ZoketltOJPZpnb17yoNc9BSoVuV9qlaIXJiPNOpsfq4ByFajSArNQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@lydell/node-pty": { + "version": "1.2.0-beta.3", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@lydell/node-pty-linux-arm64": "1.2.0-beta.3", + "@lydell/node-pty-linux-x64": "1.2.0-beta.3", + "@lydell/node-pty-win32-arm64": "1.2.0-beta.3", + "@lydell/node-pty-win32-x64": "1.2.0-beta.3" + } + }, + "node_modules/@lydell/node-pty-darwin-arm64": { + "version": "1.2.0-beta.3", + "resolved": "http://mirrors.tencentyun.com/npm/@lydell/node-pty-darwin-arm64/-/node-pty-darwin-arm64-1.2.0-beta.3.tgz", + "integrity": "sha512-owcv+e1/OSu3bf9ZBdUQqJsQF888KyuSIiPYFNn0fLhgkhm9F3Pvha76Kj5mCPnodf7hh3suDe7upw7GPRXftQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@lydell/node-pty-darwin-x64": { + "version": "1.2.0-beta.3", + "resolved": "http://mirrors.tencentyun.com/npm/@lydell/node-pty-darwin-x64/-/node-pty-darwin-x64-1.2.0-beta.3.tgz", + "integrity": "sha512-k38O+UviWrWdxtqZBBc/D8NJU11Rey8Y2YMwSWNxLv3eXZZdF5IVpbBkI/2RmLsV5nCcciqLPbukxeZnEfPlwA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@lydell/node-pty-linux-arm64": { + "version": "1.2.0-beta.3", + "resolved": "http://mirrors.tencentyun.com/npm/@lydell/node-pty-linux-arm64/-/node-pty-linux-arm64-1.2.0-beta.3.tgz", + "integrity": "sha512-HUwRpGu3O+4sv9DAQFKnyW5LYhyYu2SDUa/bdFO/t4dIFCM4uDJEq47wfRM7+aYtJTi1b3lakN8SlWeuFQqJQQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@lydell/node-pty-linux-x64": { + "version": "1.2.0-beta.3", + "resolved": "http://mirrors.tencentyun.com/npm/@lydell/node-pty-linux-x64/-/node-pty-linux-x64-1.2.0-beta.3.tgz", + "integrity": "sha512-+RRY0PoCUeQaCvPR7/UnkGbxulwbFtoTWJfe+o4T1RcNtngrgaI55I9nl8CD8uqhGrB3smKuyvPM5UtwGhASUw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@lydell/node-pty-win32-arm64": { + "version": "1.2.0-beta.3", + "resolved": "http://mirrors.tencentyun.com/npm/@lydell/node-pty-win32-arm64/-/node-pty-win32-arm64-1.2.0-beta.3.tgz", + "integrity": "sha512-UEDd9ASp2M3iIYpIzfmfBlpyn4+K1G4CAjYcHWStptCkefoSVXWTiUBIa1KjBjZi3/xmsHIDpBEYTkGWuvLt2Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@lydell/node-pty-win32-x64": { + "version": "1.2.0-beta.3", + "resolved": "http://mirrors.tencentyun.com/npm/@lydell/node-pty-win32-x64/-/node-pty-win32-x64-1.2.0-beta.3.tgz", + "integrity": "sha512-TpdqSFYx7/Rj+68tuP6F/lkRYrHCYAIJgaS1bx3SctTkb5QAQCFwOKHd4xlsivmEOMT2LdhkJggPxwX9PAO5pQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@mariozechner/clipboard": { + "version": "0.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/clipboard/-/clipboard-0.3.2.tgz", + "integrity": "sha512-IHQpksNjo7EAtGuHFU+tbWDp5LarH3HU/8WiB9O70ZEoBPHOg0/6afwSLK0QyNMMmx4Bpi/zl6+DcBXe95nWYA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@mariozechner/clipboard-darwin-arm64": "0.3.2", + "@mariozechner/clipboard-darwin-universal": "0.3.2", + "@mariozechner/clipboard-darwin-x64": "0.3.2", + "@mariozechner/clipboard-linux-arm64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-arm64-musl": "0.3.2", + "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-x64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-x64-musl": "0.3.2", + "@mariozechner/clipboard-win32-arm64-msvc": "0.3.2", + "@mariozechner/clipboard-win32-x64-msvc": "0.3.2" + } + }, + "node_modules/@mariozechner/clipboard-darwin-arm64": { + "version": "0.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/clipboard-darwin-arm64/-/clipboard-darwin-arm64-0.3.2.tgz", + "integrity": "sha512-uBf6K7Je1ihsgvmWxA8UCGCeI+nbRVRXoarZdLjl6slz94Zs1tNKFZqx7aCI5O1i3e0B6ja82zZ06BWrl0MCVw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-darwin-universal": { + "version": "0.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.2.tgz", + "integrity": "sha512-mxSheKTW2U9LsBdXy0SdmdCAE5HqNS9QUmpNHLnfJ+SsbFKALjEZc5oRrVMXxGQSirDvYf5bjmRyT0QYYonnlg==", + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-darwin-x64": { + "version": "0.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/clipboard-darwin-x64/-/clipboard-darwin-x64-0.3.2.tgz", + "integrity": "sha512-U1BcVEoidvwIp95+HJswSW+xr28EQiHR7rZjH6pn8Sja5yO4Yoe3yCN0Zm8Lo72BbSOK/fTSq0je7CJpaPCspg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-arm64-gnu": { + "version": "0.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/clipboard-linux-arm64-gnu/-/clipboard-linux-arm64-gnu-0.3.2.tgz", + "integrity": "sha512-BsinwG3yWTIjdgNCxsFlip7LkfwPk+ruw/aFCXHUg/fb5XC/Ksp+YMQ7u0LUtiKzIv/7LMXgZInJQH6gxbAaqQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-arm64-musl": { + "version": "0.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/clipboard-linux-arm64-musl/-/clipboard-linux-arm64-musl-0.3.2.tgz", + "integrity": "sha512-0/Gi5Xq2V6goXBop19ePoHvXsmJD9SzFlO3S+d6+T2b+BlPcpOu3Oa0wTjl+cZrLAAEzA86aPNBI+VVAFDFPKw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-riscv64-gnu": { + "version": "0.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/clipboard-linux-riscv64-gnu/-/clipboard-linux-riscv64-gnu-0.3.2.tgz", + "integrity": "sha512-2AFFiXB24qf0zOZsxI1GJGb9wQGlOJyN6UwoXqmKS3dpQi/l6ix30IzDDA4c4ZcCcx4D+9HLYXhC1w7Sov8pXA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-x64-gnu": { + "version": "0.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.2.tgz", + "integrity": "sha512-v6fVnsn7WMGg73Dab8QMwyFce7tzGfgEixKgzLP8f1GJqkJZi5zO4k4FOHzSgUufgLil63gnxvMpjWkgfeQN7A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-x64-musl": { + "version": "0.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.2.tgz", + "integrity": "sha512-xVUtnoMQ8v2JVyfJLKKXACA6avdnchdbBkTsZs8BgJQo29qwCp5NIHAUO8gbJ40iaEGToW5RlmVk2M9V0HsHEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-win32-arm64-msvc": { + "version": "0.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/clipboard-win32-arm64-msvc/-/clipboard-win32-arm64-msvc-0.3.2.tgz", + "integrity": "sha512-AEgg95TNi8TGgak2wSXZkXKCvAUTjWoU1Pqb0ON7JHrX78p616XUFNTJohtIon3e0w6k0pYPZeCuqRCza/Tqeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-win32-x64-msvc": { + "version": "0.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/clipboard-win32-x64-msvc/-/clipboard-win32-x64-msvc-0.3.2.tgz", + "integrity": "sha512-tGRuYpZwDOD7HBrCpyRuhGnHHSCknELvqwKKUG4JSfSB7JIU7LKRh6zx6fMUOQd8uISK35TjFg5UcNih+vJhFA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/jiti": { + "version": "2.6.5", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/@mariozechner/pi-agent-core": { + "version": "0.51.3", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/pi-agent-core/-/pi-agent-core-0.51.3.tgz", + "integrity": "sha512-pO5ScRuf7F5GCqS02vuB3gIV/MHR2cskEEUnbVbkSf0RHJb3vTICy/ACQyeI+UYk7yjFmdvQgbSUtVrYJ3q8Ag==", + "license": "MIT", + "peer": true, + "dependencies": { + "@mariozechner/pi-ai": "^0.51.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-ai": { + "version": "0.51.3", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/pi-ai/-/pi-ai-0.51.3.tgz", + "integrity": "sha512-NocfuwUPCGeNhWyfzSGKbsTqUvFmP+VihU8+xtzX9FoHvQQVJHQ49Sz8sfLK04BbEWYI9s/gZ7a9xnJ0O4cz8g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@anthropic-ai/sdk": "0.71.2", + "@aws-sdk/client-bedrock-runtime": "^3.966.0", + "@google/genai": "1.34.0", + "@mistralai/mistralai": "1.10.0", + "@sinclair/typebox": "^0.34.41", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "chalk": "^5.6.2", + "openai": "6.10.0", + "partial-json": "^0.1.7", + "proxy-agent": "^6.5.0", + "undici": "^7.19.1", + "zod-to-json-schema": "^3.24.6" + }, + "bin": { + "pi-ai": "dist/cli.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-coding-agent": { + "version": "0.51.3", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/pi-coding-agent/-/pi-coding-agent-0.51.3.tgz", + "integrity": "sha512-pu/4IxeMZMapYiSO3LWvNRztOXXKLlLNL+drjMvtgWbp9MJ8azP+5Zwsp3/vzrPvM54wCkaSa0voUEThm4Ba/Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@mariozechner/jiti": "^2.6.2", + "@mariozechner/pi-agent-core": "^0.51.3", + "@mariozechner/pi-ai": "^0.51.3", + "@mariozechner/pi-tui": "^0.51.3", + "@silvia-odwyer/photon-node": "^0.3.4", + "chalk": "^5.5.0", + "cli-highlight": "^2.1.11", + "diff": "^8.0.2", + "file-type": "^21.1.1", + "glob": "^11.0.3", + "ignore": "^7.0.5", + "marked": "^15.0.12", + "minimatch": "^10.1.1", + "proper-lockfile": "^4.1.2", + "yaml": "^2.8.2" + }, + "bin": { + "pi": "dist/cli.js" + }, + "engines": { + "node": ">=20.0.0" + }, + "optionalDependencies": { + "@mariozechner/clipboard": "^0.3.2" + } + }, + "node_modules/@mariozechner/pi-tui": { + "version": "0.51.3", + "resolved": "http://mirrors.tencentyun.com/npm/@mariozechner/pi-tui/-/pi-tui-0.51.3.tgz", + "integrity": "sha512-1B9C3oVsAcBSO0rvk4qC3Iq655LveLQDSnlseypCo/KiR5eY39Hw1XRtvq5N05mtxNuo3mRw8FMcYCwIl1BbDg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/mime-types": "^2.1.4", + "chalk": "^5.5.0", + "get-east-asian-width": "^1.3.0", + "marked": "^15.0.12", + "mime-types": "^3.0.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-tui/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "http://mirrors.tencentyun.com/npm/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mariozechner/pi-tui/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@mistralai/mistralai": { + "version": "1.10.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + } + }, + "node_modules/@mistralai/mistralai/node_modules/zod": { + "version": "3.25.76", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/@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.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas/-/canvas-0.1.90.tgz", + "integrity": "sha512-vO9j7TfwF9qYCoTOPO39yPLreTRslBVOaeIwhDZkizDvBb0MounnTl0yeWUMBxP4Pnkg9Sv+3eQwpxNUmTwt0w==", + "license": "MIT", + "peer": true, + "workspaces": [ + "e2e/*" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.90", + "@napi-rs/canvas-darwin-arm64": "0.1.90", + "@napi-rs/canvas-darwin-x64": "0.1.90", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.90", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.90", + "@napi-rs/canvas-linux-arm64-musl": "0.1.90", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.90", + "@napi-rs/canvas-linux-x64-gnu": "0.1.90", + "@napi-rs/canvas-linux-x64-musl": "0.1.90", + "@napi-rs/canvas-win32-arm64-msvc": "0.1.90", + "@napi-rs/canvas-win32-x64-msvc": "0.1.90" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.90.tgz", + "integrity": "sha512-3JBULVF+BIgr7yy7Rf8UjfbkfFx4CtXrkJFD1MDgKJ83b56o0U9ciT8ZGTCNmwWkzu8RbNKlyqPP3KYRG88y7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.90.tgz", + "integrity": "sha512-L8XVTXl+8vd8u7nPqcX77NyG5RuFdVsJapQrKV9WE3jBayq1aSMht/IH7Dwiz/RNJ86E5ZSg9pyUPFIlx52PZA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.90.tgz", + "integrity": "sha512-h0ukhlnGhacbn798VWYTQZpf6JPDzQYaow+vtQ2Fat7j7ImDdpg6tfeqvOTO1r8wS+s+VhBIFITC7aA1Aik0ZQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.90.tgz", + "integrity": "sha512-JCvTl99b/RfdBtgftqrf+5UNF7GIbp7c5YBFZ+Bd6++4Y3phaXG/4vD9ZcF1bw1P4VpALagHmxvodHuQ9/TfTg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.90.tgz", + "integrity": "sha512-vbWFp8lrP8NIM5L4zNOwnsqKIkJo0+GIRUDcLFV9XEJCptCc1FY6/tM02PT7GN4PBgochUPB1nBHdji6q3ieyQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.90.tgz", + "integrity": "sha512-8Bc0BgGEeOaux4EfIfNzcRRw0JE+lO9v6RWQFCJNM9dJFE4QJffTf88hnmbOaI6TEMpgWOKipbha3dpIdUqb/g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.90.tgz", + "integrity": "sha512-0iiVDG5IH+gJb/YUrY/pRdbsjcgvwUmeckL/0gShWAA7004ygX2ST69M1wcfyxXrzFYjdF8S/Sn6aCAeBi89XQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.90.tgz", + "integrity": "sha512-SkKmlHMvA5spXuKfh7p6TsScDf7lp5XlMbiUhjdCtWdOS6Qke/A4qGVOciy6piIUCJibL+YX+IgdGqzm2Mpx/w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.90.tgz", + "integrity": "sha512-o6QgS10gAS4vvELGDOOWYfmERXtkVRYFWBCjomILWfMgCvBVutn8M97fsMW5CrEuJI8YuxuJ7U+/DQ9oG93vDA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-arm64-msvc": { + "version": "0.1.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.90.tgz", + "integrity": "sha512-2UHO/DC1oyuSjeCAhHA0bTD9qsg58kknRqjJqRfvIEFtdqdtNTcWXMCT9rQCuJ8Yx5ldhyh2SSp7+UDqD2tXZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.90", + "resolved": "http://mirrors.tencentyun.com/npm/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.90.tgz", + "integrity": "sha512-48CxEbzua5BP4+OumSZdi3+9fNiRO8cGNBlO2bKwx1PoyD1R2AXzPtqd/no1f1uSl0W2+ihOO1v3pqT3USbmgQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@node-llama-cpp/linux-arm64": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/linux-arm64/-/linux-arm64-3.15.1.tgz", + "integrity": "sha512-g7JC/WwDyyBSmkIjSvRF2XLW+YA0z2ZVBSAKSv106mIPO4CzC078woTuTaPsykWgIaKcQRyXuW5v5XQMcT1OOA==", + "cpu": [ + "arm64", + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/linux-armv7l": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/linux-armv7l/-/linux-armv7l-3.15.1.tgz", + "integrity": "sha512-MSxR3A0vFSVWbmVSkNqNXQnI45L2Vg7/PRgJukcjChk7YzRxs9L+oQMeycVW3BsQ03mIZ0iORsZ9MNIBEbdS3g==", + "cpu": [ + "arm", + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/linux-x64": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/linux-x64/-/linux-x64-3.15.1.tgz", + "integrity": "sha512-w4SdxJaA9eJLVYWX+Jv48hTP4oO79BJQIFURMi7hXIFXbxyyOov/r6sVaQ1WiL83nVza37U5Qg4L9Gb/KRdNWQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/linux-x64-cuda": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/linux-x64-cuda/-/linux-x64-cuda-3.15.1.tgz", + "integrity": "sha512-kngwoq1KdrqSr/b6+tn5jbtGHI0tZnW5wofKssZy+Il2ge3eN9FowKbXG4FH452g6qSSVoDccAoTvYOxyLyX+w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/linux-x64-cuda-ext": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/linux-x64-cuda-ext/-/linux-x64-cuda-ext-3.15.1.tgz", + "integrity": "sha512-toepvLcXjgaQE/QGIThHBD58jbHGBWT1jhblJkCjYBRHfVOO+6n/PmVsJt+yMfu5Z93A2gF8YOvVyZXNXmGo5g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/linux-x64-vulkan": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/linux-x64-vulkan/-/linux-x64-vulkan-3.15.1.tgz", + "integrity": "sha512-CMsyQkGKpHKeOH9+ZPxo0hO0usg8jabq5/aM3JwdX9CiuXhXUa3nu3NH4RObiNi596Zwn/zWzlps0HRwcpL8rw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/mac-arm64-metal": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/mac-arm64-metal/-/mac-arm64-metal-3.15.1.tgz", + "integrity": "sha512-ePTweqohcy6Gjs1agXWy4FxAw5W4Avr7NeqqiFWJ5ngZ1U3ZXdruUHB8L/vDxyn3FzKvstrFyN7UScbi0pzXrA==", + "cpu": [ + "arm64", + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/mac-x64": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/mac-x64/-/mac-x64-3.15.1.tgz", + "integrity": "sha512-NAetSQONxpNXTBnEo7oOkKZ84wO2avBy6V9vV9ntjJLb/07g7Rar8s/jVaicc/rVl6C+8ljZNwqJeynirgAC5w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/win-arm64": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/win-arm64/-/win-arm64-3.15.1.tgz", + "integrity": "sha512-1O9tNSUgvgLL5hqgEuYiz7jRdA3+9yqzNJyPW1jExlQo442OA0eIpHBmeOtvXLwMkY7qv7wE75FdOPR7NVEnvg==", + "cpu": [ + "arm64", + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/win-x64": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/win-x64/-/win-x64-3.15.1.tgz", + "integrity": "sha512-jtoXBa6h+VPsQgefrO7HDjYv4WvxfHtUO30ABwCUDuEgM0e05YYhxMZj1z2Ns47UrquNvd/LUPCyjHKqHUN+5Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/win-x64-cuda": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/win-x64-cuda/-/win-x64-cuda-3.15.1.tgz", + "integrity": "sha512-swoyx0/dY4ixiu3mEWrIAinx0ffHn9IncELDNREKG+iIXfx6w0OujOMQ6+X+lGj+sjE01yMUP/9fv6GEp2pmBw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/win-x64-cuda-ext": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/win-x64-cuda-ext/-/win-x64-cuda-ext-3.15.1.tgz", + "integrity": "sha512-mO3Tf6D3UlFkjQF5J96ynTkjdF7dac/f5f61cEh6oU4D3hdx+cwnmBWT1gVhDSLboJYzCHtx7U2EKPP6n8HoWA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@node-llama-cpp/win-x64-vulkan": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/@node-llama-cpp/win-x64-vulkan/-/win-x64-vulkan-3.15.1.tgz", + "integrity": "sha512-BPBjUEIkFTdcHSsQyblP0v/aPPypi6uqQIq27mo4A49CYjX22JDmk4ncdBLk6cru+UkvwEEe+F2RomjoMt32aQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@octokit/app": { + "version": "16.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/app/-/app-16.1.2.tgz", + "integrity": "sha512-8j7sEpUYVj18dxvh0KWj6W/l6uAiVRBl1JBDVRqH1VHKAO/G5eRVl4yEoYACjakWers1DjUkcCHyJNQK47JqyQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/auth-app": "^8.1.2", + "@octokit/auth-unauthenticated": "^7.0.3", + "@octokit/core": "^7.0.6", + "@octokit/oauth-app": "^8.0.3", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/types": "^16.0.0", + "@octokit/webhooks": "^14.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-app": { + "version": "8.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/auth-app/-/auth-app-8.2.0.tgz", + "integrity": "sha512-vVjdtQQwomrZ4V46B9LaCsxsySxGoHsyw6IYBov/TqJVROrlYdyNgw5q6tQbB7KZt53v1l1W53RiqTvpzL907g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/auth-oauth-app": "^9.0.3", + "@octokit/auth-oauth-user": "^6.0.2", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "toad-cache": "^3.7.0", + "universal-github-app-jwt": "^2.2.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-oauth-app": { + "version": "9.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/auth-oauth-app/-/auth-oauth-app-9.0.3.tgz", + "integrity": "sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/auth-oauth-device": "^8.0.3", + "@octokit/auth-oauth-user": "^6.0.2", + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-oauth-device": { + "version": "8.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/auth-oauth-device/-/auth-oauth-device-8.0.3.tgz", + "integrity": "sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/oauth-methods": "^6.0.2", + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-oauth-user": { + "version": "6.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/auth-oauth-user/-/auth-oauth-user-6.0.2.tgz", + "integrity": "sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/auth-oauth-device": "^8.0.3", + "@octokit/oauth-methods": "^6.0.2", + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/auth-unauthenticated": { + "version": "7.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/auth-unauthenticated/-/auth-unauthenticated-7.0.3.tgz", + "integrity": "sha512-8Jb1mtUdmBHL7lGmop9mU9ArMRUTRhg8vp0T1VtZ4yd9vEm3zcLwmjQkhNEduKawOOORie61xhtYIhTDN+ZQ3g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/core": { + "version": "7.0.6", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", + "license": "MIT", + "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": "http://mirrors.tencentyun.com/npm/@octokit/endpoint/-/endpoint-11.0.2.tgz", + "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/graphql": { + "version": "9.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/oauth-app": { + "version": "8.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/oauth-app/-/oauth-app-8.0.3.tgz", + "integrity": "sha512-jnAjvTsPepyUaMu9e69hYBuozEPgYqP4Z3UnpmvoIzHDpf8EXDGvTY1l1jK0RsZ194oRd+k6Hm13oRU8EoDFwg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/auth-oauth-app": "^9.0.2", + "@octokit/auth-oauth-user": "^6.0.1", + "@octokit/auth-unauthenticated": "^7.0.2", + "@octokit/core": "^7.0.5", + "@octokit/oauth-authorization-url": "^8.0.0", + "@octokit/oauth-methods": "^6.0.1", + "@types/aws-lambda": "^8.10.83", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/oauth-authorization-url": { + "version": "8.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/oauth-authorization-url/-/oauth-authorization-url-8.0.0.tgz", + "integrity": "sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/oauth-methods": { + "version": "6.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/oauth-methods/-/oauth-methods-6.0.2.tgz", + "integrity": "sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/oauth-authorization-url": "^8.0.0", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "27.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "license": "MIT", + "peer": true + }, + "node_modules/@octokit/openapi-webhooks-types": { + "version": "12.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-12.1.0.tgz", + "integrity": "sha512-WiuzhOsiOvb7W3Pvmhf8d2C6qaLHXrWiLBP4nJ/4kydu+wpagV5Fkz9RfQwV2afYzv3PB+3xYgp4mAdNGjDprA==", + "license": "MIT", + "peer": true + }, + "node_modules/@octokit/plugin-paginate-graphql": { + "version": "6.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-6.0.0.tgz", + "integrity": "sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "14.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "17.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "8.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/plugin-retry/-/plugin-retry-8.0.3.tgz", + "integrity": "sha512-vKGx1i3MC0za53IzYBSBXcrhmd+daQDzuZfYDd52X5S0M2otf3kVZTVP8bLA3EkU0lTvd1WEC2OlNNa4G+dohA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=7" + } + }, + "node_modules/@octokit/plugin-throttling": { + "version": "11.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/plugin-throttling/-/plugin-throttling-11.0.3.tgz", + "integrity": "sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/types": "^16.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": "^7.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "10.0.7", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/request/-/request-10.0.7.tgz", + "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/endpoint": "^11.0.2", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "fast-content-type-parse": "^3.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/request-error": { + "version": "7.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/types": { + "version": "16.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/openapi-types": "^27.0.0" + } + }, + "node_modules/@octokit/webhooks": { + "version": "14.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/webhooks/-/webhooks-14.2.0.tgz", + "integrity": "sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/openapi-webhooks-types": "12.1.0", + "@octokit/request-error": "^7.0.0", + "@octokit/webhooks-methods": "^6.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/webhooks-methods": { + "version": "6.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@octokit/webhooks-methods/-/webhooks-methods-6.0.0.tgz", + "integrity": "sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "http://mirrors.tencentyun.com/npm/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT", + "peer": true + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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": "http://mirrors.tencentyun.com/npm/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "http://mirrors.tencentyun.com/npm/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@reflink/reflink": { + "version": "0.1.19", + "resolved": "http://mirrors.tencentyun.com/npm/@reflink/reflink/-/reflink-0.1.19.tgz", + "integrity": "sha512-DmCG8GzysnCZ15bres3N5AHCmwBwYgp0As6xjhQ47rAUTUXxJiK+lLUxaGsX3hd/30qUpVElh05PbGuxRPgJwA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@reflink/reflink-darwin-arm64": "0.1.19", + "@reflink/reflink-darwin-x64": "0.1.19", + "@reflink/reflink-linux-arm64-gnu": "0.1.19", + "@reflink/reflink-linux-arm64-musl": "0.1.19", + "@reflink/reflink-linux-x64-gnu": "0.1.19", + "@reflink/reflink-linux-x64-musl": "0.1.19", + "@reflink/reflink-win32-arm64-msvc": "0.1.19", + "@reflink/reflink-win32-x64-msvc": "0.1.19" + } + }, + "node_modules/@reflink/reflink-darwin-arm64": { + "version": "0.1.19", + "resolved": "http://mirrors.tencentyun.com/npm/@reflink/reflink-darwin-arm64/-/reflink-darwin-arm64-0.1.19.tgz", + "integrity": "sha512-ruy44Lpepdk1FqDz38vExBY/PVUsjxZA+chd9wozjUH9JjuDT/HEaQYA6wYN9mf041l0yLVar6BCZuWABJvHSA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-darwin-x64": { + "version": "0.1.19", + "resolved": "http://mirrors.tencentyun.com/npm/@reflink/reflink-darwin-x64/-/reflink-darwin-x64-0.1.19.tgz", + "integrity": "sha512-By85MSWrMZa+c26TcnAy8SDk0sTUkYlNnwknSchkhHpGXOtjNDUOxJE9oByBnGbeuIE1PiQsxDG3Ud+IVV9yuA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-arm64-gnu": { + "version": "0.1.19", + "resolved": "http://mirrors.tencentyun.com/npm/@reflink/reflink-linux-arm64-gnu/-/reflink-linux-arm64-gnu-0.1.19.tgz", + "integrity": "sha512-7P+er8+rP9iNeN+bfmccM4hTAaLP6PQJPKWSA4iSk2bNvo6KU6RyPgYeHxXmzNKzPVRcypZQTpFgstHam6maVg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-arm64-musl": { + "version": "0.1.19", + "resolved": "http://mirrors.tencentyun.com/npm/@reflink/reflink-linux-arm64-musl/-/reflink-linux-arm64-musl-0.1.19.tgz", + "integrity": "sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-x64-gnu": { + "version": "0.1.19", + "resolved": "http://mirrors.tencentyun.com/npm/@reflink/reflink-linux-x64-gnu/-/reflink-linux-x64-gnu-0.1.19.tgz", + "integrity": "sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-x64-musl": { + "version": "0.1.19", + "resolved": "http://mirrors.tencentyun.com/npm/@reflink/reflink-linux-x64-musl/-/reflink-linux-x64-musl-0.1.19.tgz", + "integrity": "sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-win32-arm64-msvc": { + "version": "0.1.19", + "resolved": "http://mirrors.tencentyun.com/npm/@reflink/reflink-win32-arm64-msvc/-/reflink-win32-arm64-msvc-0.1.19.tgz", + "integrity": "sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-win32-x64-msvc": { + "version": "0.1.19", + "resolved": "http://mirrors.tencentyun.com/npm/@reflink/reflink-win32-x64-msvc/-/reflink-win32-x64-msvc-0.1.19.tgz", + "integrity": "sha512-E//yT4ni2SyhwP8JRjVGWr3cbnhWDiPLgnQ66qqaanjjnMiu3O/2tjCPQXlcGc/DEYofpDc9fvhv6tALQsMV9w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@silvia-odwyer/photon-node": { + "version": "0.3.4", + "resolved": "http://mirrors.tencentyun.com/npm/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", + "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.48", + "resolved": "http://mirrors.tencentyun.com/npm/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", + "license": "MIT", + "peer": true + }, + "node_modules/@slack/bolt": { + "version": "4.6.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@slack/socket-mode": "^2.0.5", + "@slack/types": "^2.18.0", + "@slack/web-api": "^7.12.0", + "axios": "^1.12.0", + "express": "^5.0.0", + "path-to-regexp": "^8.1.0", + "raw-body": "^3", + "tsscmp": "^1.0.6" + }, + "engines": { + "node": ">=18", + "npm": ">=8.6.0" + }, + "peerDependencies": { + "@types/express": "^5.0.0" + } + }, + "node_modules/@slack/logger": { + "version": "4.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/@slack/logger/-/logger-4.0.0.tgz", + "integrity": "sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": ">=18.0.0" + }, + "engines": { + "node": ">= 18", + "npm": ">= 8.6.0" + } + }, + "node_modules/@slack/oauth": { + "version": "3.0.4", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@types/jsonwebtoken": "^9", + "@types/node": ">=18", + "jsonwebtoken": "^9" + }, + "engines": { + "node": ">=18", + "npm": ">=8.6.0" + } + }, + "node_modules/@slack/socket-mode": { + "version": "2.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@types/node": ">=18", + "@types/ws": "^8", + "eventemitter3": "^5", + "ws": "^8" + }, + "engines": { + "node": ">= 18", + "npm": ">= 8.6.0" + } + }, + "node_modules/@slack/types": { + "version": "2.19.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + } + }, + "node_modules/@slack/web-api": { + "version": "7.13.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@types/node": ">=18.0.0", + "@types/retry": "0.12.0", + "axios": "^1.11.0", + "eventemitter3": "^5.0.1", + "form-data": "^4.0.4", + "is-electron": "2.2.2", + "is-stream": "^2", + "p-queue": "^6", + "p-retry": "^4", + "retry": "^0.13.1" + }, + "engines": { + "node": ">= 18", + "npm": ">= 8.6.0" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.6", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.22.1", + "resolved": "http://mirrors.tencentyun.com/npm/@smithy/core/-/core-3.22.1.tgz", + "integrity": "sha512-x3ie6Crr58MWrm4viHqqy2Du2rHYZjwu8BekasrQx4ca+Y24dzVAwq3yErdqIbc2G3I0kLQA13PQ+/rde+u65g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^4.2.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.11", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@smithy/util-hex-encoding": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.9", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.13", + "resolved": "http://mirrors.tencentyun.com/npm/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.13.tgz", + "integrity": "sha512-x6vn0PjYmGdNuKh/juUJJewZh7MoQ46jYaJ2mvekF4EesMuFfrl4LaW/k97Zjf8PTCPQmPgMvwewg7eNoH9n5w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.22.1", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.30", + "resolved": "http://mirrors.tencentyun.com/npm/@smithy/middleware-retry/-/middleware-retry-4.4.30.tgz", + "integrity": "sha512-CBGyFvN0f8hlnqKH/jckRDz78Snrp345+PVk8Ux7pnkUCW97Iinse59lY78hBt04h1GZ6hjBN94BRwZy1xC8Bg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/service-error-classification": "^4.2.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.9", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.4.9", + "resolved": "http://mirrors.tencentyun.com/npm/@smithy/node-http-handler/-/node-http-handler-4.4.9.tgz", + "integrity": "sha512-KX5Wml5mF+luxm1szW4QDz32e3NObgJ4Fyw+irhph4I/2geXwUy4jkIMUs5ZPGflRBeR6BUkC2wqIab4Llgm3w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/abort-controller": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.3", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "@smithy/types": "^4.12.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.11.2", + "resolved": "http://mirrors.tencentyun.com/npm/@smithy/smithy-client/-/smithy-client-4.11.2.tgz", + "integrity": "sha512-SCkGmFak/xC1n7hKRsUr6wOnBTJ3L22Qd4e8H1fQIuKTAjntwgU8lrdMe7uHdiT2mJAOWA/60qaW9tiMu69n1A==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.22.1", + "@smithy/middleware-endpoint": "^4.4.13", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.12.0", + "resolved": "http://mirrors.tencentyun.com/npm/@smithy/types/-/types-4.12.0.tgz", + "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.29", + "resolved": "http://mirrors.tencentyun.com/npm/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.29.tgz", + "integrity": "sha512-nIGy3DNRmOjaYaaKcQDzmWsro9uxlaqUOhZDHQed9MW/GmkBZPtnU70Pu1+GT9IBmUXwRdDuiyaeiy9Xtpn3+Q==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.32", + "resolved": "http://mirrors.tencentyun.com/npm/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.32.tgz", + "integrity": "sha512-7dtFff6pu5fsjqrVve0YMhrnzJtccCWDacNKOkiZjJ++fmjGExmmSu341x+WU6Oc1IccL7lDuaUj7SfrHpWc5Q==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/config-resolver": "^4.4.6", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.11", + "resolved": "http://mirrors.tencentyun.com/npm/@smithy/util-stream/-/util-stream-4.5.11.tgz", + "integrity": "sha512-lKmZ0S/3Qj2OF5H1+VzvDLb6kRxGzZHq6f3rAsoSu5cTLGsn3v3VQBA8czkNNXlLjoFEtVu3OQT2jEeOtOE2CA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.9", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@tinyhttp/content-disposition": { + "version": "2.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/@tinyhttp/content-disposition/-/content-disposition-2.2.4.tgz", + "integrity": "sha512-5Kc5CM2Ysn3vTTArBs2vESUt0AQiWZA86yc1TI3B+lxXmtEq133C1nxXNOgnzhrivdPZIh3zLj5gDnZjoLL5GA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.17.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "http://mirrors.tencentyun.com/npm/@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" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT", + "peer": true + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "http://mirrors.tencentyun.com/npm/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.160", + "resolved": "http://mirrors.tencentyun.com/npm/@types/aws-lambda/-/aws-lambda-8.10.160.tgz", + "integrity": "sha512-uoO4QVQNWFPJMh26pXtmtrRfGshPUSpMZGUyUQY20FhfHEElEBOPKgVmFs1z+kbpyBsRs2JnoOPT7++Z4GA9pA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "http://mirrors.tencentyun.com/npm/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bun": { + "version": "1.3.6", + "resolved": "http://mirrors.tencentyun.com/npm/@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": "http://mirrors.tencentyun.com/npm/@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": "http://mirrors.tencentyun.com/npm/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/@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": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "http://mirrors.tencentyun.com/npm/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/mime-types": { + "version": "2.1.4", + "resolved": "http://mirrors.tencentyun.com/npm/@types/mime-types/-/mime-types-2.1.4.tgz", + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/node": { + "version": "25.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/@types/node/-/node-25.2.1.tgz", + "integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "http://mirrors.tencentyun.com/npm/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "http://mirrors.tencentyun.com/npm/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "http://mirrors.tencentyun.com/npm/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/@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": "http://mirrors.tencentyun.com/npm/@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": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "http://mirrors.tencentyun.com/npm/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@whiskeysockets/baileys": { + "version": "7.0.0-rc.9", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "async-mutex": "^0.5.0", + "libsignal": "git+https://github.com/whiskeysockets/libsignal-node.git", + "lru-cache": "^11.1.0", + "music-metadata": "^11.7.0", + "p-queue": "^9.0.0", + "pino": "^9.6", + "protobufjs": "^7.2.4", + "ws": "^8.13.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "audio-decode": "^2.1.3", + "jimp": "^1.6.0", + "link-preview-js": "^3.0.0", + "sharp": "*" + }, + "peerDependenciesMeta": { + "audio-decode": { + "optional": true + }, + "jimp": { + "optional": true + }, + "link-preview-js": { + "optional": true + } + } + }, + "node_modules/@whiskeysockets/baileys/node_modules/p-queue": { + "version": "9.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@whiskeysockets/baileys/node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "http://mirrors.tencentyun.com/npm/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "6.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "http://mirrors.tencentyun.com/npm/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "http://mirrors.tencentyun.com/npm/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT", + "peer": true + }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "license": "ISC", + "peer": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "peer": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0", + "peer": true + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "http://mirrors.tencentyun.com/npm/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "license": "MIT", + "peer": true, + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "http://mirrors.tencentyun.com/npm/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT", + "peer": true + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT", + "peer": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "http://mirrors.tencentyun.com/npm/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/basic-ftp": { + "version": "5.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/basic-ftp/-/basic-ftp-5.1.0.tgz", + "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC", + "peer": true + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "http://mirrors.tencentyun.com/npm/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "license": "MIT", + "peer": true + }, + "node_modules/bowser": { + "version": "2.13.1", + "resolved": "http://mirrors.tencentyun.com/npm/bowser/-/bowser-2.13.1.tgz", + "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", + "license": "MIT", + "peer": true + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT", + "peer": true + }, + "node_modules/bun-types": { + "version": "1.3.6", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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", + "hookified": "^1.15.0", + "keyv": "^5.5.5", + "qified": "^0.6.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chmodrp": { + "version": "1.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/chmodrp/-/chmodrp-1.0.2.tgz", + "integrity": "sha512-TdngOlFV1FLTzU0o1w8MB6/BFywhtLC0SzRTGJU7T9lmdjlCWeMRt1iVo0Ki+ldwNk0BqNiKoc8xpLZEQ8mY1w==", + "license": "MIT", + "peer": true + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "peer": true, + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "http://mirrors.tencentyun.com/npm/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", + "peer": true, + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/chalk": { + "version": "4.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "peer": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "http://mirrors.tencentyun.com/npm/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "http://mirrors.tencentyun.com/npm/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cmake-js": { + "version": "7.4.0", + "resolved": "http://mirrors.tencentyun.com/npm/cmake-js/-/cmake-js-7.4.0.tgz", + "integrity": "sha512-Lw0JxEHrmk+qNj1n9W9d4IvkDdYTBn7l2BW6XmtLj7WPpIo2shvxUy+YokfjMxAAOELNonQwX3stkPhM5xSC2Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "axios": "^1.6.5", + "debug": "^4", + "fs-extra": "^11.2.0", + "memory-stream": "^1.0.0", + "node-api-headers": "^1.1.0", + "npmlog": "^6.0.2", + "rc": "^1.2.7", + "semver": "^7.5.4", + "tar": "^6.2.0", + "url-join": "^4.0.1", + "which": "^2.0.2", + "yargs": "^17.7.2" + }, + "bin": { + "cmake-js": "bin/cmake-js" + }, + "engines": { + "node": ">= 14.15.0" + } + }, + "node_modules/cmake-js/node_modules/isexe": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC", + "peer": true + }, + "node_modules/cmake-js/node_modules/which": { + "version": "2.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "http://mirrors.tencentyun.com/npm/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT", + "peer": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "http://mirrors.tencentyun.com/npm/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "peer": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "http://mirrors.tencentyun.com/npm/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "peer": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", + "peer": true + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT", + "peer": true + }, + "node_modules/croner": { + "version": "10.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/croner/-/croner-10.0.1.tgz", + "integrity": "sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==", + "funding": [ + { + "type": "other", + "url": "https://paypal.me/hexagonpp" + }, + { + "type": "github", + "url": "https://github.com/sponsors/hexagon" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC", + "peer": true + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "http://mirrors.tencentyun.com/npm/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/curve25519-js/-/curve25519-js-0.0.4.tgz", + "integrity": "sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w==", + "license": "MIT", + "peer": true + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "http://mirrors.tencentyun.com/npm/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "peer": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "8.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/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.38", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause", + "peer": true + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "17.2.4", + "resolved": "http://mirrors.tencentyun.com/npm/dotenv/-/dotenv-17.2.4.tgz", + "integrity": "sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT", + "peer": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT", + "peer": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "peer": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-var": { + "version": "7.5.0", + "resolved": "http://mirrors.tencentyun.com/npm/env-var/-/env-var-7.5.0.tgz", + "integrity": "sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT", + "peer": true + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT", + "peer": true + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "peer": true, + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "http://mirrors.tencentyun.com/npm/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT", + "peer": true + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "http://mirrors.tencentyun.com/npm/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT", + "peer": true + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/fast-xml-parser": { + "version": "5.3.4", + "resolved": "http://mirrors.tencentyun.com/npm/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz", + "integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-type": { + "version": "21.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/filename-reserved-regex": { + "version": "3.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", + "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/filenamify": { + "version": "6.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/filenamify/-/filenamify-6.0.0.tgz", + "integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "filename-reserved-regex": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "http://mirrors.tencentyun.com/npm/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "http://mirrors.tencentyun.com/npm/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "peer": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/fs-extra/-/fs-extra-11.3.3.tgz", + "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "http://mirrors.tencentyun.com/npm/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "peer": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gaxios": { + "version": "7.1.3", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "license": "MIT", + "peer": true, + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/google-auth-library": { + "version": "10.5.0", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "gaxios": "^7.0.0", + "gcp-metadata": "^8.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "http://mirrors.tencentyun.com/npm/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC", + "peer": true + }, + "node_modules/grammy": { + "version": "1.39.3", + "resolved": "http://mirrors.tencentyun.com/npm/grammy/-/grammy-1.39.3.tgz", + "integrity": "sha512-7arRRoOtOh9UwMwANZ475kJrWV6P3/EGNooeHlY0/SwZv4t3ZZ3Uiz9cAXK8Zg9xSdgmm8T21kx6n7SZaWvOcw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@grammyjs/types": "3.23.0", + "abort-controller": "^3.0.0", + "debug": "^4.4.3", + "node-fetch": "^2.7.0" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + } + }, + "node_modules/grammy/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gtoken": { + "version": "8.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "license": "MIT", + "peer": true, + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", + "peer": true + }, + "node_modules/hashery": { + "version": "1.4.0", + "resolved": "http://mirrors.tencentyun.com/npm/hashery/-/hashery-1.4.0.tgz", + "integrity": "sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "hookified": "^1.14.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "http://mirrors.tencentyun.com/npm/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.7", + "resolved": "http://mirrors.tencentyun.com/npm/hono/-/hono-4.11.7.tgz", + "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "license": "MIT", + "peer": true + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT", + "peer": true + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "http://mirrors.tencentyun.com/npm/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT", + "peer": true + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "http://mirrors.tencentyun.com/npm/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC", + "peer": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "http://mirrors.tencentyun.com/npm/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC", + "peer": true + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/ipull/-/ipull-3.9.3.tgz", + "integrity": "sha512-ZMkxaopfwKHwmEuGDYx7giNBdLxbHbRCWcQVA1D2eqE4crUguupfxej6s7UqbidYEwT69dkyumYkY8DPHIxF9g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tinyhttp/content-disposition": "^2.2.0", + "async-retry": "^1.3.3", + "chalk": "^5.3.0", + "ci-info": "^4.0.0", + "cli-spinners": "^2.9.2", + "commander": "^10.0.0", + "eventemitter3": "^5.0.1", + "filenamify": "^6.0.0", + "fs-extra": "^11.1.1", + "is-unicode-supported": "^2.0.0", + "lifecycle-utils": "^2.0.1", + "lodash.debounce": "^4.0.8", + "lowdb": "^7.0.1", + "pretty-bytes": "^6.1.0", + "pretty-ms": "^8.0.0", + "sleep-promise": "^9.1.0", + "slice-ansi": "^7.1.0", + "stdout-update": "^4.0.1", + "strip-ansi": "^7.1.0" + }, + "bin": { + "ipull": "dist/cli/cli.js" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/ido-pluto/ipull?sponsor=1" + }, + "optionalDependencies": { + "@reflink/reflink": "^0.1.16" + } + }, + "node_modules/ipull/node_modules/lifecycle-utils": { + "version": "2.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/lifecycle-utils/-/lifecycle-utils-2.1.0.tgz", + "integrity": "sha512-AnrXnE2/OF9PHCyFg0RSqsnQTzV991XaZA/buhFDoc58xU7rhSCDgCz/09Lqpsn4MpoPHt7TRAXV1kWZypFVsA==", + "license": "MIT", + "peer": true + }, + "node_modules/ipull/node_modules/parse-ms": { + "version": "3.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/parse-ms/-/parse-ms-3.0.0.tgz", + "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ipull/node_modules/pretty-ms": { + "version": "8.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/pretty-ms/-/pretty-ms-8.0.0.tgz", + "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "parse-ms": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-electron": { + "version": "2.2.2", + "resolved": "http://mirrors.tencentyun.com/npm/is-electron/-/is-electron-2.2.2.tgz", + "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==", + "license": "MIT", + "peer": true + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT", + "peer": true + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT", + "peer": true + }, + "node_modules/isexe": { + "version": "3.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/isexe/-/isexe-3.1.2.tgz", + "integrity": "sha512-mIcis6w+JiQf3P7t7mg/35GKB4T1FQsBOtMIvuKw4YErj5RjtbhcTd5/I30fmkmGMwvI0WlzSNN+27K0QCMkAw==", + "license": "BlueOak-1.0.0", + "peer": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/jackspeak": { + "version": "4.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/jackspeak/-/jackspeak-4.2.1.tgz", + "integrity": "sha512-GPBXyfcZSGujjddPeA+V34bW70ZJT7jzCEbloVasSH4yjiqWqXHX8iZQtZdVbOhc5esSeAIuiSmMutRZQB/olg==", + "license": "BlueOak-1.0.0", + "peer": true, + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT", + "peer": true + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/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/jwa": { + "version": "2.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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" + } + }, + "node_modules/keyv": { + "version": "5.6.0", + "resolved": "http://mirrors.tencentyun.com/npm/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/libsignal": { + "name": "@whiskeysockets/libsignal-node", + "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" + } + }, + "node_modules/libsignal/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "http://mirrors.tencentyun.com/npm/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "license": "MIT", + "peer": true + }, + "node_modules/libsignal/node_modules/long": { + "version": "4.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/libsignal/node_modules/protobufjs": { + "version": "6.8.8", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.0", + "@types/node": "^10.1.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/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.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/lifecycle-utils/-/lifecycle-utils-3.1.0.tgz", + "integrity": "sha512-kVvegv+r/icjIo1dkHv1hznVQi4FzEVglJD2IU4w07HzevIyH3BAYsFZzEIbBk/nNZjXHGgclJ5g9rz9QdBCLw==", + "license": "MIT", + "peer": true + }, + "node_modules/linkedom": { + "version": "0.18.12", + "resolved": "http://mirrors.tencentyun.com/npm/linkedom/-/linkedom-0.18.12.tgz", + "integrity": "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==", + "license": "ISC", + "peer": true, + "dependencies": { + "css-select": "^5.1.0", + "cssom": "^0.5.0", + "html-escaper": "^3.0.3", + "htmlparser2": "^10.0.0", + "uhyphen": "^0.2.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "canvas": ">= 2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.identity": { + "version": "3.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/lodash.identity/-/lodash.identity-3.0.0.tgz", + "integrity": "sha512-AupTIzdLQxJS5wIYUQlgGyk2XRTfGXA+MCghDHqZk0pzUNYvd3EESS6dkChNauNYVIutcb0dfHw1ri9Q1yPV8Q==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "http://mirrors.tencentyun.com/npm/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "http://mirrors.tencentyun.com/npm/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "http://mirrors.tencentyun.com/npm/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.pickby": { + "version": "4.6.0", + "resolved": "http://mirrors.tencentyun.com/npm/lodash.pickby/-/lodash.pickby-4.6.0.tgz", + "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==", + "license": "MIT", + "peer": true + }, + "node_modules/log-symbols": { + "version": "7.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/log-symbols/-/log-symbols-7.0.1.tgz", + "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-unicode-supported": "^2.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/lowdb": { + "version": "7.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/lowdb/-/lowdb-7.0.1.tgz", + "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", + "license": "MIT", + "peer": true, + "dependencies": { + "steno": "^4.0.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/lru-cache": { + "version": "11.2.5", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "http://mirrors.tencentyun.com/npm/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "license": "MIT", + "peer": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/memory-stream/-/memory-stream-1.0.0.tgz", + "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==", + "license": "MIT", + "peer": true, + "dependencies": { + "readable-stream": "^3.4.0" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "http://mirrors.tencentyun.com/npm/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/minimatch/-/minimatch-10.1.2.tgz", + "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "license": "BlueOak-1.0.0", + "peer": true, + "dependencies": { + "@isaacs/brace-expansion": "^5.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "peer": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "http://mirrors.tencentyun.com/npm/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "http://mirrors.tencentyun.com/npm/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "http://mirrors.tencentyun.com/npm/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "peer": true + }, + "node_modules/music-metadata": { + "version": "11.11.2", + "resolved": "http://mirrors.tencentyun.com/npm/music-metadata/-/music-metadata-11.11.2.tgz", + "integrity": "sha512-tJx+lsDg1bGUOxojKKj12BIvccBBUcVa6oWrvOchCF0WAQ9E5t/hK35ILp1z3wWrUSYtgg57LfRbvVMkxGIyzA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "file-type": "^21.3.0", + "media-typer": "^1.1.0", + "strtok3": "^10.3.4", + "token-types": "^6.1.2", + "uint8array-extras": "^1.5.0", + "win-guid": "^0.2.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "5.1.6", + "resolved": "http://mirrors.tencentyun.com/npm/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "http://mirrors.tencentyun.com/npm/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-api-headers": { + "version": "1.8.0", + "resolved": "http://mirrors.tencentyun.com/npm/node-api-headers/-/node-api-headers-1.8.0.tgz", + "integrity": "sha512-jfnmiKWjRAGbdD1yQS28bknFM1tbHC1oucyuMPjmkEs+kpiu76aRs40WlTmBmyEgzDM76ge1DQ7XJ3R5deiVjQ==", + "license": "MIT", + "peer": true + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-edge-tts": { + "version": "1.2.10", + "resolved": "http://mirrors.tencentyun.com/npm/node-edge-tts/-/node-edge-tts-1.2.10.tgz", + "integrity": "sha512-bV2i4XU54D45+US0Zm1HcJRkifuB3W438dWyuJEHLQdKxnuqlI1kim2MOvR6Q3XUQZvfF9PoDyR1Rt7aeXhPdQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "https-proxy-agent": "^7.0.1", + "ws": "^8.13.0", + "yargs": "^17.7.2" + }, + "bin": { + "node-edge-tts": "bin.js" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-llama-cpp": { + "version": "3.15.1", + "resolved": "http://mirrors.tencentyun.com/npm/node-llama-cpp/-/node-llama-cpp-3.15.1.tgz", + "integrity": "sha512-/fBNkuLGR2Q8xj2eeV12KXKZ9vCS2+o6aP11lW40pB9H6f0B3wOALi/liFrjhHukAoiH6C9wFTPzv6039+5DRA==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@huggingface/jinja": "^0.5.3", + "async-retry": "^1.3.3", + "bytes": "^3.1.2", + "chalk": "^5.4.1", + "chmodrp": "^1.0.2", + "cmake-js": "^7.4.0", + "cross-spawn": "^7.0.6", + "env-var": "^7.5.0", + "filenamify": "^6.0.0", + "fs-extra": "^11.3.0", + "ignore": "^7.0.4", + "ipull": "^3.9.2", + "is-unicode-supported": "^2.1.0", + "lifecycle-utils": "^3.0.1", + "log-symbols": "^7.0.0", + "nanoid": "^5.1.5", + "node-addon-api": "^8.3.1", + "octokit": "^5.0.3", + "ora": "^8.2.0", + "pretty-ms": "^9.2.0", + "proper-lockfile": "^4.1.2", + "semver": "^7.7.1", + "simple-git": "^3.27.0", + "slice-ansi": "^7.1.0", + "stdout-update": "^4.0.1", + "strip-ansi": "^7.1.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "nlc": "dist/cli/cli.js", + "node-llama-cpp": "dist/cli/cli.js" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/giladgd" + }, + "optionalDependencies": { + "@node-llama-cpp/linux-arm64": "3.15.1", + "@node-llama-cpp/linux-armv7l": "3.15.1", + "@node-llama-cpp/linux-x64": "3.15.1", + "@node-llama-cpp/linux-x64-cuda": "3.15.1", + "@node-llama-cpp/linux-x64-cuda-ext": "3.15.1", + "@node-llama-cpp/linux-x64-vulkan": "3.15.1", + "@node-llama-cpp/mac-arm64-metal": "3.15.1", + "@node-llama-cpp/mac-x64": "3.15.1", + "@node-llama-cpp/win-arm64": "3.15.1", + "@node-llama-cpp/win-x64": "3.15.1", + "@node-llama-cpp/win-x64-cuda": "3.15.1", + "@node-llama-cpp/win-x64-cuda-ext": "3.15.1", + "@node-llama-cpp/win-x64-vulkan": "3.15.1" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/node-readable-to-web-readable-stream": { + "version": "0.4.2", + "resolved": "http://mirrors.tencentyun.com/npm/node-readable-to-web-readable-stream/-/node-readable-to-web-readable-stream-0.4.2.tgz", + "integrity": "sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "peer": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/octokit": { + "version": "5.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/octokit/-/octokit-5.0.5.tgz", + "integrity": "sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/app": "^16.1.2", + "@octokit/core": "^7.0.6", + "@octokit/oauth-app": "^8.0.3", + "@octokit/plugin-paginate-graphql": "^6.0.0", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0", + "@octokit/plugin-retry": "^8.0.3", + "@octokit/plugin-throttling": "^11.0.3", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "@octokit/webhooks": "^14.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openai": { + "version": "6.10.0", + "resolved": "http://mirrors.tencentyun.com/npm/openai/-/openai-6.10.0.tgz", + "integrity": "sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openclaw": { + "version": "2026.2.3", + "resolved": "http://mirrors.tencentyun.com/npm/openclaw/-/openclaw-2026.2.3.tgz", + "integrity": "sha512-nAYjR0sfZf85h6h9idqWvose0v4fQ+6ATk7uldNg3bQ8EzOpFrPzMCx75rGjAj9bsBFTmT9/9eMAIref+cxVIw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@agentclientprotocol/sdk": "0.13.1", + "@aws-sdk/client-bedrock": "^3.981.0", + "@buape/carbon": "0.14.0", + "@clack/prompts": "^1.0.0", + "@grammyjs/runner": "^2.0.3", + "@grammyjs/transformer-throttler": "^1.2.1", + "@homebridge/ciao": "^1.3.4", + "@larksuiteoapi/node-sdk": "^1.42.0", + "@line/bot-sdk": "^10.6.0", + "@lydell/node-pty": "1.2.0-beta.3", + "@mariozechner/pi-agent-core": "0.51.3", + "@mariozechner/pi-ai": "0.51.3", + "@mariozechner/pi-coding-agent": "0.51.3", + "@mariozechner/pi-tui": "0.51.3", + "@mozilla/readability": "^0.6.0", + "@sinclair/typebox": "0.34.48", + "@slack/bolt": "^4.6.0", + "@slack/web-api": "^7.13.0", + "@whiskeysockets/baileys": "7.0.0-rc.9", + "ajv": "^8.17.1", + "chalk": "^5.6.2", + "chokidar": "^5.0.0", + "cli-highlight": "^2.1.11", + "commander": "^14.0.3", + "croner": "^10.0.1", + "discord-api-types": "^0.38.38", + "dotenv": "^17.2.3", + "express": "^5.2.1", + "file-type": "^21.3.0", + "grammy": "^1.39.3", + "hono": "4.11.7", + "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.624", + "playwright-core": "1.58.1", + "proper-lockfile": "^4.1.2", + "qrcode-terminal": "^0.12.0", + "sharp": "^0.34.5", + "signal-utils": "^0.21.1", + "sqlite-vec": "0.1.7-alpha.2", + "tar": "7.5.7", + "tslog": "^4.10.2", + "undici": "^7.20.0", + "ws": "^8.19.0", + "yaml": "^2.8.2", + "zod": "^4.3.6" + }, + "bin": { + "openclaw": "openclaw.mjs" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@napi-rs/canvas": "^0.1.89", + "node-llama-cpp": "3.15.1" + } + }, + "node_modules/openclaw/node_modules/chownr": { + "version": "3.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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/openclaw/node_modules/commander": { + "version": "14.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/openclaw/node_modules/minizlib": { + "version": "3.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "peer": true, + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/openclaw/node_modules/tar": { + "version": "7.5.7", + "resolved": "http://mirrors.tencentyun.com/npm/tar/-/tar-7.5.7.tgz", + "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "license": "BlueOak-1.0.0", + "peer": true, + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/openclaw/node_modules/yallist": { + "version": "5.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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/ora": { + "version": "8.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "license": "MIT", + "peer": true, + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "http://mirrors.tencentyun.com/npm/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT", + "peer": true + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "license": "MIT", + "peer": true, + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/osc-progress": { + "version": "0.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "http://mirrors.tencentyun.com/npm/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT", + "peer": true + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", + "peer": true, + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0", + "peer": true + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "license": "MIT", + "peer": true + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "license": "MIT", + "peer": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", + "license": "MIT", + "peer": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/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" + } + }, + "node_modules/pdfjs-dist": { + "version": "5.4.624", + "resolved": "http://mirrors.tencentyun.com/npm/pdfjs-dist/-/pdfjs-dist-5.4.624.tgz", + "integrity": "sha512-sm6TxKTtWv1Oh6n3C6J6a8odejb5uO4A4zo/2dgkHuC0iu8ZMAXOezEODkVaoVp8nX1Xzr+0WxFJJmUr45hQzg==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=20.16.0 || >=22.3.0" + }, + "optionalDependencies": { + "@napi-rs/canvas": "^0.1.88", + "node-readable-to-web-readable-stream": "^0.4.2" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC", + "peer": true + }, + "node_modules/pino": { + "version": "9.14.0", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "license": "MIT", + "peer": true + }, + "node_modules/playwright-core": { + "version": "1.58.1", + "resolved": "http://mirrors.tencentyun.com/npm/playwright-core/-/playwright-core-1.58.1.tgz", + "integrity": "sha512-bcWzOaTxcW+VOOGBCQgnaKToLJ65d6AqfLVKEWvexyS3AS6rbXl+xdpYRMGSRBClPvyj44njOWoxjNdL/H9UNg==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prism-media": { + "version": "1.3.5", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "node-opus": "^0.3.3", + "opusscript": "^0.0.8" + }, + "peerDependenciesMeta": { + "@discordjs/opus": { + "optional": true + }, + "ffmpeg-static": { + "optional": true + }, + "node-opus": { + "optional": true + }, + "opusscript": { + "optional": true + } + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/retry": { + "version": "0.12.0", + "resolved": "http://mirrors.tencentyun.com/npm/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "http://mirrors.tencentyun.com/npm/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "http://mirrors.tencentyun.com/npm/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/qified/-/qified-0.6.0.tgz", + "integrity": "sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hookified": "^1.14.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/qrcode-terminal": { + "version": "0.12.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "http://mirrors.tencentyun.com/npm/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT", + "peer": true + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "http://mirrors.tencentyun.com/npm/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "peer": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "http://mirrors.tencentyun.com/npm/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", + "peer": true, + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/@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", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/rimraf/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "http://mirrors.tencentyun.com/npm/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT", + "peer": true + }, + "node_modules/rimraf/node_modules/glob": { + "version": "10.5.0", + "resolved": "http://mirrors.tencentyun.com/npm/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "peer": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "http://mirrors.tencentyun.com/npm/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC", + "peer": true + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/string-width": { + "version": "5.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rimraf/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "peer": true + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "http://mirrors.tencentyun.com/npm/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/send/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "http://mirrors.tencentyun.com/npm/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC", + "peer": true + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT", + "peer": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC", + "peer": true + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "http://mirrors.tencentyun.com/npm/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "peer": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "http://mirrors.tencentyun.com/npm/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "peer": true + }, + "node_modules/signal-polyfill": { + "version": "0.2.2", + "resolved": "http://mirrors.tencentyun.com/npm/signal-polyfill/-/signal-polyfill-0.2.2.tgz", + "integrity": "sha512-p63Y4Er5/eMQ9RHg0M0Y64NlsQKpiu6MDdhBXpyywRuWiPywhJTpKJ1iB5K2hJEbFZ0BnDS7ZkJ+0AfTuL37Rg==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/signal-utils": { + "version": "0.21.1", + "resolved": "http://mirrors.tencentyun.com/npm/signal-utils/-/signal-utils-0.21.1.tgz", + "integrity": "sha512-i9cdLSvVH4j8ql8mz2lyrA93xL499P8wEbIev3ldSriXeUwqh+wM4Q5VPhIZ19gPtIS4BOopJuKB8l1+wH9LCg==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "signal-polyfill": "^0.2.0" + } + }, + "node_modules/simple-git": { + "version": "3.30.0", + "resolved": "http://mirrors.tencentyun.com/npm/simple-git/-/simple-git-3.30.0.tgz", + "integrity": "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT", + "peer": true + }, + "node_modules/sleep-promise": { + "version": "9.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/sleep-promise/-/sleep-promise-9.1.0.tgz", + "integrity": "sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA==", + "license": "MIT", + "peer": true + }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "http://mirrors.tencentyun.com/npm/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "peer": true, + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "http://mirrors.tencentyun.com/npm/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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", + "sqlite-vec-linux-arm64": "0.1.7-alpha.2", + "sqlite-vec-linux-x64": "0.1.7-alpha.2", + "sqlite-vec-windows-x64": "0.1.7-alpha.2" + } + }, + "node_modules/sqlite-vec-darwin-arm64": { + "version": "0.1.7-alpha.2", + "resolved": "http://mirrors.tencentyun.com/npm/sqlite-vec-darwin-arm64/-/sqlite-vec-darwin-arm64-0.1.7-alpha.2.tgz", + "integrity": "sha512-raIATOqFYkeCHhb/t3r7W7Cf2lVYdf4J3ogJ6GFc8PQEgHCPEsi+bYnm2JT84MzLfTlSTIdxr4/NKv+zF7oLPw==", + "cpu": [ + "arm64" + ], + "license": "MIT OR Apache", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/sqlite-vec-darwin-x64": { + "version": "0.1.7-alpha.2", + "resolved": "http://mirrors.tencentyun.com/npm/sqlite-vec-darwin-x64/-/sqlite-vec-darwin-x64-0.1.7-alpha.2.tgz", + "integrity": "sha512-jeZEELsQjjRsVojsvU5iKxOvkaVuE+JYC8Y4Ma8U45aAERrDYmqZoHvgSG7cg1PXL3bMlumFTAmHynf1y4pOzA==", + "cpu": [ + "x64" + ], + "license": "MIT OR Apache", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/sqlite-vec-linux-arm64": { + "version": "0.1.7-alpha.2", + "resolved": "http://mirrors.tencentyun.com/npm/sqlite-vec-linux-arm64/-/sqlite-vec-linux-arm64-0.1.7-alpha.2.tgz", + "integrity": "sha512-6Spj4Nfi7tG13jsUG+W7jnT0bCTWbyPImu2M8nWp20fNrd1SZ4g3CSlDAK8GBdavX7wRlbBHCZ+BDa++rbDewA==", + "cpu": [ + "arm64" + ], + "license": "MIT OR Apache", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/sqlite-vec-linux-x64": { + "version": "0.1.7-alpha.2", + "resolved": "http://mirrors.tencentyun.com/npm/sqlite-vec-linux-x64/-/sqlite-vec-linux-x64-0.1.7-alpha.2.tgz", + "integrity": "sha512-IcgrbHaDccTVhXDf8Orwdc2+hgDLAFORl6OBUhcvlmwswwBP1hqBTSEhovClG4NItwTOBNgpwOoQ7Qp3VDPWLg==", + "cpu": [ + "x64" + ], + "license": "MIT OR Apache", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/sqlite-vec-windows-x64": { + "version": "0.1.7-alpha.2", + "resolved": "http://mirrors.tencentyun.com/npm/sqlite-vec-windows-x64/-/sqlite-vec-windows-x64-0.1.7-alpha.2.tgz", + "integrity": "sha512-TRP6hTjAcwvQ6xpCZvjP00pdlda8J38ArFy1lMYhtQWXiIBmWnhMaMbq4kaeCYwvTTddfidatRS+TJrwIKB/oQ==", + "cpu": [ + "x64" + ], + "license": "MIT OR Apache", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "license": "MIT", + "peer": true + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "http://mirrors.tencentyun.com/npm/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stdout-update": { + "version": "4.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/stdout-update/-/stdout-update-4.0.1.tgz", + "integrity": "sha512-wiS21Jthlvl1to+oorePvcyrIkiG/6M3D3VTmDUlJm7Cy6SbFhKkAvX+YBuHLxck/tO3mrdpC/cNesigQc3+UQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-escapes": "^6.2.0", + "ansi-styles": "^6.2.1", + "string-width": "^7.1.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/stdout-update/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "http://mirrors.tencentyun.com/npm/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT", + "peer": true + }, + "node_modules/stdout-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/steno": { + "version": "4.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/steno/-/steno-4.0.2.tgz", + "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strnum": { + "version": "2.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/strtok3": { + "version": "10.3.4", + "resolved": "http://mirrors.tencentyun.com/npm/strtok3/-/strtok3-10.3.4.tgz", + "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "peer": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT", + "peer": true + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT", + "peer": true + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "http://mirrors.tencentyun.com/npm/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "peer": true + }, + "node_modules/tslog": { + "version": "4.10.2", + "resolved": "http://mirrors.tencentyun.com/npm/tslog/-/tslog-4.10.2.tgz", + "integrity": "sha512-XuELoRpMR+sq8fuWwX7P0bcj+PRNiicOKDEb3fGNURhxWVyykCi9BNq7c4uVz7h7P0sj8qgBsr5SWS6yBClq3g==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/fullstack-build/tslog?sponsor=1" + } + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "http://mirrors.tencentyun.com/npm/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "http://mirrors.tencentyun.com/npm/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/uhyphen/-/uhyphen-0.2.0.tgz", + "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", + "license": "ISC", + "peer": true + }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "http://mirrors.tencentyun.com/npm/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici": { + "version": "7.20.0", + "resolved": "http://mirrors.tencentyun.com/npm/undici/-/undici-7.20.0.tgz", + "integrity": "sha512-MJZrkjyd7DeC+uPZh+5/YaMDxFiiEEaDgbUSVMXayofAkDWF1088CDo+2RPg7B1BuS1qf1vgNE7xqwPxE0DuSQ==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "http://mirrors.tencentyun.com/npm/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/universal-github-app-jwt": { + "version": "2.2.2", + "resolved": "http://mirrors.tencentyun.com/npm/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz", + "integrity": "sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==", + "license": "MIT", + "peer": true + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "http://mirrors.tencentyun.com/npm/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "license": "ISC", + "peer": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "license": "MIT", + "peer": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", + "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", + "license": "ISC", + "peer": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/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" + } + }, + "node_modules/which": { + "version": "5.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "http://mirrors.tencentyun.com/npm/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/win-guid": { + "version": "0.2.1", + "resolved": "http://mirrors.tencentyun.com/npm/win-guid/-/win-guid-0.2.1.tgz", + "integrity": "sha512-gEIQU4mkgl2OPeoNrWflcJFJ3Ae2BPd4eCsHHA/XikslkIVms/nHhvnvzIZV7VLmBvtFlDOzLt9rrZT+n6D67A==", + "license": "MIT", + "peer": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/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": "http://mirrors.tencentyun.com/npm/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "http://mirrors.tencentyun.com/npm/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "http://mirrors.tencentyun.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "http://mirrors.tencentyun.com/npm/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC", + "peer": true + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "http://mirrors.tencentyun.com/npm/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "http://mirrors.tencentyun.com/npm/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "http://mirrors.tencentyun.com/npm/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "peer": true + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "http://mirrors.tencentyun.com/npm/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "license": "ISC", + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "http://mirrors.tencentyun.com/npm/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", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "http://mirrors.tencentyun.com/npm/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "http://mirrors.tencentyun.com/npm/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "http://mirrors.tencentyun.com/npm/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "http://mirrors.tencentyun.com/npm/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/wecom/package.json b/wecom/package.json new file mode 100644 index 00000000..c9e83da5 --- /dev/null +++ b/wecom/package.json @@ -0,0 +1,72 @@ +{ + "name": "wecom", + "version": "2026.2.5", + "type": "module", + "description": "OpenClaw WeCom (WeChat Work) intelligent bot channel plugin", + "openclaw": { + "extensions": [ + "./index.ts" + ], + "channel": { + "id": "wecom", + "label": "WeCom", + "selectionLabel": "WeCom (plugin)", + "detailLabel": "WeCom Bot", + "docsPath": "/channels/wecom", + "docsLabel": "wecom", + "blurb": "Enterprise WeCom intelligent bot (API mode) via encrypted webhooks + passive replies.", + "aliases": [ + "wechatwork", + "wework", + "qywx", + "企微", + "企业微信" + ], + "order": 85 + }, + "install": { + "npmSpec": "@openclaw/wecom", + "localPath": "extensions/wecom", + "defaultChoice": "npm" + } + }, + "dependencies": { + "fast-xml-parser": "5.3.4", + "undici": "^7.20.0", + "zod": "^4.3.6" + }, + "peerDependencies": { + "openclaw": ">=2026.1.26" + }, + "devDependencies": { + "@types/node": "^25.2.0", + "typescript": "^5.9.3" + }, + "clawdbot": { + "extensions": [ + "./index.ts" + ], + "channel": { + "id": "wecom", + "label": "WeCom", + "selectionLabel": "WeCom (plugin)", + "detailLabel": "WeCom Bot", + "docsPath": "/channels/wecom", + "docsLabel": "wecom", + "blurb": "Enterprise WeCom intelligent bot (API mode) via encrypted webhooks + passive replies.", + "aliases": [ + "wechatwork", + "wework", + "qywx", + "企微", + "企业微信" + ], + "order": 85 + }, + "install": { + "npmSpec": "@openclaw/wecom", + "localPath": "extensions/wecom", + "defaultChoice": "npm" + } + } +} diff --git a/wecom/scripts/test-proxy.ts b/wecom/scripts/test-proxy.ts new file mode 100644 index 00000000..a53e9b93 --- /dev/null +++ b/wecom/scripts/test-proxy.ts @@ -0,0 +1,70 @@ +import { wecomFetch } from "../src/http.js"; + +const args = process.argv.slice(2); +let proxyUrl = ""; + +if (args.includes("--host")) { + const hostIndex = args.indexOf("--host"); + const portIndex = args.indexOf("--port"); + const userIndex = args.indexOf("--user"); + const passIndex = args.indexOf("--pass"); + + if (hostIndex === -1 || portIndex === -1) { + console.error("Error: --host and --port are required when using specific params."); + process.exit(1); + } + + const host = args[hostIndex + 1]; + const port = args[portIndex + 1]; + const user = userIndex !== -1 ? args[userIndex + 1] : ""; + const pass = passIndex !== -1 ? args[passIndex + 1] : ""; + + if (user && pass) { + // Safe encoding + proxyUrl = `http://${encodeURIComponent(user)}:${encodeURIComponent(pass)}@${host}:${port}`; + } else { + proxyUrl = `http://${host}:${port}`; + } +} else { + proxyUrl = args[0] || process.env.PROXY_URL || ""; +} + +if (!proxyUrl) { + console.error("Usage: npx tsx extensions/wecom/scripts/test-proxy.ts "); + console.error(" OR: npx tsx extensions/wecom/scripts/test-proxy.ts --host --port --user --pass "); + process.exit(1); +} + +console.log(`Testing proxy: ${proxyUrl.replace(/:([^:@]+)@/, ":***@")}`); // Mask password in log + +async function run() { + try { + // 1. Test IP echo to verify traffic goes through proxy + console.log("1. Checking IP via httpbin.org..."); + const ipRes = await wecomFetch("https://httpbin.org/ip", {}, { proxyUrl, timeoutMs: 10000 }); + if (!ipRes.ok) { + throw new Error(`IP check failed: ${ipRes.status} ${ipRes.statusText}`); + } + const ipJson = await ipRes.json(); + console.log(" Result:", ipJson); + + // 2. Test WeCom API connectivity + console.log("2. Checking WeCom connectivity..."); + const wecomRes = await wecomFetch("https://qyapi.weixin.qq.com/cgi-bin/gettoken", {}, { proxyUrl, timeoutMs: 10000 }); + const wecomJson = await wecomRes.json(); + console.log(" Result:", wecomJson); + console.log("✅ Proxy works!"); + + } catch (err) { + // Extract cause for better debugging + const cause = (err as any).cause; + if (cause) { + console.error("❌ Proxy test failed (Cause):", cause); + } else { + console.error("❌ Proxy test failed:", err); + } + process.exit(1); + } +} + +run(); diff --git a/wecom/src/accounts.ts b/wecom/src/accounts.ts new file mode 100644 index 00000000..10a49da2 --- /dev/null +++ b/wecom/src/accounts.ts @@ -0,0 +1,72 @@ +import type { OpenClawConfig } from "openclaw/plugin-sdk"; +import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk"; + +import type { ResolvedWecomAccount, WecomAccountConfig, WecomConfig } from "./types.js"; + +function listConfiguredAccountIds(cfg: OpenClawConfig): string[] { + const accounts = (cfg.channels?.wecom as WecomConfig | undefined)?.accounts; + if (!accounts || typeof accounts !== "object") return []; + return Object.keys(accounts).filter(Boolean); +} + +export function listWecomAccountIds(cfg: OpenClawConfig): string[] { + const ids = listConfiguredAccountIds(cfg); + if (ids.length === 0) return [DEFAULT_ACCOUNT_ID]; + return ids.sort((a, b) => a.localeCompare(b)); +} + +export function resolveDefaultWecomAccountId(cfg: OpenClawConfig): string { + const wecomConfig = cfg.channels?.wecom as WecomConfig | undefined; + if (wecomConfig?.defaultAccount?.trim()) return wecomConfig.defaultAccount.trim(); + const ids = listWecomAccountIds(cfg); + if (ids.includes(DEFAULT_ACCOUNT_ID)) return DEFAULT_ACCOUNT_ID; + return ids[0] ?? DEFAULT_ACCOUNT_ID; +} + +function resolveAccountConfig( + cfg: OpenClawConfig, + accountId: string, +): WecomAccountConfig | undefined { + const accounts = (cfg.channels?.wecom as WecomConfig | undefined)?.accounts; + if (!accounts || typeof accounts !== "object") return undefined; + return accounts[accountId] as WecomAccountConfig | undefined; +} + +function mergeWecomAccountConfig(cfg: OpenClawConfig, accountId: string): WecomAccountConfig { + const raw = (cfg.channels?.wecom ?? {}) as WecomConfig; + const { accounts: _ignored, defaultAccount: _ignored2, ...base } = raw; + const account = resolveAccountConfig(cfg, accountId) ?? {}; + return { ...base, ...account }; +} + +export function resolveWecomAccount(params: { + cfg: OpenClawConfig; + accountId?: string | null; +}): ResolvedWecomAccount { + const accountId = normalizeAccountId(params.accountId); + const baseEnabled = (params.cfg.channels?.wecom as WecomConfig | undefined)?.enabled !== false; + const merged = mergeWecomAccountConfig(params.cfg, accountId); + const enabled = baseEnabled && merged.enabled !== false; + + const token = merged.token?.trim() || undefined; + const encodingAESKey = merged.encodingAESKey?.trim() || undefined; + const receiveId = merged.receiveId?.trim() ?? ""; + const configured = Boolean(token && encodingAESKey); + + return { + accountId, + name: merged.name?.trim() || undefined, + enabled, + configured, + token, + encodingAESKey, + receiveId, + config: merged, + }; +} + +export function listEnabledWecomAccounts(cfg: OpenClawConfig): ResolvedWecomAccount[] { + return listWecomAccountIds(cfg) + .map((accountId) => resolveWecomAccount({ cfg, accountId })) + .filter((account) => account.enabled); +} diff --git a/wecom/src/agent/api-client.ts b/wecom/src/agent/api-client.ts new file mode 100644 index 00000000..fb07e4d5 --- /dev/null +++ b/wecom/src/agent/api-client.ts @@ -0,0 +1,336 @@ +/** + * WeCom Agent API 客户端 + * 管理 AccessToken 缓存和 API 调用 + */ + +import crypto from "node:crypto"; +import { API_ENDPOINTS, LIMITS } from "../types/constants.js"; +import type { ResolvedAgentAccount } from "../types/index.js"; +import { readResponseBodyAsBuffer, wecomFetch } from "../http.js"; +import { resolveWecomEgressProxyUrlFromNetwork } from "../config/index.js"; + +/** + * **TokenCache (AccessToken 缓存结构)** + * + * 用于缓存企业微信 API 调用所需的 AccessToken。 + * @property token 缓存的 Token 字符串 + * @property expiresAt 过期时间戳 (ms) + * @property refreshPromise 当前正在进行的刷新 Promise (防止并发刷新) + */ +type TokenCache = { + token: string; + expiresAt: number; + refreshPromise: Promise | null; +}; + +const tokenCaches = new Map(); + +/** + * **getAccessToken (获取 AccessToken)** + * + * 获取企业微信 API 调用所需的 AccessToken。 + * 具备自动缓存和过期刷新机制。 + * + * @param agent Agent 账号信息 + * @returns 有效的 AccessToken + */ +export async function getAccessToken(agent: ResolvedAgentAccount): Promise { + const cacheKey = `${agent.corpId}:${agent.agentId}`; + let cache = tokenCaches.get(cacheKey); + + if (!cache) { + cache = { token: "", expiresAt: 0, refreshPromise: null }; + tokenCaches.set(cacheKey, cache); + } + + const now = Date.now(); + if (cache.token && cache.expiresAt > now + LIMITS.TOKEN_REFRESH_BUFFER_MS) { + return cache.token; + } + + // 防止并发刷新 + if (cache.refreshPromise) { + return cache.refreshPromise; + } + + cache.refreshPromise = (async () => { + try { + const url = `${API_ENDPOINTS.GET_TOKEN}?corpid=${encodeURIComponent(agent.corpId)}&corpsecret=${encodeURIComponent(agent.corpSecret)}`; + const res = await wecomFetch(url, undefined, { proxyUrl: resolveWecomEgressProxyUrlFromNetwork(agent.network), timeoutMs: LIMITS.REQUEST_TIMEOUT_MS }); + const json = await res.json() as { access_token?: string; expires_in?: number; errcode?: number; errmsg?: string }; + + if (!json?.access_token) { + throw new Error(`gettoken failed: ${json?.errcode} ${json?.errmsg}`); + } + + cache!.token = json.access_token; + cache!.expiresAt = Date.now() + (json.expires_in ?? 7200) * 1000; + return cache!.token; + } finally { + cache!.refreshPromise = null; + } + })(); + + return cache.refreshPromise; +} + +/** + * **sendText (发送文本消息)** + * + * 调用 `message/send` (Agent) 或 `appchat/send` (群聊) 发送文本。 + * + * @param params.agent 发送方 Agent + * @param params.toUser 接收用户 ID (单聊可选,可与 toParty/toTag 同时使用) + * @param params.toParty 接收部门 ID (单聊可选) + * @param params.toTag 接收标签 ID (单聊可选) + * @param params.chatId 接收群 ID (群聊模式必填,互斥) + * @param params.text 消息内容 + */ +export async function sendText(params: { + agent: ResolvedAgentAccount; + toUser?: string; + toParty?: string; + toTag?: string; + chatId?: string; + text: string; +}): Promise { + const { agent, toUser, toParty, toTag, chatId, text } = params; + const token = await getAccessToken(agent); + + const useChat = Boolean(chatId); + const url = useChat + ? `${API_ENDPOINTS.SEND_APPCHAT}?access_token=${encodeURIComponent(token)}` + : `${API_ENDPOINTS.SEND_MESSAGE}?access_token=${encodeURIComponent(token)}`; + + const body = useChat + ? { chatid: chatId, msgtype: "text", text: { content: text } } + : { + touser: toUser, + toparty: toParty, + totag: toTag, + msgtype: "text", + agentid: agent.agentId, + text: { content: text } + }; + + const res = await wecomFetch(url, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }, { proxyUrl: resolveWecomEgressProxyUrlFromNetwork(agent.network), timeoutMs: LIMITS.REQUEST_TIMEOUT_MS }); + const json = await res.json() as { + errcode?: number; + errmsg?: string; + invaliduser?: string; + invalidparty?: string; + invalidtag?: string; + }; + + if (json?.errcode !== 0) { + throw new Error(`send failed: ${json?.errcode} ${json?.errmsg}`); + } + + if (json?.invaliduser || json?.invalidparty || json?.invalidtag) { + const details = [ + json.invaliduser ? `invaliduser=${json.invaliduser}` : "", + json.invalidparty ? `invalidparty=${json.invalidparty}` : "", + json.invalidtag ? `invalidtag=${json.invalidtag}` : "" + ].filter(Boolean).join(", "); + throw new Error(`send partial failure: ${details}`); + } +} + +/** + * **uploadMedia (上传媒体文件)** + * + * 上传临时素材到企业微信。 + * 素材有效期为 3 天。 + * + * @param params.type 媒体类型 (image, voice, video, file) + * @param params.buffer 文件二进制数据 + * @param params.filename 文件名 (需包含正确扩展名) + * @returns 媒体 ID (media_id) + */ +export async function uploadMedia(params: { + agent: ResolvedAgentAccount; + type: "image" | "voice" | "video" | "file"; + buffer: Buffer; + filename: string; +}): Promise { + const { agent, type, buffer, filename } = params; + const token = await getAccessToken(agent); + // 添加 debug=1 参数获取更多错误信息 + const url = `${API_ENDPOINTS.UPLOAD_MEDIA}?access_token=${encodeURIComponent(token)}&type=${encodeURIComponent(type)}&debug=1`; + + // DEBUG: 输出上传信息 + console.log(`[wecom-upload] Uploading media: type=${type}, filename=${filename}, size=${buffer.length} bytes`); + + // 手动构造 multipart/form-data 请求体 + // 企业微信要求包含 filename 和 filelength + const boundary = `----WebKitFormBoundary${crypto.randomBytes(16).toString("hex")}`; + + // 根据文件类型设置 Content-Type + const contentTypeMap: Record = { + jpg: "image/jpg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", + bmp: "image/bmp", amr: "voice/amr", mp4: "video/mp4", + }; + const ext = filename.split(".").pop()?.toLowerCase() || ""; + const fileContentType = contentTypeMap[ext] || "application/octet-stream"; + + // 构造 multipart body + const header = Buffer.from( + `--${boundary}\r\n` + + `Content-Disposition: form-data; name="media"; filename="${filename}"; filelength=${buffer.length}\r\n` + + `Content-Type: ${fileContentType}\r\n\r\n` + ); + const footer = Buffer.from(`\r\n--${boundary}--\r\n`); + const body = Buffer.concat([header, buffer, footer]); + + console.log(`[wecom-upload] Multipart body size=${body.length}, boundary=${boundary}, fileContentType=${fileContentType}`); + + const res = await wecomFetch(url, { + method: "POST", + headers: { + "Content-Type": `multipart/form-data; boundary=${boundary}`, + "Content-Length": String(body.length), + }, + body: body, + }, { proxyUrl: resolveWecomEgressProxyUrlFromNetwork(agent.network), timeoutMs: LIMITS.REQUEST_TIMEOUT_MS }); + const json = await res.json() as { media_id?: string; errcode?: number; errmsg?: string }; + + // DEBUG: 输出完整响应 + console.log(`[wecom-upload] Response:`, JSON.stringify(json)); + + if (!json?.media_id) { + throw new Error(`upload failed: ${json?.errcode} ${json?.errmsg}`); + } + return json.media_id; +} + +/** + * **sendMedia (发送媒体消息)** + * + * 发送图片、音频、视频或文件。需先通过 `uploadMedia` 获取 media_id。 + * + * @param params.agent 发送方 Agent + * @param params.toUser 接收用户 ID (单聊可选) + * @param params.toParty 接收部门 ID (单聊可选) + * @param params.toTag 接收标签 ID (单聊可选) + * @param params.chatId 接收群 ID (群聊模式必填) + * @param params.mediaId 媒体 ID + * @param params.mediaType 媒体类型 + * @param params.title 视频标题 (可选) + * @param params.description 视频描述 (可选) + */ +export async function sendMedia(params: { + agent: ResolvedAgentAccount; + toUser?: string; + toParty?: string; + toTag?: string; + chatId?: string; + mediaId: string; + mediaType: "image" | "voice" | "video" | "file"; + title?: string; + description?: string; +}): Promise { + const { agent, toUser, toParty, toTag, chatId, mediaId, mediaType, title, description } = params; + const token = await getAccessToken(agent); + + const useChat = Boolean(chatId); + const url = useChat + ? `${API_ENDPOINTS.SEND_APPCHAT}?access_token=${encodeURIComponent(token)}` + : `${API_ENDPOINTS.SEND_MESSAGE}?access_token=${encodeURIComponent(token)}`; + + const mediaPayload = mediaType === "video" + ? { media_id: mediaId, title: title ?? "Video", description: description ?? "" } + : { media_id: mediaId }; + + const body = useChat + ? { chatid: chatId, msgtype: mediaType, [mediaType]: mediaPayload } + : { + touser: toUser, + toparty: toParty, + totag: toTag, + msgtype: mediaType, + agentid: agent.agentId, + [mediaType]: mediaPayload + }; + + const res = await wecomFetch(url, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }, { proxyUrl: resolveWecomEgressProxyUrlFromNetwork(agent.network), timeoutMs: LIMITS.REQUEST_TIMEOUT_MS }); + const json = await res.json() as { + errcode?: number; + errmsg?: string; + invaliduser?: string; + invalidparty?: string; + invalidtag?: string; + }; + + if (json?.errcode !== 0) { + throw new Error(`send ${mediaType} failed: ${json?.errcode} ${json?.errmsg}`); + } + + if (json?.invaliduser || json?.invalidparty || json?.invalidtag) { + const details = [ + json.invaliduser ? `invaliduser=${json.invaliduser}` : "", + json.invalidparty ? `invalidparty=${json.invalidparty}` : "", + json.invalidtag ? `invalidtag=${json.invalidtag}` : "" + ].filter(Boolean).join(", "); + throw new Error(`send ${mediaType} partial failure: ${details}`); + } +} + +/** + * **downloadMedia (下载媒体文件)** + * + * 通过 media_id 从企业微信服务器下载临时素材。 + * + * @returns { buffer, contentType } + */ +export async function downloadMedia(params: { + agent: ResolvedAgentAccount; + mediaId: string; + maxBytes?: number; +}): Promise<{ buffer: Buffer; contentType: string; filename?: string }> { + const { agent, mediaId } = params; + const token = await getAccessToken(agent); + const url = `${API_ENDPOINTS.DOWNLOAD_MEDIA}?access_token=${encodeURIComponent(token)}&media_id=${encodeURIComponent(mediaId)}`; + + const res = await wecomFetch(url, undefined, { proxyUrl: resolveWecomEgressProxyUrlFromNetwork(agent.network), timeoutMs: LIMITS.REQUEST_TIMEOUT_MS }); + + if (!res.ok) { + throw new Error(`download failed: ${res.status}`); + } + + const contentType = res.headers.get("content-type") || "application/octet-stream"; + const disposition = res.headers.get("content-disposition") || ""; + const filename = (() => { + // 兼容:filename="a.md" / filename=a.md / filename*=UTF-8''a%2Eb.md + const mStar = disposition.match(/filename\*\s*=\s*([^;]+)/i); + if (mStar) { + const raw = mStar[1]!.trim().replace(/^"(.*)"$/, "$1"); + const parts = raw.split("''"); + const encoded = parts.length === 2 ? parts[1]! : raw; + try { + return decodeURIComponent(encoded); + } catch { + return encoded; + } + } + const m = disposition.match(/filename\s*=\s*([^;]+)/i); + if (!m) return undefined; + return m[1]!.trim().replace(/^"(.*)"$/, "$1") || undefined; + })(); + + // 检查是否返回了错误 JSON + if (contentType.includes("application/json")) { + const json = await res.json() as { errcode?: number; errmsg?: string }; + throw new Error(`download failed: ${json?.errcode} ${json?.errmsg}`); + } + + const buffer = await readResponseBodyAsBuffer(res, params.maxBytes); + return { buffer, contentType, filename }; +} diff --git a/wecom/src/agent/handler.ts b/wecom/src/agent/handler.ts new file mode 100644 index 00000000..b4294e11 --- /dev/null +++ b/wecom/src/agent/handler.ts @@ -0,0 +1,566 @@ +/** + * WeCom Agent Webhook 处理器 + * 处理 XML 格式回调 + */ + +import { pathToFileURL } from "node:url"; +import path from "node:path"; +import type { IncomingMessage, ServerResponse } from "node:http"; +import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk"; +import type { ResolvedAgentAccount } from "../types/index.js"; +import { LIMITS } from "../types/constants.js"; +import { decryptWecomEncrypted, verifyWecomSignature, computeWecomMsgSignature, encryptWecomPlaintext } from "../crypto/index.js"; +import { extractEncryptFromXml, buildEncryptedXmlResponse } from "../crypto/xml.js"; +import { parseXml, extractMsgType, extractFromUser, extractContent, extractChatId, extractMediaId, extractMsgId, extractFileName } from "../shared/xml-parser.js"; +import { sendText, downloadMedia } from "./api-client.js"; +import { getWecomRuntime } from "../runtime.js"; +import type { WecomAgentInboundMessage } from "../types/index.js"; +import { buildWecomUnauthorizedCommandPrompt, resolveWecomCommandAuthorization } from "../shared/command-auth.js"; +import { resolveWecomMediaMaxBytes } from "../config/index.js"; + +/** 错误提示信息 */ +const ERROR_HELP = ""; + +// Agent webhook 幂等去重池(防止企微回调重试导致重复回复) +// 注意:这是进程内内存去重,重启会清空;但足以覆盖企微的短周期重试。 +const RECENT_MSGID_TTL_MS = 10 * 60 * 1000; +const recentAgentMsgIds = new Map(); + +function rememberAgentMsgId(msgId: string): boolean { + const now = Date.now(); + const existing = recentAgentMsgIds.get(msgId); + if (existing && now - existing < RECENT_MSGID_TTL_MS) return false; + recentAgentMsgIds.set(msgId, now); + // 简单清理:只在写入时做一次线性 prune,避免无界增长 + for (const [k, ts] of recentAgentMsgIds) { + if (now - ts >= RECENT_MSGID_TTL_MS) recentAgentMsgIds.delete(k); + } + return true; +} + +function looksLikeTextFile(buffer: Buffer): boolean { + const sampleSize = Math.min(buffer.length, 4096); + if (sampleSize === 0) return true; + let bad = 0; + for (let i = 0; i < sampleSize; i++) { + const b = buffer[i]!; + const isWhitespace = b === 0x09 || b === 0x0a || b === 0x0d; // \t \n \r + const isPrintable = b >= 0x20 && b !== 0x7f; + if (!isWhitespace && !isPrintable) bad++; + } + // 非可打印字符占比太高,基本可判断为二进制 + return bad / sampleSize <= 0.02; +} + +function analyzeTextHeuristic(buffer: Buffer): { sampleSize: number; badCount: number; badRatio: number } { + const sampleSize = Math.min(buffer.length, 4096); + if (sampleSize === 0) return { sampleSize: 0, badCount: 0, badRatio: 0 }; + let badCount = 0; + for (let i = 0; i < sampleSize; i++) { + const b = buffer[i]!; + const isWhitespace = b === 0x09 || b === 0x0a || b === 0x0d; + const isPrintable = b >= 0x20 && b !== 0x7f; + if (!isWhitespace && !isPrintable) badCount++; + } + return { sampleSize, badCount, badRatio: badCount / sampleSize }; +} + +function previewHex(buffer: Buffer, maxBytes = 32): string { + const n = Math.min(buffer.length, maxBytes); + if (n <= 0) return ""; + return buffer + .subarray(0, n) + .toString("hex") + .replace(/(..)/g, "$1 ") + .trim(); +} + +function buildTextFilePreview(buffer: Buffer, maxChars: number): string | undefined { + if (!looksLikeTextFile(buffer)) return undefined; + const text = buffer.toString("utf8"); + if (!text.trim()) return undefined; + const truncated = text.length > maxChars ? `${text.slice(0, maxChars)}\n…(已截断)` : text; + return truncated; +} + +/** + * **AgentWebhookParams (Webhook 处理器参数)** + * + * 传递给 Agent Webhook 处理函数的上下文参数集合。 + * @property req Node.js 原始请求对象 + * @property res Node.js 原始响应对象 + * @property agent 解析后的 Agent 账号信息 + * @property config 全局插件配置 + * @property core OpenClaw 插件运行时 + * @property log 可选日志输出函数 + * @property error 可选错误输出函数 + */ +export type AgentWebhookParams = { + req: IncomingMessage; + res: ServerResponse; + agent: ResolvedAgentAccount; + config: OpenClawConfig; + core: PluginRuntime; + log?: (msg: string) => void; + error?: (msg: string) => void; +}; + +/** + * **resolveQueryParams (解析查询参数)** + * + * 辅助函数:从 IncomingMessage 中解析 URL 查询字符串,用于获取签名、时间戳等参数。 + */ +function resolveQueryParams(req: IncomingMessage): URLSearchParams { + const url = new URL(req.url ?? "/", "http://localhost"); + return url.searchParams; +} + +/** + * **readRawBody (读取原始请求体)** + * + * 异步读取 HTTP POST 请求的原始 BODY 数据(XML 字符串)。 + * 包含最大体积限制检查,防止内存溢出攻击。 + */ +async function readRawBody(req: IncomingMessage, maxSize: number = LIMITS.MAX_REQUEST_BODY_SIZE): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + let size = 0; + + req.on("data", (chunk: Buffer) => { + size += chunk.length; + if (size > maxSize) { + reject(new Error("Request body too large")); + req.destroy(); + return; + } + chunks.push(chunk); + }); + + req.on("end", () => { + resolve(Buffer.concat(chunks).toString("utf8")); + }); + + req.on("error", reject); + }); +} + +/** + * **handleUrlVerification (处理 URL 验证)** + * + * 处理企业微信 Agent 配置时的 GET 请求验证。 + * 流程: + * 1. 验证 msg_signature 签名。 + * 2. 解密 echostr 参数。 + * 3. 返回解密后的明文 echostr。 + */ +async function handleUrlVerification( + req: IncomingMessage, + res: ServerResponse, + agent: ResolvedAgentAccount, +): Promise { + const query = resolveQueryParams(req); + const timestamp = query.get("timestamp") ?? ""; + const nonce = query.get("nonce") ?? ""; + const echostr = query.get("echostr") ?? ""; + const signature = query.get("msg_signature") ?? ""; + const remote = req.socket?.remoteAddress ?? "unknown"; + + // 不输出敏感参数内容,仅输出存在性 + // 用于排查:是否有请求打到 /wecom/agent + // 以及是否带齐 timestamp/nonce/msg_signature/echostr + // eslint-disable-next-line no-unused-vars + const _debug = { remote, hasTimestamp: Boolean(timestamp), hasNonce: Boolean(nonce), hasSig: Boolean(signature), hasEchostr: Boolean(echostr) }; + + const valid = verifyWecomSignature({ + token: agent.token, + timestamp, + nonce, + encrypt: echostr, + signature, + }); + + if (!valid) { + res.statusCode = 401; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(`unauthorized - 签名验证失败,请检查 Token 配置${ERROR_HELP}`); + return true; + } + + try { + const plain = decryptWecomEncrypted({ + encodingAESKey: agent.encodingAESKey, + receiveId: agent.corpId, + encrypt: echostr, + }); + res.statusCode = 200; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(plain); + return true; + } catch { + res.statusCode = 400; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(`decrypt failed - 解密失败,请检查 EncodingAESKey 配置${ERROR_HELP}`); + return true; + } +} + +/** + * 处理消息回调 (POST) + */ +async function handleMessageCallback(params: AgentWebhookParams): Promise { + const { req, res, agent, config, core, log, error } = params; + + try { + log?.(`[wecom-agent] inbound: method=${req.method ?? "UNKNOWN"} remote=${req.socket?.remoteAddress ?? "unknown"}`); + const rawXml = await readRawBody(req); + log?.(`[wecom-agent] inbound: rawXmlBytes=${Buffer.byteLength(rawXml, "utf8")}`); + const encrypted = extractEncryptFromXml(rawXml); + log?.(`[wecom-agent] inbound: hasEncrypt=${Boolean(encrypted)} encryptLen=${encrypted ? String(encrypted).length : 0}`); + + const query = resolveQueryParams(req); + const timestamp = query.get("timestamp") ?? ""; + const nonce = query.get("nonce") ?? ""; + const signature = query.get("msg_signature") ?? ""; + log?.( + `[wecom-agent] inbound: query timestamp=${timestamp ? "yes" : "no"} nonce=${nonce ? "yes" : "no"} msg_signature=${signature ? "yes" : "no"}`, + ); + + // 验证签名 + const valid = verifyWecomSignature({ + token: agent.token, + timestamp, + nonce, + encrypt: encrypted, + signature, + }); + + if (!valid) { + error?.(`[wecom-agent] inbound: signature invalid`); + res.statusCode = 401; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(`unauthorized - 签名验证失败${ERROR_HELP}`); + return true; + } + + // 解密 + const decrypted = decryptWecomEncrypted({ + encodingAESKey: agent.encodingAESKey, + receiveId: agent.corpId, + encrypt: encrypted, + }); + log?.(`[wecom-agent] inbound: decryptedBytes=${Buffer.byteLength(decrypted, "utf8")}`); + + // 解析 XML + const msg = parseXml(decrypted); + const msgType = extractMsgType(msg); + const fromUser = extractFromUser(msg); + const chatId = extractChatId(msg); + const msgId = extractMsgId(msg); + if (msgId) { + const ok = rememberAgentMsgId(msgId); + if (!ok) { + log?.(`[wecom-agent] duplicate msgId=${msgId} from=${fromUser} chatId=${chatId ?? "N/A"} type=${msgType}; skipped`); + res.statusCode = 200; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end("success"); + return true; + } + } + const content = String(extractContent(msg) ?? ""); + + const preview = content.length > 100 ? `${content.slice(0, 100)}…` : content; + log?.(`[wecom-agent] ${msgType} from=${fromUser} chatId=${chatId ?? "N/A"} msgId=${msgId ?? "N/A"} content=${preview}`); + + // 先返回 success (Agent 模式使用 API 发送回复,不用被动回复) + res.statusCode = 200; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end("success"); + + // 异步处理消息 + processAgentMessage({ + agent, + config, + core, + fromUser, + chatId, + msgType, + content, + msg, + log, + error, + }).catch((err) => { + error?.(`[wecom-agent] process failed: ${String(err)}`); + }); + + return true; + } catch (err) { + error?.(`[wecom-agent] callback failed: ${String(err)}`); + res.statusCode = 400; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(`error - 回调处理失败${ERROR_HELP}`); + return true; + } +} + +/** + * **processAgentMessage (处理 Agent 消息)** + * + * 异步处理解密后的消息内容,并触发 OpenClaw Agent。 + * 流程: + * 1. 路由解析:根据 userid或群ID 确定 Agent 路由。 + * 2. 媒体处理:如果是图片/文件等,下载资源。 + * 3. 上下文构建:创建 Inbound Context。 + * 4. 会话记录:更新 Session 状态。 + * 5. 调度回复:将 Agent 的响应通过 `api-client` 发送回企业微信。 + */ +async function processAgentMessage(params: { + agent: ResolvedAgentAccount; + config: OpenClawConfig; + core: PluginRuntime; + fromUser: string; + chatId?: string; + msgType: string; + content: string; + msg: WecomAgentInboundMessage; + log?: (msg: string) => void; + error?: (msg: string) => void; +}): Promise { + const { agent, config, core, fromUser, chatId, content, msg, msgType, log, error } = params; + + const isGroup = Boolean(chatId); + const peerId = isGroup ? chatId! : fromUser; + const mediaMaxBytes = resolveWecomMediaMaxBytes(config); + + // 处理媒体文件 + const attachments: any[] = []; // TODO: define specific type + let finalContent = content; + let mediaPath: string | undefined; + let mediaType: string | undefined; + + if (["image", "voice", "video", "file"].includes(msgType)) { + const mediaId = extractMediaId(msg); + if (mediaId) { + try { + log?.(`[wecom-agent] downloading media: ${mediaId} (${msgType})`); + const { buffer, contentType, filename: headerFileName } = await downloadMedia({ agent, mediaId, maxBytes: mediaMaxBytes }); + const xmlFileName = extractFileName(msg); + const originalFileName = (xmlFileName || headerFileName || `${mediaId}.bin`).trim(); + const heuristic = analyzeTextHeuristic(buffer); + + // 推断文件名后缀 + const extMap: Record = { + "image/jpeg": "jpg", "image/png": "png", "image/gif": "gif", + "audio/amr": "amr", "audio/speex": "speex", "video/mp4": "mp4", + }; + const textPreview = msgType === "file" ? buildTextFilePreview(buffer, 12_000) : undefined; + const looksText = Boolean(textPreview); + const originalExt = path.extname(originalFileName).toLowerCase(); + const normalizedContentType = + looksText && originalExt === ".md" ? "text/markdown" : + looksText && (!contentType || contentType === "application/octet-stream") + ? "text/plain; charset=utf-8" + : contentType; + + const ext = extMap[normalizedContentType] || (looksText ? "txt" : "bin"); + const filename = `${mediaId}.${ext}`; + + log?.( + `[wecom-agent] file meta: msgType=${msgType} mediaId=${mediaId} size=${buffer.length} maxBytes=${mediaMaxBytes} ` + + `contentType=${contentType} normalizedContentType=${normalizedContentType} originalFileName=${originalFileName} ` + + `xmlFileName=${xmlFileName ?? "N/A"} headerFileName=${headerFileName ?? "N/A"} ` + + `textHeuristic(sample=${heuristic.sampleSize}, bad=${heuristic.badCount}, ratio=${heuristic.badRatio.toFixed(4)}) ` + + `headHex="${previewHex(buffer)}"`, + ); + + // 使用 Core SDK 保存媒体文件 + const saved = await core.channel.media.saveMediaBuffer( + buffer, + normalizedContentType, + "inbound", // context/scope + mediaMaxBytes, // limit + originalFileName + ); + + log?.(`[wecom-agent] media saved to: ${saved.path}`); + mediaPath = saved.path; + mediaType = normalizedContentType; + + // 构建附件 + attachments.push({ + name: originalFileName, + mimeType: normalizedContentType, + url: pathToFileURL(saved.path).href, // 使用跨平台安全的文件 URL + }); + + // 更新文本提示 + if (textPreview) { + finalContent = [ + content, + "", + "文件内容预览:", + "```", + textPreview, + "```", + `(已下载 ${buffer.length} 字节)`, + ].join("\n"); + } else { + if (msgType === "file") { + finalContent = [ + content, + "", + `已收到文件:${originalFileName}`, + `文件类型:${normalizedContentType || contentType || "未知"}`, + "提示:当前仅对文本/Markdown/JSON/CSV/HTML/PDF(可选)做内容抽取;其他二进制格式请转为 PDF 或复制文本内容。", + `(已下载 ${buffer.length} 字节)`, + ].join("\n"); + } else { + finalContent = `${content} (已下载 ${buffer.length} 字节)`; + } + } + log?.(`[wecom-agent] file preview: enabled=${looksText} finalContentLen=${finalContent.length} attachments=${attachments.length}`); + } catch (err) { + error?.(`[wecom-agent] media processing failed: ${String(err)}`); + finalContent = [ + content, + "", + `媒体处理失败:${String(err)}`, + `提示:可在 OpenClaw 配置中提高 channels.wecom.media.maxBytes(当前=${mediaMaxBytes})`, + `例如:openclaw config set channels.wecom.media.maxBytes ${50 * 1024 * 1024}`, + ].join("\n"); + } + } else { + const keys = Object.keys((msg as unknown as Record) ?? {}).slice(0, 50).join(","); + error?.(`[wecom-agent] mediaId not found for ${msgType}; keys=${keys}`); + } + } + + // 解析路由 + const route = core.channel.routing.resolveAgentRoute({ + cfg: config, + channel: "wecom", + accountId: agent.accountId, + peer: { kind: isGroup ? "group" : "dm", id: peerId }, + }); + + // 构建上下文 + const fromLabel = isGroup ? `group:${peerId}` : `user:${fromUser}`; + const storePath = core.channel.session.resolveStorePath(config.session?.store, { + agentId: route.agentId, + }); + const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(config); + const previousTimestamp = core.channel.session.readSessionUpdatedAt({ + storePath, + sessionKey: route.sessionKey, + }); + const body = core.channel.reply.formatAgentEnvelope({ + channel: "WeCom", + from: fromLabel, + previousTimestamp, + envelope: envelopeOptions, + body: finalContent, + }); + + const authz = await resolveWecomCommandAuthorization({ + core, + cfg: config, + // Agent 门禁应读取 channels.wecom.agent.dm(即 agent.config.dm),而不是 channels.wecom.dm(不存在) + accountConfig: agent.config as any, + rawBody: finalContent, + senderUserId: fromUser, + }); + log?.(`[wecom-agent] authz: dmPolicy=${authz.dmPolicy} shouldCompute=${authz.shouldComputeAuth} sender=${fromUser.toLowerCase()} senderAllowed=${authz.senderAllowed} authorizerConfigured=${authz.authorizerConfigured} commandAuthorized=${String(authz.commandAuthorized)}`); + + // 命令门禁:未授权时必须明确回复(Agent 侧用私信提示) + if (authz.shouldComputeAuth && authz.commandAuthorized !== true) { + const prompt = buildWecomUnauthorizedCommandPrompt({ senderUserId: fromUser, dmPolicy: authz.dmPolicy, scope: "agent" }); + try { + await sendText({ agent, toUser: fromUser, chatId: undefined, text: prompt }); + log?.(`[wecom-agent] unauthorized command: replied via DM to ${fromUser}`); + } catch (err: unknown) { + error?.(`[wecom-agent] unauthorized command reply failed: ${String(err)}`); + } + return; + } + + const ctxPayload = core.channel.reply.finalizeInboundContext({ + Body: body, + RawBody: finalContent, + CommandBody: finalContent, + Attachments: attachments.length > 0 ? attachments : undefined, + From: isGroup ? `wecom:group:${peerId}` : `wecom:${fromUser}`, + To: `wecom:${peerId}`, + SessionKey: route.sessionKey, + AccountId: route.accountId, + ChatType: isGroup ? "group" : "direct", + ConversationLabel: fromLabel, + SenderName: fromUser, + SenderId: fromUser, + Provider: "wecom", + Surface: "wecom", + OriginatingChannel: "wecom", + // 标记为 Agent 会话的回复路由目标,避免与 Bot 会话混淆: + // - 用于让 /new /reset 这类命令回执不被 Bot 侧策略拦截 + // - 群聊场景也统一路由为私信触发者(与 deliver 策略一致) + OriginatingTo: `wecom-agent:${fromUser}`, + CommandAuthorized: authz.commandAuthorized ?? true, + MediaPath: mediaPath, + MediaType: mediaType, + MediaUrl: mediaPath, + }); + + // 记录会话 + await core.channel.session.recordInboundSession({ + storePath, + sessionKey: ctxPayload.SessionKey ?? route.sessionKey, + ctx: ctxPayload, + onRecordError: (err: unknown) => { + error?.(`[wecom-agent] session record failed: ${String(err)}`); + }, + }); + + // 调度回复 + await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({ + ctx: ctxPayload, + cfg: config, + dispatcherOptions: { + deliver: async (payload: { text?: string }, info: { kind: string }) => { + const text = payload.text ?? ""; + if (!text) return; + + try { + // 统一策略:Agent 模式在群聊场景默认只私信触发者(避免 wr/wc chatId 86008) + await sendText({ agent, toUser: fromUser, chatId: undefined, text }); + log?.(`[wecom-agent] reply delivered (${info.kind}) to ${fromUser}`); + } catch (err: unknown) { + error?.(`[wecom-agent] reply failed: ${String(err)}`); + } + }, + onError: (err: unknown, info: { kind: string }) => { + error?.(`[wecom-agent] ${info.kind} reply error: ${String(err)}`); + }, + }, + replyOptions: { + disableBlockStreaming: true, + }, + }); +} + +/** + * **handleAgentWebhook (Agent Webhook 入口)** + * + * 统一处理 Agent 模式的 Webhook 请求。 + * 根据 HTTP 方法分发到 URL 验证 (GET) 或 消息处理 (POST)。 + */ +export async function handleAgentWebhook(params: AgentWebhookParams): Promise { + const { req } = params; + + if (req.method === "GET") { + return handleUrlVerification(req, params.res, params.agent); + } + + if (req.method === "POST") { + return handleMessageCallback(params); + } + + return false; +} diff --git a/wecom/src/agent/index.ts b/wecom/src/agent/index.ts new file mode 100644 index 00000000..c5dc879a --- /dev/null +++ b/wecom/src/agent/index.ts @@ -0,0 +1,12 @@ +/** + * WeCom Agent 模块导出 + */ + +export { handleAgentWebhook, type AgentWebhookParams } from "./handler.js"; +export { + getAccessToken, + sendText, + uploadMedia, + sendMedia, + downloadMedia, +} from "./api-client.js"; diff --git a/wecom/src/channel.ts b/wecom/src/channel.ts new file mode 100644 index 00000000..8f532a6f --- /dev/null +++ b/wecom/src/channel.ts @@ -0,0 +1,259 @@ +import type { + ChannelAccountSnapshot, + ChannelPlugin, + OpenClawConfig, +} from "openclaw/plugin-sdk"; +import { + buildChannelConfigSchema, + DEFAULT_ACCOUNT_ID, + setAccountEnabledInConfigSection, +} from "openclaw/plugin-sdk"; + +import { resolveWecomAccounts } from "./config/index.js"; +import { WecomConfigSchema } from "./config/index.js"; +import type { ResolvedAgentAccount, ResolvedBotAccount } from "./types/index.js"; +import { registerAgentWebhookTarget, registerWecomWebhookTarget } from "./monitor.js"; +import { wecomOnboardingAdapter } from "./onboarding.js"; +import { wecomOutbound } from "./outbound.js"; + +const meta = { + id: "wecom", + label: "WeCom", + selectionLabel: "WeCom (plugin)", + docsPath: "/channels/wecom", + docsLabel: "wecom", + blurb: "Enterprise WeCom intelligent bot (API mode) via encrypted webhooks + passive replies.", + aliases: ["wechatwork", "wework", "qywx", "企微", "企业微信"], + order: 85, + quickstartAllowFrom: true, +}; + +function normalizeWecomMessagingTarget(raw: string): string | undefined { + const trimmed = raw.trim(); + if (!trimmed) return undefined; + return trimmed.replace(/^(wecom-agent|wecom|wechatwork|wework|qywx):/i, "").trim() || undefined; +} + +type ResolvedWecomAccount = { + accountId: string; + name?: string; + enabled: boolean; + configured: boolean; + bot?: ResolvedBotAccount; + agent?: ResolvedAgentAccount; +}; + +/** + * **resolveWecomAccount (解析账号配置)** + * + * 从全局配置中解析出 WeCom 渠道的配置状态。 + * 兼容 Bot 和 Agent 两种模式的配置检查。 + */ +function resolveWecomAccount(cfg: OpenClawConfig): ResolvedWecomAccount { + const enabled = (cfg.channels?.wecom as { enabled?: boolean } | undefined)?.enabled !== false; + const accounts = resolveWecomAccounts(cfg); + const bot = accounts.bot; + const agent = accounts.agent; + const configured = Boolean(bot?.configured || agent?.configured); + return { + accountId: DEFAULT_ACCOUNT_ID, + enabled, + configured, + bot, + agent, + }; +} + +export const wecomPlugin: ChannelPlugin = { + id: "wecom", + meta, + onboarding: wecomOnboardingAdapter, + capabilities: { + chatTypes: ["direct", "group"], + media: true, + reactions: false, + threads: false, + polls: false, + nativeCommands: false, + blockStreaming: true, + }, + reload: { configPrefixes: ["channels.wecom"] }, + configSchema: buildChannelConfigSchema(WecomConfigSchema), + config: { + listAccountIds: () => [DEFAULT_ACCOUNT_ID], + resolveAccount: (cfg) => resolveWecomAccount(cfg as OpenClawConfig), + defaultAccountId: () => DEFAULT_ACCOUNT_ID, + setAccountEnabled: ({ cfg, accountId, enabled }) => + setAccountEnabledInConfigSection({ + cfg: cfg as OpenClawConfig, + sectionKey: "wecom", + accountId, + enabled, + allowTopLevel: true, + }), + deleteAccount: ({ cfg }) => { + const next = { ...(cfg as OpenClawConfig) }; + if (next.channels?.wecom) { + const channels = { ...(next.channels ?? {}) } as Record; + delete (channels as Record).wecom; + return { ...next, channels } as OpenClawConfig; + } + return next; + }, + isConfigured: (account) => account.configured, + describeAccount: (account): ChannelAccountSnapshot => ({ + accountId: account.accountId, + name: account.name, + enabled: account.enabled, + configured: account.configured, + webhookPath: account.bot?.config ? "/wecom/bot" : account.agent?.config ? "/wecom/agent" : "/wecom", + }), + resolveAllowFrom: ({ cfg, accountId }) => { + const account = resolveWecomAccount(cfg as OpenClawConfig); + // 与其他渠道保持一致:直接返回 allowFrom,空则允许所有人 + const allowFrom = account.agent?.config.dm?.allowFrom ?? account.bot?.config.dm?.allowFrom ?? []; + return allowFrom.map((entry) => String(entry)); + }, + formatAllowFrom: ({ allowFrom }) => + allowFrom + .map((entry) => String(entry).trim()) + .filter(Boolean) + .map((entry) => entry.toLowerCase()), + }, + // security 配置在 WeCom 中不需要,框架会通过 resolveAllowFrom 自动判断 + groups: { + // WeCom bots are usually mention-gated by the platform in groups already. + resolveRequireMention: () => true, + }, + threading: { + resolveReplyToMode: () => "off", + }, + messaging: { + normalizeTarget: normalizeWecomMessagingTarget, + targetResolver: { + looksLikeId: (raw) => Boolean(raw.trim()), + hint: "", + }, + }, + outbound: { + ...wecomOutbound, + }, + status: { + defaultRuntime: { + accountId: DEFAULT_ACCOUNT_ID, + running: false, + lastStartAt: null, + lastStopAt: null, + lastError: null, + }, + buildChannelSummary: ({ snapshot }) => ({ + configured: snapshot.configured ?? false, + running: snapshot.running ?? false, + webhookPath: snapshot.webhookPath ?? null, + lastStartAt: snapshot.lastStartAt ?? null, + lastStopAt: snapshot.lastStopAt ?? null, + lastError: snapshot.lastError ?? null, + lastInboundAt: snapshot.lastInboundAt ?? null, + lastOutboundAt: snapshot.lastOutboundAt ?? null, + probe: snapshot.probe, + lastProbeAt: snapshot.lastProbeAt ?? null, + }), + probeAccount: async () => ({ ok: true }), + buildAccountSnapshot: ({ account, runtime }) => ({ + accountId: account.accountId, + name: account.name, + enabled: account.enabled, + configured: account.configured, + webhookPath: account.bot?.config ? "/wecom/bot" : account.agent?.config ? "/wecom/agent" : "/wecom", + running: runtime?.running ?? false, + lastStartAt: runtime?.lastStartAt ?? null, + lastStopAt: runtime?.lastStopAt ?? null, + lastError: runtime?.lastError ?? null, + lastInboundAt: runtime?.lastInboundAt ?? null, + lastOutboundAt: runtime?.lastOutboundAt ?? null, + dmPolicy: account.bot?.config.dm?.policy ?? "pairing", + }), + }, + gateway: { + /** + * **startAccount (启动账号)** + * + * 插件生命周期:启动 + * 职责: + * 1. 检查配置是否有效。 + * 2. 注册 Bot Webhook (`/wecom`, `/wecom/bot`)。 + * 3. 注册 Agent Webhook (`/wecom/agent`)。 + * 4. 更新运行时状态 (Running)。 + * 5. 返回停止回调 (Cleanup)。 + */ + startAccount: async (ctx) => { + const account = ctx.account; + const bot = account.bot; + const agent = account.agent; + const botConfigured = Boolean(bot?.configured); + const agentConfigured = Boolean(agent?.configured); + + if (!botConfigured && !agentConfigured) { + ctx.log?.warn(`[${account.accountId}] wecom not configured; skipping webhook registration`); + ctx.setStatus({ accountId: account.accountId, running: false, configured: false }); + return { stop: () => { } }; + } + + const unregisters: Array<() => void> = []; + if (bot && botConfigured) { + for (const path of ["/wecom", "/wecom/bot"]) { + unregisters.push( + registerWecomWebhookTarget({ + account: bot, + config: ctx.cfg as OpenClawConfig, + runtime: ctx.runtime, + // The HTTP handler resolves the active PluginRuntime via getWecomRuntime(). + // The stored target only needs to be decrypt/verify-capable. + core: ({} as unknown) as any, + path, + statusSink: (patch) => ctx.setStatus({ accountId: ctx.accountId, ...patch }), + }), + ); + } + ctx.log?.info(`[${account.accountId}] wecom bot webhook registered at /wecom and /wecom/bot`); + } + if (agent && agentConfigured) { + unregisters.push( + registerAgentWebhookTarget({ + agent, + config: ctx.cfg as OpenClawConfig, + runtime: ctx.runtime, + }), + ); + ctx.log?.info(`[${account.accountId}] wecom agent webhook registered at /wecom/agent`); + } + + ctx.setStatus({ + accountId: account.accountId, + running: true, + configured: true, + webhookPath: botConfigured ? "/wecom/bot" : "/wecom/agent", + lastStartAt: Date.now(), + }); + return { + stop: () => { + for (const unregister of unregisters) { + unregister(); + } + ctx.setStatus({ + accountId: account.accountId, + running: false, + lastStopAt: Date.now(), + }); + }, + }; + }, + stopAccount: async (ctx) => { + ctx.setStatus({ + accountId: ctx.account.accountId, + running: false, + lastStopAt: Date.now(), + }); + }, + }, +}; diff --git a/wecom/src/config-schema.ts b/wecom/src/config-schema.ts new file mode 100644 index 00000000..62183026 --- /dev/null +++ b/wecom/src/config-schema.ts @@ -0,0 +1,41 @@ +import { z } from "zod"; + +const allowFromEntry = z.union([z.string(), z.number()]); + +const dmSchema = z + .object({ + enabled: z.boolean().optional(), + policy: z.enum(["pairing", "allowlist", "open", "disabled"]).optional(), + allowFrom: z.array(allowFromEntry).optional(), + }) + .optional(); + +export const WecomConfigSchema = z.object({ + name: z.string().optional(), + enabled: z.boolean().optional(), + + webhookPath: z.string().optional(), + token: z.string().optional(), + encodingAESKey: z.string().optional(), + receiveId: z.string().optional(), + + streamPlaceholderContent: z.string().optional(), + debounceMs: z.number().optional(), + + welcomeText: z.string().optional(), + dm: dmSchema, + + defaultAccount: z.string().optional(), + accounts: z.object({}).catchall(z.object({ + name: z.string().optional(), + enabled: z.boolean().optional(), + webhookPath: z.string().optional(), + token: z.string().optional(), + encodingAESKey: z.string().optional(), + receiveId: z.string().optional(), + streamPlaceholderContent: z.string().optional(), + debounceMs: z.number().optional(), + welcomeText: z.string().optional(), + dm: dmSchema, + })).optional(), +}); diff --git a/wecom/src/config/accounts.ts b/wecom/src/config/accounts.ts new file mode 100644 index 00000000..615154c5 --- /dev/null +++ b/wecom/src/config/accounts.ts @@ -0,0 +1,99 @@ +/** + * WeCom 账号解析与模式检测 + */ + +import type { OpenClawConfig } from "openclaw/plugin-sdk"; +import type { + WecomConfig, + WecomBotConfig, + WecomAgentConfig, + WecomNetworkConfig, + ResolvedBotAccount, + ResolvedAgentAccount, + ResolvedMode, + ResolvedWecomAccounts, +} from "../types/index.js"; + +const DEFAULT_ACCOUNT_ID = "default"; + +/** + * 检测配置中启用的模式 + */ +export function detectMode(config: WecomConfig | undefined): ResolvedMode { + if (!config) return { bot: false, agent: false }; + + const botConfigured = Boolean( + config.bot?.token && config.bot?.encodingAESKey + ); + const agentConfigured = Boolean( + config.agent?.corpId && config.agent?.corpSecret && config.agent?.agentId && + config.agent?.token && config.agent?.encodingAESKey + ); + + return { bot: botConfigured, agent: agentConfigured }; +} + +/** + * 解析 Bot 模式账号 + */ +function resolveBotAccount(config: WecomBotConfig): ResolvedBotAccount { + return { + accountId: DEFAULT_ACCOUNT_ID, + enabled: true, + configured: Boolean(config.token && config.encodingAESKey), + token: config.token, + encodingAESKey: config.encodingAESKey, + receiveId: config.receiveId?.trim() ?? "", + config, + }; +} + +/** + * 解析 Agent 模式账号 + */ +function resolveAgentAccount(config: WecomAgentConfig, network?: WecomNetworkConfig): ResolvedAgentAccount { + const agentIdRaw = config.agentId; + const agentId = typeof agentIdRaw === "number" ? agentIdRaw : Number(agentIdRaw); + + return { + accountId: DEFAULT_ACCOUNT_ID, + enabled: true, + configured: Boolean( + config.corpId && config.corpSecret && agentId && + config.token && config.encodingAESKey + ), + corpId: config.corpId, + corpSecret: config.corpSecret, + agentId, + token: config.token, + encodingAESKey: config.encodingAESKey, + config, + network, + }; +} + +/** + * 解析 WeCom 账号 (双模式) + */ +export function resolveWecomAccounts(cfg: OpenClawConfig): ResolvedWecomAccounts { + const wecom = cfg.channels?.wecom as WecomConfig | undefined; + + if (!wecom || wecom.enabled === false) { + return {}; + } + + const mode = detectMode(wecom); + + return { + bot: mode.bot && wecom.bot ? { ...resolveBotAccount(wecom.bot), network: wecom.network } : undefined, + agent: mode.agent && wecom.agent ? resolveAgentAccount(wecom.agent, wecom.network) : undefined, + }; +} + +/** + * 检查是否有任何模式启用 + */ +export function isWecomEnabled(cfg: OpenClawConfig): boolean { + const accounts = resolveWecomAccounts(cfg); + return Boolean(accounts.bot?.configured || accounts.agent?.configured); +} diff --git a/wecom/src/config/index.ts b/wecom/src/config/index.ts new file mode 100644 index 00000000..3a1f17c8 --- /dev/null +++ b/wecom/src/config/index.ts @@ -0,0 +1,12 @@ +/** + * WeCom 配置模块导出 + */ + +export { WecomConfigSchema, type WecomConfigInput } from "./schema.js"; +export { + detectMode, + resolveWecomAccounts, + isWecomEnabled, +} from "./accounts.js"; +export { resolveWecomEgressProxyUrl, resolveWecomEgressProxyUrlFromNetwork } from "./network.js"; +export { DEFAULT_WECOM_MEDIA_MAX_BYTES, resolveWecomMediaMaxBytes } from "./media.js"; diff --git a/wecom/src/config/media.ts b/wecom/src/config/media.ts new file mode 100644 index 00000000..ac1afd5c --- /dev/null +++ b/wecom/src/config/media.ts @@ -0,0 +1,14 @@ +import type { OpenClawConfig } from "openclaw/plugin-sdk"; + +// 默认给一个相对“够用”的上限(80MB),避免视频/较大文件频繁触发失败。 +// 仍保留上限以防止恶意大文件把进程内存打爆(下载实现会读入内存再保存)。 +export const DEFAULT_WECOM_MEDIA_MAX_BYTES = 80 * 1024 * 1024; + +export function resolveWecomMediaMaxBytes(cfg: OpenClawConfig): number { + const raw = (cfg.channels?.wecom as any)?.media?.maxBytes; + const n = typeof raw === "number" ? raw : Number(raw); + if (Number.isFinite(n) && n > 0) { + return Math.floor(n); + } + return DEFAULT_WECOM_MEDIA_MAX_BYTES; +} diff --git a/wecom/src/config/network.ts b/wecom/src/config/network.ts new file mode 100644 index 00000000..0534c763 --- /dev/null +++ b/wecom/src/config/network.ts @@ -0,0 +1,16 @@ +import type { OpenClawConfig } from "openclaw/plugin-sdk"; + +import type { WecomConfig, WecomNetworkConfig } from "../types/index.js"; + +export function resolveWecomEgressProxyUrlFromNetwork(network?: WecomNetworkConfig): string | undefined { + const env = (process.env.OPENCLAW_WECOM_EGRESS_PROXY_URL ?? process.env.WECOM_EGRESS_PROXY_URL ?? "").trim(); + if (env) return env; + + const fromCfg = network?.egressProxyUrl?.trim() ?? ""; + return fromCfg || undefined; +} + +export function resolveWecomEgressProxyUrl(cfg: OpenClawConfig): string | undefined { + const wecom = cfg.channels?.wecom as WecomConfig | undefined; + return resolveWecomEgressProxyUrlFromNetwork(wecom?.network); +} diff --git a/wecom/src/config/schema.ts b/wecom/src/config/schema.ts new file mode 100644 index 00000000..02810682 --- /dev/null +++ b/wecom/src/config/schema.ts @@ -0,0 +1,104 @@ +/** + * WeCom 配置 Schema (Zod) + */ + +import { z } from "zod"; + +/** + * **dmSchema (单聊配置)** + * + * 控制单聊行为(如允许名单、策略)。 + * @property enabled - 是否启用单聊 [默认: true] + * @property policy - 访问策略: "pairing" (需配对, 默认), "allowlist" (仅在名单), "open" (所有人), "disabled" (禁用) + * @property allowFrom - 允许的用户ID或群ID列表 (仅当 policy="allowlist" 时生效) + */ +const dmSchema = z.object({ + enabled: z.boolean().optional(), + policy: z.enum(["pairing", "allowlist", "open", "disabled"]).optional(), + allowFrom: z.array(z.union([z.string(), z.number()])).optional(), +}).optional(); + +/** + * **mediaSchema (媒体处理配置)** + * + * 控制媒体文件的下载和缓存行为。 + * @property tempDir - 临时文件下载目录 + * @property retentionHours - 临时文件保留时间(小时) + * @property cleanupOnStart - 启动时是否自动清理旧文件 + * @property maxBytes - 允许下载的最大字节数 + */ +const mediaSchema = z.object({ + tempDir: z.string().optional(), + retentionHours: z.number().optional(), + cleanupOnStart: z.boolean().optional(), + maxBytes: z.number().optional(), +}).optional(); + +/** + * **networkSchema (网络配置)** + * + * 控制 HTTP 请求行为,特别是出站代理。 + * @property timeoutMs - 请求超时时间 (毫秒) + * @property retries - 重试次数 + * @property retryDelayMs - 重试间隔 (毫秒) + * @property egressProxyUrl - 出站 HTTP 代理 (如 "http://127.0.0.1:7890") + */ +const networkSchema = z.object({ + timeoutMs: z.number().optional(), + retries: z.number().optional(), + retryDelayMs: z.number().optional(), + egressProxyUrl: z.string().optional(), +}).optional(); + +/** + * **botSchema (Bot 模式配置)** + * + * 用于配置企业微信内部机器人 (Webhook 模式)。 + * @property token - 企业微信后台设置的 Token + * @property encodingAESKey - 企业微信后台设置的 EncodingAESKey + * @property receiveId - (可选) 接收者ID,通常不用填 + * @property streamPlaceholderContent - (可选) 流式响应中的占位符,默认为 "Thinking..."或空 + * @property welcomeText - (可选) 用户首次对话时的欢迎语 + * @property dm - 单聊策略覆盖配置 + */ +const botSchema = z.object({ + token: z.string(), + encodingAESKey: z.string(), + receiveId: z.string().optional(), + streamPlaceholderContent: z.string().optional(), + welcomeText: z.string().optional(), + dm: dmSchema, +}).optional(); + +/** + * **agentSchema (Agent 模式配置)** + * + * 用于配置企业微信自建应用 (Agent)。 + * @property corpId - 企业 ID (CorpID) + * @property corpSecret - 应用 Secret + * @property agentId - 应用 AgentId (数字) + * @property token - 回调配置 Token + * @property encodingAESKey - 回调配置 EncodingAESKey + * @property welcomeText - (可选) 欢迎语 + * @property dm - 单聊策略覆盖配置 + */ +const agentSchema = z.object({ + corpId: z.string(), + corpSecret: z.string(), + agentId: z.union([z.string(), z.number()]), + token: z.string(), + encodingAESKey: z.string(), + welcomeText: z.string().optional(), + dm: dmSchema, +}).optional(); + +/** 顶层 WeCom 配置 Schema */ +export const WecomConfigSchema = z.object({ + enabled: z.boolean().optional(), + bot: botSchema, + agent: agentSchema, + media: mediaSchema, + network: networkSchema, +}); + +export type WecomConfigInput = z.infer; diff --git a/wecom/src/crypto.test.ts b/wecom/src/crypto.test.ts new file mode 100644 index 00000000..c9495da3 --- /dev/null +++ b/wecom/src/crypto.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; + +import { computeWecomMsgSignature, decryptWecomEncrypted, encryptWecomPlaintext } from "./crypto.js"; + +describe("wecom crypto", () => { + it("round-trips plaintext", () => { + const encodingAESKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"; // 43 chars base64 (plus '=' padding) + const plaintext = JSON.stringify({ hello: "world" }); + const encrypt = encryptWecomPlaintext({ encodingAESKey, receiveId: "", plaintext }); + const decrypted = decryptWecomEncrypted({ encodingAESKey, receiveId: "", encrypt }); + expect(decrypted).toBe(plaintext); + }); + + it("pads correctly when raw length is a multiple of 32", () => { + const encodingAESKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"; + // raw length = 20 + plaintext.length + receiveId.length; choose plaintext length % 32 === 12 + const plaintext = "x".repeat(12); + const encrypt = encryptWecomPlaintext({ encodingAESKey, receiveId: "", plaintext }); + const decrypted = decryptWecomEncrypted({ encodingAESKey, receiveId: "", encrypt }); + expect(decrypted).toBe(plaintext); + }); + + it("computes sha1 msg signature", () => { + const sig = computeWecomMsgSignature({ + token: "token", + timestamp: "123", + nonce: "456", + encrypt: "ENCRYPT", + }); + expect(sig).toMatch(/^[a-f0-9]{40}$/); + }); +}); diff --git a/wecom/src/crypto.ts b/wecom/src/crypto.ts new file mode 100644 index 00000000..ae9c71e2 --- /dev/null +++ b/wecom/src/crypto.ts @@ -0,0 +1,176 @@ +import crypto from "node:crypto"; + +/** + * **decodeEncodingAESKey (解码 AES Key)** + * + * 将企业微信配置的 Base64 编码的 AES Key 解码为 Buffer。 + * 包含补全 Padding 和长度校验 (必须32字节)。 + */ +export function decodeEncodingAESKey(encodingAESKey: string): Buffer { + const trimmed = encodingAESKey.trim(); + if (!trimmed) throw new Error("encodingAESKey missing"); + const withPadding = trimmed.endsWith("=") ? trimmed : `${trimmed}=`; + const key = Buffer.from(withPadding, "base64"); + if (key.length !== 32) { + throw new Error(`invalid encodingAESKey (expected 32 bytes after base64 decode, got ${key.length})`); + } + return key; +} + +// WeCom uses PKCS#7 padding with a block size of 32 bytes (not AES's 16-byte block). +// This is compatible with AES-CBC as 32 is a multiple of 16, but it requires manual padding/unpadding. +export const WECOM_PKCS7_BLOCK_SIZE = 32; + +function pkcs7Pad(buf: Buffer, blockSize: number): Buffer { + const mod = buf.length % blockSize; + const pad = mod === 0 ? blockSize : blockSize - mod; + const padByte = Buffer.from([pad]); + return Buffer.concat([buf, Buffer.alloc(pad, padByte[0]!)]); +} + +/** + * **pkcs7Unpad (去除 PKCS#7 填充)** + * + * 移除 AES 解密后的 PKCS#7 填充字节。 + * 包含填充合法性校验。 + */ +export function pkcs7Unpad(buf: Buffer, blockSize: number): Buffer { + if (buf.length === 0) throw new Error("invalid pkcs7 payload"); + const pad = buf[buf.length - 1]!; + if (pad < 1 || pad > blockSize) { + throw new Error("invalid pkcs7 padding"); + } + if (pad > buf.length) { + throw new Error("invalid pkcs7 payload"); + } + // Best-effort validation (all padding bytes equal). + for (let i = 0; i < pad; i += 1) { + if (buf[buf.length - 1 - i] !== pad) { + throw new Error("invalid pkcs7 padding"); + } + } + return buf.subarray(0, buf.length - pad); +} + +function sha1Hex(input: string): string { + return crypto.createHash("sha1").update(input).digest("hex"); +} + +/** + * **computeWecomMsgSignature (计算消息签名)** + * + * 算法:sha1(sort(token, timestamp, nonce, encrypt_msg)) + */ +export function computeWecomMsgSignature(params: { + token: string; + timestamp: string; + nonce: string; + encrypt: string; +}): string { + const parts = [params.token, params.timestamp, params.nonce, params.encrypt] + .map((v) => String(v ?? "")) + .sort(); + return sha1Hex(parts.join("")); +} + +/** + * **verifyWecomSignature (验证消息签名)** + * + * 比较计算出的签名与企业微信传入的签名是否一致。 + */ +export function verifyWecomSignature(params: { + token: string; + timestamp: string; + nonce: string; + encrypt: string; + signature: string; +}): boolean { + const expected = computeWecomMsgSignature({ + token: params.token, + timestamp: params.timestamp, + nonce: params.nonce, + encrypt: params.encrypt, + }); + return expected === params.signature; +} + +/** + * **decryptWecomEncrypted (解密企业微信消息)** + * + * 将企业微信的 AES 加密包解密为明文。 + * 流程: + * 1. Base64 解码 AESKey 并获取 IV (前16字节)。 + * 2. AES-CBC 解密。 + * 3. 去除 PKCS#7 填充。 + * 4. 拆解协议包结构: [16字节随机串][4字节长度][消息体][接收者ID]。 + * 5. 校验接收者ID (ReceiveId)。 + */ +export function decryptWecomEncrypted(params: { + encodingAESKey: string; + receiveId?: string; + encrypt: string; +}): string { + const aesKey = decodeEncodingAESKey(params.encodingAESKey); + const iv = aesKey.subarray(0, 16); + const decipher = crypto.createDecipheriv("aes-256-cbc", aesKey, iv); + decipher.setAutoPadding(false); + const decryptedPadded = Buffer.concat([ + decipher.update(Buffer.from(params.encrypt, "base64")), + decipher.final(), + ]); + const decrypted = pkcs7Unpad(decryptedPadded, WECOM_PKCS7_BLOCK_SIZE); + + if (decrypted.length < 20) { + throw new Error(`invalid decrypted payload (expected at least 20 bytes, got ${decrypted.length})`); + } + + // 16 bytes random + 4 bytes network-order length + msg + receiveId (optional) + const msgLen = decrypted.readUInt32BE(16); + const msgStart = 20; + const msgEnd = msgStart + msgLen; + if (msgEnd > decrypted.length) { + throw new Error(`invalid decrypted msg length (msgEnd=${msgEnd}, payloadLength=${decrypted.length})`); + } + const msg = decrypted.subarray(msgStart, msgEnd).toString("utf8"); + + const receiveId = params.receiveId ?? ""; + if (receiveId) { + const trailing = decrypted.subarray(msgEnd).toString("utf8"); + if (trailing !== receiveId) { + throw new Error(`receiveId mismatch (expected "${receiveId}", got "${trailing}")`); + } + } + + return msg; +} + +/** + * **encryptWecomPlaintext (加密回复消息)** + * + * 将明文消息打包为企业微信的加密格式。 + * 流程: + * 1. 构造协议包: [16字节随机串][4字节长度][消息体][接收者ID]。 + * 2. PKCS#7 填充。 + * 3. AES-CBC 加密。 + * 4. 转 Base64。 + */ +export function encryptWecomPlaintext(params: { + encodingAESKey: string; + receiveId?: string; + plaintext: string; +}): string { + const aesKey = decodeEncodingAESKey(params.encodingAESKey); + const iv = aesKey.subarray(0, 16); + const random16 = crypto.randomBytes(16); + const msg = Buffer.from(params.plaintext ?? "", "utf8"); + const msgLen = Buffer.alloc(4); + msgLen.writeUInt32BE(msg.length, 0); + const receiveId = Buffer.from(params.receiveId ?? "", "utf8"); + + const raw = Buffer.concat([random16, msgLen, msg, receiveId]); + const padded = pkcs7Pad(raw, WECOM_PKCS7_BLOCK_SIZE); + const cipher = crypto.createCipheriv("aes-256-cbc", aesKey, iv); + cipher.setAutoPadding(false); + const encrypted = Buffer.concat([cipher.update(padded), cipher.final()]); + return encrypted.toString("base64"); +} diff --git a/wecom/src/crypto/aes.ts b/wecom/src/crypto/aes.ts new file mode 100644 index 00000000..77857547 --- /dev/null +++ b/wecom/src/crypto/aes.ts @@ -0,0 +1,108 @@ +/** + * WeCom AES-256-CBC 加解密核心 + * Bot 和 Agent 模式共用 + */ + +import crypto from "node:crypto"; +import { CRYPTO } from "../types/constants.js"; + +export function decodeEncodingAESKey(encodingAESKey: string): Buffer { + const trimmed = encodingAESKey.trim(); + if (!trimmed) throw new Error("encodingAESKey missing"); + const withPadding = trimmed.endsWith("=") ? trimmed : `${trimmed}=`; + const key = Buffer.from(withPadding, "base64"); + if (key.length !== CRYPTO.AES_KEY_LENGTH) { + throw new Error(`invalid encodingAESKey (expected ${CRYPTO.AES_KEY_LENGTH} bytes, got ${key.length})`); + } + return key; +} + +function pkcs7Pad(buf: Buffer, blockSize: number): Buffer { + const mod = buf.length % blockSize; + const pad = mod === 0 ? blockSize : blockSize - mod; + const padByte = Buffer.from([pad]); + return Buffer.concat([buf, Buffer.alloc(pad, padByte[0]!)]); +} + +export function pkcs7Unpad(buf: Buffer, blockSize: number): Buffer { + if (buf.length === 0) throw new Error("invalid pkcs7 payload"); + const pad = buf[buf.length - 1]!; + if (pad < 1 || pad > blockSize) { + throw new Error("invalid pkcs7 padding"); + } + if (pad > buf.length) { + throw new Error("invalid pkcs7 payload"); + } + for (let i = 0; i < pad; i += 1) { + if (buf[buf.length - 1 - i] !== pad) { + throw new Error("invalid pkcs7 padding"); + } + } + return buf.subarray(0, buf.length - pad); +} + +/** + * 解密 WeCom 加密消息 + */ +export function decryptWecomEncrypted(params: { + encodingAESKey: string; + receiveId?: string; + encrypt: string; +}): string { + const aesKey = decodeEncodingAESKey(params.encodingAESKey); + const iv = aesKey.subarray(0, 16); + const decipher = crypto.createDecipheriv("aes-256-cbc", aesKey, iv); + decipher.setAutoPadding(false); + const decryptedPadded = Buffer.concat([ + decipher.update(Buffer.from(params.encrypt, "base64")), + decipher.final(), + ]); + const decrypted = pkcs7Unpad(decryptedPadded, CRYPTO.PKCS7_BLOCK_SIZE); + + if (decrypted.length < 20) { + throw new Error(`invalid payload (expected >=20 bytes, got ${decrypted.length})`); + } + + // 16 bytes random + 4 bytes length + msg + receiveId + const msgLen = decrypted.readUInt32BE(16); + const msgStart = 20; + const msgEnd = msgStart + msgLen; + if (msgEnd > decrypted.length) { + throw new Error(`invalid msg length (msgEnd=${msgEnd}, total=${decrypted.length})`); + } + const msg = decrypted.subarray(msgStart, msgEnd).toString("utf8"); + + const receiveId = params.receiveId ?? ""; + if (receiveId) { + const trailing = decrypted.subarray(msgEnd).toString("utf8"); + if (trailing !== receiveId) { + throw new Error(`receiveId mismatch (expected "${receiveId}", got "${trailing}")`); + } + } + + return msg; +} + +/** + * 加密明文为 WeCom 格式 + */ +export function encryptWecomPlaintext(params: { + encodingAESKey: string; + receiveId?: string; + plaintext: string; +}): string { + const aesKey = decodeEncodingAESKey(params.encodingAESKey); + const iv = aesKey.subarray(0, 16); + const random16 = crypto.randomBytes(16); + const msg = Buffer.from(params.plaintext ?? "", "utf8"); + const msgLen = Buffer.alloc(4); + msgLen.writeUInt32BE(msg.length, 0); + const receiveId = Buffer.from(params.receiveId ?? "", "utf8"); + + const raw = Buffer.concat([random16, msgLen, msg, receiveId]); + const padded = pkcs7Pad(raw, CRYPTO.PKCS7_BLOCK_SIZE); + const cipher = crypto.createCipheriv("aes-256-cbc", aesKey, iv); + cipher.setAutoPadding(false); + const encrypted = Buffer.concat([cipher.update(padded), cipher.final()]); + return encrypted.toString("base64"); +} diff --git a/wecom/src/crypto/index.ts b/wecom/src/crypto/index.ts new file mode 100644 index 00000000..96249503 --- /dev/null +++ b/wecom/src/crypto/index.ts @@ -0,0 +1,24 @@ +/** + * WeCom 加解密模块导出 + */ + +// AES 加解密 +export { + decodeEncodingAESKey, + pkcs7Unpad, + decryptWecomEncrypted, + encryptWecomPlaintext, +} from "./aes.js"; + +// 签名验证 +export { + computeWecomMsgSignature, + verifyWecomSignature, +} from "./signature.js"; + +// XML 辅助 +export { + extractEncryptFromXml, + extractToUserNameFromXml, + buildEncryptedXmlResponse, +} from "./xml.js"; diff --git a/wecom/src/crypto/signature.ts b/wecom/src/crypto/signature.ts new file mode 100644 index 00000000..940bfe91 --- /dev/null +++ b/wecom/src/crypto/signature.ts @@ -0,0 +1,43 @@ +/** + * WeCom 签名计算与验证 + */ + +import crypto from "node:crypto"; + +function sha1Hex(input: string): string { + return crypto.createHash("sha1").update(input).digest("hex"); +} + +/** + * 计算 WeCom 消息签名 + */ +export function computeWecomMsgSignature(params: { + token: string; + timestamp: string; + nonce: string; + encrypt: string; +}): string { + const parts = [params.token, params.timestamp, params.nonce, params.encrypt] + .map((v) => String(v ?? "")) + .sort(); + return sha1Hex(parts.join("")); +} + +/** + * 验证 WeCom 消息签名 + */ +export function verifyWecomSignature(params: { + token: string; + timestamp: string; + nonce: string; + encrypt: string; + signature: string; +}): boolean { + const expected = computeWecomMsgSignature({ + token: params.token, + timestamp: params.timestamp, + nonce: params.nonce, + encrypt: params.encrypt, + }); + return expected === params.signature; +} diff --git a/wecom/src/crypto/xml.ts b/wecom/src/crypto/xml.ts new file mode 100644 index 00000000..6f9b6d57 --- /dev/null +++ b/wecom/src/crypto/xml.ts @@ -0,0 +1,49 @@ +/** + * WeCom XML 加解密辅助函数 + * 用于 Agent 模式处理 XML 格式回调 + */ + +/** + * 从 XML 密文中提取 Encrypt 字段 + */ +export function extractEncryptFromXml(xml: string): string { + const match = /<\/Encrypt>/s.exec(xml); + if (!match?.[1]) { + // 尝试不带 CDATA 的格式 + const altMatch = /(.*?)<\/Encrypt>/s.exec(xml); + if (!altMatch?.[1]) { + throw new Error("Invalid XML: missing Encrypt field"); + } + return altMatch[1]; + } + return match[1]; +} + +/** + * 从 XML 中提取 ToUserName (CorpID) + */ +export function extractToUserNameFromXml(xml: string): string { + const match = /<\/ToUserName>/s.exec(xml); + if (!match?.[1]) { + const altMatch = /(.*?)<\/ToUserName>/s.exec(xml); + return altMatch?.[1] ?? ""; + } + return match[1]; +} + +/** + * 构建加密 XML 响应 + */ +export function buildEncryptedXmlResponse(params: { + encrypt: string; + signature: string; + timestamp: string; + nonce: string; +}): string { + return ` + + +${params.timestamp} + +`; +} diff --git a/wecom/src/http.ts b/wecom/src/http.ts new file mode 100644 index 00000000..a1ea9a2c --- /dev/null +++ b/wecom/src/http.ts @@ -0,0 +1,102 @@ +import type { Dispatcher } from "undici"; +import { ProxyAgent, fetch as undiciFetch } from "undici"; + +type ProxyDispatcher = Dispatcher; + +const proxyDispatchers = new Map(); + +/** + * **getProxyDispatcher (获取代理 Dispatcher)** + * + * 缓存并复用 ProxyAgent,避免重复创建连接池。 + */ +function getProxyDispatcher(proxyUrl: string): ProxyDispatcher { + const existing = proxyDispatchers.get(proxyUrl); + if (existing) return existing; + const created = new ProxyAgent(proxyUrl); + proxyDispatchers.set(proxyUrl, created); + return created; +} + +function mergeAbortSignal(params: { + signal?: AbortSignal; + timeoutMs?: number; +}): AbortSignal | undefined { + const signals: AbortSignal[] = []; + if (params.signal) signals.push(params.signal); + if (params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0) { + signals.push(AbortSignal.timeout(params.timeoutMs)); + } + if (!signals.length) return undefined; + if (signals.length === 1) return signals[0]; + return AbortSignal.any(signals); +} + +/** + * **WecomHttpOptions (HTTP 选项)** + * + * @property proxyUrl 代理服务器地址 + * @property timeoutMs 请求超时时间 (毫秒) + * @property signal AbortSignal 信号 + */ +export type WecomHttpOptions = { + proxyUrl?: string; + timeoutMs?: number; + signal?: AbortSignal; +}; + +/** + * **wecomFetch (统一 HTTP 请求)** + * + * 基于 `undici` 的 fetch 封装,自动处理 ProxyAgent 和 Timeout。 + * 所有对企业微信 API 的调用都应经过此函数。 + */ +export async function wecomFetch(input: string | URL, init?: RequestInit, opts?: WecomHttpOptions): Promise { + const proxyUrl = opts?.proxyUrl?.trim() ?? ""; + const dispatcher = proxyUrl ? getProxyDispatcher(proxyUrl) : undefined; + + const initSignal = init?.signal ?? undefined; + const signal = mergeAbortSignal({ signal: opts?.signal ?? initSignal, timeoutMs: opts?.timeoutMs }); + const nextInit: RequestInit & { dispatcher?: Dispatcher } = { + ...(init ?? {}), + ...(signal ? { signal } : {}), + ...(dispatcher ? { dispatcher } : {}), + }; + + return undiciFetch(input, nextInit as Parameters[1]) as unknown as Promise; +} + +/** + * **readResponseBodyAsBuffer (读取响应 Body)** + * + * 将 Response Body 读取为 Buffer,支持最大字节限制以防止内存溢出。 + * 适用于下载媒体文件等场景。 + */ +export async function readResponseBodyAsBuffer(res: Response, maxBytes?: number): Promise { + if (!res.body) return Buffer.alloc(0); + + const limit = maxBytes && Number.isFinite(maxBytes) && maxBytes > 0 ? maxBytes : undefined; + const chunks: Uint8Array[] = []; + let total = 0; + + const reader = res.body.getReader(); + while (true) { + const { done, value } = await reader.read(); + if (done) break; + if (!value) continue; + + total += value.byteLength; + if (limit && total > limit) { + try { + await reader.cancel("body too large"); + } catch { + // ignore + } + throw new Error(`response body too large (>${limit} bytes)`); + } + chunks.push(value); + } + + return Buffer.concat(chunks.map((c) => Buffer.from(c))); +} + diff --git a/wecom/src/media.test.ts b/wecom/src/media.test.ts new file mode 100644 index 00000000..2decb494 --- /dev/null +++ b/wecom/src/media.test.ts @@ -0,0 +1,55 @@ +import { describe, it, expect, vi } from "vitest"; +import { decryptWecomMedia } from "./media.js"; +import { WECOM_PKCS7_BLOCK_SIZE } from "./crypto.js"; +import crypto from "node:crypto"; + +const { undiciFetch } = vi.hoisted(() => { + const undiciFetch = vi.fn(); + return { undiciFetch }; +}); + +vi.mock("undici", () => ({ + fetch: undiciFetch, + ProxyAgent: class ProxyAgent { }, +})); + +function pkcs7Pad(buf: Buffer, blockSize: number): Buffer { + const mod = buf.length % blockSize; + const pad = mod === 0 ? blockSize : blockSize - mod; + const padByte = Buffer.from([pad]); + return Buffer.concat([buf, Buffer.alloc(pad, padByte[0]!)]); +} + +describe("decryptWecomMedia", () => { + it("should download and decrypt media successfully", async () => { + // 1. Setup Key and Data + const aesKeyBase64 = "jWmYm7qr5nMoCAstdRmNjt3p7vsH8HkK+qiJqQ0aaaa="; // 32 bytes when decoded + padding + const aesKey = Buffer.from(aesKeyBase64 + "=", "base64"); + const iv = aesKey.subarray(0, 16); + + const originalData = Buffer.from("Hello WeCom Image Data", "utf8"); + + // 2. Encrypt manually (AES-256-CBC + PKCS7) + const padded = pkcs7Pad(originalData, WECOM_PKCS7_BLOCK_SIZE); + const cipher = crypto.createCipheriv("aes-256-cbc", aesKey, iv); + cipher.setAutoPadding(false); + const encrypted = Buffer.concat([cipher.update(padded), cipher.final()]); + + // 3. Mock HTTP fetch + undiciFetch.mockResolvedValue(new Response(encrypted)); + + // 4. Test + const decrypted = await decryptWecomMedia("http://mock.url/image", aesKeyBase64); + + // 5. Assert + expect(decrypted.toString("utf8")).toBe("Hello WeCom Image Data"); + expect(undiciFetch).toHaveBeenCalledWith( + "http://mock.url/image", + expect.objectContaining({ signal: expect.anything() }), + ); + }); + + it("should fail if key is invalid", async () => { + await expect(decryptWecomMedia("http://url", "invalid-key")).rejects.toThrow(); + }); +}); diff --git a/wecom/src/media.ts b/wecom/src/media.ts new file mode 100644 index 00000000..b84d5e42 --- /dev/null +++ b/wecom/src/media.ts @@ -0,0 +1,55 @@ +import crypto from "node:crypto"; +import { decodeEncodingAESKey, pkcs7Unpad, WECOM_PKCS7_BLOCK_SIZE } from "./crypto.js"; +import { readResponseBodyAsBuffer, wecomFetch, type WecomHttpOptions } from "./http.js"; + +/** + * **decryptWecomMedia (解密企业微信媒体文件)** + * + * 简易封装:直接传入 URL 和 AES Key 下载并解密。 + * 企业微信媒体文件使用与消息体相同的 AES-256-CBC 加密,IV 为 AES Key 前16字节。 + * 解密后需移除 PKCS#7 填充。 + */ +export async function decryptWecomMedia(url: string, encodingAESKey: string, maxBytes?: number): Promise { + return decryptWecomMediaWithHttp(url, encodingAESKey, { maxBytes }); +} + +/** + * **decryptWecomMediaWithHttp (解密企业微信媒体 - 高级)** + * + * 支持传递 HTTP 选项(如 Proxy、Timeout)。 + * 流程: + * 1. 下载加密内容。 + * 2. 准备 AES Key 和 IV。 + * 3. AES-CBC 解密。 + * 4. PKCS#7 去除填充。 + */ +export async function decryptWecomMediaWithHttp( + url: string, + encodingAESKey: string, + params?: { maxBytes?: number; http?: WecomHttpOptions }, +): Promise { + // 1. Download encrypted content + const res = await wecomFetch(url, undefined, { ...params?.http, timeoutMs: params?.http?.timeoutMs ?? 15_000 }); + if (!res.ok) { + throw new Error(`failed to download media: ${res.status}`); + } + const encryptedData = await readResponseBodyAsBuffer(res, params?.maxBytes); + + // 2. Prepare Key and IV + const aesKey = decodeEncodingAESKey(encodingAESKey); + const iv = aesKey.subarray(0, 16); + + // 3. Decrypt + const decipher = crypto.createDecipheriv("aes-256-cbc", aesKey, iv); + decipher.setAutoPadding(false); // We handle padding manually + const decryptedPadded = Buffer.concat([ + decipher.update(encryptedData), + decipher.final(), + ]); + + // 4. Unpad + // Note: Unlike msg bodies, usually removing PKCS#7 padding is enough for media files. + // The Python SDK logic: pad_len = decrypted_data[-1]; decrypted_data = decrypted_data[:-pad_len] + // Our pkcs7Unpad function does exactly this + validation. + return pkcs7Unpad(decryptedPadded, WECOM_PKCS7_BLOCK_SIZE); +} diff --git a/wecom/src/monitor.active.test.ts b/wecom/src/monitor.active.test.ts new file mode 100644 index 00000000..7da6d859 --- /dev/null +++ b/wecom/src/monitor.active.test.ts @@ -0,0 +1,239 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { sendActiveMessage, handleWecomWebhookRequest, registerWecomWebhookTarget } from "./monitor.js"; +import * as cryptoHelpers from "./crypto.js"; +import * as runtime from "./runtime.js"; +import * as agentApi from "./agent/api-client.js"; +import { IncomingMessage, ServerResponse } from "node:http"; +import { Socket } from "node:net"; +import * as crypto from "node:crypto"; + +const { undiciFetch } = vi.hoisted(() => { + const undiciFetch = vi.fn(); + return { undiciFetch }; +}); + +vi.mock("undici", () => ({ + fetch: undiciFetch, + ProxyAgent: class ProxyAgent { }, +})); + +vi.mock("./agent/api-client.js", () => ({ + sendText: vi.fn(), + sendMedia: vi.fn(), + uploadMedia: vi.fn(), +})); + +// Helpers +function createMockRequest(bodyObj: any): IncomingMessage { + const socket = new Socket(); + const req = new IncomingMessage(socket); + req.method = "POST"; + req.url = "/wecom?timestamp=123&nonce=456&signature=789"; + req.push(JSON.stringify(bodyObj)); + req.push(null); + return req; +} + +function createMockResponse(): ServerResponse { + const req = new IncomingMessage(new Socket()); + const res = new ServerResponse(req); + res.end = vi.fn() as any; + res.setHeader = vi.fn(); + (res as any).statusCode = 200; + return res; +} + +describe("Monitor Active Features", () => { + let capturedDeliver: ((payload: { text: string }) => Promise) | undefined; + let mockCore: any; + let msgSeq = 0; + let senderUserId = ""; + let senderChatId = ""; + // Valid 32-byte AES Key (Base64 encoded) + const validKey = "jWmYm7qr5nMoCAstdRmNjt3p7vsH8HkK+qiJqQ0aaaa="; + + beforeEach(() => { + vi.useFakeTimers(); + capturedDeliver = undefined; + vi.restoreAllMocks(); + undiciFetch.mockClear(); + msgSeq += 1; + senderUserId = `zhangsan-${msgSeq}`; + senderChatId = `wr123-${msgSeq}`; + + // Spy on crypto.randomBytes (default export in monitor.ts usage) + vi.spyOn(crypto.default, "randomBytes").mockImplementation((size) => { + return Buffer.alloc(size, 0x11); + }); + + // Mock Crypto Helpers + // Wespy on verifyWecomSignature to always pass + vi.spyOn(cryptoHelpers, "verifyWecomSignature").mockReturnValue(true); + + // We spy on decryptWecomEncrypted to return our mock plaintext + // Note: For this to work despite direct import in monitor.ts, we rely on Vitest's + // module mocking capabilities or the fact that * exports might be live bindings. + // If this fails, we will know. + vi.spyOn(cryptoHelpers, "decryptWecomEncrypted").mockImplementation((opts) => { + return JSON.stringify({ + msgid: `test-msg-id-${msgSeq}`, + aibotid: "bot-1", + chattype: "group", + chatid: senderChatId, + from: { userid: senderUserId }, + msgtype: "text", + text: { content: "hello" }, + response_url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=test-key" + }); + }); + + mockCore = { + channel: { + text: { + resolveMarkdownTableMode: () => "off", + convertMarkdownTables: (t: string) => t.replace(/\|/g, "-") + }, + commands: { + shouldComputeCommandAuthorized: () => false, + resolveCommandAuthorizedFromAuthorizers: () => true, + }, + pairing: { + readAllowFromStore: async () => [], + }, + reply: { + finalizeInboundContext: (c: any) => c, + resolveEnvelopeFormatOptions: () => ({}), + formatAgentEnvelope: () => "", + dispatchReplyWithBufferedBlockDispatcher: async (opts: any) => { + capturedDeliver = opts.dispatcherOptions.deliver; + return; + } + }, + routing: { resolveAgentRoute: () => ({ agentId: "1", sessionKey: "1", accountId: "1" }) }, + session: { + resolveStorePath: () => "", + readSessionUpdatedAt: () => 0, + recordInboundSession: vi.fn() + } + }, + logging: { shouldLogVerbose: () => false } + }; + + vi.spyOn(runtime, "getWecomRuntime").mockReturnValue(mockCore); + + registerWecomWebhookTarget({ + account: { accountId: "1", enabled: true, configured: true, token: "T", encodingAESKey: validKey, receiveId: "R", config: {} as any }, + config: { + channels: { + wecom: { + enabled: true, + agent: { + corpId: "corp", + corpSecret: "secret", + agentId: 1000002, + token: "token", + encodingAESKey: "aes", + }, + }, + }, + } as any, + runtime: { log: () => { } }, + core: mockCore, + path: "/wecom" + }); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("should protect tags from table conversion", async () => { + const req = createMockRequest({ encrypt: "mock-encrypt" }); + const res = createMockResponse(); + await handleWecomWebhookRequest(req, res); + + // The WeCom monitor debounces inbound messages before starting the agent. + // `flushPending` triggers async agent start without awaiting it, so give the + // microtask queue a chance to run after the timer fires. + await vi.runOnlyPendingTimersAsync(); + await Promise.resolve(); + await Promise.resolve(); + + expect(capturedDeliver).toBeDefined(); + + const payload = { text: "Out | side\nInside | Think" }; + const convertSpy = vi.spyOn(mockCore.channel.text, "convertMarkdownTables"); + + await capturedDeliver!(payload); + + const calledArg = convertSpy.mock.calls[0][0]; + expect(calledArg).toContain("__THINK_PLACEHOLDER_0__"); + expect(calledArg).not.toContain(""); + }); + + it("should store response_url and allow active message sending", async () => { + const req = createMockRequest({ encrypt: "mock-encrypt" }); + const res = createMockResponse(); + + // We use a real key but mocked randomBytes. + // However, `handleWecomWebhookRequest` calls `buildEncryptedJsonReply` -> `encryptWecomPlaintext`. + // `encryptWecomPlaintext` uses the key. Since it's valid, it should work fine. + // We don't verify the OUTPUT of handleWecomWebhookRequest, just that it runs and sets up state. + + await handleWecomWebhookRequest(req, res); + + const streamId = Buffer.alloc(16, 0x11).toString("hex"); + + undiciFetch.mockResolvedValue(new Response("ok", { status: 200 })); + await sendActiveMessage(streamId, "Active Hello"); + + expect(undiciFetch).toHaveBeenCalledWith( + "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=test-key", + expect.objectContaining({ + method: "POST", + headers: expect.objectContaining({ "Content-Type": "application/json" }), + body: JSON.stringify({ msgtype: "text", text: { content: "Active Hello" } }), + }), + ); + }); + + it("should fallback non-image media to agent DM (and push a Chinese prompt)", async () => { + const { uploadMedia, sendMedia } = agentApi as any; + uploadMedia.mockResolvedValue("media-id-1"); + sendMedia.mockResolvedValue(undefined); + + const req = createMockRequest({ encrypt: "mock-encrypt" }); + const res = createMockResponse(); + await handleWecomWebhookRequest(req, res); + + await vi.advanceTimersByTimeAsync(600); + await Promise.resolve(); + await Promise.resolve(); + + expect(capturedDeliver).toBeDefined(); + + // Create a local PDF to force non-image content-type inference. + const fs = await import("node:fs/promises"); + const os = await import("node:os"); + const path = await import("node:path"); + const tmp = path.join(os.tmpdir(), `wecom-test-${Date.now()}.pdf`); + await fs.writeFile(tmp, Buffer.from("pdf")); + + undiciFetch.mockResolvedValue(new Response("ok", { status: 200 })); + + await capturedDeliver!({ text: "here", mediaUrls: [tmp] } as any); + + expect(uploadMedia).toHaveBeenCalled(); + expect(sendMedia).toHaveBeenCalledWith( + expect.objectContaining({ + toUser: senderUserId, + mediaType: "file", + }), + ); + // Ensure we attempted to push a prompt to response_url (uses undici fetch). + expect(undiciFetch).toHaveBeenCalled(); + }); + + // 注:本机路径(/Users/... 或 /tmp/...)短路发图逻辑属于运行态特性, + // 单测在 fake timers + module singleton 状态下容易引入脆弱性;这里优先覆盖更关键的兜底链路与去重逻辑。 +}); diff --git a/wecom/src/monitor.integration.test.ts b/wecom/src/monitor.integration.test.ts new file mode 100644 index 00000000..83ade6a9 --- /dev/null +++ b/wecom/src/monitor.integration.test.ts @@ -0,0 +1,207 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { handleWecomWebhookRequest, registerWecomWebhookTarget } from "./monitor.js"; +import { encryptWecomPlaintext, computeWecomMsgSignature, WECOM_PKCS7_BLOCK_SIZE } from "./crypto.js"; +import * as runtime from "./runtime.js"; +import crypto from "node:crypto"; +import { IncomingMessage, ServerResponse } from "node:http"; +import { Socket } from "node:net"; + +const { undiciFetch } = vi.hoisted(() => { + const undiciFetch = vi.fn(); + return { undiciFetch }; +}); + +vi.mock("undici", () => ({ + fetch: undiciFetch, + ProxyAgent: class ProxyAgent { }, +})); + +// Helpers to simulate HTTP request +function createMockRequest(bodyObj: any, query: URLSearchParams): IncomingMessage { + const socket = new Socket(); + const req = new IncomingMessage(socket); + req.method = "POST"; + req.url = `/wecom?${query.toString()}`; + req.push(JSON.stringify(bodyObj)); + req.push(null); + return req; +} + +function createMockResponse(): ServerResponse & { _getData: () => string, _getStatusCode: () => number } { + const req = new IncomingMessage(new Socket()); + const res = new ServerResponse(req); + let data = ""; + res.write = (chunk: any) => { data += chunk; return true; }; + res.end = (chunk: any) => { if (chunk) data += chunk; return res; }; + (res as any)._getData = () => data; + (res as any)._getStatusCode = () => res.statusCode; + return res as any; +} + +// PKCS7 Pad Helper for manual encryption +function pkcs7Pad(buf: Buffer, blockSize: number): Buffer { + const mod = buf.length % blockSize; + const pad = mod === 0 ? blockSize : blockSize - mod; + const padByte = Buffer.from([pad]); + return Buffer.concat([buf, Buffer.alloc(pad, padByte[0]!)]); +} + +describe("Monitor Integration: Inbound Image", () => { + const token = "MY_TOKEN"; + const encodingAESKey = "jWmYm7qr5nMoCAstdRmNjt3p7vsH8HkK+qiJqQ0aaaa="; // 32 bytes key + const receiveId = "MY_CORPID"; + let unregisterTarget: (() => void) | null = null; + + // Mock Core Runtime + const mockDeliver = vi.fn(); + const mockCore = { + channel: { + routing: { resolveAgentRoute: () => ({ agentId: "agent-1", sessionKey: "sess-1", accountId: "acc-1" }) }, + commands: { + shouldComputeCommandAuthorized: () => false, + resolveCommandAuthorizedFromAuthorizers: () => true, + }, + pairing: { + readAllowFromStore: async () => [], + }, + session: { + resolveStorePath: () => "store/path", + readSessionUpdatedAt: () => 0, + recordInboundSession: vi.fn(), + }, + reply: { + formatAgentEnvelope: () => "formatted-body", + finalizeInboundContext: (ctx: any) => ctx, + resolveEnvelopeFormatOptions: () => ({}), + dispatchReplyWithBufferedBlockDispatcher: async (opts: any) => { + // Simulate Agent processing by calling deliver immediately or later + // For this test, verifying the Inbound Body is enough. + // The delivery payload is what the AGENT sees. + // But wait, dispatchReply... is for OUTBOUND streaming replies. + // startAgentForStream calls it. + // We really want to spy on what `rawBody` was passed to startAgentForStream context. + + // Actually `recordInboundSession` receives `ctx` which contains `RawBody`. + return; + }, + }, + text: { resolveMarkdownTableMode: () => "off", convertMarkdownTables: (t: string) => t }, + }, + logging: { shouldLogVerbose: () => true }, + }; + + beforeEach(() => { + vi.spyOn(runtime, "getWecomRuntime").mockReturnValue(mockCore as any); + + unregisterTarget?.(); + unregisterTarget = registerWecomWebhookTarget({ + account: { + accountId: "test-acc", + name: "Test", + enabled: true, + configured: true, + token, + encodingAESKey, + receiveId, + config: {} as any + }, + config: {} as any, + runtime: { log: console.log, error: console.error }, + core: mockCore as any, + path: "/wecom" + }); + }); + + afterEach(() => { + unregisterTarget?.(); + unregisterTarget = null; + vi.restoreAllMocks(); + }); + + // Mock media saving + const mockSaveMediaBuffer = vi.fn().mockResolvedValue({ path: "/tmp/saved-image.jpg", contentType: "image/jpeg" }); + (mockCore.channel as any).media = { saveMediaBuffer: mockSaveMediaBuffer }; + + it("should decrypt inbound image, save it, and inject into context", async () => { + // 1. Prepare Encrypted Media (The "File" on WeCom Server) + const fileContent = Buffer.from("fake-image-data"); + const aesKey = Buffer.from(encodingAESKey + "=", "base64"); + const iv = aesKey.subarray(0, 16); + + // Encrypt content (WeCom does this) + const cipher = crypto.createCipheriv("aes-256-cbc", aesKey, iv); + cipher.setAutoPadding(false); + const encryptedMedia = Buffer.concat([cipher.update(pkcs7Pad(fileContent, WECOM_PKCS7_BLOCK_SIZE)), cipher.final()]); + + // Mock HTTP fetch to return this encrypted media + undiciFetch.mockResolvedValue(new Response(encryptedMedia)); + + // 2. Prepare Inbound Message (The Webhook JSON) + const imageUrl = "http://wecom.server/media/123"; + const inboundMsg = { + msgtype: "image", + image: { url: imageUrl }, + from: { userid: "testuser" } + }; + + // 3. Encrypt the *Inbound Message* Payload (The Envelope) + const timestamp = String(Math.floor(Date.now() / 1000)); + const nonce = "123456"; + const encrypt = encryptWecomPlaintext({ + encodingAESKey, + receiveId, + plaintext: JSON.stringify(inboundMsg) + }); + const msgSignature = computeWecomMsgSignature({ token, timestamp, nonce, encrypt }); + + const query = new URLSearchParams({ + msg_signature: msgSignature, + timestamp, + nonce + }); + + const bodyObj = { + touser: receiveId, + agentid: "10001", + encrypt, // Standard WeCom POST body structure + }; + + // 4. Send Request + const req = createMockRequest(bodyObj, query); + const res = createMockResponse(); + + await handleWecomWebhookRequest(req, res); + + // Wait for debounce timer to trigger agent (DEFAULT_DEBOUNCE_MS = 500ms) + await new Promise(resolve => setTimeout(resolve, 600)); + + // 5. Verify + // Check recordInboundSession was called with correct RawBody and Media Context + expect(mockCore.channel.session.recordInboundSession).toHaveBeenCalled(); + const recordCall = (mockCore.channel.session.recordInboundSession as any).mock.calls[0][0]; + const ctx = recordCall.ctx; + + // Expect: [image] + expect(ctx.RawBody).toBe("[image]"); + + // Expect media to be saved + expect(mockSaveMediaBuffer).toHaveBeenCalledWith( + expect.any(Buffer), // The decrypted buffer + "image/jpeg", + "inbound", + expect.any(Number), // maxBytes + "image.jpg" + ); + const savedBuffer = mockSaveMediaBuffer.mock.calls[0][0]; + expect(savedBuffer.toString()).toBe("fake-image-data"); + + // Expect Context Injection + expect(ctx.MediaPath).toBe("/tmp/saved-image.jpg"); + expect(ctx.MediaType).toBe("image/jpeg"); + + expect(undiciFetch).toHaveBeenCalledWith( + imageUrl, + expect.objectContaining({ signal: expect.anything() }), + ); + }); +}); diff --git a/wecom/src/monitor.ts b/wecom/src/monitor.ts new file mode 100644 index 00000000..53379d6b --- /dev/null +++ b/wecom/src/monitor.ts @@ -0,0 +1,1802 @@ +import type { IncomingMessage, ServerResponse } from "node:http"; +import { pathToFileURL } from "node:url"; + +import crypto from "node:crypto"; + +import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk"; + +import type { ResolvedAgentAccount } from "./types/index.js"; +import type { ResolvedBotAccount } from "./types/index.js"; +import type { WecomInboundMessage, WecomInboundQuote } from "./types.js"; +import { decryptWecomEncrypted, encryptWecomPlaintext, verifyWecomSignature, computeWecomMsgSignature } from "./crypto.js"; +import { getWecomRuntime } from "./runtime.js"; +import { decryptWecomMedia, decryptWecomMediaWithHttp } from "./media.js"; +import { WEBHOOK_PATHS } from "./types/constants.js"; +import { handleAgentWebhook } from "./agent/index.js"; +import { resolveWecomAccounts, resolveWecomEgressProxyUrl, resolveWecomMediaMaxBytes } from "./config/index.js"; +import { wecomFetch } from "./http.js"; +import { sendText as sendAgentText, sendMedia as sendAgentMedia, uploadMedia } from "./agent/api-client.js"; +import axios from "axios"; + +/** + * **核心监控模块 (Monitor Loop)** + * + * 负责接收企业微信 Webhook 回调,处理消息流、媒体解密、消息去重防抖,并分发给 Agent 处理。 + * 它是插件与企业微信交互的“心脏”,管理着所有会话的生命周期。 + */ + +import type { WecomRuntimeEnv, WecomWebhookTarget, StreamState, PendingInbound, ActiveReplyState } from "./monitor/types.js"; +import { monitorState, LIMITS } from "./monitor/state.js"; +import { buildWecomUnauthorizedCommandPrompt, resolveWecomCommandAuthorization } from "./shared/command-auth.js"; + +// Global State +monitorState.streamStore.setFlushHandler((pending) => void flushPending(pending)); + +// Stores (convenience aliases) +const streamStore = monitorState.streamStore; +const activeReplyStore = monitorState.activeReplyStore; + +// Target Registry +const webhookTargets = new Map(); + +// Agent 模式 target 存储 +type AgentWebhookTarget = { + agent: ResolvedAgentAccount; + config: OpenClawConfig; + runtime: WecomRuntimeEnv; + // ... +}; +const agentTargets = new Map(); + +const pendingInbounds = new Map(); + +const STREAM_MAX_BYTES = LIMITS.STREAM_MAX_BYTES; +const STREAM_MAX_DM_BYTES = 200_000; +const BOT_WINDOW_MS = 6 * 60 * 1000; +const BOT_SWITCH_MARGIN_MS = 30 * 1000; +// REQUEST_TIMEOUT_MS is available in LIMITS but defined locally in other functions, we can leave it or use LIMITS.REQUEST_TIMEOUT_MS +// Keeping local variables for now if they are used, or we can replace usages. +// The constants STREAM_TTL_MS and ACTIVE_REPLY_TTL_MS are internalized in state.ts, so we can remove them here. + +/** 错误提示信息 */ +const ERROR_HELP = ""; + +/** + * **normalizeWebhookPath (标准化 Webhook 路径)** + * + * 将用户配置的路径统一格式化为以 `/` 开头且不以 `/` 结尾的字符串。 + * 例如: `wecom` -> `/wecom` + */ +function normalizeWebhookPath(raw: string): string { + const trimmed = raw.trim(); + if (!trimmed) return "/"; + const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`; + if (withSlash.length > 1 && withSlash.endsWith("/")) return withSlash.slice(0, -1); + return withSlash; +} + + +/** + * **ensurePruneTimer (启动清理定时器)** + * + * 当有活跃的 Webhook Target 注册时,调用 MonitorState 启动自动清理任务。 + * 清理任务包括:删除过期 Stream、移除无效 Active Reply URL 等。 + */ +function ensurePruneTimer() { + monitorState.startPruning(); +} + +/** + * **checkPruneTimer (检查并停止清理定时器)** + * + * 当没有活跃的 Webhook Target 时(Bot 和 Agent 均移除),停止清理任务以节省资源。 + */ +function checkPruneTimer() { + const hasBot = webhookTargets.size > 0; + const hasAgent = agentTargets.size > 0; + if (!hasBot && !hasAgent) { + monitorState.stopPruning(); + } +} + + + + +function truncateUtf8Bytes(text: string, maxBytes: number): string { + const buf = Buffer.from(text, "utf8"); + if (buf.length <= maxBytes) return text; + const slice = buf.subarray(buf.length - maxBytes); + return slice.toString("utf8"); +} + +/** + * **jsonOk (返回 JSON 响应)** + * + * 辅助函数:向企业微信服务器返回 HTTP 200 及 JSON 内容。 + * 注意企业微信要求加密内容以 Content-Type: text/plain 返回,但这里为了通用性使用了标准 JSON 响应, + * 并通过 Content-Type 修正适配。 + */ +function jsonOk(res: ServerResponse, body: unknown): void { + res.statusCode = 200; + // WeCom's reference implementation returns the encrypted JSON as text/plain. + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(JSON.stringify(body)); +} + +/** + * **readJsonBody (读取 JSON 请求体)** + * + * 异步读取 HTTP 请求体并解析为 JSON。包含大小限制检查,防止大包攻击。 + * + * @param req HTTP 请求对象 + * @param maxBytes 最大允许字节数 + */ +async function readJsonBody(req: IncomingMessage, maxBytes: number) { + const chunks: Buffer[] = []; + let total = 0; + return await new Promise<{ ok: boolean; value?: unknown; error?: string }>((resolve) => { + req.on("data", (chunk: Buffer) => { + total += chunk.length; + if (total > maxBytes) { + resolve({ ok: false, error: "payload too large" }); + req.destroy(); + return; + } + chunks.push(chunk); + }); + req.on("end", () => { + try { + const raw = Buffer.concat(chunks).toString("utf8"); + if (!raw.trim()) { + resolve({ ok: false, error: "empty payload" }); + return; + } + resolve({ ok: true, value: JSON.parse(raw) as unknown }); + } catch (err) { + resolve({ ok: false, error: err instanceof Error ? err.message : String(err) }); + } + }); + req.on("error", (err) => { + resolve({ ok: false, error: err instanceof Error ? err.message : String(err) }); + }); + }); +} + +/** + * **buildEncryptedJsonReply (构建加密回复)** + * + * 将明文 JSON 包装成企业微信要求的加密 XML/JSON 格式(此处实际返回 JSON 结构)。 + * 包含签名计算逻辑。 + */ +function buildEncryptedJsonReply(params: { + account: ResolvedBotAccount; + plaintextJson: unknown; + nonce: string; + timestamp: string; +}): { encrypt: string; msgsignature: string; timestamp: string; nonce: string } { + const plaintext = JSON.stringify(params.plaintextJson ?? {}); + const encrypt = encryptWecomPlaintext({ + encodingAESKey: params.account.encodingAESKey ?? "", + receiveId: params.account.receiveId ?? "", + plaintext, + }); + const msgsignature = computeWecomMsgSignature({ + token: params.account.token ?? "", + timestamp: params.timestamp, + nonce: params.nonce, + encrypt, + }); + return { + encrypt, + msgsignature, + timestamp: params.timestamp, + nonce: params.nonce, + }; +} + +function resolveQueryParams(req: IncomingMessage): URLSearchParams { + const url = new URL(req.url ?? "/", "http://localhost"); + return url.searchParams; +} + +function resolvePath(req: IncomingMessage): string { + const url = new URL(req.url ?? "/", "http://localhost"); + return normalizeWebhookPath(url.pathname || "/"); +} + +function resolveSignatureParam(params: URLSearchParams): string { + return ( + params.get("msg_signature") ?? + params.get("msgsignature") ?? + params.get("signature") ?? + "" + ); +} + +function buildStreamPlaceholderReply(params: { + streamId: string; + placeholderContent?: string; +}): { msgtype: "stream"; stream: { id: string; finish: boolean; content: string } } { + const content = params.placeholderContent?.trim() || "1"; + return { + msgtype: "stream", + stream: { + id: params.streamId, + finish: false, + // Spec: "第一次回复内容为 1" works as a minimal placeholder. + content, + }, + }; +} + +function buildStreamImmediateTextReply(params: { streamId: string; content: string }): { msgtype: "stream"; stream: { id: string; finish: boolean; content: string } } { + return { + msgtype: "stream", + stream: { + id: params.streamId, + finish: true, + content: params.content.trim() || "1", + }, + }; +} + +function buildStreamTextPlaceholderReply(params: { streamId: string; content: string }): { msgtype: "stream"; stream: { id: string; finish: boolean; content: string } } { + return { + msgtype: "stream", + stream: { + id: params.streamId, + finish: false, + content: params.content.trim() || "1", + }, + }; +} + +function buildStreamReplyFromState(state: StreamState): { msgtype: "stream"; stream: { id: string; finish: boolean; content: string } } { + const content = truncateUtf8Bytes(state.content, STREAM_MAX_BYTES); + // Images handled? The original code had image logic. + // Ensure we return message item if images exist + return { + msgtype: "stream", + stream: { + id: state.streamId, + finish: state.finished, + content, + ...(state.finished && state.images?.length ? { + msg_item: state.images.map(img => ({ + msgtype: "image", + image: { base64: img.base64, md5: img.md5 } + })) + } : {}) + }, + }; +} + +function appendDmContent(state: StreamState, text: string): void { + const next = state.dmContent ? `${state.dmContent}\n\n${text}`.trim() : text.trim(); + state.dmContent = truncateUtf8Bytes(next, STREAM_MAX_DM_BYTES); +} + +function computeTaskKey(target: WecomWebhookTarget, msg: WecomInboundMessage): string | undefined { + const msgid = msg.msgid ? String(msg.msgid) : ""; + if (!msgid) return undefined; + const aibotid = String((msg as any).aibotid ?? "unknown").trim() || "unknown"; + return `bot:${target.account.accountId}:${aibotid}:${msgid}`; +} + +function resolveAgentAccountOrUndefined(cfg: OpenClawConfig): ResolvedAgentAccount | undefined { + const agent = resolveWecomAccounts(cfg).agent; + return agent?.configured ? agent : undefined; +} + +function buildFallbackPrompt(params: { + kind: "media" | "timeout" | "error"; + agentConfigured: boolean; + userId?: string; + filename?: string; + chatType?: "group" | "direct"; +}): string { + const who = params.userId ? `(${params.userId})` : ""; + const scope = params.chatType === "group" ? "群聊" : params.chatType === "direct" ? "私聊" : "会话"; + if (!params.agentConfigured) { + return `${scope}中需要通过应用私信发送${params.filename ? `(${params.filename})` : ""},但管理员尚未配置企业微信自建应用(Agent)通道。请联系管理员配置后再试。${who}`.trim(); + } + if (!params.userId) { + return `${scope}中需要通过应用私信兜底发送${params.filename ? `(${params.filename})` : ""},但本次回调未能识别触发者 userid(请检查企微回调字段 from.userid / fromuserid)。请联系管理员排查配置。`.trim(); + } + if (params.kind === "media") { + return `已生成文件${params.filename ? `(${params.filename})` : ""},将通过应用私信发送给你。${who}`.trim(); + } + if (params.kind === "timeout") { + return `内容较长,为避免超时,后续内容将通过应用私信发送给你。${who}`.trim(); + } + return `交付出现异常,已尝试通过应用私信发送给你。${who}`.trim(); +} + +async function sendBotFallbackPromptNow(params: { streamId: string; text: string }): Promise { + const responseUrl = getActiveReplyUrl(params.streamId); + if (!responseUrl) { + throw new Error("no response_url(无法主动推送群内提示)"); + } + await useActiveReplyOnce(params.streamId, async ({ responseUrl, proxyUrl }) => { + const payload = { + msgtype: "stream", + stream: { + id: params.streamId, + finish: true, + content: truncateUtf8Bytes(params.text, STREAM_MAX_BYTES) || "1", + }, + }; + const res = await wecomFetch( + responseUrl, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }, + { proxyUrl, timeoutMs: LIMITS.REQUEST_TIMEOUT_MS }, + ); + if (!res.ok) { + throw new Error(`fallback prompt push failed: ${res.status}`); + } + }); +} + +async function sendAgentDmText(params: { + agent: ResolvedAgentAccount; + userId: string; + text: string; + core: PluginRuntime; +}): Promise { + const chunks = params.core.channel.text.chunkText(params.text, 20480); + for (const chunk of chunks) { + const trimmed = chunk.trim(); + if (!trimmed) continue; + await sendAgentText({ agent: params.agent, toUser: params.userId, text: trimmed }); + } +} + +async function sendAgentDmMedia(params: { + agent: ResolvedAgentAccount; + userId: string; + mediaUrlOrPath: string; + contentType?: string; + filename: string; +}): Promise { + let buffer: Buffer; + let inferredContentType = params.contentType; + + const looksLikeUrl = /^https?:\/\//i.test(params.mediaUrlOrPath); + if (looksLikeUrl) { + const res = await fetch(params.mediaUrlOrPath, { signal: AbortSignal.timeout(30_000) }); + if (!res.ok) throw new Error(`media download failed: ${res.status}`); + buffer = Buffer.from(await res.arrayBuffer()); + inferredContentType = inferredContentType || res.headers.get("content-type") || "application/octet-stream"; + } else { + const fs = await import("node:fs/promises"); + buffer = await fs.readFile(params.mediaUrlOrPath); + } + + let mediaType: "image" | "voice" | "video" | "file" = "file"; + const ct = (inferredContentType || "").toLowerCase(); + if (ct.startsWith("image/")) mediaType = "image"; + else if (ct.startsWith("audio/")) mediaType = "voice"; + else if (ct.startsWith("video/")) mediaType = "video"; + + const mediaId = await uploadMedia({ + agent: params.agent, + type: mediaType, + buffer, + filename: params.filename, + }); + await sendAgentMedia({ + agent: params.agent, + toUser: params.userId, + mediaId, + mediaType, + }); +} + +function extractLocalImagePathsFromText(params: { + text: string; + mustAlsoAppearIn: string; +}): string[] { + const text = params.text; + const mustAlsoAppearIn = params.mustAlsoAppearIn; + if (!text.trim()) return []; + + // Conservative: only accept common macOS absolute paths for images. + // Also require that the exact path appeared in the user's original message to prevent exfil. + const exts = "(png|jpg|jpeg|gif|webp|bmp)"; + const re = new RegExp(String.raw`(\/(?:Users|tmp)\/[^\s"'<>]+?\.${exts})`, "gi"); + const found = new Set(); + let m: RegExpExecArray | null; + while ((m = re.exec(text))) { + const p = m[1]; + if (!p) continue; + if (!mustAlsoAppearIn.includes(p)) continue; + found.add(p); + } + return Array.from(found); +} + +function extractLocalFilePathsFromText(text: string): string[] { + if (!text.trim()) return []; + + // Conservative: only accept common macOS absolute paths. + // This is primarily for “send local file” style requests (operator/debug usage). + const re = new RegExp(String.raw`(\/(?:Users|tmp)\/[^\s"'<>]+)`, "g"); + const found = new Set(); + let m: RegExpExecArray | null; + while ((m = re.exec(text))) { + const p = m[1]; + if (!p) continue; + found.add(p); + } + return Array.from(found); +} + +function guessContentTypeFromPath(filePath: string): string | undefined { + const ext = filePath.split(".").pop()?.toLowerCase(); + if (!ext) return undefined; + const map: Record = { + png: "image/png", + jpg: "image/jpeg", + jpeg: "image/jpeg", + gif: "image/gif", + webp: "image/webp", + bmp: "image/bmp", + pdf: "application/pdf", + txt: "text/plain", + md: "text/markdown", + json: "application/json", + zip: "application/zip", + }; + return map[ext]; +} + +function looksLikeSendLocalFileIntent(rawBody: string): boolean { + const t = rawBody.trim(); + if (!t) return false; + // Heuristic: treat as “send file” intent only when there is an explicit local path AND a send-ish verb. + // This avoids accidentally sending a file when the user is merely referencing a path. + return /(发送|发给|发到|转发|把.*发|把.*发送|帮我发|给我发)/.test(t); +} + +function storeActiveReply(streamId: string, responseUrl?: string, proxyUrl?: string): void { + activeReplyStore.store(streamId, responseUrl, proxyUrl); +} + +function getActiveReplyUrl(streamId: string): string | undefined { + return activeReplyStore.getUrl(streamId); +} + +async function useActiveReplyOnce(streamId: string, fn: (params: { responseUrl: string; proxyUrl?: string }) => Promise): Promise { + return activeReplyStore.use(streamId, fn); +} + + +function logVerbose(target: WecomWebhookTarget, message: string): void { + const should = + target.core.logging?.shouldLogVerbose?.() ?? + (() => { + try { + return getWecomRuntime().logging.shouldLogVerbose(); + } catch { + return false; + } + })(); + if (!should) return; + target.runtime.log?.(`[wecom] ${message}`); +} + +function logInfo(target: WecomWebhookTarget, message: string): void { + target.runtime.log?.(`[wecom] ${message}`); +} + +function resolveWecomSenderUserId(msg: WecomInboundMessage): string | undefined { + const direct = msg.from?.userid?.trim(); + if (direct) return direct; + const legacy = String((msg as any).fromuserid ?? (msg as any).from_userid ?? (msg as any).fromUserId ?? "").trim(); + return legacy || undefined; +} + +function parseWecomPlainMessage(raw: string): WecomInboundMessage { + const parsed = JSON.parse(raw) as unknown; + if (!parsed || typeof parsed !== "object") { + return {}; + } + return parsed as WecomInboundMessage; +} + +type InboundResult = { + body: string; + media?: { + buffer: Buffer; + contentType: string; + filename: string; + }; +}; + +/** + * **processInboundMessage (处理接收消息)** + * + * 解析企业微信传入的消息体。 + * 主要职责: + * 1. 识别媒体消息(Image/File/Mixed)。 + * 2. 如果存在媒体文件,调用 `media.ts` 进行解密和下载。 + * 3. 构造统一的 `InboundResult` 供后续 Agent 处理。 + * + * @param target Webhook 目标配置 + * @param msg 企业微信原始消息对象 + */ +async function processInboundMessage(target: WecomWebhookTarget, msg: WecomInboundMessage): Promise { + const msgtype = String(msg.msgtype ?? "").toLowerCase(); + const aesKey = target.account.encodingAESKey; + const maxBytes = resolveWecomMediaMaxBytes(target.config); + const proxyUrl = resolveWecomEgressProxyUrl(target.config); + + // 图片消息处理:如果存在 url 且配置了 aesKey,则尝试解密下载 + if (msgtype === "image") { + const url = String((msg as any).image?.url ?? "").trim(); + if (url && aesKey) { + try { + const buf = await decryptWecomMediaWithHttp(url, aesKey, { maxBytes, http: { proxyUrl } }); + return { + body: "[image]", + media: { + buffer: buf, + contentType: "image/jpeg", // WeCom images are usually generic; safest assumption or could act as generic + filename: "image.jpg", + } + }; + } catch (err) { + target.runtime.error?.(`Failed to decrypt inbound image: ${String(err)}`); + target.runtime.error?.( + `图片解密失败: ${String(err)}; 可调大 channels.wecom.media.maxBytes(当前=${maxBytes})例如:openclaw config set channels.wecom.media.maxBytes ${50 * 1024 * 1024}`, + ); + return { body: `[image] (decryption failed: ${typeof err === 'object' && err ? (err as any).message : String(err)})` }; + } + } + } + + if (msgtype === "file") { + const url = String((msg as any).file?.url ?? "").trim(); + if (url && aesKey) { + try { + const buf = await decryptWecomMediaWithHttp(url, aesKey, { maxBytes, http: { proxyUrl } }); + return { + body: "[file]", + media: { + buffer: buf, + contentType: "application/octet-stream", + filename: "file.bin", // WeCom doesn't guarantee filename in webhook payload always, defaulting + } + }; + } catch (err) { + target.runtime.error?.( + `Failed to decrypt inbound file: ${String(err)}; 可调大 channels.wecom.media.maxBytes(当前=${maxBytes})例如:openclaw config set channels.wecom.media.maxBytes ${50 * 1024 * 1024}`, + ); + return { body: `[file] (decryption failed: ${typeof err === 'object' && err ? (err as any).message : String(err)})` }; + } + } + } + + // Mixed message handling: extract first media if available + if (msgtype === "mixed") { + const items = (msg as any).mixed?.msg_item; + if (Array.isArray(items)) { + let foundMedia: InboundResult["media"] | undefined = undefined; + let bodyParts: string[] = []; + + for (const item of items) { + const t = String(item.msgtype ?? "").toLowerCase(); + if (t === "text") { + const content = String(item.text?.content ?? "").trim(); + if (content) bodyParts.push(content); + } else if ((t === "image" || t === "file") && !foundMedia && aesKey) { + // Found first media, try to download + const url = String(item[t]?.url ?? "").trim(); + if (url) { + try { + const buf = await decryptWecomMediaWithHttp(url, aesKey, { maxBytes, http: { proxyUrl } }); + foundMedia = { + buffer: buf, + contentType: t === "image" ? "image/jpeg" : "application/octet-stream", + filename: t === "image" ? "image.jpg" : "file.bin" + }; + bodyParts.push(`[${t}]`); + } catch (err) { + target.runtime.error?.( + `Failed to decrypt mixed ${t}: ${String(err)}; 可调大 channels.wecom.media.maxBytes(当前=${maxBytes})例如:openclaw config set channels.wecom.media.maxBytes ${50 * 1024 * 1024}`, + ); + bodyParts.push(`[${t}] (decryption failed)`); + } + } else { + bodyParts.push(`[${t}]`); + } + } else { + // Other items or already found media -> just placeholder + bodyParts.push(`[${t}]`); + } + } + return { + body: bodyParts.join("\n"), + media: foundMedia + }; + } + } + + return { body: buildInboundBody(msg) }; +} + + +/** + * Flush pending inbound messages after debounce timeout. + * Merges all buffered message contents and starts agent processing. + */ +/** + * **flushPending (刷新待处理消息 / 核心 Agent 触发点)** + * + * 当防抖计时器结束时被调用。 + * 核心逻辑: + * 1. 聚合所有 pending 的消息内容(用于上下文)。 + * 2. 获取 PluginRuntime。 + * 3. 标记 Stream 为 Started。 + * 4. 调用 `startAgentForStream` 启动 Agent 流程。 + * 5. 处理异常并更新 Stream 状态为 Error。 + */ +async function flushPending(pending: PendingInbound): Promise { + const { streamId, target, msg, contents, msgids, conversationKey, batchKey } = pending; + + // Merge all message contents (each is already formatted by buildInboundBody) + const mergedContents = contents.filter(c => c.trim()).join("\n").trim(); + + let core: PluginRuntime | null = null; + try { + core = getWecomRuntime(); + } catch (err) { + logVerbose(target, `flush pending: runtime not ready: ${String(err)}`); + streamStore.markFinished(streamId); + logInfo(target, `queue: runtime not ready,结束批次并推进 streamId=${streamId}`); + streamStore.onStreamFinished(streamId); + return; + } + + if (core) { + streamStore.markStarted(streamId); + const enrichedTarget: WecomWebhookTarget = { ...target, core }; + logInfo(target, `flush pending: start batch streamId=${streamId} batchKey=${batchKey} conversationKey=${conversationKey} mergedCount=${contents.length}`); + logVerbose(target, `防抖结束: 开始处理聚合消息 数量=${contents.length} streamId=${streamId}`); + + // Pass the first msg (with its media structure), and mergedContents for multi-message context + startAgentForStream({ + target: enrichedTarget, + accountId: target.account.accountId, + msg, + streamId, + mergedContents: contents.length > 1 ? mergedContents : undefined, + mergedMsgids: msgids.length > 1 ? msgids : undefined, + }).catch((err) => { + streamStore.updateStream(streamId, (state) => { + state.error = err instanceof Error ? err.message : String(err); + state.content = state.content || `Error: ${state.error}`; + state.finished = true; + }); + target.runtime.error?.(`[${target.account.accountId}] wecom agent failed (处理失败): ${String(err)}`); + streamStore.onStreamFinished(streamId); + }); + } +} + + +/** + * **waitForStreamContent (等待流内容)** + * + * 用于长轮询 (Long Polling) 场景:阻塞等待流输出内容,直到超时或流结束。 + * 这保证了用户能尽快收到第一批响应,而不是空转。 + */ +async function waitForStreamContent(streamId: string, maxWaitMs: number): Promise { + if (maxWaitMs <= 0) return; + const startedAt = Date.now(); + await new Promise((resolve) => { + const tick = () => { + const state = streamStore.getStream(streamId); + if (!state) return resolve(); + if (state.error || state.finished) return resolve(); + if (state.content.trim()) return resolve(); + if (Date.now() - startedAt >= maxWaitMs) return resolve(); + setTimeout(tick, 25); + }; + tick(); + }); +} + +/** + * **startAgentForStream (启动 Agent 处理流程)** + * + * 将接收到的(或聚合的)消息转换为 OpenClaw 内部格式,并分发给对应的 Agent。 + * 包含: + * 1. 消息解密与媒体保存。 + * 2. 路由解析 (Agent Route)。 + * 3. 鉴权 (Command Authorization)。 + * 4. 会话记录 (Session Recording)。 + * 5. 触发 Agent 响应 (Dispatch Reply)。 + * 6. 处理 Agent 输出(包括文本、Markdown 表格转换、 标签保护、模板卡片识别)。 + */ +async function startAgentForStream(params: { + target: WecomWebhookTarget; + accountId: string; + msg: WecomInboundMessage; + streamId: string; + mergedContents?: string; // Combined content from debounced messages + mergedMsgids?: string[]; +}): Promise { + const { target, msg, streamId } = params; + const core = target.core; + const config = target.config; + const account = target.account; + + const userid = resolveWecomSenderUserId(msg) || "unknown"; + const chatType = msg.chattype === "group" ? "group" : "direct"; + const chatId = msg.chattype === "group" ? (msg.chatid?.trim() || "unknown") : userid; + const taskKey = computeTaskKey(target, msg); + const aibotid = String((msg as any).aibotid ?? "").trim() || undefined; + + // 更新 Stream 状态:记录上下文信息(用户ID、ChatType等) + streamStore.updateStream(streamId, (s) => { + s.userId = userid; + s.chatType = chatType === "group" ? "group" : "direct"; + s.chatId = chatId; + s.taskKey = taskKey; + s.aibotid = aibotid; + }); + + // 1. 处理入站消息 (Decrypt media if any) + // 解析消息体,若是图片/文件则自动解密 + let { body: rawBody, media } = await processInboundMessage(target, msg); + + // 若存在从防抖逻辑聚合来的多条消息内容,则覆盖 rawBody + if (params.mergedContents) { + rawBody = params.mergedContents; + } + + // P0: 群聊/私聊里“让 Bot 发送本机图片/文件路径”的场景,优先走 Bot 原会话交付(图片), + // 非图片文件则走 Agent 私信兜底,并确保 Bot 会话里有中文提示。 + // + // 典型背景:Agent 主动发群 chatId(wr/wc...)在很多情况下会 86008,无论怎么“修复”都发不出去; + // 这种请求如果能被动回复图片,就必须由 Bot 在群内交付。 + const directLocalPaths = extractLocalFilePathsFromText(rawBody); + if (directLocalPaths.length) { + logVerbose( + target, + `local-path: 检测到用户消息包含本机路径 count=${directLocalPaths.length} intent=${looksLikeSendLocalFileIntent(rawBody)}`, + ); + } + if (directLocalPaths.length && looksLikeSendLocalFileIntent(rawBody)) { + const fs = await import("node:fs/promises"); + const pathModule = await import("node:path"); + const imageExts = new Set(["png", "jpg", "jpeg", "gif", "webp", "bmp"]); + + const imagePaths: string[] = []; + const otherPaths: string[] = []; + for (const p of directLocalPaths) { + const ext = pathModule.extname(p).slice(1).toLowerCase(); + if (imageExts.has(ext)) imagePaths.push(p); + else otherPaths.push(p); + } + + // 1) 图片:优先 Bot 群内/原会话交付(被动/流式 msg_item) + if (imagePaths.length > 0 && otherPaths.length === 0) { + const loaded: Array<{ base64: string; md5: string; path: string }> = []; + for (const p of imagePaths) { + try { + const buf = await fs.readFile(p); + const base64 = buf.toString("base64"); + const md5 = crypto.createHash("md5").update(buf).digest("hex"); + loaded.push({ base64, md5, path: p }); + } catch (err) { + target.runtime.error?.(`local-path: 读取图片失败 path=${p}: ${String(err)}`); + } + } + + if (loaded.length > 0) { + streamStore.updateStream(streamId, (s) => { + s.images = loaded.map(({ base64, md5 }) => ({ base64, md5 })); + s.content = loaded.length === 1 + ? `已发送图片(${pathModule.basename(loaded[0]!.path)})` + : `已发送 ${loaded.length} 张图片`; + s.finished = true; + }); + + const responseUrl = getActiveReplyUrl(streamId); + if (responseUrl) { + try { + const finalReply = buildStreamReplyFromState(streamStore.getStream(streamId)!) as unknown as Record; + await useActiveReplyOnce(streamId, async ({ responseUrl, proxyUrl }) => { + const res = await wecomFetch( + responseUrl, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(finalReply), + }, + { proxyUrl, timeoutMs: LIMITS.REQUEST_TIMEOUT_MS }, + ); + if (!res.ok) throw new Error(`local-path image push failed: ${res.status}`); + }); + logVerbose(target, `local-path: 已通过 Bot response_url 推送图片 frames=final images=${loaded.length}`); + } catch (err) { + target.runtime.error?.(`local-path: Bot 主动推送图片失败(将依赖 stream_refresh 拉取): ${String(err)}`); + } + } else { + logVerbose(target, `local-path: 无 response_url,等待 stream_refresh 拉取最终图片`); + } + // 该消息已完成,推进队列处理下一批 + streamStore.onStreamFinished(streamId); + return; + } + } + + // 2) 非图片文件:Bot 会话里提示 + Agent 私信兜底(目标锁定 userId) + if (otherPaths.length > 0) { + const agentCfg = resolveAgentAccountOrUndefined(config); + const agentOk = Boolean(agentCfg); + + const filename = otherPaths.length === 1 ? otherPaths[0]!.split("/").pop()! : `${otherPaths.length} 个文件`; + const prompt = buildFallbackPrompt({ + kind: "media", + agentConfigured: agentOk, + userId: userid, + filename, + chatType, + }); + + streamStore.updateStream(streamId, (s) => { + s.fallbackMode = "media"; + s.finished = true; + s.content = prompt; + s.fallbackPromptSentAt = s.fallbackPromptSentAt ?? Date.now(); + }); + + try { + await sendBotFallbackPromptNow({ streamId, text: prompt }); + logVerbose(target, `local-path: 文件兜底提示已推送`); + } catch (err) { + target.runtime.error?.(`local-path: 文件兜底提示推送失败: ${String(err)}`); + } + + if (!agentCfg) { + streamStore.onStreamFinished(streamId); + return; + } + if (!userid || userid === "unknown") { + target.runtime.error?.(`local-path: 无法识别触发者 userId,无法 Agent 私信发送文件`); + streamStore.onStreamFinished(streamId); + return; + } + + for (const p of otherPaths) { + const alreadySent = streamStore.getStream(streamId)?.agentMediaKeys?.includes(p); + if (alreadySent) continue; + try { + await sendAgentDmMedia({ + agent: agentCfg, + userId: userid, + mediaUrlOrPath: p, + contentType: guessContentTypeFromPath(p), + filename: p.split("/").pop() || "file", + }); + streamStore.updateStream(streamId, (s) => { + s.agentMediaKeys = Array.from(new Set([...(s.agentMediaKeys ?? []), p])); + }); + logVerbose(target, `local-path: 文件已通过 Agent 私信发送 user=${userid} path=${p}`); + } catch (err) { + target.runtime.error?.(`local-path: Agent 私信发送文件失败 path=${p}: ${String(err)}`); + } + } + streamStore.onStreamFinished(streamId); + return; + } + } + + // 2. Save media if present + let mediaPath: string | undefined; + let mediaType: string | undefined; + if (media) { + try { + const maxBytes = resolveWecomMediaMaxBytes(target.config); + const saved = await core.channel.media.saveMediaBuffer( + media.buffer, + media.contentType, + "inbound", + maxBytes, + media.filename + ); + mediaPath = saved.path; + mediaType = saved.contentType; + logVerbose(target, `saved inbound media to ${mediaPath} (${mediaType})`); + } catch (err) { + target.runtime.error?.(`Failed to save inbound media: ${String(err)}`); + } + } + + const route = core.channel.routing.resolveAgentRoute({ + cfg: config, + channel: "wecom", + accountId: account.accountId, + peer: { kind: chatType === "group" ? "group" : "dm", id: chatId }, + }); + + logVerbose(target, `starting agent processing (streamId=${streamId}, agentId=${route.agentId}, peerKind=${chatType}, peerId=${chatId})`); + logVerbose(target, `启动 Agent 处理: streamId=${streamId} 路由=${route.agentId} 类型=${chatType} ID=${chatId}`); + + const fromLabel = chatType === "group" ? `group:${chatId}` : `user:${userid}`; + const storePath = core.channel.session.resolveStorePath(config.session?.store, { + agentId: route.agentId, + }); + const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(config); + const previousTimestamp = core.channel.session.readSessionUpdatedAt({ + storePath, + sessionKey: route.sessionKey, + }); + const body = core.channel.reply.formatAgentEnvelope({ + channel: "WeCom", + from: fromLabel, + previousTimestamp, + envelope: envelopeOptions, + body: rawBody, + }); + + const authz = await resolveWecomCommandAuthorization({ + core, + cfg: config, + accountConfig: account.config, + rawBody, + senderUserId: userid, + }); + const commandAuthorized = authz.commandAuthorized; + logVerbose( + target, + `authz: dmPolicy=${authz.dmPolicy} shouldCompute=${authz.shouldComputeAuth} sender=${userid.toLowerCase()} senderAllowed=${authz.senderAllowed} authorizerConfigured=${authz.authorizerConfigured} commandAuthorized=${String(authz.commandAuthorized)}`, + ); + + // 命令门禁:如果这是命令且未授权,必须给用户一个明确的中文回复(不能静默忽略) + if (authz.shouldComputeAuth && authz.commandAuthorized !== true) { + const prompt = buildWecomUnauthorizedCommandPrompt({ senderUserId: userid, dmPolicy: authz.dmPolicy, scope: "bot" }); + streamStore.updateStream(streamId, (s) => { + s.finished = true; + s.content = prompt; + }); + try { + await sendBotFallbackPromptNow({ streamId, text: prompt }); + logInfo(target, `authz: 未授权命令已提示用户 streamId=${streamId}`); + } catch (err) { + target.runtime.error?.(`authz: 未授权命令提示推送失败 streamId=${streamId}: ${String(err)}`); + } + streamStore.onStreamFinished(streamId); + return; + } + + const rawBodyNormalized = rawBody.trim(); + const isResetCommand = /^\/(new|reset)(?:\s|$)/i.test(rawBodyNormalized); + const resetCommandKind = isResetCommand ? (rawBodyNormalized.match(/^\/(new|reset)/i)?.[1]?.toLowerCase() ?? "new") : null; + + const attachments = mediaPath ? [{ + name: media?.filename || "file", + mimeType: mediaType, + url: pathToFileURL(mediaPath).href + }] : undefined; + + const ctxPayload = core.channel.reply.finalizeInboundContext({ + Body: body, + RawBody: rawBody, + CommandBody: rawBody, + Attachments: attachments, + From: chatType === "group" ? `wecom:group:${chatId}` : `wecom:${userid}`, + To: `wecom:${chatId}`, + SessionKey: route.sessionKey, + AccountId: route.accountId, + ChatType: chatType, + ConversationLabel: fromLabel, + SenderName: userid, + SenderId: userid, + Provider: "wecom", + Surface: "wecom", + MessageSid: msg.msgid, + CommandAuthorized: commandAuthorized, + OriginatingChannel: "wecom", + OriginatingTo: `wecom:${chatId}`, + MediaPath: mediaPath, + MediaType: mediaType, + MediaUrl: mediaPath, // Local path for now + }); + + await core.channel.session.recordInboundSession({ + storePath, + sessionKey: ctxPayload.SessionKey ?? route.sessionKey, + ctx: ctxPayload, + onRecordError: (err) => { + target.runtime.error?.(`wecom: failed updating session meta: ${String(err)}`); + }, + }); + + const tableMode = core.channel.text.resolveMarkdownTableMode({ + cfg: config, + channel: "wecom", + accountId: account.accountId, + }); + + // WeCom Bot 会话交付约束: + // - 图片应尽量由 Bot 在原会话交付(流式最终帧 msg_item)。 + // - 非图片文件走 Agent 私信兜底(本文件中实现),并由 Bot 给出提示。 + // + // 重要:message 工具不是 sandbox 工具,必须通过 cfg.tools.deny 禁用。 + // 否则 Agent 可能直接通过 message 工具私信/发群,绕过 Bot 交付链路,导致群里“没有任何提示”。 + const cfgForDispatch = (() => { + const baseTools = (config as any)?.tools ?? {}; + const baseSandbox = (baseTools as any)?.sandbox ?? {}; + const baseSandboxTools = (baseSandbox as any)?.tools ?? {}; + const existingDeny = Array.isArray((baseSandboxTools as any).deny) ? ((baseSandboxTools as any).deny as string[]) : []; + const deny = Array.from(new Set([...existingDeny, "message"])); + return { + ...(config as any), + tools: { + ...baseTools, + sandbox: { + ...baseSandbox, + tools: { + ...baseSandboxTools, + deny, + }, + }, + }, + } as OpenClawConfig; + })(); + logVerbose(target, `tool-policy: WeCom Bot 会话已禁用 message 工具(tools.sandbox.tools.deny += message,防止绕过 Bot 交付)`); + + // 调度 Agent 回复 + // 使用 dispatchReplyWithBufferedBlockDispatcher 可以处理流式输出 buffer + await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({ + ctx: ctxPayload, + cfg: cfgForDispatch, + dispatcherOptions: { + deliver: async (payload) => { + let text = payload.text ?? ""; + + // 保护 标签不被 markdown 表格转换破坏 + const thinkRegex = /([\s\S]*?)<\/think>/g; + const thinks: string[] = []; + text = text.replace(thinkRegex, (match: string) => { + thinks.push(match); + return `__THINK_PLACEHOLDER_${thinks.length - 1}__`; + }); + + // [A2UI] Detect template_card JSON output from Agent + const trimmedText = text.trim(); + if (trimmedText.startsWith("{") && trimmedText.includes('"template_card"')) { + try { + const parsed = JSON.parse(trimmedText); + if (parsed.template_card) { + const isSingleChat = msg.chattype !== "group"; + const responseUrl = getActiveReplyUrl(streamId); + + if (responseUrl && isSingleChat) { + // 单聊且有 response_url:发送卡片 + await useActiveReplyOnce(streamId, async ({ responseUrl, proxyUrl }) => { + const res = await wecomFetch( + responseUrl, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + msgtype: "template_card", + template_card: parsed.template_card, + }), + }, + { proxyUrl, timeoutMs: LIMITS.REQUEST_TIMEOUT_MS }, + ); + if (!res.ok) { + throw new Error(`template_card send failed: ${res.status}`); + } + }); + logVerbose(target, `sent template_card: task_id=${parsed.template_card.task_id}`); + streamStore.updateStream(streamId, (s) => { + s.finished = true; + s.content = "[已发送交互卡片]"; + }); + target.statusSink?.({ lastOutboundAt: Date.now() }); + return; + } else { + // 群聊 或 无 response_url:降级为文本描述 + logVerbose(target, `template_card fallback to text (group=${!isSingleChat}, hasUrl=${!!responseUrl})`); + const cardTitle = parsed.template_card.main_title?.title || "交互卡片"; + const cardDesc = parsed.template_card.main_title?.desc || ""; + const buttons = parsed.template_card.button_list?.map((b: any) => b.text).join(" / ") || ""; + text = `📋 **${cardTitle}**${cardDesc ? `\n${cardDesc}` : ""}${buttons ? `\n\n选项: ${buttons}` : ""}`; + } + } + } catch { /* parse fail, use normal text */ } + } + + text = core.channel.text.convertMarkdownTables(text, tableMode); + + // Restore tags + thinks.forEach((think, i) => { + text = text.replace(`__THINK_PLACEHOLDER_${i}__`, think); + }); + + const current = streamStore.getStream(streamId); + if (!current) return; + + if (!current.images) current.images = []; + if (!current.agentMediaKeys) current.agentMediaKeys = []; + + logVerbose( + target, + `deliver: chatType=${current.chatType ?? chatType} user=${current.userId ?? userid} textLen=${text.length} mediaCount=${(payload.mediaUrls?.length ?? 0) + (payload.mediaUrl ? 1 : 0)}`, + ); + + // If the model referenced a local image path in its reply but did not emit mediaUrl(s), + // we can still deliver it via Bot *only* when that exact path appeared in the user's + // original message (rawBody). This prevents the model from exfiltrating arbitrary files. + if (!payload.mediaUrl && !(payload.mediaUrls?.length ?? 0) && text.includes("/")) { + const candidates = extractLocalImagePathsFromText({ text, mustAlsoAppearIn: rawBody }); + if (candidates.length > 0) { + logVerbose(target, `media: 从输出文本推断到本机图片路径(来自用户原消息)count=${candidates.length}`); + for (const p of candidates) { + try { + const fs = await import("node:fs/promises"); + const pathModule = await import("node:path"); + const buf = await fs.readFile(p); + const ext = pathModule.extname(p).slice(1).toLowerCase(); + const imageExts: Record = { + jpg: "image/jpeg", + jpeg: "image/jpeg", + png: "image/png", + gif: "image/gif", + webp: "image/webp", + bmp: "image/bmp", + }; + const contentType = imageExts[ext] ?? "application/octet-stream"; + if (!contentType.startsWith("image/")) { + continue; + } + const base64 = buf.toString("base64"); + const md5 = crypto.createHash("md5").update(buf).digest("hex"); + current.images.push({ base64, md5 }); + logVerbose(target, `media: 已加载本机图片用于 Bot 交付 path=${p}`); + } catch (err) { + target.runtime.error?.(`media: 读取本机图片失败 path=${p}: ${String(err)}`); + } + } + } + } + + // Always accumulate content for potential Agent DM fallback (not limited by STREAM_MAX_BYTES). + if (text.trim()) { + streamStore.updateStream(streamId, (s) => { + appendDmContent(s, text); + }); + } + + // Timeout fallback (group only): near 6min window, stop bot stream and switch to Agent DM. + const now = Date.now(); + const deadline = current.createdAt + BOT_WINDOW_MS; + const switchAt = deadline - BOT_SWITCH_MARGIN_MS; + const nearTimeout = !current.fallbackMode && !current.finished && now >= switchAt; + if (nearTimeout) { + const agentCfg = resolveAgentAccountOrUndefined(config); + const agentOk = Boolean(agentCfg); + const prompt = buildFallbackPrompt({ + kind: "timeout", + agentConfigured: agentOk, + userId: current.userId, + chatType: current.chatType, + }); + logVerbose( + target, + `fallback(timeout): 触发切换(接近 6 分钟)chatType=${current.chatType} agentConfigured=${agentOk} hasResponseUrl=${Boolean(getActiveReplyUrl(streamId))}`, + ); + streamStore.updateStream(streamId, (s) => { + s.fallbackMode = "timeout"; + s.finished = true; + s.content = prompt; + s.fallbackPromptSentAt = s.fallbackPromptSentAt ?? Date.now(); + }); + try { + await sendBotFallbackPromptNow({ streamId, text: prompt }); + logVerbose(target, `fallback(timeout): 群内提示已推送`); + } catch (err) { + target.runtime.error?.(`wecom bot fallback prompt push failed (timeout) streamId=${streamId}: ${String(err)}`); + } + return; + } + + const mediaUrls = payload.mediaUrls || (payload.mediaUrl ? [payload.mediaUrl] : []); + for (const mediaPath of mediaUrls) { + try { + let buf: Buffer; + let contentType: string | undefined; + let filename: string; + + const looksLikeUrl = /^https?:\/\//i.test(mediaPath); + + if (looksLikeUrl) { + const loaded = await core.channel.media.fetchRemoteMedia({ url: mediaPath }); + buf = loaded.buffer; + contentType = loaded.contentType; + filename = loaded.fileName ?? "attachment"; + } else { + const fs = await import("node:fs/promises"); + const pathModule = await import("node:path"); + buf = await fs.readFile(mediaPath); + filename = pathModule.basename(mediaPath); + const ext = pathModule.extname(mediaPath).slice(1).toLowerCase(); + const imageExts: Record = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp", bmp: "image/bmp" }; + contentType = imageExts[ext] ?? "application/octet-stream"; + } + + if (contentType?.startsWith("image/")) { + const base64 = buf.toString("base64"); + const md5 = crypto.createHash("md5").update(buf).digest("hex"); + current.images.push({ base64, md5 }); + logVerbose(target, `media: 识别为图片 contentType=${contentType} filename=${filename}`); + } else { + // Non-image media: Bot 不支持原样发送(尤其群聊),统一切换到 Agent 私信兜底,并在 Bot 会话里提示用户。 + const agentCfg = resolveAgentAccountOrUndefined(config); + const agentOk = Boolean(agentCfg); + const alreadySent = current.agentMediaKeys.includes(mediaPath); + logVerbose( + target, + `fallback(media): 检测到非图片文件 chatType=${current.chatType} contentType=${contentType ?? "unknown"} filename=${filename} agentConfigured=${agentOk} alreadySent=${alreadySent} hasResponseUrl=${Boolean(getActiveReplyUrl(streamId))}`, + ); + + if (agentCfg && !alreadySent && current.userId) { + try { + await sendAgentDmMedia({ + agent: agentCfg, + userId: current.userId, + mediaUrlOrPath: mediaPath, + contentType, + filename, + }); + logVerbose(target, `fallback(media): 文件已通过 Agent 私信发送 user=${current.userId}`); + streamStore.updateStream(streamId, (s) => { + s.agentMediaKeys = Array.from(new Set([...(s.agentMediaKeys ?? []), mediaPath])); + }); + } catch (err) { + target.runtime.error?.(`wecom agent dm media failed: ${String(err)}`); + } + } + + if (!current.fallbackMode) { + const prompt = buildFallbackPrompt({ + kind: "media", + agentConfigured: agentOk, + userId: current.userId, + filename, + chatType: current.chatType, + }); + streamStore.updateStream(streamId, (s) => { + s.fallbackMode = "media"; + s.finished = true; + s.content = prompt; + s.fallbackPromptSentAt = s.fallbackPromptSentAt ?? Date.now(); + }); + try { + await sendBotFallbackPromptNow({ streamId, text: prompt }); + logVerbose(target, `fallback(media): 群内提示已推送`); + } catch (err) { + target.runtime.error?.(`wecom bot fallback prompt push failed (media) streamId=${streamId}: ${String(err)}`); + } + } + return; + } + } catch (err) { + target.runtime.error?.(`Failed to process outbound media: ${mediaPath}: ${String(err)}`); + } + } + + // If we are in fallback mode, do not continue updating the bot stream content. + const mode = streamStore.getStream(streamId)?.fallbackMode; + if (mode) return; + + const nextText = current.content + ? `${current.content}\n\n${text}`.trim() + : text.trim(); + + streamStore.updateStream(streamId, (s) => { + s.content = truncateUtf8Bytes(nextText, STREAM_MAX_BYTES); + if (current.images?.length) s.images = current.images; // ensure images are saved + }); + target.statusSink?.({ lastOutboundAt: Date.now() }); + }, + onError: (err, info) => { + target.runtime.error?.(`[${account.accountId}] wecom ${info.kind} reply failed: ${String(err)}`); + }, + }, + }); + + // /new /reset:OpenClaw 核心会通过 routeReply 发送英文回执(✅ New session started...), + // 但 WeCom 双模式下这条回执可能会走 Agent 私信,导致“从 Bot 发,却在 Agent 再回一条”。 + // 该英文回执已在 wecom outbound 层做抑制/改写;这里补一个“同会话中文回执”,保证用户可理解。 + if (isResetCommand) { + const current = streamStore.getStream(streamId); + const hasAnyContent = Boolean(current?.content?.trim()); + if (current && !hasAnyContent) { + const ackText = resetCommandKind === "reset" ? "✅ 已重置会话。" : "✅ 已开启新会话。"; + streamStore.updateStream(streamId, (s) => { + s.content = ackText; + s.finished = true; + }); + } + } + + streamStore.markFinished(streamId); + + // Timeout fallback final delivery (Agent DM): send once after the agent run completes. + const finishedState = streamStore.getStream(streamId); + if (finishedState?.fallbackMode === "timeout" && !finishedState.finalDeliveredAt) { + const agentCfg = resolveAgentAccountOrUndefined(config); + if (!agentCfg) { + // Agent not configured - group prompt already explains the situation. + streamStore.updateStream(streamId, (s) => { s.finalDeliveredAt = Date.now(); }); + } else if (finishedState.userId) { + const dmText = (finishedState.dmContent ?? "").trim(); + if (dmText) { + try { + logVerbose(target, `fallback(timeout): 开始通过 Agent 私信发送剩余内容 user=${finishedState.userId} len=${dmText.length}`); + await sendAgentDmText({ agent: agentCfg, userId: finishedState.userId, text: dmText, core }); + logVerbose(target, `fallback(timeout): Agent 私信发送完成 user=${finishedState.userId}`); + } catch (err) { + target.runtime.error?.(`wecom agent dm text failed (timeout): ${String(err)}`); + } + } + streamStore.updateStream(streamId, (s) => { s.finalDeliveredAt = Date.now(); }); + } + } + + // Bot 群聊图片兜底: + // 依赖企业微信的“流式消息刷新”回调来拉取最终消息有时会出现客户端未能及时拉取到最后一帧的情况, + // 导致最终的图片(msg_item)没有展示。若存在 response_url,则在流结束后主动推送一次最终 stream 回复。 + // 注:该行为以 response_url 是否可用为准;失败则仅记录日志,不影响原有刷新链路。 + if (chatType === "group") { + const state = streamStore.getStream(streamId); + const hasImages = Boolean(state?.images?.length); + const responseUrl = getActiveReplyUrl(streamId); + if (state && hasImages && responseUrl) { + const finalReply = buildStreamReplyFromState(state) as unknown as Record; + try { + await useActiveReplyOnce(streamId, async ({ responseUrl, proxyUrl }) => { + const res = await wecomFetch( + responseUrl, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(finalReply), + }, + { proxyUrl, timeoutMs: LIMITS.REQUEST_TIMEOUT_MS }, + ); + if (!res.ok) { + throw new Error(`final stream push failed: ${res.status}`); + } + }); + logVerbose(target, `final stream pushed via response_url (group) streamId=${streamId}, images=${state.images?.length ?? 0}`); + } catch (err) { + target.runtime.error?.(`final stream push via response_url failed (group) streamId=${streamId}: ${String(err)}`); + } + } + } + + // 推进会话队列:如果 2/3 已排队,当前批次结束后自动开始下一批次 + logInfo(target, `queue: 当前批次结束,尝试推进下一批 streamId=${streamId}`); + + // 体验优化:如果本批次中有“回执流”(ack stream)(例如 3 被合并到 2),则在批次结束时更新这些回执流, + // 避免它们永久停留在“已合并排队处理中…”。 + const ackStreamIds = streamStore.drainAckStreamsForBatch(streamId); + if (ackStreamIds.length > 0) { + const mergedDoneHint = "✅ 已合并处理完成,请查看上一条回复。"; + for (const ackId of ackStreamIds) { + streamStore.updateStream(ackId, (s) => { + s.content = mergedDoneHint; + s.finished = true; + }); + } + logInfo(target, `queue: 已更新回执流 count=${ackStreamIds.length} batchStreamId=${streamId}`); + } + + streamStore.onStreamFinished(streamId); +} + +function formatQuote(quote: WecomInboundQuote): string { + const type = quote.msgtype ?? ""; + if (type === "text") return quote.text?.content || ""; + if (type === "image") return `[引用: 图片] ${quote.image?.url || ""}`; + if (type === "mixed" && quote.mixed?.msg_item) { + const items = quote.mixed.msg_item.map((item) => { + if (item.msgtype === "text") return item.text?.content; + if (item.msgtype === "image") return `[图片] ${item.image?.url || ""}`; + return ""; + }).filter(Boolean).join(" "); + return `[引用: 图文] ${items}`; + } + if (type === "voice") return `[引用: 语音] ${quote.voice?.content || ""}`; + if (type === "file") return `[引用: 文件] ${quote.file?.url || ""}`; + return ""; +} + +function buildInboundBody(msg: WecomInboundMessage): string { + let body = ""; + const msgtype = String(msg.msgtype ?? "").toLowerCase(); + + if (msgtype === "text") body = (msg as any).text?.content || ""; + else if (msgtype === "voice") body = (msg as any).voice?.content || "[voice]"; + else if (msgtype === "mixed") { + const items = (msg as any).mixed?.msg_item; + if (Array.isArray(items)) { + body = items.map((item: any) => { + const t = String(item?.msgtype ?? "").toLowerCase(); + if (t === "text") return item?.text?.content || ""; + if (t === "image") return `[image] ${item?.image?.url || ""}`; + return `[${t || "item"}]`; + }).filter(Boolean).join("\n"); + } else body = "[mixed]"; + } else if (msgtype === "image") body = `[image] ${(msg as any).image?.url || ""}`; + else if (msgtype === "file") body = `[file] ${(msg as any).file?.url || ""}`; + else if (msgtype === "event") body = `[event] ${(msg as any).event?.eventtype || ""}`; + else if (msgtype === "stream") body = `[stream_refresh] ${(msg as any).stream?.id || ""}`; + else body = msgtype ? `[${msgtype}]` : ""; + + const quote = (msg as any).quote; + if (quote) { + const quoteText = formatQuote(quote).trim(); + if (quoteText) body += `\n\n> ${quoteText}`; + } + return body; +} + +/** + * **registerWecomWebhookTarget (注册 Webhook 目标)** + * + * 注册一个 Bot 模式的接收端点。 + * 同时会触发清理定时器的检查(如果有新注册,确保定时器运行)。 + * 返回一个注销函数。 + */ +export function registerWecomWebhookTarget(target: WecomWebhookTarget): () => void { + const key = normalizeWebhookPath(target.path); + const normalizedTarget = { ...target, path: key }; + const existing = webhookTargets.get(key) ?? []; + webhookTargets.set(key, [...existing, normalizedTarget]); + ensurePruneTimer(); + return () => { + const updated = (webhookTargets.get(key) ?? []).filter((entry) => entry !== normalizedTarget); + if (updated.length > 0) webhookTargets.set(key, updated); + else webhookTargets.delete(key); + checkPruneTimer(); + }; +} + +/** + * 注册 Agent 模式 Webhook Target + */ +export function registerAgentWebhookTarget(target: AgentWebhookTarget): () => void { + const key = WEBHOOK_PATHS.AGENT; + agentTargets.set(key, target); + ensurePruneTimer(); + return () => { + agentTargets.delete(key); + checkPruneTimer(); + }; +} + +/** + * **handleWecomWebhookRequest (HTTP 请求入口)** + * + * 处理来自企业微信的所有 Webhook 请求。 + * 职责: + * 1. 路由分发:区分 Agent 模式 (`/wecom/agent`) 和 Bot 模式 (其他路径)。 + * 2. 安全校验:验证企业微信签名 (Signature)。 + * 3. 消息解密:处理企业微信的加密包。 + * 4. 响应处理: + * - GET 请求:处理 EchoStr 验证。 + * - POST 请求:接收消息,放入 StreamStore,返回流式 First Chunk。 + */ +export async function handleWecomWebhookRequest(req: IncomingMessage, res: ServerResponse): Promise { + const path = resolvePath(req); + const reqId = crypto.randomUUID().slice(0, 8); + const remote = req.socket?.remoteAddress ?? "unknown"; + const ua = String(req.headers["user-agent"] ?? ""); + const cl = String(req.headers["content-length"] ?? ""); + // 不输出敏感参数内容,仅输出是否存在(排查“有没有打到网关/有没有带签名参数”) + const q = resolveQueryParams(req); + const hasTimestamp = Boolean(q.get("timestamp")); + const hasNonce = Boolean(q.get("nonce")); + const hasEchostr = Boolean(q.get("echostr")); + const hasMsgSig = Boolean(q.get("msg_signature")); + const hasSignature = Boolean(q.get("signature")); + console.log( + `[wecom] inbound(http): reqId=${reqId} path=${path} method=${req.method ?? "UNKNOWN"} remote=${remote} ua=${ua ? `"${ua}"` : "N/A"} contentLength=${cl || "N/A"} query={timestamp:${hasTimestamp},nonce:${hasNonce},echostr:${hasEchostr},msg_signature:${hasMsgSig},signature:${hasSignature}}`, + ); + + // Agent 模式路由: /wecom/agent + if (path === WEBHOOK_PATHS.AGENT) { + const agentTarget = agentTargets.get(WEBHOOK_PATHS.AGENT); + if (agentTarget) { + const core = getWecomRuntime(); + const query = resolveQueryParams(req); + const timestamp = query.get("timestamp") ?? ""; + const nonce = query.get("nonce") ?? ""; + const hasSig = Boolean(query.get("msg_signature")); + const remote = req.socket?.remoteAddress ?? "unknown"; + agentTarget.runtime.log?.( + `[wecom] inbound(agent): reqId=${reqId} method=${req.method ?? "UNKNOWN"} remote=${remote} timestamp=${timestamp ? "yes" : "no"} nonce=${nonce ? "yes" : "no"} msg_signature=${hasSig ? "yes" : "no"}`, + ); + return handleAgentWebhook({ + req, + res, + agent: agentTarget.agent, + config: agentTarget.config, + core, + log: agentTarget.runtime.log, + error: agentTarget.runtime.error, + }); + } + // 未注册 Agent,返回 404 + res.statusCode = 404; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(`agent not configured - Agent 模式未配置,请运行 openclaw onboarding${ERROR_HELP}`); + return true; + } + + // Bot 模式路由: /wecom, /wecom/bot + const targets = webhookTargets.get(path); + if (!targets || targets.length === 0) return false; + + const query = resolveQueryParams(req); + const timestamp = query.get("timestamp") ?? ""; + const nonce = query.get("nonce") ?? ""; + const signature = resolveSignatureParam(query); + + if (req.method === "GET") { + const echostr = query.get("echostr") ?? ""; + const target = targets.find(c => c.account.token && verifyWecomSignature({ token: c.account.token, timestamp, nonce, encrypt: echostr, signature })); + if (!target || !target.account.encodingAESKey) { + res.statusCode = 401; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(`unauthorized - Bot 签名验证失败,请检查 Token 配置${ERROR_HELP}`); + return true; + } + try { + const plain = decryptWecomEncrypted({ encodingAESKey: target.account.encodingAESKey, receiveId: target.account.receiveId, encrypt: echostr }); + res.statusCode = 200; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(plain); + return true; + } catch (err) { + res.statusCode = 400; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(`decrypt failed - 解密失败,请检查 EncodingAESKey${ERROR_HELP}`); + return true; + } + } + + if (req.method !== "POST") return false; + + const body = await readJsonBody(req, 1024 * 1024); + if (!body.ok) { + res.statusCode = 400; + res.end(body.error || "invalid payload"); + return true; + } + const record = body.value as any; + const encrypt = String(record?.encrypt ?? record?.Encrypt ?? ""); + // Bot POST 回调体积/字段诊断(不输出 encrypt 内容) + console.log( + `[wecom] inbound(bot): reqId=${reqId} rawJsonBytes=${Buffer.byteLength(JSON.stringify(record), "utf8")} hasEncrypt=${Boolean(encrypt)} encryptLen=${encrypt.length}`, + ); + const target = targets.find(c => c.account.token && verifyWecomSignature({ token: c.account.token, timestamp, nonce, encrypt, signature })); + if (!target || !target.account.configured || !target.account.encodingAESKey) { + res.statusCode = 401; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(`unauthorized - Bot 签名验证失败${ERROR_HELP}`); + return true; + } + + // 选定 target 后,把 reqId 带入结构化日志,方便串联排查 + logInfo(target, `inbound(bot): reqId=${reqId} selectedAccount=${target.account.accountId} path=${path}`); + + let plain: string; + try { + plain = decryptWecomEncrypted({ encodingAESKey: target.account.encodingAESKey, receiveId: target.account.receiveId, encrypt }); + } catch (err) { + res.statusCode = 400; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(`decrypt failed - 解密失败${ERROR_HELP}`); + return true; + } + + const msg = parseWecomPlainMessage(plain); + const msgtype = String(msg.msgtype ?? "").toLowerCase(); + const proxyUrl = resolveWecomEgressProxyUrl(target.config); + + // Handle Event + if (msgtype === "event") { + const eventtype = String((msg as any).event?.eventtype ?? "").toLowerCase(); + + if (eventtype === "template_card_event") { + const msgid = msg.msgid ? String(msg.msgid) : undefined; + + // Dedupe: skip if already processed this event + if (msgid && streamStore.getStreamByMsgId(msgid)) { + logVerbose(target, `template_card_event: already processed msgid=${msgid}, skipping`); + jsonOk(res, buildEncryptedJsonReply({ account: target.account, plaintextJson: {}, nonce, timestamp })); + return true; + } + + const cardEvent = (msg as any).event?.template_card_event; + let interactionDesc = `[卡片交互] 按钮: ${cardEvent?.event_key || "unknown"}`; + if (cardEvent?.selected_items?.selected_item?.length) { + const selects = cardEvent.selected_items.selected_item.map((i: any) => `${i.question_key}=${i.option_ids?.option_id?.join(",")}`); + interactionDesc += ` 选择: ${selects.join("; ")}`; + } + if (cardEvent?.task_id) interactionDesc += ` (任务ID: ${cardEvent.task_id})`; + + jsonOk(res, buildEncryptedJsonReply({ account: target.account, plaintextJson: {}, nonce, timestamp })); + + const streamId = streamStore.createStream({ msgid }); + streamStore.markStarted(streamId); + storeActiveReply(streamId, msg.response_url); + const core = getWecomRuntime(); + startAgentForStream({ + target: { ...target, core }, + accountId: target.account.accountId, + msg: { ...msg, msgtype: "text", text: { content: interactionDesc } } as any, + streamId, + }).catch(err => target.runtime.error?.(`interaction failed: ${String(err)}`)); + return true; + } + + if (eventtype === "enter_chat") { + const welcome = target.account.config.welcomeText?.trim(); + jsonOk(res, buildEncryptedJsonReply({ account: target.account, plaintextJson: welcome ? { msgtype: "text", text: { content: welcome } } : {}, nonce, timestamp })); + return true; + } + + jsonOk(res, buildEncryptedJsonReply({ account: target.account, plaintextJson: {}, nonce, timestamp })); + return true; + } + + // Handle Stream Refresh + if (msgtype === "stream") { + const streamId = String((msg as any).stream?.id ?? "").trim(); + const state = streamStore.getStream(streamId); + const reply = state ? buildStreamReplyFromState(state) : buildStreamReplyFromState({ streamId: streamId || "unknown", createdAt: Date.now(), updatedAt: Date.now(), started: true, finished: true, content: "" }); + jsonOk(res, buildEncryptedJsonReply({ account: target.account, plaintextJson: reply, nonce, timestamp })); + return true; + } + + // Handle Message (with Debounce) + try { + const userid = resolveWecomSenderUserId(msg) || "unknown"; + const chatId = msg.chattype === "group" ? (msg.chatid?.trim() || "unknown") : userid; + const conversationKey = `wecom:${target.account.accountId}:${userid}:${chatId}`; + const msgContent = buildInboundBody(msg); + + logInfo( + target, + `inbound: msgtype=${msgtype} chattype=${String(msg.chattype ?? "")} chatid=${String(msg.chatid ?? "")} from=${userid} msgid=${String(msg.msgid ?? "")} hasResponseUrl=${Boolean((msg as any).response_url)}`, + ); + + // 去重: 若 msgid 已存在于 StreamStore,说明是重试请求,直接返回占位符 + if (msg.msgid) { + const existingStreamId = streamStore.getStreamByMsgId(String(msg.msgid)); + if (existingStreamId) { + logInfo(target, `message: 重复的 msgid=${msg.msgid},跳过处理并返回占位符 streamId=${existingStreamId}`); + jsonOk(res, buildEncryptedJsonReply({ + account: target.account, + plaintextJson: buildStreamPlaceholderReply({ + streamId: existingStreamId, + placeholderContent: target.account.config.streamPlaceholderContent + }), + nonce, + timestamp + })); + return true; + } + } + + // 加入 Pending 队列 (防抖/聚合) + // 消息不会立即处理,而是等待防抖计时器结束(flushPending)后统一触发 + const { streamId, status } = streamStore.addPendingMessage({ + conversationKey, + target, + msg, + msgContent, + nonce, + timestamp, + debounceMs: (target.account.config as any).debounceMs + }); + + // 无论是否新建,都尽量保存 response_url(用于兜底提示/最终帧推送) + if (msg.response_url) { + storeActiveReply(streamId, msg.response_url, proxyUrl); + } + + const defaultPlaceholder = target.account.config.streamPlaceholderContent; + const queuedPlaceholder = "已收到,已排队处理中..."; + const mergedQueuedPlaceholder = "已收到,已合并排队处理中..."; + + if (status === "active_new") { + jsonOk(res, buildEncryptedJsonReply({ + account: target.account, + plaintextJson: buildStreamPlaceholderReply({ + streamId, + placeholderContent: defaultPlaceholder + }), + nonce, + timestamp + })); + return true; + } + + if (status === "queued_new") { + logInfo(target, `queue: 已进入下一批次 streamId=${streamId} msgid=${String(msg.msgid ?? "")}`); + jsonOk(res, buildEncryptedJsonReply({ + account: target.account, + plaintextJson: buildStreamPlaceholderReply({ + streamId, + placeholderContent: queuedPlaceholder + }), + nonce, + timestamp + })); + return true; + } + + // active_merged / queued_merged:合并进某个批次,但本条消息不应该刷出“完整答案”,否则用户会看到重复内容。 + // 做法:为本条 msgid 创建一个“回执 stream”,先显示“已合并排队”,并在批次结束时自动更新为“已合并处理完成”。 + const ackStreamId = streamStore.createStream({ msgid: String(msg.msgid ?? "") || undefined }); + streamStore.updateStream(ackStreamId, (s) => { + s.finished = false; + s.started = true; + s.content = mergedQueuedPlaceholder; + }); + if (msg.msgid) streamStore.setStreamIdForMsgId(String(msg.msgid), ackStreamId); + streamStore.addAckStreamForBatch({ batchStreamId: streamId, ackStreamId }); + logInfo(target, `queue: 已合并排队(回执流) ackStreamId=${ackStreamId} mergedIntoStreamId=${streamId} msgid=${String(msg.msgid ?? "")}`); + jsonOk(res, buildEncryptedJsonReply({ + account: target.account, + plaintextJson: buildStreamTextPlaceholderReply({ streamId: ackStreamId, content: mergedQueuedPlaceholder }), + nonce, + timestamp + })); + return true; + } catch (err) { + target.runtime.error?.(`[wecom] Bot message handler crashed: ${String(err)}`); + // 尽量返回 200,避免企微重试风暴;同时给一个可见的错误文本 + jsonOk(res, buildEncryptedJsonReply({ + account: target.account, + plaintextJson: { msgtype: "text", text: { content: "服务内部错误:Bot 处理异常,请稍后重试。" } }, + nonce, + timestamp + })); + return true; + } +} + +export async function sendActiveMessage(streamId: string, content: string): Promise { + await useActiveReplyOnce(streamId, async ({ responseUrl, proxyUrl }) => { + const res = await wecomFetch( + responseUrl, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ msgtype: "text", text: { content } }), + }, + { proxyUrl, timeoutMs: LIMITS.REQUEST_TIMEOUT_MS }, + ); + if (!res.ok) { + throw new Error(`active send failed: ${res.status}`); + } + }); +} diff --git a/wecom/src/monitor.webhook.test.ts b/wecom/src/monitor.webhook.test.ts new file mode 100644 index 00000000..6e6cd4bf --- /dev/null +++ b/wecom/src/monitor.webhook.test.ts @@ -0,0 +1,311 @@ +import { IncomingMessage, ServerResponse } from "node:http"; +import { Socket } from "node:net"; + +import { describe, expect, it } from "vitest"; + +import type { OpenClawConfig } from "openclaw/plugin-sdk"; + +import type { ResolvedWecomAccount } from "./types.js"; +import { computeWecomMsgSignature, decryptWecomEncrypted, encryptWecomPlaintext } from "./crypto.js"; +import { handleWecomWebhookRequest, registerWecomWebhookTarget } from "./monitor.js"; + +function createMockRequest(params: { + method: "GET" | "POST"; + url: string; + body?: unknown; +}): IncomingMessage { + const socket = new Socket(); + const req = new IncomingMessage(socket); + req.method = params.method; + req.url = params.url; + if (params.method === "POST") { + req.push(JSON.stringify(params.body ?? {})); + } + req.push(null); + return req; +} + +function createMockResponse(): ServerResponse & { + _getData: () => string; + _getStatusCode: () => number; +} { + const req = new IncomingMessage(new Socket()); + const res = new ServerResponse(req); + let data = ""; + res.write = (chunk: any) => { + data += String(chunk); + return true; + }; + res.end = (chunk: any) => { + if (chunk) data += String(chunk); + return res; + }; + (res as any)._getData = () => data; + (res as any)._getStatusCode = () => res.statusCode; + return res as any; +} + +describe("handleWecomWebhookRequest", () => { + const token = "test-token"; + const encodingAESKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"; + + it("handles GET url verification", async () => { + const account: ResolvedWecomAccount = { + accountId: "default", + name: "Test", + enabled: true, + configured: true, + token, + encodingAESKey, + receiveId: "", + config: { webhookPath: "/hook", token, encodingAESKey }, + }; + + const unregister = registerWecomWebhookTarget({ + account, + config: {} as OpenClawConfig, + runtime: {}, + core: {} as any, + path: "/hook", + }); + + try { + const timestamp = "13500001234"; + const nonce = "123412323"; + const echostr = encryptWecomPlaintext({ + encodingAESKey, + receiveId: "", + plaintext: "ping", + }); + const msg_signature = computeWecomMsgSignature({ token, timestamp, nonce, encrypt: echostr }); + const req = createMockRequest({ + method: "GET", + url: `/hook?msg_signature=${encodeURIComponent(msg_signature)}×tamp=${encodeURIComponent(timestamp)}&nonce=${encodeURIComponent(nonce)}&echostr=${encodeURIComponent(echostr)}`, + }); + const res = createMockResponse(); + const handled = await handleWecomWebhookRequest(req, res); + expect(handled).toBe(true); + expect(res._getStatusCode()).toBe(200); + expect(res._getData()).toBe("ping"); + } finally { + unregister(); + } + }); + + it("handles POST callback and returns encrypted stream placeholder", async () => { + const account: ResolvedWecomAccount = { + accountId: "default", + name: "Test", + enabled: true, + configured: true, + token, + encodingAESKey, + receiveId: "", + config: { webhookPath: "/hook", token, encodingAESKey }, + }; + + const unregister = registerWecomWebhookTarget({ + account, + config: {} as OpenClawConfig, + runtime: {}, + core: {} as any, + path: "/hook", + }); + + try { + const timestamp = "1700000000"; + const nonce = "nonce"; + const plain = JSON.stringify({ + msgid: "MSGID", + aibotid: "AIBOTID", + chattype: "single", + from: { userid: "USERID" }, + response_url: "RESPONSEURL", + msgtype: "text", + text: { content: "hello" }, + }); + const encrypt = encryptWecomPlaintext({ encodingAESKey, receiveId: "", plaintext: plain }); + const msg_signature = computeWecomMsgSignature({ token, timestamp, nonce, encrypt }); + + const req = createMockRequest({ + method: "POST", + url: `/hook?msg_signature=${encodeURIComponent(msg_signature)}×tamp=${encodeURIComponent(timestamp)}&nonce=${encodeURIComponent(nonce)}`, + body: { encrypt }, + }); + const res = createMockResponse(); + const handled = await handleWecomWebhookRequest(req, res); + expect(handled).toBe(true); + expect(res._getStatusCode()).toBe(200); + + const json = JSON.parse(res._getData()) as any; + expect(typeof json.encrypt).toBe("string"); + expect(typeof json.msgsignature).toBe("string"); + expect(typeof json.timestamp).toBe("string"); + expect(typeof json.nonce).toBe("string"); + + const replyPlain = decryptWecomEncrypted({ + encodingAESKey, + receiveId: "", + encrypt: json.encrypt, + }); + const reply = JSON.parse(replyPlain) as any; + expect(reply.msgtype).toBe("stream"); + expect(reply.stream?.content).toBe("1"); + expect(reply.stream?.finish).toBe(false); + expect(typeof reply.stream?.id).toBe("string"); + expect(reply.stream?.id.length).toBeGreaterThan(0); + + const expectedSig = computeWecomMsgSignature({ + token, + timestamp: String(json.timestamp), + nonce: String(json.nonce), + encrypt: String(json.encrypt), + }); + expect(json.msgsignature).toBe(expectedSig); + } finally { + unregister(); + } + }); + + it("supports custom streamPlaceholderContent", async () => { + const account: ResolvedWecomAccount = { + accountId: "default", + name: "Test", + enabled: true, + configured: true, + token, + encodingAESKey, + receiveId: "", + config: { webhookPath: "/hook", token, encodingAESKey, streamPlaceholderContent: "正在思考..." }, + }; + + const unregister = registerWecomWebhookTarget({ + account, + config: {} as OpenClawConfig, + runtime: {}, + core: {} as any, + path: "/hook", + }); + + try { + const timestamp = "1700000001"; + const nonce = "nonce2"; + const plain = JSON.stringify({ + msgid: "MSGID2", + aibotid: "AIBOTID", + chattype: "single", + from: { userid: "USERID2" }, + response_url: "RESPONSEURL", + msgtype: "text", + text: { content: "hello" }, + }); + const encrypt = encryptWecomPlaintext({ encodingAESKey, receiveId: "", plaintext: plain }); + const msg_signature = computeWecomMsgSignature({ token, timestamp, nonce, encrypt }); + + const req = createMockRequest({ + method: "POST", + url: `/hook?msg_signature=${encodeURIComponent(msg_signature)}×tamp=${encodeURIComponent(timestamp)}&nonce=${encodeURIComponent(nonce)}`, + body: { encrypt }, + }); + const res = createMockResponse(); + const handled = await handleWecomWebhookRequest(req, res); + expect(handled).toBe(true); + expect(res._getStatusCode()).toBe(200); + + const json = JSON.parse(res._getData()) as any; + const replyPlain = decryptWecomEncrypted({ + encodingAESKey, + receiveId: "", + encrypt: json.encrypt, + }); + const reply = JSON.parse(replyPlain) as any; + expect(reply.msgtype).toBe("stream"); + expect(reply.stream?.content).toBe("正在思考..."); + expect(reply.stream?.finish).toBe(false); + } finally { + unregister(); + } + }); + + it("returns a queued stream for 2, and an ack stream for merged follow-ups", async () => { + const token = "TOKEN"; + const encodingAESKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"; + const account: any = { + accountId: "default", + enabled: true, + configured: true, + token, + encodingAESKey, + receiveId: "", + config: { + streamPlaceholderContent: "正在思考...", + debounceMs: 10_000, + }, + }; + + const unregister = registerWecomWebhookTarget({ + account, + config: {} as OpenClawConfig, + runtime: {}, + core: {} as any, + path: "/hook-merge", + }); + + try { + const timestamp = "1700000002"; + const nonce = "nonce-merge"; + + const makeReq = (msgid: string) => { + const plain = JSON.stringify({ + msgid, + aibotid: "AIBOTID", + chattype: "single", + from: { userid: "USERID_QUEUE" }, + response_url: "RESPONSEURL", + msgtype: "text", + text: { content: "hello" }, + }); + const encrypt = encryptWecomPlaintext({ encodingAESKey, receiveId: "", plaintext: plain }); + const msg_signature = computeWecomMsgSignature({ token, timestamp, nonce, encrypt }); + return createMockRequest({ + method: "POST", + url: `/hook-merge?msg_signature=${encodeURIComponent(msg_signature)}×tamp=${encodeURIComponent(timestamp)}&nonce=${encodeURIComponent(nonce)}`, + body: { encrypt }, + }); + }; + + const res1 = createMockResponse(); + await handleWecomWebhookRequest(makeReq("MSGID-M1"), res1); + const json1 = JSON.parse(res1._getData()) as any; + const replyPlain1 = decryptWecomEncrypted({ encodingAESKey, receiveId: "", encrypt: json1.encrypt }); + const reply1 = JSON.parse(replyPlain1) as any; + expect(reply1.msgtype).toBe("stream"); + expect(reply1.stream?.finish).toBe(false); + + const res2 = createMockResponse(); + await handleWecomWebhookRequest(makeReq("MSGID-M2"), res2); + const json2 = JSON.parse(res2._getData()) as any; + const replyPlain2 = decryptWecomEncrypted({ encodingAESKey, receiveId: "", encrypt: json2.encrypt }); + const reply2 = JSON.parse(replyPlain2) as any; + expect(reply2.msgtype).toBe("stream"); + expect(reply2.stream?.finish).toBe(false); + expect(reply2.stream?.id).not.toBe(reply1.stream?.id); + expect(reply2.stream?.content).toContain("排队"); + + const res3 = createMockResponse(); + await handleWecomWebhookRequest(makeReq("MSGID-M3"), res3); + const json3 = JSON.parse(res3._getData()) as any; + const replyPlain3 = decryptWecomEncrypted({ encodingAESKey, receiveId: "", encrypt: json3.encrypt }); + const reply3 = JSON.parse(replyPlain3) as any; + expect(reply3.msgtype).toBe("stream"); + // merged follow-up should get its own ack stream (not finished yet); + // it will be updated to a final hint after the merged batch completes. + expect(reply3.stream?.finish).toBe(false); + expect(reply3.stream?.id).not.toBe(reply1.stream?.id); + expect(reply3.stream?.id).not.toBe(reply2.stream?.id); + expect(reply3.stream?.content).toContain("合并"); + } finally { + unregister(); + } + }); +}); diff --git a/wecom/src/monitor/state.queue.test.ts b/wecom/src/monitor/state.queue.test.ts new file mode 100644 index 00000000..b3791836 --- /dev/null +++ b/wecom/src/monitor/state.queue.test.ts @@ -0,0 +1,185 @@ +import { describe, expect, test, vi } from "vitest"; + +import type { WecomInboundMessage } from "../types.js"; +import type { WecomWebhookTarget } from "./types.js"; +import { StreamStore } from "./state.js"; + +describe("wecom StreamStore queue", () => { + test("does not merge into active batch; flushes queued batch after active finishes", async () => { + vi.useFakeTimers(); + try { + const store = new StreamStore(); + const flushed: string[] = []; + store.setFlushHandler((pending) => flushed.push(pending.streamId)); + + const target = { + account: {} as any, + config: {} as any, + runtime: {}, + core: {} as any, + path: "/wecom", + } satisfies WecomWebhookTarget; + + const conversationKey = "wecom:default:U:C"; + + const msg1 = { msgid: "M1" } satisfies WecomInboundMessage; + const msg2 = { msgid: "M2" } satisfies WecomInboundMessage; + + const r1 = store.addPendingMessage({ + conversationKey, + target, + msg: msg1, + msgContent: "1", + nonce: "n", + timestamp: "t", + debounceMs: 10, + }); + const r2 = store.addPendingMessage({ + conversationKey, + target, + msg: msg2, + msgContent: "2", + nonce: "n", + timestamp: "t", + debounceMs: 10, + }); + + expect(r1.status).toBe("active_new"); + // 初始批次不接收合并:第二条进入 queued + expect(r2.status).toBe("queued_new"); + expect(r2.streamId).not.toBe(r1.streamId); + + // Follow-ups within queued should merge into queued (status queued_merged). + const r3 = store.addPendingMessage({ + conversationKey, + target, + msg: { msgid: "M3" } as any, + msgContent: "3", + nonce: "n", + timestamp: "t", + debounceMs: 10, + }); + expect(r3.status).toBe("queued_merged"); + expect(r3.streamId).toBe(r2.streamId); + + // Active batch flushes at debounce time. + await vi.advanceTimersByTimeAsync(11); + expect(flushed).toEqual([r1.streamId]); + + // Queued batch timer also fires, but cannot flush until active finishes. + await vi.advanceTimersByTimeAsync(11); + expect(flushed).toEqual([r1.streamId]); + + // Once the active stream finishes, queued batch is promoted and flushes immediately. + store.onStreamFinished(r1.streamId); + expect(flushed).toEqual([r1.streamId, r2.streamId]); + } finally { + vi.useRealTimers(); + } + }); + + test("merges into active batch when it has not started yet (even after promotion)", async () => { + vi.useFakeTimers(); + try { + const store = new StreamStore(); + const flushed: string[] = []; + store.setFlushHandler((pending) => flushed.push(pending.streamId)); + + const target = { + account: {} as any, + config: {} as any, + runtime: {}, + core: {} as any, + path: "/wecom", + } satisfies WecomWebhookTarget; + + const conversationKey = "wecom:default:U:C2"; + + // 1 becomes active and flushes; mark as started to simulate "processing started". + const r1 = store.addPendingMessage({ + conversationKey, + target, + msg: { msgid: "M1" } as any, + msgContent: "1", + nonce: "n", + timestamp: "t", + debounceMs: 10, + }); + store.markStarted(r1.streamId); + await vi.advanceTimersByTimeAsync(11); + expect(flushed).toEqual([r1.streamId]); + + // 2 enters queued with a longer debounce; it should NOT become readyToFlush yet. + const r2 = store.addPendingMessage({ + conversationKey, + target, + msg: { msgid: "M2" } as any, + msgContent: "2", + nonce: "n", + timestamp: "t", + debounceMs: 100, + }); + expect(flushed).toEqual([r1.streamId]); + + // Finish 1, promote 2 to active (but do NOT flush immediately since it's not readyToFlush). + store.onStreamFinished(r1.streamId); + expect(flushed).toEqual([r1.streamId]); + + // Now 2 is active, but (in real monitor) it may still be in debounce before markStarted. + // We simulate that by NOT calling markStarted. Follow-up should merge into active (same streamId). + const r3 = store.addPendingMessage({ + conversationKey, + target, + msg: { msgid: "M3" } as any, + msgContent: "3", + nonce: "n", + timestamp: "t", + debounceMs: 10, + }); + expect(r3.streamId).toBe(r2.streamId); + expect(r3.status).toBe("active_merged"); + } finally { + vi.useRealTimers(); + } + }); + + test("clears conversation state when idle so next message becomes active", async () => { + const store = new StreamStore(); + store.setFlushHandler(() => { }); + + const target = { + account: {} as any, + config: {} as any, + runtime: {}, + core: {} as any, + path: "/wecom", + } satisfies WecomWebhookTarget; + + const conversationKey = "wecom:default:U:idle"; + + const r1 = store.addPendingMessage({ + conversationKey, + target, + msg: { msgid: "M1" } as any, + msgContent: "1", + nonce: "n", + timestamp: "t", + debounceMs: 10, + }); + store.markStarted(r1.streamId); + store.markFinished(r1.streamId); + store.onStreamFinished(r1.streamId); + + const r2 = store.addPendingMessage({ + conversationKey, + target, + msg: { msgid: "M2" } as any, + msgContent: "2", + nonce: "n", + timestamp: "t", + debounceMs: 10, + }); + expect(r2.status).toBe("active_new"); + expect(r2.streamId).not.toBe(r1.streamId); + }); +}); diff --git a/wecom/src/monitor/state.ts b/wecom/src/monitor/state.ts new file mode 100644 index 00000000..09bd42af --- /dev/null +++ b/wecom/src/monitor/state.ts @@ -0,0 +1,514 @@ +import crypto from "node:crypto"; +import type { StreamState, PendingInbound, ActiveReplyState, WecomWebhookTarget } from "./types.js"; +import type { WecomInboundMessage } from "../types.js"; + +// Constants +export const LIMITS = { + STREAM_TTL_MS: 10 * 60 * 1000, + ACTIVE_REPLY_TTL_MS: 60 * 60 * 1000, + DEFAULT_DEBOUNCE_MS: 500, + STREAM_MAX_BYTES: 20_480, + REQUEST_TIMEOUT_MS: 15_000 +}; + +/** + * **StreamStore (流状态会话存储)** + * + * 管理企业微信回调的流式会话状态、消息去重和防抖聚合逻辑。 + * 负责维护 msgid 到 streamId 的映射,以及临时缓存待处理的 Pending 消息。 + */ +export class StreamStore { + private streams = new Map(); + private msgidToStreamId = new Map(); + private pendingInbounds = new Map(); + private conversationState = new Map(); + private streamIdToBatchKey = new Map(); + private batchStreamIdToAckStreamIds = new Map(); + private onFlush?: (pending: PendingInbound) => void; + + /** + * **setFlushHandler (设置防抖刷新回调)** + * + * 当防抖计时器结束时调用的处理函数。通常用于触发 Agent 进行消息处理。 + * @param handler 回调函数,接收聚合后的 PendingInbound 对象 + */ + public setFlushHandler(handler: (pending: PendingInbound) => void) { + this.onFlush = handler; + } + + /** + * **createStream (创建流会话)** + * + * 初始化一个新的流式会话状态。 + * @param params.msgid (可选) 企业微信消息 ID,用于后续去重映射 + * @returns 生成的 streamId (Hex 字符串) + */ + createStream(params: { msgid?: string; conversationKey?: string; batchKey?: string }): string { + const streamId = crypto.randomBytes(16).toString("hex"); + + if (params.msgid) { + this.msgidToStreamId.set(String(params.msgid), streamId); + } + + this.streams.set(streamId, { + streamId, + msgid: params.msgid, + conversationKey: params.conversationKey, + batchKey: params.batchKey, + createdAt: Date.now(), + updatedAt: Date.now(), + started: false, + finished: false, + content: "" + }); + + if (params.batchKey) { + this.streamIdToBatchKey.set(streamId, params.batchKey); + } + + return streamId; + } + + /** + * **getStream (获取流状态)** + * + * 根据 streamId 获取当前的会话状态。 + * @param streamId 流会话 ID + */ + getStream(streamId: string): StreamState | undefined { + return this.streams.get(streamId); + } + + /** + * **getStreamByMsgId (通过 msgid 查找流 ID)** + * + * 用于消息去重:检查该 msgid 是否已经关联由正在进行或已完成的流会话。 + * @param msgid 企业微信消息 ID + */ + getStreamByMsgId(msgid: string): string | undefined { + return this.msgidToStreamId.get(String(msgid)); + } + + setStreamIdForMsgId(msgid: string, streamId: string): void { + const key = String(msgid).trim(); + const value = String(streamId).trim(); + if (!key || !value) return; + this.msgidToStreamId.set(key, value); + } + + /** + * 将“回执流”(ack stream) 关联到某个“批次流”(batch stream)。 + * 用于:当用户连发多条消息被合并排队时,让后续消息的 stream 最终也能更新为可理解的提示,而不是永久停留在“已合并排队…”。 + */ + addAckStreamForBatch(params: { batchStreamId: string; ackStreamId: string }): void { + const batchStreamId = params.batchStreamId.trim(); + const ackStreamId = params.ackStreamId.trim(); + if (!batchStreamId || !ackStreamId) return; + const list = this.batchStreamIdToAckStreamIds.get(batchStreamId) ?? []; + list.push(ackStreamId); + this.batchStreamIdToAckStreamIds.set(batchStreamId, list); + } + + /** + * 取出并清空某个批次流关联的所有回执流。 + */ + drainAckStreamsForBatch(batchStreamId: string): string[] { + const key = batchStreamId.trim(); + if (!key) return []; + const list = this.batchStreamIdToAckStreamIds.get(key) ?? []; + this.batchStreamIdToAckStreamIds.delete(key); + return list; + } + + /** + * **updateStream (更新流状态)** + * + * 原子更新流状态,并自动刷新 updatedAt 时间戳。 + * @param streamId 流会话 ID + * @param mutator 状态修改函数 + */ + updateStream(streamId: string, mutator: (state: StreamState) => void): void { + const state = this.streams.get(streamId); + if (state) { + mutator(state); + state.updatedAt = Date.now(); + } + } + + /** + * **markStarted (标记流开始)** + * + * 标记该流会话已经开始处理(通常在 Agent 启动后调用)。 + */ + markStarted(streamId: string): void { + this.updateStream(streamId, (s) => { s.started = true; }); + } + + /** + * **markFinished (标记流结束)** + * + * 标记该流会话已完成,不再接收内容更新。 + */ + markFinished(streamId: string): void { + this.updateStream(streamId, (s) => { s.finished = true; }); + } + + /** + * **addPendingMessage (添加待处理消息 / 防抖聚合)** + * + * 将收到的消息加入待处理队列。如果相同 pendingKey 已存在,则是防抖聚合;否则创建新条目。 + * 会自动设置或重置防抖定时器。 + * + * @param params 消息参数 + * @returns { streamId, isNew } isNew=true 表示这是新的一组消息,需初始化 ActiveReply + */ + addPendingMessage(params: { + conversationKey: string; + target: WecomWebhookTarget; + msg: WecomInboundMessage; + msgContent: string; + nonce: string; + timestamp: string; + debounceMs?: number; + }): { streamId: string; status: "active_new" | "active_merged" | "queued_new" | "queued_merged" } { + const { conversationKey, target, msg, msgContent, nonce, timestamp, debounceMs } = params; + const effectiveDebounceMs = debounceMs ?? LIMITS.DEFAULT_DEBOUNCE_MS; + + const state = this.conversationState.get(conversationKey); + if (!state) { + // 第一批次(active) + const batchKey = conversationKey; + const streamId = this.createStream({ msgid: msg.msgid, conversationKey, batchKey }); + const pending: PendingInbound = { + streamId, + conversationKey, + batchKey, + target, + msg, + contents: [msgContent], + msgids: msg.msgid ? [msg.msgid] : [], + nonce, + timestamp, + createdAt: Date.now(), + timeout: setTimeout(() => { + this.requestFlush(batchKey); + }, effectiveDebounceMs) + }; + this.pendingInbounds.set(batchKey, pending); + this.conversationState.set(conversationKey, { activeBatchKey: batchKey, queue: [], nextSeq: 1 }); + return { streamId, status: "active_new" }; + } + + // 合并规则(排队语义): + // - 初始批次(batchKey===conversationKey)不接收合并:避免 1/2 都刷出同一份最终答案。 + // - 如果 active 批次是“排队批次”(batchKey!=conversationKey)且还没开始处理(started=false), + // 则允许把后续消息合并进该 active 批次(典型:1 很快结束,2 变 active 但还没开始跑,3 合并到 2)。 + const activeBatchKey = state.activeBatchKey; + const activeIsInitial = activeBatchKey === conversationKey; + const activePending = this.pendingInbounds.get(activeBatchKey); + if (activePending && !activeIsInitial) { + const activeStream = this.streams.get(activePending.streamId); + const activeStarted = Boolean(activeStream?.started); + if (!activeStarted) { + activePending.contents.push(msgContent); + if (msg.msgid) { + activePending.msgids.push(msg.msgid); + // 注意:不把该 msgid 映射到 active streamId(避免该消息最终也刷出同一份完整答案) + } + if (activePending.timeout) clearTimeout(activePending.timeout); + activePending.timeout = setTimeout(() => { + this.requestFlush(activeBatchKey); + }, effectiveDebounceMs); + return { streamId: activePending.streamId, status: "active_merged" }; + } + } + + // active 批次已经开始处理;后续消息进入队列批次(queued),并允许在队列批次内做防抖聚合。 + const queuedBatchKey = state.queue[0]; + if (queuedBatchKey) { + const existingQueued = this.pendingInbounds.get(queuedBatchKey); + if (existingQueued) { + existingQueued.contents.push(msgContent); + if (msg.msgid) { + existingQueued.msgids.push(msg.msgid); + // 注意:不把该 msgid 映射到 queued streamId(避免该消息最终也刷出同一份完整答案) + } + if (existingQueued.timeout) clearTimeout(existingQueued.timeout); + + existingQueued.timeout = setTimeout(() => { + this.requestFlush(queuedBatchKey); + }, effectiveDebounceMs); + return { streamId: existingQueued.streamId, status: "queued_merged" }; + } + } + + // 创建新的 queued 批次(会话只保留 1 个“下一批次”,后续消息继续合并到该批次) + const seq = state.nextSeq++; + const batchKey = `${conversationKey}#q${seq}`; + state.queue = [batchKey]; + const streamId = this.createStream({ msgid: msg.msgid, conversationKey, batchKey }); + const pending: PendingInbound = { + streamId, + conversationKey, + batchKey, + target, + msg, + contents: [msgContent], + msgids: msg.msgid ? [msg.msgid] : [], + nonce, + timestamp, + createdAt: Date.now(), + timeout: setTimeout(() => { + this.requestFlush(batchKey); + }, effectiveDebounceMs) + }; + this.pendingInbounds.set(batchKey, pending); + this.conversationState.set(conversationKey, state); + return { streamId, status: "queued_new" }; + } + + /** + * 请求刷新:如果该批次当前为 active,则立即 flush;否则标记 ready,等待前序批次完成后再 flush。 + */ + private requestFlush(batchKey: string): void { + const pending = this.pendingInbounds.get(batchKey); + if (!pending) return; + + const state = this.conversationState.get(pending.conversationKey); + const isActive = state?.activeBatchKey === batchKey; + if (!isActive) { + if (pending.timeout) { + clearTimeout(pending.timeout); + pending.timeout = null; + } + pending.readyToFlush = true; + return; + } + this.flushPending(batchKey); + } + + /** + * **flushPending (触发消息处理)** + * + * 内部方法:防抖时间结束后,将聚合的消息一次性推送给 flushHandler。 + */ + private flushPending(pendingKey: string): void { + const pending = this.pendingInbounds.get(pendingKey); + if (!pending) return; + + this.pendingInbounds.delete(pendingKey); + if (pending.timeout) { + clearTimeout(pending.timeout); + pending.timeout = null; + } + pending.readyToFlush = false; + + if (this.onFlush) { + this.onFlush(pending); + } + } + + /** + * 在一个 stream 完成后推进会话队列:将 queued 批次提升为 active,并在需要时触发 flush。 + */ + onStreamFinished(streamId: string): void { + const batchKey = this.streamIdToBatchKey.get(streamId); + const state = batchKey ? this.streams.get(streamId) : undefined; + const conversationKey = state?.conversationKey; + if (!batchKey || !conversationKey) return; + + const conv = this.conversationState.get(conversationKey); + if (!conv) return; + if (conv.activeBatchKey !== batchKey) return; + + const next = conv.queue.shift(); + if (!next) { + // 队列为空:会话已空闲。删除状态,避免后续消息被误判为“排队但永远不触发”。 + this.conversationState.delete(conversationKey); + return; + } + conv.activeBatchKey = next; + this.conversationState.set(conversationKey, conv); + + const pending = this.pendingInbounds.get(next); + if (!pending) return; + if (pending.readyToFlush) { + this.flushPending(next); + } + // 否则等待该批次自己的 debounce timer 到期后 requestFlush(next) 执行 + } + + /** + * **prune (清理过期状态)** + * + * 清理过期的流会话、msgid 映射以及残留的 Pending 消息。 + * @param now 当前时间戳 (毫秒) + */ + prune(now: number = Date.now()): void { + const streamCutoff = now - LIMITS.STREAM_TTL_MS; + + // 清理过期的流会话 + for (const [id, state] of this.streams.entries()) { + if (state.updatedAt < streamCutoff) { + this.streams.delete(id); + if (state.msgid) { + // 如果 msgid 映射仍指向该 stream,则一并移除 + if (this.msgidToStreamId.get(state.msgid) === id) { + this.msgidToStreamId.delete(state.msgid); + } + } + } + } + + // 清理悬空的 msgid 映射 (Double check) + for (const [msgid, id] of this.msgidToStreamId.entries()) { + if (!this.streams.has(id)) { + this.msgidToStreamId.delete(msgid); + } + } + + // 清理超时的 Pending 消息 (通常由 timeout 清理,此处作为兜底) + for (const [key, pending] of this.pendingInbounds.entries()) { + if (now - pending.createdAt > LIMITS.STREAM_TTL_MS) { + if (pending.timeout) clearTimeout(pending.timeout); + this.pendingInbounds.delete(key); + } + } + + // 清理 conversationState:active 已不存在且队列为空的会话 + for (const [convKey, conv] of this.conversationState.entries()) { + const activeExists = this.pendingInbounds.has(conv.activeBatchKey) || Array.from(this.streamIdToBatchKey.values()).includes(conv.activeBatchKey); + const hasQueue = conv.queue.length > 0; + if (!activeExists && !hasQueue) { + this.conversationState.delete(convKey); + } + } + } +} + +/** + * **ActiveReplyStore (主动回复地址存储)** + * + * 管理企业微信回调中的 `response_url` (用于被动回复转主动推送) 和 `proxyUrl`。 + * 支持 'once' (一次性) 或 'multi' (多次) 使用策略。 + */ +export class ActiveReplyStore { + private activeReplies = new Map(); + + /** + * @param policy 使用策略: "once" (默认,销毁式) 或 "multi" + */ + constructor(private policy: "once" | "multi" = "once") { } + + /** + * **store (存储回复地址)** + * + * 关联 streamId 与 response_url。 + */ + store(streamId: string, responseUrl?: string, proxyUrl?: string): void { + const url = responseUrl?.trim(); + if (!url) return; + this.activeReplies.set(streamId, { response_url: url, proxyUrl, createdAt: Date.now() }); + } + + /** + * **getUrl (获取回复地址)** + * + * 获取指定 streamId 关联的 response_url。 + */ + getUrl(streamId: string): string | undefined { + return this.activeReplies.get(streamId)?.response_url; + } + + /** + * **use (消耗回复地址)** + * + * 使用存储的 response_url 执行操作。 + * - 如果策略是 "once",第二次调用会抛错。 + * - 自动更新使用时间 (usedAt)。 + * + * @param streamId 流会话 ID + * @param fn 执行函数,接收 { responseUrl, proxyUrl } + */ + async use(streamId: string, fn: (params: { responseUrl: string; proxyUrl?: string }) => Promise): Promise { + const state = this.activeReplies.get(streamId); + if (!state?.response_url) { + return; // 无 URL 可用,安全跳过 + } + + if (this.policy === "once" && state.usedAt) { + throw new Error(`response_url already used for stream ${streamId} (Policy: once)`); + } + + try { + await fn({ responseUrl: state.response_url, proxyUrl: state.proxyUrl }); + state.usedAt = Date.now(); + } catch (err: unknown) { + state.lastError = err instanceof Error ? err.message : String(err); + throw err; + } + } + + /** + * **prune (清理过期地址)** + * + * 清理超过 TTL 的 active reply 记录。 + */ + prune(now: number = Date.now()): void { + const cutoff = now - LIMITS.ACTIVE_REPLY_TTL_MS; + for (const [id, state] of this.activeReplies.entries()) { + if (state.createdAt < cutoff) { + this.activeReplies.delete(id); + } + } + } +} + +/** + * **MonitorState (全局监控状态容器)** + * + * 模块单例,统一管理 StreamStore 和 ActiveReplyStore 实例。 + * 提供生命周期方法 (startPruning / stopPruning) 以自动清理过期数据。 + */ +class MonitorState { + /** 主要的流状态存储 */ + public readonly streamStore = new StreamStore(); + /** 主动回复地址存储 */ + public readonly activeReplyStore = new ActiveReplyStore("multi"); + + private pruneInterval?: NodeJS.Timeout; + + /** + * **startPruning (启动自动清理)** + * + * 启动定时器,定期清理过期的流和回复地址。应在插件有活跃 Target 时调用。 + * @param intervalMs 清理间隔 (默认 60s) + */ + public startPruning(intervalMs: number = 60_000): void { + if (this.pruneInterval) return; + this.pruneInterval = setInterval(() => { + const now = Date.now(); + this.streamStore.prune(now); + this.activeReplyStore.prune(now); + }, intervalMs); + } + + /** + * **stopPruning (停止自动清理)** + * + * 停止定时器。应在插件无活跃 Target 时调用以释放资源。 + */ + public stopPruning(): void { + if (this.pruneInterval) { + clearInterval(this.pruneInterval); + this.pruneInterval = undefined; + } + } +} + +/** + * **monitorState (全局单例)** + * + * 导出全局唯一的 MonitorState 实例,供整个应用共享状态。 + */ +export const monitorState = new MonitorState(); diff --git a/wecom/src/monitor/types.ts b/wecom/src/monitor/types.ts new file mode 100644 index 00000000..93e2b197 --- /dev/null +++ b/wecom/src/monitor/types.ts @@ -0,0 +1,136 @@ + +import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk"; +import type { ResolvedBotAccount } from "../types/index.js"; +import type { WecomInboundMessage } from "../types.js"; + +/** + * **WecomRuntimeEnv (运行时环境)** + * + * 包含基础的日志和错误报告接口,用于解耦对 PluginRuntime 的直接依赖。 + */ +export type WecomRuntimeEnv = { + log?: (message: string) => void; + error?: (message: string) => void; +}; + +/** + * **WecomWebhookTarget (Webhook 目标上下文)** + * + * 描述一个注册的 Bot 接收端点。包含处理该端点所需的所有上下文信息。 + * + * @property account 解析后的 Bot 账号信息 (Token, AESKey 等) + * @property config 插件全局配置 + * @property runtime 运行时环境 (日志) + * @property core OpenClaw 插件核心运行时 + * @property path 该 Target 注册的 Webhook 路径 + * @property statusSink 用于上报最后收发消息时间的回调 + */ +export type WecomWebhookTarget = { + account: ResolvedBotAccount; + config: OpenClawConfig; + runtime: WecomRuntimeEnv; + core: PluginRuntime; + path: string; + /** 反馈最后接收/发送时间 */ + statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void; +}; + +/** + * **StreamState (流式会话状态)** + * + * 记录一个流式请求的生命周期状态。 + * + * @property streamId 唯一会话 ID + * @property msgid 关联的企业微信消息 ID (用于去重) + * @property createdAt 创建时间 + * @property updatedAt 最后更新时间 (用于 Prune) + * @property started 是否已开始处理 (Agent 已介入) + * @property finished 是否已完成 (Agent 输出完毕或出错) + * @property error 错误信息 (如有) + * @property content 已积累的响应内容 (用于长轮询返回) + * @property images 过程中生成的图片 (Base64 + MD5) + */ +export type StreamState = { + streamId: string; + msgid?: string; + /** 会话键(同一人同一会话,用于队列/批次) */ + conversationKey?: string; + /** 批次键(conversationKey + 批次序号) */ + batchKey?: string; + /** 触发者 userid(用于 Agent 私信兜底) */ + userId?: string; + /** 会话类型(用于群聊兜底逻辑) */ + chatType?: "group" | "direct"; + /** 群聊 chatid(用于日志/提示,不用于 Agent 发群) */ + chatId?: string; + /** 智能机器人 aibotid(用于 taskKey 生成与日志) */ + aibotid?: string; + /** Bot 回调幂等键(用于最终交付幂等) */ + taskKey?: string; + createdAt: number; + updatedAt: number; + started: boolean; + finished: boolean; + error?: string; + content: string; + images?: { base64: string; md5: string }[]; + /** 兜底模式(仅作为内部状态,不暴露给企微) */ + fallbackMode?: "media" | "timeout" | "error"; + /** 群内兜底提示是否已发送(用于防重复刷屏) */ + fallbackPromptSentAt?: number; + /** Agent 私信最终交付是否已完成(用于防重复发送) */ + finalDeliveredAt?: number; + /** 用于私信兜底的完整内容(不受 STREAM_MAX_BYTES 限制,但仍需上限保护) */ + dmContent?: string; + /** 已通过 Agent 私信发送过的媒体标识(防重复发送附件) */ + agentMediaKeys?: string[]; +}; + +/** + * **PendingInbound (待处理/防抖消息)** + * + * 暂存在队列中的消息,等待防抖计时器结束进行聚合。 + * + * @property streamId 预分配的流 ID + * @property target 目标 Webhook 上下文 + * @property msg 原始消息对象 (如果聚合,通常指第一条) + * @property contents 聚合的消息内容列表 + * @property media 附带的媒体文件 (如果有) + * @property msgids 聚合的所有消息 ID (用于去重) + * @property timeout 防抖定时器句柄 + */ +export type PendingInbound = { + streamId: string; + conversationKey: string; + batchKey: string; + target: WecomWebhookTarget; + msg: WecomInboundMessage; + contents: string[]; + media?: { buffer: Buffer; contentType: string; filename: string }; + msgids: string[]; + nonce: string; + timestamp: string; + timeout: ReturnType | null; + /** 已到达防抖截止时间,但因前序批次仍在处理中而暂存 */ + readyToFlush?: boolean; + createdAt: number; +}; + +/** + * **ActiveReplyState (主动回复地址状态)** + * + * 存储企业微信回调中提供的 `response_url`,用于后续将流式响应转为主动推送(template_card)等。 + * + * @property response_url 企业微信提供的回调回复 URL + * @property proxyUrl 如果配置了代理,存储代理地址 + * @property createdAt 创建时间 + * @property usedAt 使用时间 (仅当 policy="once" 时有意义) + * @property lastError 最后一次发送失败的错误信息 + */ +export type ActiveReplyState = { + response_url: string; + proxyUrl?: string; + createdAt: number; + usedAt?: number; + lastError?: string; +}; diff --git a/wecom/src/onboarding.ts b/wecom/src/onboarding.ts new file mode 100644 index 00000000..0dc1033f --- /dev/null +++ b/wecom/src/onboarding.ts @@ -0,0 +1,472 @@ +/** + * WeCom 配置向导 (Onboarding) + * 支持 Bot、Agent 和双模式同时启动的交互式配置流程 + */ + +import type { + ChannelOnboardingAdapter, + ChannelOnboardingDmPolicy, + OpenClawConfig, + WizardPrompter, +} from "openclaw/plugin-sdk"; +import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk"; +import type { WecomConfig, WecomBotConfig, WecomAgentConfig, WecomDmConfig } from "./types/index.js"; + +const channel = "wecom" as const; + +type WecomMode = "bot" | "agent" | "both"; + +// ============================================================ +// 辅助函数 +// ============================================================ + +function getWecomConfig(cfg: OpenClawConfig): WecomConfig | undefined { + return cfg.channels?.wecom as WecomConfig | undefined; +} + +function setWecomEnabled(cfg: OpenClawConfig, enabled: boolean): OpenClawConfig { + return { + ...cfg, + channels: { + ...cfg.channels, + wecom: { + ...(cfg.channels?.wecom ?? {}), + enabled, + }, + }, + } as OpenClawConfig; +} + +function setWecomBotConfig(cfg: OpenClawConfig, bot: WecomBotConfig): OpenClawConfig { + return { + ...cfg, + channels: { + ...cfg.channels, + wecom: { + ...(cfg.channels?.wecom ?? {}), + enabled: true, + bot, + }, + }, + } as OpenClawConfig; +} + +function setWecomAgentConfig(cfg: OpenClawConfig, agent: WecomAgentConfig): OpenClawConfig { + return { + ...cfg, + channels: { + ...cfg.channels, + wecom: { + ...(cfg.channels?.wecom ?? {}), + enabled: true, + agent, + }, + }, + } as OpenClawConfig; +} + +function setGatewayBindLan(cfg: OpenClawConfig): OpenClawConfig { + return { + ...cfg, + gateway: { + ...(cfg.gateway ?? {}), + bind: "lan", + }, + } as OpenClawConfig; +} + +function setWecomDmPolicy( + cfg: OpenClawConfig, + mode: "bot" | "agent", + dm: WecomDmConfig, +): OpenClawConfig { + const wecom = getWecomConfig(cfg) ?? {}; + if (mode === "bot") { + return { + ...cfg, + channels: { + ...cfg.channels, + wecom: { + ...wecom, + bot: { + ...wecom.bot, + dm, + }, + }, + }, + } as OpenClawConfig; + } + return { + ...cfg, + channels: { + ...cfg.channels, + wecom: { + ...wecom, + agent: { + ...wecom.agent, + dm, + }, + }, + }, + } as OpenClawConfig; +} + +// ============================================================ +// 欢迎与引导 +// ============================================================ + +async function showWelcome(prompter: WizardPrompter): Promise { + await prompter.note( + [ + "🚀 欢迎使用企业微信(WeCom)接入向导", + "本插件支持「智能体 Bot」与「自建应用 Agent」双模式并行。", + ].join("\n"), + "WeCom 配置向导", + ); +} + +// ============================================================ +// 模式选择 +// ============================================================ + +async function promptMode(prompter: WizardPrompter): Promise { + const choice = await prompter.select({ + message: "请选择您要配置的接入模式:", + options: [ + { + value: "bot", + label: "Bot 模式 (智能机器人)", + hint: "回调速度快,支持流式占位符,适合日常对话", + }, + { + value: "agent", + label: "Agent 模式 (自建应用)", + hint: "功能最全,支持 API 主动推送、发送文件/视频、交互卡片", + }, + { + value: "both", + label: "双模式 (Bot + Agent 同时启用)", + hint: "推荐:Bot 用于快速对话,Agent 用于主动推送和媒体发送", + }, + ], + initialValue: "both", + }); + return choice as WecomMode; +} + +// ============================================================ +// Bot 模式配置 +// ============================================================ + +async function configureBotMode( + cfg: OpenClawConfig, + prompter: WizardPrompter, +): Promise { + await prompter.note( + [ + "正在配置 Bot 模式...", + "", + "💡 操作指南: 请在企微后台【管理工具 -> 智能机器人】开启 API 模式。", + "🔗 回调 URL: https://您的域名/wecom/bot", + "", + "请先在后台填入回调 URL,然后获取以下信息。", + ].join("\n"), + "Bot 模式配置", + ); + + const token = String( + await prompter.text({ + message: "请输入 Token:", + validate: (value: string | undefined) => (value?.trim() ? undefined : "Token 不能为空"), + }), + ).trim(); + + const encodingAESKey = String( + await prompter.text({ + message: "请输入 EncodingAESKey:", + validate: (value: string | undefined) => { + const v = value?.trim() ?? ""; + if (!v) return "EncodingAESKey 不能为空"; + if (v.length !== 43) return "EncodingAESKey 应为 43 个字符"; + return undefined; + }, + }), + ).trim(); + + const streamPlaceholder = await prompter.text({ + message: "流式占位符 (可选):", + placeholder: "正在思考...", + initialValue: "正在思考...", + }); + + const welcomeText = await prompter.text({ + message: "欢迎语 (可选):", + placeholder: "你好!我是 AI 助手", + initialValue: "你好!我是 AI 助手", + }); + + const botConfig: WecomBotConfig = { + token, + encodingAESKey, + streamPlaceholderContent: streamPlaceholder?.trim() || undefined, + welcomeText: welcomeText?.trim() || undefined, + }; + + return setWecomBotConfig(cfg, botConfig); +} + +// ============================================================ +// Agent 模式配置 +// ============================================================ + +async function configureAgentMode( + cfg: OpenClawConfig, + prompter: WizardPrompter, +): Promise { + await prompter.note( + [ + "正在配置 Agent 模式...", + "", + "💡 操作指南: 请在企微后台【应用管理 -> 自建应用】创建应用。", + ].join("\n"), + "Agent 模式配置", + ); + + const corpId = String( + await prompter.text({ + message: "请输入 CorpID (企业ID):", + validate: (value: string | undefined) => (value?.trim() ? undefined : "CorpID 不能为空"), + }), + ).trim(); + + const agentIdStr = String( + await prompter.text({ + message: "请输入 AgentID (应用ID):", + validate: (value: string | undefined) => { + const v = value?.trim() ?? ""; + if (!v) return "AgentID 不能为空"; + if (!/^\d+$/.test(v)) return "AgentID 应为数字"; + return undefined; + }, + }), + ).trim(); + const agentId = Number(agentIdStr); + + const corpSecret = String( + await prompter.text({ + message: "请输入 Secret (应用密钥):", + validate: (value: string | undefined) => (value?.trim() ? undefined : "Secret 不能为空"), + }), + ).trim(); + + await prompter.note( + [ + "💡 操作指南: 请在自建应用详情页进入【接收消息 -> 设置API接收】。", + "🔗 回调 URL: https://您的域名/wecom/agent", + "", + "请先在后台填入回调 URL,然后获取以下信息。", + ].join("\n"), + "回调配置", + ); + + const token = String( + await prompter.text({ + message: "请输入 Token (回调令牌):", + validate: (value: string | undefined) => (value?.trim() ? undefined : "Token 不能为空"), + }), + ).trim(); + + const encodingAESKey = String( + await prompter.text({ + message: "请输入 EncodingAESKey (回调加密密钥):", + validate: (value: string | undefined) => { + const v = value?.trim() ?? ""; + if (!v) return "EncodingAESKey 不能为空"; + if (v.length !== 43) return "EncodingAESKey 应为 43 个字符"; + return undefined; + }, + }), + ).trim(); + + const welcomeText = await prompter.text({ + message: "欢迎语 (可选):", + placeholder: "欢迎使用智能助手", + initialValue: "欢迎使用智能助手", + }); + + const agentConfig: WecomAgentConfig = { + corpId, + corpSecret, + agentId, + token, + encodingAESKey, + welcomeText: welcomeText?.trim() || undefined, + }; + + return setWecomAgentConfig(cfg, agentConfig); +} + +// ============================================================ +// DM 策略配置 +// ============================================================ + +async function promptDmPolicy( + cfg: OpenClawConfig, + prompter: WizardPrompter, + modes: ("bot" | "agent")[], +): Promise { + const policyChoice = await prompter.select({ + message: "请选择私聊 (DM) 访问策略:", + options: [ + { value: "pairing", label: "配对模式", hint: "推荐:安全,未知用户需授权" }, + { value: "allowlist", label: "白名单模式", hint: "仅允许特定 UserID" }, + { value: "open", label: "开放模式", hint: "任何人可发起" }, + { value: "disabled", label: "禁用私聊", hint: "不接受私聊消息" }, + ], + initialValue: "pairing", + }); + + const policy = policyChoice as "pairing" | "allowlist" | "open" | "disabled"; + let allowFrom: string[] | undefined; + + if (policy === "allowlist") { + const allowFromStr = String( + await prompter.text({ + message: "请输入白名单 UserID (多个用逗号分隔):", + placeholder: "user1,user2", + validate: (value: string | undefined) => (value?.trim() ? undefined : "请输入至少一个 UserID"), + }), + ).trim(); + allowFrom = allowFromStr.split(",").map((s) => s.trim()).filter(Boolean); + } + + const dm: WecomDmConfig = { policy, allowFrom }; + + let result = cfg; + for (const mode of modes) { + result = setWecomDmPolicy(result, mode, dm); + } + return result; +} + +// ============================================================ +// 配置汇总 +// ============================================================ + +async function showSummary(cfg: OpenClawConfig, prompter: WizardPrompter): Promise { + const wecom = getWecomConfig(cfg); + const lines: string[] = ["✅ 配置已保存!", ""]; + + if (wecom?.bot?.token) { + lines.push("📱 Bot 模式: 已配置"); + lines.push(` 回调 URL: https://您的域名/wecom/bot`); + } + + if (wecom?.agent?.corpId) { + lines.push("🏢 Agent 模式: 已配置"); + lines.push(` 回调 URL: https://您的域名/wecom/agent`); + } + + lines.push(""); + lines.push("⚠️ 请确保您已在企微后台填写了正确的回调 URL,"); + lines.push(" 并点击了后台的『保存』按钮完成验证。"); + + await prompter.note(lines.join("\n"), "配置完成"); +} + +// ============================================================ +// DM Policy Adapter +// ============================================================ + +const dmPolicy: ChannelOnboardingDmPolicy = { + label: "WeCom", + channel, + policyKey: "channels.wecom.bot.dm.policy", + allowFromKey: "channels.wecom.bot.dm.allowFrom", + getCurrent: (cfg: OpenClawConfig) => { + const wecom = getWecomConfig(cfg); + return (wecom?.bot?.dm?.policy ?? "pairing") as "pairing"; + }, + setPolicy: (cfg: OpenClawConfig, policy: "pairing" | "allowlist" | "open" | "disabled") => { + return setWecomDmPolicy(cfg, "bot", { policy }); + }, + promptAllowFrom: async ({ cfg, prompter }: { cfg: OpenClawConfig; prompter: WizardPrompter }) => { + const allowFromStr = String( + await prompter.text({ + message: "请输入白名单 UserID:", + validate: (value: string | undefined) => (value?.trim() ? undefined : "请输入 UserID"), + }), + ).trim(); + const allowFrom = allowFromStr.split(",").map((s) => s.trim()).filter(Boolean); + return setWecomDmPolicy(cfg, "bot", { policy: "allowlist", allowFrom }); + }, +}; + +// ============================================================ +// Onboarding Adapter +// ============================================================ + +export const wecomOnboardingAdapter: ChannelOnboardingAdapter = { + channel, + dmPolicy, + getStatus: async ({ cfg }: { cfg: OpenClawConfig }) => { + const wecom = getWecomConfig(cfg); + const botConfigured = Boolean(wecom?.bot?.token && wecom?.bot?.encodingAESKey); + const agentConfigured = Boolean( + wecom?.agent?.corpId && wecom?.agent?.corpSecret && wecom?.agent?.agentId, + ); + const configured = botConfigured || agentConfigured; + + const statusParts: string[] = []; + if (botConfigured) statusParts.push("Bot ✓"); + if (agentConfigured) statusParts.push("Agent ✓"); + + return { + channel, + configured, + statusLines: [ + `WeCom: ${configured ? statusParts.join(" + ") : "需要配置"}`, + ], + selectionHint: configured + ? `configured · ${statusParts.join(" + ")}` + : "enterprise-ready · dual-mode", + quickstartScore: configured ? 1 : 8, + }; + }, + configure: async ({ cfg, prompter }: { cfg: OpenClawConfig; prompter: WizardPrompter }) => { + // 1. 欢迎 + await showWelcome(prompter); + + // 2. 模式选择 + const mode = await promptMode(prompter); + + let next = cfg; + const configuredModes: ("bot" | "agent")[] = []; + + // 3. 配置 Bot + if (mode === "bot" || mode === "both") { + next = await configureBotMode(next, prompter); + configuredModes.push("bot"); + } + + // 4. 配置 Agent + if (mode === "agent" || mode === "both") { + next = await configureAgentMode(next, prompter); + configuredModes.push("agent"); + } + + // 5. DM 策略 + next = await promptDmPolicy(next, prompter, configuredModes); + + // 6. 启用通道 + next = setWecomEnabled(next, true); + + // 7. 设置 gateway.bind 为 lan(允许外部访问回调) + next = setGatewayBindLan(next); + + // 8. 汇总 + await showSummary(next, prompter); + + return { cfg: next, accountId: DEFAULT_ACCOUNT_ID }; + }, +}; diff --git a/wecom/src/outbound.test.ts b/wecom/src/outbound.test.ts new file mode 100644 index 00000000..2b23e70e --- /dev/null +++ b/wecom/src/outbound.test.ts @@ -0,0 +1,143 @@ +import { describe, expect, it, vi } from "vitest"; + +vi.mock("./agent/api-client.js", () => ({ + sendText: vi.fn(), + sendMedia: vi.fn(), + uploadMedia: vi.fn(), +})); + +describe("wecomOutbound", () => { + it("does not crash when called with core outbound params", async () => { + const { wecomOutbound } = await import("./outbound.js"); + await expect( + wecomOutbound.sendMedia({ + cfg: {}, + to: "wr-test-chat", + text: "caption", + mediaUrl: "https://example.com/media.png", + } as any), + ).rejects.toThrow(/Agent mode/i); + }); + + it("routes sendText to agent chatId/userid", async () => { + const { wecomOutbound } = await import("./outbound.js"); + const api = await import("./agent/api-client.js"); + const now = vi.spyOn(Date, "now").mockReturnValue(123); + (api.sendText as any).mockResolvedValue(undefined); + + const cfg = { + channels: { + wecom: { + enabled: true, + agent: { + corpId: "corp", + corpSecret: "secret", + agentId: 1000002, + token: "token", + encodingAESKey: "aes", + }, + }, + }, + }; + + // Chat ID (wr/wc) is intentionally NOT supported for Agent outbound. + await expect(wecomOutbound.sendText({ cfg, to: "wr123", text: "hello" } as any)).rejects.toThrow( + /不支持向群 chatId 发送/, + ); + expect(api.sendText).not.toHaveBeenCalled(); + + // Test: User ID (Default) + const userResult = await wecomOutbound.sendText({ + cfg, + to: "userid123", + text: "hi", + } as any); + expect(api.sendText).toHaveBeenCalledWith( + expect.objectContaining({ + chatId: undefined, + toUser: "userid123", + toParty: undefined, + toTag: undefined, + text: "hi", + }), + ); + expect(userResult.messageId).toBe("agent-123"); + + (api.sendText as any).mockClear(); + + // Test: User ID explicit + await wecomOutbound.sendText({ cfg, to: "user:zhangsan", text: "hi" } as any); + expect(api.sendText).toHaveBeenCalledWith( + expect.objectContaining({ toUser: "zhangsan", toParty: undefined }), + ); + + (api.sendText as any).mockClear(); + + // Test: Party ID (Numeric) + await wecomOutbound.sendText({ cfg, to: "1001", text: "hi party" } as any); + expect(api.sendText).toHaveBeenCalledWith( + expect.objectContaining({ toUser: undefined, toParty: "1001" }), + ); + + (api.sendText as any).mockClear(); + + // Test: Party ID Explicit + await wecomOutbound.sendText({ cfg, to: "party:2002", text: "hi party 2" } as any); + expect(api.sendText).toHaveBeenCalledWith( + expect.objectContaining({ toUser: undefined, toParty: "2002" }), + ); + + (api.sendText as any).mockClear(); + + // Test: Tag ID Explicit + await wecomOutbound.sendText({ cfg, to: "tag:1", text: "hi tag" } as any); + expect(api.sendText).toHaveBeenCalledWith( + expect.objectContaining({ toUser: undefined, toTag: "1" }), + ); + + now.mockRestore(); + }); + + it("suppresses /new ack for bot sessions but not agent sessions", async () => { + const { wecomOutbound } = await import("./outbound.js"); + const api = await import("./agent/api-client.js"); + const now = vi.spyOn(Date, "now").mockReturnValue(456); + (api.sendText as any).mockResolvedValue(undefined); + (api.sendText as any).mockClear(); + + const cfg = { + channels: { + wecom: { + enabled: true, + agent: { + corpId: "corp", + corpSecret: "secret", + agentId: 1000002, + token: "token", + encodingAESKey: "aes", + }, + }, + }, + }; + + const ack = "✅ New session started · model: openai-codex/gpt-5.2"; + + // Bot 会话(wecom:...)应抑制,避免私信回执 + const r1 = await wecomOutbound.sendText({ cfg, to: "wecom:userid123", text: ack } as any); + expect(api.sendText).not.toHaveBeenCalled(); + expect(r1.messageId).toBe("suppressed-456"); + + (api.sendText as any).mockClear(); + + // Agent 会话(wecom-agent:...)允许发送回执 + await wecomOutbound.sendText({ cfg, to: "wecom-agent:userid123", text: ack } as any); + expect(api.sendText).toHaveBeenCalledWith( + expect.objectContaining({ + toUser: "userid123", + text: "✅ 已开启新会话(模型:openai-codex/gpt-5.2)", + }), + ); + + now.mockRestore(); + }); +}); diff --git a/wecom/src/outbound.ts b/wecom/src/outbound.ts new file mode 100644 index 00000000..d3fe6231 --- /dev/null +++ b/wecom/src/outbound.ts @@ -0,0 +1,200 @@ +import type { ChannelOutboundAdapter, ChannelOutboundContext } from "openclaw/plugin-sdk"; + +import { sendText as sendAgentText, sendMedia as sendAgentMedia, uploadMedia } from "./agent/api-client.js"; +import { resolveWecomAccounts } from "./config/index.js"; +import { getWecomRuntime } from "./runtime.js"; + +import { resolveWecomTarget } from "./target.js"; + +function resolveAgentConfigOrThrow(cfg: ChannelOutboundContext["cfg"]) { + const account = resolveWecomAccounts(cfg).agent; + if (!account?.configured) { + throw new Error( + "WeCom outbound requires Agent mode. Configure channels.wecom.agent (corpId/corpSecret/agentId/token/encodingAESKey).", + ); + } + // 注意:不要在日志里输出 corpSecret 等敏感信息 + console.log(`[wecom-outbound] Using agent config: corpId=${account.corpId}, agentId=${account.agentId}`); + return account; +} + +export const wecomOutbound: ChannelOutboundAdapter = { + deliveryMode: "direct", + chunkerMode: "text", + textChunkLimit: 20480, + chunker: (text, limit) => { + try { + return getWecomRuntime().channel.text.chunkText(text, limit); + } catch { + return [text]; + } + }, + sendText: async ({ cfg, to, text }: ChannelOutboundContext) => { + // signal removed - not supported in current SDK + + const agent = resolveAgentConfigOrThrow(cfg); + const target = resolveWecomTarget(to); + if (!target) { + throw new Error("WeCom outbound requires a target (userid, partyid, tagid or chatid)."); + } + + // 体验优化:/new /reset 的“New session started”回执在 OpenClaw 核心里是英文固定文案, + // 且通过 routeReply 走 wecom outbound(Agent 主动发送)。 + // 在 WeCom“双模式”场景下,这会造成: + // - 用户在 Bot 会话发 /new,但却收到一条 Agent 私信回执(双重回复/错会话)。 + // 因此: + // - Bot 会话目标:抑制该回执(Bot 会话里由 wecom 插件补中文回执)。 + // - Agent 会话目标(wecom-agent:):允许发送,但改写成中文。 + let outgoingText = text; + const trimmed = String(outgoingText ?? "").trim(); + const rawTo = typeof to === "string" ? to.trim().toLowerCase() : ""; + const isAgentSessionTarget = rawTo.startsWith("wecom-agent:"); + const looksLikeNewSessionAck = + /new session started/i.test(trimmed) && /model:/i.test(trimmed); + + if (looksLikeNewSessionAck) { + if (!isAgentSessionTarget) { + console.log(`[wecom-outbound] Suppressed command ack to avoid Bot/Agent double-reply (len=${trimmed.length})`); + return { channel: "wecom", messageId: `suppressed-${Date.now()}`, timestamp: Date.now() }; + } + + const modelLabel = (() => { + const m = trimmed.match(/model:\s*([^\n()]+)\s*/i); + return m?.[1]?.trim(); + })(); + const rewritten = modelLabel ? `✅ 已开启新会话(模型:${modelLabel})` : "✅ 已开启新会话。"; + console.log(`[wecom-outbound] Rewrote command ack for agent session (len=${rewritten.length})`); + outgoingText = rewritten; + } + + const { touser, toparty, totag, chatid } = target; + if (chatid) { + throw new Error( + `企业微信(WeCom)Agent 主动发送不支持向群 chatId 发送(chatId=${chatid})。` + + `该路径在实际环境中经常失败(例如 86008:无权限访问该会话/会话由其他应用创建)。` + + `请改为发送给用户(userid / user:xxx),或由 Bot 模式在群内交付。`, + ); + } + console.log(`[wecom-outbound] Sending text to target=${JSON.stringify(target)} (len=${outgoingText.length})`); + + try { + await sendAgentText({ + agent, + toUser: touser, + toParty: toparty, + toTag: totag, + chatId: chatid, + text: outgoingText, + }); + console.log(`[wecom-outbound] Successfully sent text to ${JSON.stringify(target)}`); + } catch (err) { + console.error(`[wecom-outbound] Failed to send text to ${JSON.stringify(target)}:`, err); + throw err; + } + + return { + channel: "wecom", + messageId: `agent-${Date.now()}`, + timestamp: Date.now(), + }; + }, + sendMedia: async ({ cfg, to, text, mediaUrl }: ChannelOutboundContext) => { + // signal removed - not supported in current SDK + + const agent = resolveAgentConfigOrThrow(cfg); + const target = resolveWecomTarget(to); + if (!target) { + throw new Error("WeCom outbound requires a target (userid, partyid, tagid or chatid)."); + } + if (target.chatid) { + throw new Error( + `企业微信(WeCom)Agent 主动发送不支持向群 chatId 发送(chatId=${target.chatid})。` + + `该路径在实际环境中经常失败(例如 86008:无权限访问该会话/会话由其他应用创建)。` + + `请改为发送给用户(userid / user:xxx),或由 Bot 模式在群内交付。`, + ); + } + if (!mediaUrl) { + throw new Error("WeCom outbound requires mediaUrl."); + } + + let buffer: Buffer; + let contentType: string; + let filename: string; + + // 判断是 URL 还是本地文件路径 + const isRemoteUrl = /^https?:\/\//i.test(mediaUrl); + + if (isRemoteUrl) { + const res = await fetch(mediaUrl, { signal: AbortSignal.timeout(30000) }); + if (!res.ok) { + throw new Error(`Failed to download media: ${res.status}`); + } + buffer = Buffer.from(await res.arrayBuffer()); + contentType = res.headers.get("content-type") || "application/octet-stream"; + const urlPath = new URL(mediaUrl).pathname; + filename = urlPath.split("/").pop() || "media"; + } else { + // 本地文件路径 + const fs = await import("node:fs/promises"); + const path = await import("node:path"); + + buffer = await fs.readFile(mediaUrl); + filename = path.basename(mediaUrl); + + // 根据扩展名推断 content-type + const ext = path.extname(mediaUrl).slice(1).toLowerCase(); + const mimeTypes: Record = { + jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", + webp: "image/webp", bmp: "image/bmp", mp3: "audio/mpeg", wav: "audio/wav", + amr: "audio/amr", mp4: "video/mp4", pdf: "application/pdf", doc: "application/msword", + docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + xls: "application/vnd.ms-excel", xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }; + contentType = mimeTypes[ext] || "application/octet-stream"; + console.log(`[wecom-outbound] Reading local file: ${mediaUrl}, ext=${ext}, contentType=${contentType}`); + } + + let mediaType: "image" | "voice" | "video" | "file" = "file"; + if (contentType.startsWith("image/")) mediaType = "image"; + else if (contentType.startsWith("audio/")) mediaType = "voice"; + else if (contentType.startsWith("video/")) mediaType = "video"; + + const mediaId = await uploadMedia({ + agent, + type: mediaType, + buffer, + filename, + }); + + const { touser, toparty, totag, chatid } = target; + console.log(`[wecom-outbound] Sending media (${mediaType}) to ${JSON.stringify(target)} (mediaId=${mediaId})`); + + try { + await sendAgentMedia({ + agent, + toUser: touser, + toParty: toparty, + toTag: totag, + chatId: chatid, + mediaId, + mediaType, + ...(mediaType === "video" && text?.trim() + ? { + title: text.trim().slice(0, 64), + description: text.trim().slice(0, 512), + } + : {}), + }); + console.log(`[wecom-outbound] Successfully sent media to ${JSON.stringify(target)}`); + } catch (err) { + console.error(`[wecom-outbound] Failed to send media to ${JSON.stringify(target)}:`, err); + throw err; + } + + return { + channel: "wecom", + messageId: `agent-media-${Date.now()}`, + timestamp: Date.now(), + }; + }, +}; diff --git a/wecom/src/runtime.ts b/wecom/src/runtime.ts new file mode 100644 index 00000000..0d33e403 --- /dev/null +++ b/wecom/src/runtime.ts @@ -0,0 +1,14 @@ +import type { PluginRuntime } from "openclaw/plugin-sdk"; + +let runtime: PluginRuntime | null = null; + +export function setWecomRuntime(next: PluginRuntime): void { + runtime = next; +} + +export function getWecomRuntime(): PluginRuntime { + if (!runtime) { + throw new Error("WeCom runtime not initialized"); + } + return runtime; +} diff --git a/wecom/src/shared/command-auth.ts b/wecom/src/shared/command-auth.ts new file mode 100644 index 00000000..9f2d7852 --- /dev/null +++ b/wecom/src/shared/command-auth.ts @@ -0,0 +1,101 @@ +import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk"; + +import type { WecomAccountConfig } from "../types.js"; + +function normalizeWecomAllowFromEntry(raw: string): string { + return raw + .trim() + .toLowerCase() + .replace(/^wecom:/, "") + .replace(/^user:/, "") + .replace(/^userid:/, ""); +} + +function isWecomSenderAllowed(senderUserId: string, allowFrom: string[]): boolean { + const list = allowFrom.map((entry) => normalizeWecomAllowFromEntry(entry)).filter(Boolean); + if (list.includes("*")) return true; + const normalizedSender = normalizeWecomAllowFromEntry(senderUserId); + if (!normalizedSender) return false; + return list.includes(normalizedSender); +} + +export async function resolveWecomCommandAuthorization(params: { + core: PluginRuntime; + cfg: OpenClawConfig; + accountConfig: WecomAccountConfig; + rawBody: string; + senderUserId: string; +}): Promise<{ + shouldComputeAuth: boolean; + dmPolicy: "pairing" | "allowlist" | "open" | "disabled"; + senderAllowed: boolean; + authorizerConfigured: boolean; + commandAuthorized: boolean | undefined; + effectiveAllowFrom: string[]; +}> { + const { core, cfg, accountConfig, rawBody, senderUserId } = params; + + const dmPolicy = (accountConfig.dm?.policy ?? "pairing") as "pairing" | "allowlist" | "open" | "disabled"; + const configAllowFrom = (accountConfig.dm?.allowFrom ?? []).map((v) => String(v)); + + const shouldComputeAuth = core.channel.commands.shouldComputeCommandAuthorized(rawBody, cfg); + // WeCom channel currently does NOT support the `openclaw pairing` CLI workflow + // ("Channel wecom does not support pairing"). So we must not rely on pairing + // store approvals for command authorization here. + // + // Policy semantics: + // - open: commands are allowed for everyone by default (unless higher-level access-groups deny). + // - allowlist: commands require allowFrom entries. + // - pairing: treated the same as allowlist for WeCom (since pairing CLI is unsupported). + const effectiveAllowFrom = dmPolicy === "open" ? ["*"] : configAllowFrom; + + const senderAllowed = isWecomSenderAllowed(senderUserId, effectiveAllowFrom); + const allowAllConfigured = effectiveAllowFrom.some((entry) => normalizeWecomAllowFromEntry(entry) === "*"); + const authorizerConfigured = allowAllConfigured || effectiveAllowFrom.length > 0; + const useAccessGroups = cfg.commands?.useAccessGroups !== false; + + const commandAuthorized = shouldComputeAuth + ? core.channel.commands.resolveCommandAuthorizedFromAuthorizers({ + useAccessGroups, + authorizers: [{ configured: authorizerConfigured, allowed: senderAllowed }], + }) + : undefined; + + return { + shouldComputeAuth, + dmPolicy, + senderAllowed, + authorizerConfigured, + commandAuthorized, + effectiveAllowFrom, + }; +} + +export function buildWecomUnauthorizedCommandPrompt(params: { + senderUserId: string; + dmPolicy: "pairing" | "allowlist" | "open" | "disabled"; + scope: "bot" | "agent"; +}): string { + const user = params.senderUserId || "unknown"; + const policy = params.dmPolicy; + const scopeLabel = params.scope === "bot" ? "Bot(智能机器人)" : "Agent(自建应用)"; + const dmPrefix = params.scope === "bot" ? "channels.wecom.bot.dm" : "channels.wecom.agent.dm"; + const allowCmd = (value: string) => `openclaw config set ${dmPrefix}.allowFrom '${value}'`; + const policyCmd = (value: string) => `openclaw config set ${dmPrefix}.policy "${value}"`; + + if (policy === "disabled") { + return [ + `无权限执行命令(${scopeLabel} 已禁用:dm.policy=disabled)`, + `触发者:${user}`, + `管理员:${policyCmd("open")}(全放开)或 ${policyCmd("allowlist")}(白名单)`, + ].join("\n"); + } + // WeCom 不支持 pairing CLI,因此这里统一给出“open / allowlist”两种明确的配置指令 + return [ + `无权限执行命令(入口:${scopeLabel},userid:${user})`, + `管理员全放开:${policyCmd("open")}`, + `管理员放行该用户:${policyCmd("allowlist")}`, + `然后设置白名单:${allowCmd(JSON.stringify([user]))}`, + `如果仍被拦截:检查 commands.useAccessGroups/访问组`, + ].join("\n"); +} diff --git a/wecom/src/shared/index.ts b/wecom/src/shared/index.ts new file mode 100644 index 00000000..1c237325 --- /dev/null +++ b/wecom/src/shared/index.ts @@ -0,0 +1,5 @@ +/** + * WeCom Shared 模块导出 + */ + +export { parseXml, extractMsgType, extractFromUser, extractContent, extractChatId, extractToUser } from "./xml-parser.js"; diff --git a/wecom/src/shared/xml-parser.test.ts b/wecom/src/shared/xml-parser.test.ts new file mode 100644 index 00000000..8b6b1aeb --- /dev/null +++ b/wecom/src/shared/xml-parser.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, test } from "vitest"; + +import { extractContent, extractMediaId, extractMsgId } from "./xml-parser.js"; + +describe("wecom xml-parser", () => { + test("extractContent is robust to non-string Content", () => { + const msg: any = { MsgType: "text", Content: { "#text": "hello", "@_foo": "bar" } }; + expect(extractContent(msg)).toBe("hello"); + }); + + test("extractContent handles array content", () => { + const msg: any = { MsgType: "text", Content: ["a", "b"] }; + expect(extractContent(msg)).toBe("a\nb"); + }); + + test("extractContent handles file messages", () => { + const msg: any = { MsgType: "file", MediaId: "MEDIA123" }; + expect(extractContent(msg)).toBe("[文件消息]"); + }); + + test("extractMediaId handles object MediaId", () => { + const msg: any = { MediaId: { "#text": "MEDIA123", "@_foo": "bar" } }; + expect(extractMediaId(msg)).toBe("MEDIA123"); + }); + + test("extractMsgId handles number MsgId", () => { + const msg: any = { MsgId: 123456789 }; + expect(extractMsgId(msg)).toBe("123456789"); + }); +}); diff --git a/wecom/src/shared/xml-parser.ts b/wecom/src/shared/xml-parser.ts new file mode 100644 index 00000000..a4713e92 --- /dev/null +++ b/wecom/src/shared/xml-parser.ts @@ -0,0 +1,183 @@ +/** + * WeCom XML 解析器 + * 用于 Agent 模式解析 XML 格式消息 + */ + +import { XMLParser } from "fast-xml-parser"; +import type { WecomAgentInboundMessage } from "../types/index.js"; + +const xmlParser = new XMLParser({ + ignoreAttributes: false, + trimValues: true, + processEntities: false, +}); + +/** + * 解析 XML 字符串为消息对象 + */ +export function parseXml(xml: string): WecomAgentInboundMessage { + const obj = xmlParser.parse(xml); + const root = obj?.xml ?? obj; + return root ?? {}; +} + +/** + * 从 XML 中提取消息类型 + */ +export function extractMsgType(msg: WecomAgentInboundMessage): string { + return String(msg.MsgType ?? "").toLowerCase(); +} + +/** + * 从 XML 中提取发送者 ID + */ +export function extractFromUser(msg: WecomAgentInboundMessage): string { + return String(msg.FromUserName ?? ""); +} + +/** + * 从 XML 中提取文件名(主要用于 file 消息) + */ +export function extractFileName(msg: WecomAgentInboundMessage): string | undefined { + const raw = (msg as any).FileName ?? (msg as any).Filename ?? (msg as any).fileName ?? (msg as any).filename; + if (raw == null) return undefined; + if (typeof raw === "string") return raw.trim() || undefined; + if (typeof raw === "number" || typeof raw === "boolean" || typeof raw === "bigint") return String(raw); + if (Array.isArray(raw)) { + const merged = raw.map((v) => (v == null ? "" : String(v))).join("\n").trim(); + return merged || undefined; + } + if (typeof raw === "object") { + const obj = raw as Record; + const text = (typeof obj["#text"] === "string" ? obj["#text"] : + typeof obj["_text"] === "string" ? obj["_text"] : + typeof obj["text"] === "string" ? obj["text"] : undefined); + if (text && text.trim()) return text.trim(); + } + const s = String(raw); + return s.trim() || undefined; +} + +/** + * 从 XML 中提取接收者 ID (CorpID) + */ +export function extractToUser(msg: WecomAgentInboundMessage): string { + return String(msg.ToUserName ?? ""); +} + +/** + * 从 XML 中提取群聊 ID + */ +export function extractChatId(msg: WecomAgentInboundMessage): string | undefined { + return msg.ChatId ? String(msg.ChatId) : undefined; +} + +/** + * 从 XML 中提取消息内容 + */ +export function extractContent(msg: WecomAgentInboundMessage): string { + const msgType = extractMsgType(msg); + + const asText = (value: unknown): string => { + if (value == null) return ""; + if (typeof value === "string") return value; + if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value); + if (Array.isArray(value)) return value.map(asText).filter(Boolean).join("\n"); + if (typeof value === "object") { + const obj = value as Record; + // fast-xml-parser 在某些情况下(例如带属性)会把文本放在 "#text" + if (typeof obj["#text"] === "string") return obj["#text"]; + if (typeof obj["_text"] === "string") return obj["_text"]; + if (typeof obj["text"] === "string") return obj["text"]; + try { + return JSON.stringify(obj); + } catch { + return String(value); + } + } + return String(value); + }; + + switch (msgType) { + case "text": + return asText(msg.Content); + case "voice": + // 语音识别结果 + return asText(msg.Recognition) || "[语音消息]"; + case "image": + return `[图片] ${asText(msg.PicUrl)}`; + case "file": + return "[文件消息]"; + case "video": + return "[视频消息]"; + case "location": + return `[位置] ${asText(msg.Label)} (${asText(msg.Location_X)}, ${asText(msg.Location_Y)})`; + case "link": + return `[链接] ${asText(msg.Title)}\n${asText(msg.Description)}\n${asText(msg.Url)}`; + case "event": + return `[事件] ${asText(msg.Event)} - ${asText(msg.EventKey)}`; + default: + return `[${msgType || "未知消息类型"}]`; + } +} + +/** + * 从 XML 中提取媒体 ID (Image, Voice, Video) + * 根据官方文档,MediaId 在 Agent 回调中直接位于根节点 + */ +export function extractMediaId(msg: WecomAgentInboundMessage): string | undefined { + const raw = (msg as any).MediaId ?? (msg as any).MediaID ?? (msg as any).mediaid ?? (msg as any).mediaId; + if (raw == null) return undefined; + if (typeof raw === "string") return raw.trim() || undefined; + if (typeof raw === "number" || typeof raw === "boolean" || typeof raw === "bigint") return String(raw); + if (Array.isArray(raw)) { + const merged = raw.map((v) => (v == null ? "" : String(v))).join("\n").trim(); + return merged || undefined; + } + if (typeof raw === "object") { + const obj = raw as Record; + const text = (typeof obj["#text"] === "string" ? obj["#text"] : + typeof obj["_text"] === "string" ? obj["_text"] : + typeof obj["text"] === "string" ? obj["text"] : undefined); + if (text && text.trim()) return text.trim(); + try { + const s = JSON.stringify(obj); + return s.trim() || undefined; + } catch { + const s = String(raw); + return s.trim() || undefined; + } + } + const s = String(raw); + return s.trim() || undefined; +} + +/** + * 从 XML 中提取 MsgId(用于去重) + */ +export function extractMsgId(msg: WecomAgentInboundMessage): string | undefined { + const raw = (msg as any).MsgId ?? (msg as any).MsgID ?? (msg as any).msgid ?? (msg as any).msgId; + if (raw == null) return undefined; + if (typeof raw === "string") return raw.trim() || undefined; + if (typeof raw === "number" || typeof raw === "boolean" || typeof raw === "bigint") return String(raw); + if (Array.isArray(raw)) { + const merged = raw.map((v) => (v == null ? "" : String(v))).join("\n").trim(); + return merged || undefined; + } + if (typeof raw === "object") { + const obj = raw as Record; + const text = (typeof obj["#text"] === "string" ? obj["#text"] : + typeof obj["_text"] === "string" ? obj["_text"] : + typeof obj["text"] === "string" ? obj["text"] : undefined); + if (text && text.trim()) return text.trim(); + try { + const s = JSON.stringify(obj); + return s.trim() || undefined; + } catch { + const s = String(raw); + return s.trim() || undefined; + } + } + const s = String(raw); + return s.trim() || undefined; +} diff --git a/wecom/src/target.ts b/wecom/src/target.ts new file mode 100644 index 00000000..4c915e39 --- /dev/null +++ b/wecom/src/target.ts @@ -0,0 +1,80 @@ +/** + * WeCom Target Resolver (企业微信目标解析器) + * + * 解析 OpenClaw 的 `to` 字段(原始目标字符串),将其转换为企业微信支持的具体接收对象。 + * 支持显式前缀 (party:, tag: 等) 和基于规则的启发式推断。 + * + * **关于“目标发送”与“消息记录”的对应关系 (Target vs Inbound):** + * - **发送 (Outbound)**: 支持一对多广播 (Party/Tag)。 + * 例如发送给 `party:1`,消息会触达该部门下所有成员。 + * - **接收 (Inbound)**: 总是来自具体的 **用户 (User)** 或 **群聊 (Chat)**。 + * 当成员回复部门广播消息时,可以视为一个新的单聊会话或在该成员的现有单聊中回复。 + * 因此,Outbound Target (如 Party) 与 Inbound Source (User) 不需要也不可能 1:1 强匹配。 + * 广播是“发后即忘” (Fire-and-Forget) 的通知模式,而回复是具体的会话模式。 + */ + +export interface WecomTarget { + touser?: string; + toparty?: string; + totag?: string; + chatid?: string; +} + +/** + * Parses a raw target string into a WeComTarget object. + * 解析原始目标字符串为 WeComTarget 对象。 + * + * 逻辑: + * 1. 移除标准命名空间前缀 (wecom:, qywx: 等)。 + * 2. 检查显式类型前缀 (party:, tag:, group:, user:)。 + * 3. 启发式回退 (无前缀时): + * - 以 "wr" 或 "wc" 开头 -> Chat ID (群聊) + * - 纯数字 -> Party ID (部门) + * - 其他 -> User ID (用户) + * + * @param raw - The raw target string (e.g. "party:1", "zhangsan", "wecom:wr123") + */ +export function resolveWecomTarget(raw: string | undefined): WecomTarget | undefined { + if (!raw?.trim()) return undefined; + + // 1. Remove standard namespace prefixes (移除标准命名空间前缀) + let clean = raw.trim().replace(/^(wecom-agent|wecom|wechatwork|wework|qywx):/i, ""); + + // 2. Explicit Type Prefixes (显式类型前缀) + if (/^party:/i.test(clean)) { + return { toparty: clean.replace(/^party:/i, "").trim() }; + } + if (/^dept:/i.test(clean)) { + return { toparty: clean.replace(/^dept:/i, "").trim() }; + } + if (/^tag:/i.test(clean)) { + return { totag: clean.replace(/^tag:/i, "").trim() }; + } + if (/^group:/i.test(clean)) { + return { chatid: clean.replace(/^group:/i, "").trim() }; + } + if (/^chat:/i.test(clean)) { + return { chatid: clean.replace(/^chat:/i, "").trim() }; + } + if (/^user:/i.test(clean)) { + return { touser: clean.replace(/^user:/i, "").trim() }; + } + + // 3. Heuristics (启发式规则) + + // Chat ID typically starts with 'wr' or 'wc' + // 群聊 ID 通常以 'wr' (外部群) 或 'wc' 开头 + if (/^(wr|wc)/i.test(clean)) { + return { chatid: clean }; + } + + // Pure digits are likely Department IDs (Parties) + // 纯数字优先被视为部门 ID (Parties),方便运维配置 (如 "1" 代表根部门) + // 如果必须要发送给纯数字 ID 的用户,请使用显式前缀 "user:1001" + if (/^\d+$/.test(clean)) { + return { toparty: clean }; + } + + // Default to User (默认为用户) + return { touser: clean }; +} diff --git a/wecom/src/types.ts b/wecom/src/types.ts new file mode 100644 index 00000000..0b5da332 --- /dev/null +++ b/wecom/src/types.ts @@ -0,0 +1,159 @@ +export type WecomDmConfig = { + policy?: "pairing" | "allowlist" | "open" | "disabled"; + allowFrom?: Array; +}; + +export type WecomAccountConfig = { + name?: string; + enabled?: boolean; + + webhookPath?: string; + token?: string; + encodingAESKey?: string; + receiveId?: string; + + streamPlaceholderContent?: string; + + dm?: WecomDmConfig; + welcomeText?: string; +}; + +export type WecomConfig = WecomAccountConfig & { + accounts?: Record; + defaultAccount?: string; +}; + +export type ResolvedWecomAccount = { + accountId: string; + name?: string; + enabled: boolean; + configured: boolean; + token?: string; + encodingAESKey?: string; + receiveId: string; + config: WecomAccountConfig; +}; + +export type WecomInboundBase = { + msgid?: string; + aibotid?: string; + chattype?: "single" | "group"; + chatid?: string; + response_url?: string; + from?: { userid?: string; corpid?: string }; + msgtype?: string; +}; + +export type WecomInboundText = WecomInboundBase & { + msgtype: "text"; + text?: { content?: string }; + quote?: unknown; +}; + +export type WecomInboundVoice = WecomInboundBase & { + msgtype: "voice"; + voice?: { content?: string }; + quote?: unknown; +}; + +export type WecomInboundStreamRefresh = WecomInboundBase & { + msgtype: "stream"; + stream?: { id?: string }; +}; + +export type WecomInboundEvent = WecomInboundBase & { + msgtype: "event"; + create_time?: number; + event?: { + eventtype?: string; + [key: string]: unknown; + }; +}; + +export type WecomInboundQuote = { + msgtype?: "text" | "image" | "mixed" | "voice" | "file"; + text?: { content?: string }; + image?: { url?: string }; + mixed?: { + msg_item?: Array<{ + msgtype: "text" | "image"; + text?: { content?: string }; + image?: { url?: string }; + }>; + }; + voice?: { content?: string }; + file?: { url?: string }; +}; + +export type WecomInboundMessage = + | (WecomInboundText & { quote?: WecomInboundQuote }) + | WecomInboundVoice + | WecomInboundStreamRefresh + | WecomInboundEvent + | (WecomInboundBase & { quote?: WecomInboundQuote } & Record); + +export type WecomTemplateCard = { + card_type: "text_notice" | "news_notice" | "button_interaction" | "vote_interaction" | "multiple_interaction"; + source?: { icon_url?: string; desc?: string; desc_color?: number }; + main_title?: { title?: string; desc?: string }; + task_id?: string; + button_list?: Array<{ text: string; style?: number; key: string }>; + sub_title_text?: string; + horizontal_content_list?: Array<{ keyname: string; value?: string; type?: number; url?: string; userid?: string }>; + card_action?: { type: number; url?: string; appid?: string; pagepath?: string }; + action_menu?: { desc: string; action_list: Array<{ text: string; key: string }> }; + select_list?: Array<{ + question_key: string; + title?: string; + selected_id?: string; + option_list: Array<{ id: string; text: string }>; + }>; + submit_button?: { text: string; key: string }; + checkbox?: { + question_key: string; + option_list: Array<{ id: string; text: string; is_checked?: boolean }>; + mode?: number; + }; +}; + +export type WecomInboundTemplateCardEvent = WecomInboundBase & { + msgtype: "event"; + event: { + eventtype: "template_card_event"; + template_card_event: { + card_type: string; + event_key: string; + task_id: string; + selected_items?: { + selected_item: Array<{ + question_key: string; + option_ids: { option_id: string[] }; + }>; + }; + }; + }; +}; + + +/** + * Template card event payload (button click, checkbox, select) + */ +export type WecomTemplateCardEventPayload = { + card_type: string; + event_key: string; + task_id: string; + response_code?: string; + selected_items?: { + question_key?: string; + option_ids?: string[]; + }; +}; + +/** + * Outbound message types that can be sent via response_url + */ +export type WecomOutboundMessage = + | { msgtype: "text"; text: { content: string } } + | { msgtype: "markdown"; markdown: { content: string } } + | { msgtype: "template_card"; template_card: WecomTemplateCard }; + diff --git a/wecom/src/types/account.ts b/wecom/src/types/account.ts new file mode 100644 index 00000000..7762f379 --- /dev/null +++ b/wecom/src/types/account.ts @@ -0,0 +1,76 @@ +/** + * WeCom 账号类型定义 + */ + +import type { WecomBotConfig, WecomAgentConfig, WecomDmConfig, WecomNetworkConfig } from "./config.js"; + +/** + * 解析后的 Bot 账号 + */ +export type ResolvedBotAccount = { + /** 账号 ID */ + accountId: string; + /** 是否启用 */ + enabled: boolean; + /** 是否配置完整 */ + configured: boolean; + /** 回调 Token */ + token: string; + /** 回调加密密钥 */ + encodingAESKey: string; + /** 接收者 ID */ + receiveId: string; + /** 原始配置 */ + config: WecomBotConfig; + /** 网络配置(来自 channels.wecom.network) */ + network?: WecomNetworkConfig; +}; + +/** + * 解析后的 Agent 账号 + */ +export type ResolvedAgentAccount = { + /** 账号 ID */ + accountId: string; + /** 是否启用 */ + enabled: boolean; + /** 是否配置完整 */ + configured: boolean; + /** 企业 ID */ + corpId: string; + /** 应用 Secret */ + corpSecret: string; + /** 应用 ID (数字) */ + agentId: number; + /** 回调 Token */ + token: string; + /** 回调加密密钥 */ + encodingAESKey: string; + /** 原始配置 */ + config: WecomAgentConfig; + /** 网络配置(来自 channels.wecom.network) */ + network?: WecomNetworkConfig; +}; + +/** + * 已解析的模式状态 + */ +export type ResolvedMode = { + /** Bot 模式是否已配置 */ + bot: boolean; + /** Agent 模式是否已配置 */ + agent: boolean; +}; + +/** + * 解析后的 WeCom 账号集合 + */ +export type ResolvedWecomAccounts = { + /** Bot 模式账号 */ + bot?: ResolvedBotAccount; + /** Agent 模式账号 */ + agent?: ResolvedAgentAccount; +}; + +// Re-export 用于向后兼容 +export type { WecomDmConfig } from "./config.js"; diff --git a/wecom/src/types/config.ts b/wecom/src/types/config.ts new file mode 100644 index 00000000..077f1c68 --- /dev/null +++ b/wecom/src/types/config.ts @@ -0,0 +1,88 @@ +/** + * WeCom 双模式配置类型定义 + */ + +/** DM 策略配置 - 与其他渠道保持一致,仅用 allowFrom */ +export type WecomDmConfig = { + /** DM 策略: 'open' 允许所有人, 'pairing' 需要配对, 'allowlist' 仅允许列表, 'disabled' 禁用 */ + policy?: 'open' | 'pairing' | 'allowlist' | 'disabled'; + /** 允许的用户列表,为空表示允许所有人 */ + allowFrom?: Array; +}; + +/** 媒体处理配置 */ +export type WecomMediaConfig = { + tempDir?: string; + retentionHours?: number; + cleanupOnStart?: boolean; + maxBytes?: number; +}; + +/** 网络配置 */ +export type WecomNetworkConfig = { + timeoutMs?: number; + retries?: number; + retryDelayMs?: number; + /** + * 出口代理(用于企业可信 IP 固定出口场景)。 + * 示例: "http://proxy.company.local:3128" + */ + egressProxyUrl?: string; +}; + +/** + * Bot 模式配置 (智能体) + * 用于接收 JSON 格式回调 + 流式回复 + */ +export type WecomBotConfig = { + /** 回调 Token (企微后台生成) */ + token: string; + /** 回调加密密钥 (企微后台生成) */ + encodingAESKey: string; + /** 接收者 ID (可选,用于解密校验) */ + receiveId?: string; + /** 流式消息占位符 */ + streamPlaceholderContent?: string; + /** 欢迎语 */ + welcomeText?: string; + /** DM 策略 */ + dm?: WecomDmConfig; +}; + +/** + * Agent 模式配置 (自建应用) + * 用于接收 XML 格式回调 + API 主动发送 + */ +export type WecomAgentConfig = { + /** 企业 ID */ + corpId: string; + /** 应用 Secret */ + corpSecret: string; + /** 应用 ID */ + agentId: number | string; + /** 回调 Token (企微后台「设置API接收」) */ + token: string; + /** 回调加密密钥 (企微后台「设置API接收」) */ + encodingAESKey: string; + /** 欢迎语 */ + welcomeText?: string; + /** DM 策略 */ + dm?: WecomDmConfig; +}; + +/** + * 顶层 WeCom 配置 + * 通过 bot / agent 字段隐式指定模式 + */ +export type WecomConfig = { + /** 是否启用 */ + enabled?: boolean; + /** Bot 模式配置 (智能体) */ + bot?: WecomBotConfig; + /** Agent 模式配置 (自建应用) */ + agent?: WecomAgentConfig; + /** 媒体处理配置 */ + media?: WecomMediaConfig; + /** 网络配置 */ + network?: WecomNetworkConfig; +}; diff --git a/wecom/src/types/constants.ts b/wecom/src/types/constants.ts new file mode 100644 index 00000000..ce40925d --- /dev/null +++ b/wecom/src/types/constants.ts @@ -0,0 +1,42 @@ +/** + * WeCom 双模式常量定义 + */ + +/** 固定 Webhook 路径 */ +export const WEBHOOK_PATHS = { + /** Bot 模式 (智能体) - 兼容原有路径 */ + BOT: "/wecom", + /** Bot 模式备用路径 */ + BOT_ALT: "/wecom/bot", + /** Agent 模式 (自建应用) */ + AGENT: "/wecom/agent", +} as const; + +/** 企业微信 API 端点 */ +export const API_ENDPOINTS = { + GET_TOKEN: "https://qyapi.weixin.qq.com/cgi-bin/gettoken", + SEND_MESSAGE: "https://qyapi.weixin.qq.com/cgi-bin/message/send", + SEND_APPCHAT: "https://qyapi.weixin.qq.com/cgi-bin/appchat/send", + UPLOAD_MEDIA: "https://qyapi.weixin.qq.com/cgi-bin/media/upload", + DOWNLOAD_MEDIA: "https://qyapi.weixin.qq.com/cgi-bin/media/get", +} as const; + +/** 各类限制常量 */ +export const LIMITS = { + /** 文本消息最大字节数 */ + TEXT_MAX_BYTES: 2048, + /** Token 刷新缓冲时间 (提前刷新) */ + TOKEN_REFRESH_BUFFER_MS: 60_000, + /** HTTP 请求超时 */ + REQUEST_TIMEOUT_MS: 15_000, + /** 最大请求体大小 */ + MAX_REQUEST_BODY_SIZE: 1024 * 1024, +} as const; + +/** AES 加密常量 */ +export const CRYPTO = { + /** PKCS#7 块大小 */ + PKCS7_BLOCK_SIZE: 32, + /** AES Key 长度 */ + AES_KEY_LENGTH: 32, +} as const; diff --git a/wecom/src/types/global.d.ts b/wecom/src/types/global.d.ts new file mode 100644 index 00000000..916e22b7 --- /dev/null +++ b/wecom/src/types/global.d.ts @@ -0,0 +1,9 @@ + +declare global { + var Buffer: any; + namespace NodeJS { + interface Timeout { } + } +} + +export { }; diff --git a/wecom/src/types/index.ts b/wecom/src/types/index.ts new file mode 100644 index 00000000..f98e6006 --- /dev/null +++ b/wecom/src/types/index.ts @@ -0,0 +1,38 @@ +/** + * WeCom 类型统一导出 + */ + +// 常量 +export * from "./constants.js"; + +// 配置类型 +export type { + WecomDmConfig, + WecomMediaConfig, + WecomNetworkConfig, + WecomBotConfig, + WecomAgentConfig, + WecomConfig, +} from "./config.js"; + +// 账号类型 +export type { + ResolvedBotAccount, + ResolvedAgentAccount, + ResolvedMode, + ResolvedWecomAccounts, +} from "./account.js"; + +// 消息类型 +export type { + WecomBotInboundBase, + WecomBotInboundText, + WecomBotInboundVoice, + WecomBotInboundStreamRefresh, + WecomBotInboundEvent, + WecomBotInboundMessage, + WecomAgentInboundMessage, + WecomInboundQuote, + WecomTemplateCard, + WecomOutboundMessage, +} from "./message.js"; diff --git a/wecom/src/types/message.ts b/wecom/src/types/message.ts new file mode 100644 index 00000000..e992de75 --- /dev/null +++ b/wecom/src/types/message.ts @@ -0,0 +1,185 @@ +/** + * WeCom 消息类型定义 + * Bot 和 Agent 模式共用 + */ + +/** + * Bot 模式入站消息基础结构 (JSON) + */ +/** + * **WecomBotInboundBase (Bot 入站消息基类)** + * + * Bot 模式下 JSON 格式回调的基础字段。 + * @property msgid 消息 ID + * @property aibotid 机器人 ID + * @property chattype 会话类型: "single" | "group" + * @property chatid 群聊 ID (仅群组时存在) + * @property response_url 下行回复 URL (用于被动响应转主动推送) + * @property from 发送者信息 + */ +export type WecomBotInboundBase = { + msgid?: string; + aibotid?: string; + chattype?: "single" | "group"; + chatid?: string; + response_url?: string; + from?: { userid?: string; corpid?: string }; + msgtype?: string; + /** 附件数量 (部分消息存在) */ + attachment_count?: number; +}; + +export type WecomBotInboundText = WecomBotInboundBase & { + msgtype: "text"; + text?: { content?: string }; + quote?: WecomInboundQuote; +}; + +export type WecomBotInboundVoice = WecomBotInboundBase & { + msgtype: "voice"; + voice?: { content?: string }; + quote?: WecomInboundQuote; +}; + +export type WecomBotInboundStreamRefresh = WecomBotInboundBase & { + msgtype: "stream"; + stream?: { id?: string }; +}; + +export type WecomBotInboundEvent = WecomBotInboundBase & { + msgtype: "event"; + create_time?: number; + event?: { + eventtype?: string; + [key: string]: unknown; + }; +}; + +/** + * **WecomInboundQuote (引用消息)** + * + * 消息中引用的原始内容(如回复某条消息)。 + * 支持引用文本、图片、混合类型、语音、文件等。 + */ +export type WecomInboundQuote = { + msgtype?: "text" | "image" | "mixed" | "voice" | "file"; + /** 引用文本内容 */ + text?: { content?: string }; + /** 引用图片 URL */ + image?: { url?: string }; + /** 引用混合消息 (图文) */ + mixed?: { + msg_item?: Array<{ + msgtype: "text" | "image"; + text?: { content?: string }; + image?: { url?: string }; + }>; + }; + /** 引用语音 */ + voice?: { content?: string }; + /** 引用文件 */ + file?: { url?: string }; +}; + +export type WecomBotInboundMessage = + | WecomBotInboundText + | WecomBotInboundVoice + | WecomBotInboundStreamRefresh + | WecomBotInboundEvent + | (WecomBotInboundBase & { quote?: WecomInboundQuote } & Record); + +/** + * Agent 模式入站消息结构 (解析自 XML) + */ +/** + * **WecomAgentInboundMessage (Agent 入站消息)** + * + * Agent 模式下解析自 XML 的扁平化消息结构。 + * 键名保持 PascalCase (如 `ToUserName`)。 + */ +export type WecomAgentInboundMessage = { + ToUserName?: string; + FromUserName?: string; + CreateTime?: number; + MsgType?: string; + MsgId?: string; + AgentID?: number; + // 文本消息 + Content?: string; + // 图片消息 + PicUrl?: string; + MediaId?: string; + // 文件消息 + FileName?: string; + // 语音消息 + Format?: string; + Recognition?: string; + // 视频消息 + ThumbMediaId?: string; + // 位置消息 + Location_X?: number; + Location_Y?: number; + Scale?: number; + Label?: string; + // 链接消息 + Title?: string; + Description?: string; + Url?: string; + // 事件消息 + Event?: string; + EventKey?: string; + // 群聊 + ChatId?: string; +}; + +/** + * 模板卡片类型 + */ +/** + * **WecomTemplateCard (模板卡片)** + * + * 复杂的交互式卡片结构。 + * @property card_type 卡片类型: "text_notice" | "news_notice" | "button_interaction" ... + * @property source 来源信息 + * @property main_title 主标题 + * @property sub_title_text 副标题 + * @property horizontal_content_list 水平排列的键值列表 + * @property button_list 按钮列表 + */ +export type WecomTemplateCard = { + card_type: "text_notice" | "news_notice" | "button_interaction" | "vote_interaction" | "multiple_interaction"; + source?: { icon_url?: string; desc?: string; desc_color?: number }; + main_title?: { title?: string; desc?: string }; + task_id?: string; + button_list?: Array<{ text: string; style?: number; key: string }>; + sub_title_text?: string; + horizontal_content_list?: Array<{ + keyname: string; + value?: string; + type?: number; + url?: string; + userid?: string; + }>; + card_action?: { type: number; url?: string; appid?: string; pagepath?: string }; + action_menu?: { desc: string; action_list: Array<{ text: string; key: string }> }; + select_list?: Array<{ + question_key: string; + title?: string; + selected_id?: string; + option_list: Array<{ id: string; text: string }>; + }>; + submit_button?: { text: string; key: string }; + checkbox?: { + question_key: string; + option_list: Array<{ id: string; text: string; is_checked?: boolean }>; + mode?: number; + }; +}; + +/** + * 出站消息类型 + */ +export type WecomOutboundMessage = + | { msgtype: "text"; text: { content: string } } + | { msgtype: "markdown"; markdown: { content: string } } + | { msgtype: "template_card"; template_card: WecomTemplateCard }; diff --git a/wecom/tsconfig.json b/wecom/tsconfig.json new file mode 100644 index 00000000..5c856673 --- /dev/null +++ b/wecom/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": ".", + "declaration": false, + "sourceMap": false, + "strict": true, + "skipLibCheck": true + }, + "include": [ + "index.ts", + "src/**/*.ts" + ], + "exclude": [ + "dist", + "node_modules", + "**/*.test.ts" + ] +} diff --git a/wecom/vitest.config.ts b/wecom/vitest.config.ts new file mode 100644 index 00000000..8d37721a --- /dev/null +++ b/wecom/vitest.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from "vitest/config"; + +import baseConfig from "../../vitest.config.ts"; + +const baseTest = (baseConfig as { test?: { exclude?: string[] } }).test ?? {}; +const exclude = baseTest.exclude ?? []; + +export default defineConfig({ + ...baseConfig, + test: { + ...baseTest, + include: ["extensions/wecom/src/**/*.test.ts"], + exclude, + }, +});