From 9ec23174c0ada765cb89250de4f7ea5e8bf1f251 Mon Sep 17 00:00:00 2001 From: paisley <8197966+su8su@users.noreply.github.com> Date: Mon, 16 Mar 2026 13:19:06 +0800 Subject: [PATCH] feat(electron): auto-upgrade bundled OpenClaw plugins at app startup (#521) --- electron/api/routes/channels.ts | 104 ++--------------------- electron/main/index.ts | 7 ++ electron/main/ipc-handlers.ts | 104 ++--------------------- electron/utils/plugin-install.ts | 141 +++++++++++++++++++++++++++++++ package.json | 2 +- pnpm-lock.yaml | 106 +++-------------------- 6 files changed, 172 insertions(+), 292 deletions(-) create mode 100644 electron/utils/plugin-install.ts diff --git a/electron/api/routes/channels.ts b/electron/api/routes/channels.ts index 4708a8887..755ded5b5 100644 --- a/electron/api/routes/channels.ts +++ b/electron/api/routes/channels.ts @@ -1,8 +1,4 @@ import type { IncomingMessage, ServerResponse } from 'http'; -import { app } from 'electron'; -import { existsSync, cpSync, mkdirSync, rmSync, readFileSync } from 'node:fs'; -import { homedir } from 'node:os'; -import { join } from 'node:path'; import { deleteChannelConfig, getChannelFormValues, @@ -12,6 +8,12 @@ import { validateChannelConfig, validateChannelCredentials, } from '../../utils/channel-config'; +import { + ensureDingTalkPluginInstalled, + ensureFeishuPluginInstalled, + ensureQQBotPluginInstalled, + ensureWeComPluginInstalled, +} from '../../utils/plugin-install'; import { assignChannelToAgent, clearAllBindingsForChannel } from '../../utils/agent-config'; import { whatsAppLoginManager } from '../../utils/whatsapp-login'; import type { HostApiContext } from '../context'; @@ -46,100 +48,6 @@ function scheduleGatewayChannelSaveRefresh( void reason; } -// ── Generic plugin installer with version-aware upgrades ───────── - -function readPluginVersion(pkgJsonPath: string): string | null { - try { - const raw = readFileSync(pkgJsonPath, 'utf-8'); - const parsed = JSON.parse(raw) as { version?: string }; - return parsed.version ?? null; - } catch { - return null; - } -} - -function ensurePluginInstalled( - pluginDirName: string, - candidateSources: string[], - pluginLabel: string, -): { installed: boolean; warning?: string } { - const targetDir = join(homedir(), '.openclaw', 'extensions', pluginDirName); - const targetManifest = join(targetDir, 'openclaw.plugin.json'); - const targetPkgJson = join(targetDir, 'package.json'); - - const sourceDir = candidateSources.find((dir) => existsSync(join(dir, 'openclaw.plugin.json'))); - - // If already installed, check whether an upgrade is available - if (existsSync(targetManifest)) { - if (!sourceDir) return { installed: true }; // no bundled source to compare, keep existing - const installedVersion = readPluginVersion(targetPkgJson); - const sourceVersion = readPluginVersion(join(sourceDir, 'package.json')); - if (!sourceVersion || !installedVersion || sourceVersion === installedVersion) { - return { installed: true }; // same version or unable to compare - } - // Version differs — fall through to overwrite install - console.log( - `[plugin] Upgrading ${pluginLabel} plugin: ${installedVersion} → ${sourceVersion}`, - ); - } - - // Fresh install or upgrade - if (!sourceDir) { - return { - installed: false, - warning: `Bundled ${pluginLabel} plugin mirror not found. Checked: ${candidateSources.join(' | ')}`, - }; - } - - try { - mkdirSync(join(homedir(), '.openclaw', 'extensions'), { recursive: true }); - rmSync(targetDir, { recursive: true, force: true }); - cpSync(sourceDir, targetDir, { recursive: true, dereference: true }); - if (!existsSync(join(targetDir, 'openclaw.plugin.json'))) { - return { installed: false, warning: `Failed to install ${pluginLabel} plugin mirror (manifest missing).` }; - } - return { installed: true }; - } catch { - return { installed: false, warning: `Failed to install bundled ${pluginLabel} plugin mirror` }; - } -} - -// ── Per-channel plugin helpers (thin wrappers around ensurePluginInstalled) ── - -function buildCandidateSources(pluginDirName: string): string[] { - return app.isPackaged - ? [ - join(process.resourcesPath, 'openclaw-plugins', pluginDirName), - join(process.resourcesPath, 'app.asar.unpacked', 'build', 'openclaw-plugins', pluginDirName), - join(process.resourcesPath, 'app.asar.unpacked', 'openclaw-plugins', pluginDirName), - ] - : [ - join(app.getAppPath(), 'build', 'openclaw-plugins', pluginDirName), - join(process.cwd(), 'build', 'openclaw-plugins', pluginDirName), - join(__dirname, '../../../build/openclaw-plugins', pluginDirName), - ]; -} - -function ensureDingTalkPluginInstalled(): { installed: boolean; warning?: string } { - return ensurePluginInstalled('dingtalk', buildCandidateSources('dingtalk'), 'DingTalk'); -} - -function ensureWeComPluginInstalled(): { installed: boolean; warning?: string } { - return ensurePluginInstalled('wecom', buildCandidateSources('wecom'), 'WeCom'); -} - -function ensureFeishuPluginInstalled(): { installed: boolean; warning?: string } { - return ensurePluginInstalled( - 'feishu-openclaw-plugin', - buildCandidateSources('feishu-openclaw-plugin'), - 'Feishu', - ); -} - -function ensureQQBotPluginInstalled(): { installed: boolean; warning?: string } { - return ensurePluginInstalled('qqbot', buildCandidateSources('qqbot'), 'QQ Bot'); -} - function toComparableConfig(input: Record): Record { const next: Record = {}; for (const [key, value] of Object.entries(input)) { diff --git a/electron/main/index.ts b/electron/main/index.ts index 67ef44f5b..3748461c6 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -29,6 +29,7 @@ import { } from './main-window-focus'; import { getSetting } from '../utils/store'; import { ensureBuiltinSkillsInstalled, ensurePreinstalledSkillsInstalled } from '../utils/skill-config'; +import { ensureAllBundledPluginsInstalled } from '../utils/plugin-install'; import { startHostApiServer } from '../api/server'; import { HostEventBus } from '../api/event-bus'; import { deviceOAuthManager } from '../utils/device-oauth'; @@ -294,6 +295,12 @@ async function initialize(): Promise { logger.warn('Failed to install preinstalled skills:', error); }); + // Pre-deploy/upgrade bundled OpenClaw plugins (dingtalk, wecom, qqbot, feishu) + // to ~/.openclaw/extensions/ so they are always up-to-date after an app update. + void ensureAllBundledPluginsInstalled().catch((error) => { + logger.warn('Failed to install/upgrade bundled plugins:', error); + }); + // Bridge gateway and host-side events before any auto-start logic runs, so // renderer subscribers observe the full startup lifecycle. gatewayManager.on('status', (status: { state: string }) => { diff --git a/electron/main/ipc-handlers.ts b/electron/main/ipc-handlers.ts index 1a4945963..df0fd6236 100644 --- a/electron/main/ipc-handlers.ts +++ b/electron/main/ipc-handlers.ts @@ -3,7 +3,7 @@ * Registers all IPC handlers for main-renderer communication */ import { ipcMain, BrowserWindow, shell, dialog, app, nativeImage } from 'electron'; -import { existsSync, cpSync, mkdirSync, rmSync, readFileSync } from 'node:fs'; +import { existsSync } from 'node:fs'; import { homedir } from 'node:os'; import { join, extname, basename } from 'node:path'; import crypto from 'node:crypto'; @@ -32,6 +32,12 @@ import { validateChannelCredentials, } from '../utils/channel-config'; import { checkUvInstalled, installUv, setupManagedPython } from '../utils/uv-setup'; +import { + ensureDingTalkPluginInstalled, + ensureFeishuPluginInstalled, + ensureQQBotPluginInstalled, + ensureWeComPluginInstalled, +} from '../utils/plugin-install'; import { updateSkillConfig, getSkillConfig, getAllSkillConfigs } from '../utils/skill-config'; import { whatsAppLoginManager } from '../utils/whatsapp-login'; import { getProviderConfig } from '../utils/provider-registry'; @@ -1382,102 +1388,6 @@ function registerOpenClawHandlers(gatewayManager: GatewayManager): void { gatewayManager.debouncedReload(); }; - // ── Generic plugin installer with version-aware upgrades ───────── - - function readPluginVersion(pkgJsonPath: string): string | null { - try { - const raw = readFileSync(pkgJsonPath, 'utf-8'); - const parsed = JSON.parse(raw) as { version?: string }; - return parsed.version ?? null; - } catch { - return null; - } - } - - function ensurePluginInstalled( - pluginDirName: string, - candidateSources: string[], - pluginLabel: string, - ): { installed: boolean; warning?: string } { - const targetDir = join(homedir(), '.openclaw', 'extensions', pluginDirName); - const targetManifest = join(targetDir, 'openclaw.plugin.json'); - const targetPkgJson = join(targetDir, 'package.json'); - - const sourceDir = candidateSources.find((dir) => existsSync(join(dir, 'openclaw.plugin.json'))); - - // If already installed, check whether an upgrade is available - if (existsSync(targetManifest)) { - if (!sourceDir) return { installed: true }; - const installedVersion = readPluginVersion(targetPkgJson); - const sourceVersion = readPluginVersion(join(sourceDir, 'package.json')); - if (!sourceVersion || !installedVersion || sourceVersion === installedVersion) { - return { installed: true }; - } - logger.info(`[plugin] Upgrading ${pluginLabel} plugin: ${installedVersion} → ${sourceVersion}`); - } - - if (!sourceDir) { - logger.warn(`Bundled ${pluginLabel} plugin mirror not found in candidate paths`, { candidateSources }); - return { - installed: false, - warning: `Bundled ${pluginLabel} plugin mirror not found. Checked: ${candidateSources.join(' | ')}`, - }; - } - - try { - mkdirSync(join(homedir(), '.openclaw', 'extensions'), { recursive: true }); - rmSync(targetDir, { recursive: true, force: true }); - cpSync(sourceDir, targetDir, { recursive: true, dereference: true }); - - if (!existsSync(join(targetDir, 'openclaw.plugin.json'))) { - return { installed: false, warning: `Failed to install ${pluginLabel} plugin mirror (manifest missing).` }; - } - - logger.info(`Installed ${pluginLabel} plugin from bundled mirror: ${sourceDir}`); - return { installed: true }; - } catch (error) { - logger.warn(`Failed to install ${pluginLabel} plugin from bundled mirror:`, error); - return { - installed: false, - warning: `Failed to install bundled ${pluginLabel} plugin mirror`, - }; - } - } - - function buildCandidateSources(pluginDirName: string): string[] { - return app.isPackaged - ? [ - join(process.resourcesPath, 'openclaw-plugins', pluginDirName), - join(process.resourcesPath, 'app.asar.unpacked', 'build', 'openclaw-plugins', pluginDirName), - join(process.resourcesPath, 'app.asar.unpacked', 'openclaw-plugins', pluginDirName), - ] - : [ - join(app.getAppPath(), 'build', 'openclaw-plugins', pluginDirName), - join(process.cwd(), 'build', 'openclaw-plugins', pluginDirName), - join(__dirname, '../../build/openclaw-plugins', pluginDirName), - ]; - } - - function ensureDingTalkPluginInstalled(): { installed: boolean; warning?: string } { - return ensurePluginInstalled('dingtalk', buildCandidateSources('dingtalk'), 'DingTalk'); - } - - function ensureWeComPluginInstalled(): { installed: boolean; warning?: string } { - return ensurePluginInstalled('wecom', buildCandidateSources('wecom'), 'WeCom'); - } - - function ensureFeishuPluginInstalled(): { installed: boolean; warning?: string } { - return ensurePluginInstalled( - 'feishu-openclaw-plugin', - buildCandidateSources('feishu-openclaw-plugin'), - 'Feishu', - ); - } - - function ensureQQBotPluginInstalled(): { installed: boolean; warning?: string } { - return ensurePluginInstalled('qqbot', buildCandidateSources('qqbot'), 'QQ Bot'); - } - // Get OpenClaw package status ipcMain.handle('openclaw:status', () => { const status = getOpenClawStatus(); diff --git a/electron/utils/plugin-install.ts b/electron/utils/plugin-install.ts new file mode 100644 index 000000000..34f589364 --- /dev/null +++ b/electron/utils/plugin-install.ts @@ -0,0 +1,141 @@ +/** + * Shared OpenClaw Plugin Install Utilities + * + * Provides version-aware install/upgrade logic for bundled OpenClaw plugins + * (DingTalk, WeCom, QQBot, Feishu). Used both at app startup (to auto-upgrade + * stale plugins) and when a user configures a channel. + */ +import { app } from 'electron'; +import { existsSync, cpSync, mkdirSync, rmSync, readFileSync } from 'node:fs'; +import { homedir } from 'node:os'; +import { join } from 'node:path'; +import { logger } from './logger'; + +// ── Version helper ─────────────────────────────────────────────────────────── + +function readPluginVersion(pkgJsonPath: string): string | null { + try { + const raw = readFileSync(pkgJsonPath, 'utf-8'); + const parsed = JSON.parse(raw) as { version?: string }; + return parsed.version ?? null; + } catch { + return null; + } +} + +// ── Core install / upgrade logic ───────────────────────────────────────────── + +export function ensurePluginInstalled( + pluginDirName: string, + candidateSources: string[], + pluginLabel: string, +): { installed: boolean; warning?: string } { + const targetDir = join(homedir(), '.openclaw', 'extensions', pluginDirName); + const targetManifest = join(targetDir, 'openclaw.plugin.json'); + const targetPkgJson = join(targetDir, 'package.json'); + + const sourceDir = candidateSources.find((dir) => existsSync(join(dir, 'openclaw.plugin.json'))); + + // If already installed, check whether an upgrade is available + if (existsSync(targetManifest)) { + if (!sourceDir) return { installed: true }; // no bundled source to compare, keep existing + const installedVersion = readPluginVersion(targetPkgJson); + const sourceVersion = readPluginVersion(join(sourceDir, 'package.json')); + if (!sourceVersion || !installedVersion || sourceVersion === installedVersion) { + return { installed: true }; // same version or unable to compare + } + // Version differs — fall through to overwrite install + logger.info( + `[plugin] Upgrading ${pluginLabel} plugin: ${installedVersion} → ${sourceVersion}`, + ); + } + + // Fresh install or upgrade + if (!sourceDir) { + return { + installed: false, + warning: `Bundled ${pluginLabel} plugin mirror not found. Checked: ${candidateSources.join(' | ')}`, + }; + } + + try { + mkdirSync(join(homedir(), '.openclaw', 'extensions'), { recursive: true }); + rmSync(targetDir, { recursive: true, force: true }); + cpSync(sourceDir, targetDir, { recursive: true, dereference: true }); + if (!existsSync(join(targetDir, 'openclaw.plugin.json'))) { + return { installed: false, warning: `Failed to install ${pluginLabel} plugin mirror (manifest missing).` }; + } + logger.info(`Installed ${pluginLabel} plugin from bundled mirror: ${sourceDir}`); + return { installed: true }; + } catch { + return { installed: false, warning: `Failed to install bundled ${pluginLabel} plugin mirror` }; + } +} + +// ── Candidate source path builder ──────────────────────────────────────────── + +export function buildCandidateSources(pluginDirName: string): string[] { + return app.isPackaged + ? [ + join(process.resourcesPath, 'openclaw-plugins', pluginDirName), + join(process.resourcesPath, 'app.asar.unpacked', 'build', 'openclaw-plugins', pluginDirName), + join(process.resourcesPath, 'app.asar.unpacked', 'openclaw-plugins', pluginDirName), + ] + : [ + join(app.getAppPath(), 'build', 'openclaw-plugins', pluginDirName), + join(process.cwd(), 'build', 'openclaw-plugins', pluginDirName), + join(__dirname, '../../build/openclaw-plugins', pluginDirName), + ]; +} + +// ── Per-channel plugin helpers ─────────────────────────────────────────────── + +export function ensureDingTalkPluginInstalled(): { installed: boolean; warning?: string } { + return ensurePluginInstalled('dingtalk', buildCandidateSources('dingtalk'), 'DingTalk'); +} + +export function ensureWeComPluginInstalled(): { installed: boolean; warning?: string } { + return ensurePluginInstalled('wecom', buildCandidateSources('wecom'), 'WeCom'); +} + +export function ensureFeishuPluginInstalled(): { installed: boolean; warning?: string } { + return ensurePluginInstalled( + 'feishu-openclaw-plugin', + buildCandidateSources('feishu-openclaw-plugin'), + 'Feishu', + ); +} + +export function ensureQQBotPluginInstalled(): { installed: boolean; warning?: string } { + return ensurePluginInstalled('qqbot', buildCandidateSources('qqbot'), 'QQ Bot'); +} + +// ── Bulk startup installer ─────────────────────────────────────────────────── + +/** + * All bundled plugins, in the same order as after-pack.cjs BUNDLED_PLUGINS. + */ +const ALL_BUNDLED_PLUGINS = [ + { fn: ensureDingTalkPluginInstalled, label: 'DingTalk' }, + { fn: ensureWeComPluginInstalled, label: 'WeCom' }, + { fn: ensureQQBotPluginInstalled, label: 'QQ Bot' }, + { fn: ensureFeishuPluginInstalled, label: 'Feishu' }, +] as const; + +/** + * Ensure all bundled OpenClaw plugins are installed/upgraded in + * `~/.openclaw/extensions/`. Designed to be called once at app startup + * as a fire-and-forget task — errors are logged but never thrown. + */ +export async function ensureAllBundledPluginsInstalled(): Promise { + for (const { fn, label } of ALL_BUNDLED_PLUGINS) { + try { + const result = fn(); + if (result.warning) { + logger.warn(`[plugin] ${label}: ${result.warning}`); + } + } catch (error) { + logger.warn(`[plugin] Failed to install/upgrade ${label} plugin:`, error); + } + } +} diff --git a/package.json b/package.json index 5b077d00b..4a6131fef 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ }, "devDependencies": { "@eslint/js": "^10.0.1", - "@larksuite/openclaw-lark": "2026.3.12", + "@larksuite/openclaw-lark": "2026.3.15", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c77362e83..e5dc9caa4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,8 +37,8 @@ importers: specifier: ^10.0.1 version: 10.0.1(eslint@10.0.0(jiti@1.21.7)) '@larksuite/openclaw-lark': - specifier: 2026.3.12 - version: 2026.3.12 + specifier: 2026.3.15 + version: 2026.3.15 '@radix-ui/react-dialog': specifier: ^1.1.15 version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -296,10 +296,6 @@ packages: resolution: {integrity: sha512-ag7Qx78m1K3Dv7xlFgeHS4jBdopGZUISgVBMUy7Cj4fIgVH9EBmsc5K4hWozL8BJQctWke8Wsl96O7Gd+HCGhg==} engines: {node: '>=20.0.0'} - '@aws-sdk/client-bedrock@3.1006.0': - resolution: {integrity: sha512-CWUrrlWaFDYZIK2rNIa9FUVn3wC3lZszz0r6bq5yRiRj1P+dSd+ZpGdlRYDAi6Nq9dsainEXdpadiuUwzL6YZQ==} - engines: {node: '>=20.0.0'} - '@aws-sdk/client-bedrock@3.1009.0': resolution: {integrity: sha512-KzLNqSg1T59sSlQvEA4EL3oDIAMidM54AB1b+UGouPFuUrrwGp2uUlZUYzIIlCvqpf7wEDh8wypqXISRItkgdg==} engines: {node: '>=20.0.0'} @@ -444,10 +440,6 @@ packages: resolution: {integrity: sha512-vMxd+ivKqSxU9bHx5vmAlFKDAkjGotFU56IOkDa5DaTu1WWwbcse0yFHEm9I537oVvodaiwMl3VBwgHfzQ2rvw==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1006.0': - resolution: {integrity: sha512-eCBaQI1w5PcliOdh8Y0YONOim2zNSTEK4E7gXYC4vIqiT/lzVODIFxmpc8oOBLPSANzcr9daIPPtjQ2C75dLFg==} - engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1009.0': resolution: {integrity: sha512-KCPLuTqN9u0Rr38Arln78fRG9KXpzsPWmof+PZzfAHMMQq2QED6YjQrkrfiH7PDefLWEposY1o4/eGwrmKA4JA==} engines: {node: '>=20.0.0'} @@ -1229,8 +1221,8 @@ packages: '@kwsites/promise-deferred@1.1.1': resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} - '@larksuite/openclaw-lark@2026.3.12': - resolution: {integrity: sha512-MNcDrerQrO42I09w+M8q6dwnWHMKxOnXSCLG4qNwcekjGeDmA53lIuWJtGMpjTzvDjYkoWnN+8Zg78+FRCSV9w==} + '@larksuite/openclaw-lark@2026.3.15': + resolution: {integrity: sha512-YGk63CgYG+YDUO2+TxdSPJZA6km52OpqAqf26vZHMUqawEKhiT4bcfD5JHh6VGazjUwRk5Bze1UyVKncc0csBA==} '@larksuiteoapi/node-sdk@1.59.0': resolution: {integrity: sha512-sBpkruTvZDOxnVtoTbepWKRX0j1Y1ZElQYu0x7+v088sI9pcpbVp6ZzCGn62dhrKPatzNyCJyzYCPXPYQWccrA==} @@ -2513,10 +2505,6 @@ packages: resolution: {integrity: sha512-eWX2mdt1ktpn8+40iiMc404uGrih+2fxiky3zBcPjtXKj6HLRdYlmhrPkJi7JTJm8dpXR6BWVWEDBXtaWMKD6A==} engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} - '@slack/web-api@7.14.1': - resolution: {integrity: sha512-RoygyteJeFswxDPJjUMESn9dldWVMD2xUcHHd9DenVavSfVC6FeVnSdDerOO7m8LLvw4Q132nQM4hX8JiF7dng==} - engines: {node: '>= 18', npm: '>= 8.6.0'} - '@slack/web-api@7.15.0': resolution: {integrity: sha512-va7zYIt3QHG1x9M/jqXXRPFMoOVlVSSRHC5YH+DzKYsrz5xUKOA3lR4THsu/Zxha9N1jOndbKFKLtr0WOPW1Vw==} engines: {node: '>= 18', npm: '>= 8.6.0'} @@ -7211,51 +7199,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-bedrock@3.1006.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.19 - '@aws-sdk/credential-provider-node': 3.972.19 - '@aws-sdk/middleware-host-header': 3.972.7 - '@aws-sdk/middleware-logger': 3.972.7 - '@aws-sdk/middleware-recursion-detection': 3.972.7 - '@aws-sdk/middleware-user-agent': 3.972.20 - '@aws-sdk/region-config-resolver': 3.972.7 - '@aws-sdk/token-providers': 3.1006.0 - '@aws-sdk/types': 3.973.5 - '@aws-sdk/util-endpoints': 3.996.4 - '@aws-sdk/util-user-agent-browser': 3.972.7 - '@aws-sdk/util-user-agent-node': 3.973.5 - '@smithy/config-resolver': 4.4.10 - '@smithy/core': 3.23.9 - '@smithy/fetch-http-handler': 5.3.13 - '@smithy/hash-node': 4.2.11 - '@smithy/invalid-dependency': 4.2.11 - '@smithy/middleware-content-length': 4.2.11 - '@smithy/middleware-endpoint': 4.4.23 - '@smithy/middleware-retry': 4.4.40 - '@smithy/middleware-serde': 4.2.12 - '@smithy/middleware-stack': 4.2.11 - '@smithy/node-config-provider': 4.3.11 - '@smithy/node-http-handler': 4.4.14 - '@smithy/protocol-http': 5.3.11 - '@smithy/smithy-client': 4.12.3 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.11 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.39 - '@smithy/util-defaults-mode-node': 4.2.42 - '@smithy/util-endpoints': 3.3.2 - '@smithy/util-middleware': 4.2.11 - '@smithy/util-retry': 4.2.11 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-bedrock@3.1009.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -7791,18 +7734,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/token-providers@3.1006.0': - dependencies: - '@aws-sdk/core': 3.973.19 - '@aws-sdk/nested-clients': 3.996.8 - '@aws-sdk/types': 3.973.5 - '@smithy/property-provider': 4.2.11 - '@smithy/shared-ini-file-loader': 4.4.6 - '@smithy/types': 4.13.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/token-providers@3.1009.0': dependencies: '@aws-sdk/core': 3.973.20 @@ -8671,7 +8602,7 @@ snapshots: '@kwsites/promise-deferred@1.1.1': optional: true - '@larksuite/openclaw-lark@2026.3.12': + '@larksuite/openclaw-lark@2026.3.15': dependencies: '@larksuiteoapi/node-sdk': 1.59.0 '@sinclair/typebox': 0.34.48 @@ -8874,7 +8805,7 @@ snapshots: chalk: 5.6.2 cli-highlight: 2.1.11 diff: 8.0.3 - file-type: 21.3.0 + file-type: 21.3.2 glob: 11.1.0 marked: 15.0.12 minimatch: 10.2.4 @@ -9961,23 +9892,6 @@ snapshots: '@slack/types@2.20.1': {} - '@slack/web-api@7.14.1': - dependencies: - '@slack/logger': 4.0.0 - '@slack/types': 2.20.0 - '@types/node': 25.3.0 - '@types/retry': 0.12.0 - axios: 1.13.5(debug@4.4.3) - eventemitter3: 5.0.4 - form-data: 4.0.5 - is-electron: 2.2.2 - is-stream: 2.0.1 - p-queue: 6.6.2 - p-retry: 4.6.2 - retry: 0.13.1 - transitivePeerDependencies: - - debug - '@slack/web-api@7.15.0': dependencies: '@slack/logger': 4.0.1 @@ -11467,7 +11381,7 @@ snapshots: clawdbot@2026.1.24-3(@discordjs/opus@0.10.0(encoding@0.1.13))(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(@types/express@5.0.6)(devtools-protocol@0.0.1596832)(encoding@0.1.13)(opusscript@0.1.1)(typescript@5.9.3): dependencies: '@agentclientprotocol/sdk': 0.13.1(zod@4.3.6) - '@aws-sdk/client-bedrock': 3.1006.0 + '@aws-sdk/client-bedrock': 3.1009.0 '@buape/carbon': 0.14.0(@discordjs/opus@0.10.0(encoding@0.1.13))(hono@4.11.4)(opusscript@0.1.1) '@clack/prompts': 0.11.0 '@grammyjs/runner': 2.0.3(grammy@1.41.1(encoding@0.1.13)) @@ -11482,7 +11396,7 @@ snapshots: '@mozilla/readability': 0.6.0 '@sinclair/typebox': 0.34.47 '@slack/bolt': 4.6.0(@types/express@5.0.6) - '@slack/web-api': 7.14.1 + '@slack/web-api': 7.15.0 '@whiskeysockets/baileys': 7.0.0-rc.9(sharp@0.34.5) ajv: 8.18.0 body-parser: 2.2.2 @@ -11496,7 +11410,7 @@ snapshots: discord-api-types: 0.38.42 dotenv: 17.3.1 express: 5.2.1 - file-type: 21.3.0 + file-type: 21.3.2 grammy: 1.41.1(encoding@0.1.13) hono: 4.11.4 jiti: 2.6.1 @@ -11515,7 +11429,7 @@ snapshots: sqlite-vec: 0.1.7-alpha.2 tar: 7.5.4 tslog: 4.10.2 - undici: 7.22.0 + undici: 7.24.1 ws: 8.19.0 yaml: 2.8.2 zod: 4.3.6