From 38bc364aedb7d875564a786905b7bc0f5db9fdaa Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 16 Mar 2026 21:36:22 -0700 Subject: [PATCH] Runtime: narrow WhatsApp login tool surface --- .../runtime/runtime-whatsapp-login-tool.ts | 71 +++++++++++++++++++ src/plugins/runtime/runtime-whatsapp.ts | 4 +- src/plugins/runtime/types-channel.ts | 2 +- 3 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 src/plugins/runtime/runtime-whatsapp-login-tool.ts diff --git a/src/plugins/runtime/runtime-whatsapp-login-tool.ts b/src/plugins/runtime/runtime-whatsapp-login-tool.ts new file mode 100644 index 00000000000..88b5d0e6138 --- /dev/null +++ b/src/plugins/runtime/runtime-whatsapp-login-tool.ts @@ -0,0 +1,71 @@ +import { Type } from "@sinclair/typebox"; +import type { ChannelAgentTool } from "openclaw/plugin-sdk/channel-runtime"; + +export function createRuntimeWhatsAppLoginTool(): ChannelAgentTool { + return { + label: "WhatsApp Login", + name: "whatsapp_login", + ownerOnly: true, + description: "Generate a WhatsApp QR code for linking, or wait for the scan to complete.", + parameters: Type.Object({ + action: Type.Unsafe<"start" | "wait">({ + type: "string", + enum: ["start", "wait"], + }), + timeoutMs: Type.Optional(Type.Number()), + force: Type.Optional(Type.Boolean()), + }), + execute: async (_toolCallId, args) => { + const { startWebLoginWithQr, waitForWebLogin } = + await import("../../../extensions/whatsapp/src/login-qr.js"); + const action = (args as { action?: string })?.action ?? "start"; + if (action === "wait") { + const result = await waitForWebLogin({ + timeoutMs: + typeof (args as { timeoutMs?: unknown }).timeoutMs === "number" + ? (args as { timeoutMs?: number }).timeoutMs + : undefined, + }); + return { + content: [{ type: "text", text: result.message }], + details: { connected: result.connected }, + }; + } + + const result = await startWebLoginWithQr({ + timeoutMs: + typeof (args as { timeoutMs?: unknown }).timeoutMs === "number" + ? (args as { timeoutMs?: number }).timeoutMs + : undefined, + force: + typeof (args as { force?: unknown }).force === "boolean" + ? (args as { force?: boolean }).force + : false, + }); + + if (!result.qrDataUrl) { + return { + content: [ + { + type: "text", + text: result.message, + }, + ], + details: { qr: false }, + }; + } + + const text = [ + result.message, + "", + "Open WhatsApp -> Linked Devices and scan:", + "", + `![whatsapp-qr](${result.qrDataUrl})`, + ].join("\n"); + return { + content: [{ type: "text", text }], + details: { qr: true }, + }; + }, + }; +} diff --git a/src/plugins/runtime/runtime-whatsapp.ts b/src/plugins/runtime/runtime-whatsapp.ts index 20d36a936f0..21a92aefe09 100644 --- a/src/plugins/runtime/runtime-whatsapp.ts +++ b/src/plugins/runtime/runtime-whatsapp.ts @@ -6,7 +6,7 @@ import { readWebSelfId, webAuthExists, } from "../../../extensions/whatsapp/src/auth-store.js"; -import { createWhatsAppLoginTool } from "../../channels/plugins/agent-tools/whatsapp-login.js"; +import { createRuntimeWhatsAppLoginTool } from "./runtime-whatsapp-login-tool.js"; import type { PluginRuntime } from "./types.js"; const sendMessageWhatsAppLazy: PluginRuntime["channel"]["whatsapp"]["sendMessageWhatsApp"] = async ( @@ -106,6 +106,6 @@ export function createRuntimeWhatsApp(): PluginRuntime["channel"]["whatsapp"] { waitForWebLogin: waitForWebLoginLazy, monitorWebChannel: monitorWebChannelLazy, handleWhatsAppAction: handleWhatsAppActionLazy, - createLoginTool: createWhatsAppLoginTool, + createLoginTool: createRuntimeWhatsAppLoginTool, }; } diff --git a/src/plugins/runtime/types-channel.ts b/src/plugins/runtime/types-channel.ts index f8e6e095ef5..1b0c21044a8 100644 --- a/src/plugins/runtime/types-channel.ts +++ b/src/plugins/runtime/types-channel.ts @@ -211,7 +211,7 @@ export type PluginRuntimeChannel = { waitForWebLogin: typeof import("../../../extensions/whatsapp/src/login-qr.js").waitForWebLogin; monitorWebChannel: typeof import("../../channels/web/index.js").monitorWebChannel; handleWhatsAppAction: typeof import("../../agents/tools/whatsapp-actions.js").handleWhatsAppAction; - createLoginTool: typeof import("../../channels/plugins/agent-tools/whatsapp-login.js").createWhatsAppLoginTool; + createLoginTool: typeof import("./runtime-whatsapp-login-tool.js").createRuntimeWhatsAppLoginTool; }; line: { listLineAccountIds: typeof import("../../line/accounts.js").listLineAccountIds;