UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

199 lines (198 loc) • 9.22 kB
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-1VuiLaaR.mjs"; import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.mjs"; import { tt as withPluginCommandExecution } from "./runtime-U4i1bMh7.mjs"; import { f as isOperatorScope, t as ADMIN_SCOPE } from "./operator-scopes-BO7_XQCg.mjs"; import { r as logVerbose } from "./globals-yEiTitH8.mjs"; import { r as isTrustedReservedCommandOwner, t as canExposeSenderIsOwner } from "./command-registry-state-BXL4nUAg.mjs"; import { t as isReservedCommandName } from "./command-registration-sr-4cELZ.mjs"; import { O as resolveCommandConversationResolution } from "./session-binding-service-wvWqRl2A.mjs"; import { c as getCurrentPluginConversationBinding, f as requestPluginConversationBinding, s as detachPluginConversationBinding } from "./conversation-binding-e4SMpsaM.mjs"; import { t as resolveBoundAgentIdForSession } from "./session-agent-binding-DZbaYGPD.mjs"; import { t as pluginCommandSupportsChannel } from "./plugin-command-metadata-BOq2c1ES.mjs"; //#region src/plugins/plugin-command-execution.ts /** Exact-registry plugin command execution shared by focused and compatibility runtimes. */ const MAX_ARGS_LENGTH = 4096; const blockedCompaction = (reason) => ({ compacted: false, reason }); function sanitizeArgs(args) { if (!args) return; let sanitized = ""; for (const char of truncateUtf16Safe(args, MAX_ARGS_LENGTH)) { const code = char.charCodeAt(0); if (!(code <= 31 && code !== 9 && code !== 10 || code === 127)) sanitized += char; } return sanitized; } function resolveBindingConversation(params) { const channelPlugin = params.registry.channels.find((entry) => entry.plugin.id === params.channel)?.plugin; if (!channelPlugin?.bindings?.resolveCommandConversation) return null; return resolveCommandConversationResolution({ cfg: params.config, plugin: channelPlugin, channel: params.channel, accountId: params.accountId, threadId: params.messageThreadId, threadParentId: params.threadParentId, senderId: params.senderId, originatingTo: params.originatingTo ?? params.from, commandTo: params.to, fallbackTo: params.to ?? params.from }); } function buildRuntimeContext(command, params, invocationSignal, assertOwnerCurrent) { const sessionKey = params.sessionKey?.trim(); const agentId = resolveBoundAgentIdForSession({ config: params.config, agentId: params.agentId, sessionKey }); const compactCurrent = params.runtimeContext?.compactCurrent; if (!sessionKey && !agentId) return; return { llm: { complete: async (request) => { const { createRuntimeLlm } = await import("./runtime-llm.runtime.js"); return await createRuntimeLlm({ getConfig: () => params.config, authority: { caller: { kind: "plugin", id: command.pluginId, name: command.pluginName }, pluginIdForPolicy: command.pluginId, requiresBoundAgent: true, ...sessionKey ? { sessionKey } : {}, ...agentId ? { agentId } : {}, ...params.authProfileId ? { preferredProfile: params.authProfileId } : {}, allowAgentIdOverride: false, allowModelOverride: false, allowComplete: true } }).complete(request); } }, ...compactCurrent && params.sessionTarget ? { compactCurrent: async () => { if (invocationSignal.aborted) return blockedCompaction("command invocation closed"); return await compactCurrent(invocationSignal, assertOwnerCurrent); } } : {} }; } async function executeRegisteredPluginCommand(registry, params) { const assertAdmittedOwner = params.assertOwnerCurrent; const { command, args, senderId, channel, isAuthorizedSender, commandBody, config } = params; if (!pluginCommandSupportsChannel(command, channel)) { logVerbose(`Plugin command /${command.name} skipped on unsupported channel ${channel}`); return { continueAgent: true }; } if (command.requireAuth !== false && !isAuthorizedSender) { logVerbose(`Plugin command /${command.name} blocked: unauthorized sender ${senderId || "<unknown>"}`); return { text: "⚠️ This command requires authorization." }; } if (command.requiredScopes !== void 0 && !Array.isArray(command.requiredScopes)) { logVerbose(`Plugin command /${command.name} blocked: invalid requiredScopes configuration`); return { text: "⚠️ This command has invalid gateway scope configuration." }; } const requiredScopes = command.requiredScopes ?? []; for (const scope of requiredScopes) if (!isOperatorScope(scope)) { logVerbose(`Plugin command /${command.name} blocked: unknown gateway scope`); return { text: "⚠️ This command has invalid gateway scope configuration." }; } if (requiredScopes.length > 0) { const scopes = Array.isArray(params.gatewayClientScopes) ? new Set(params.gatewayClientScopes) : void 0; const hasAdmin = scopes?.has(ADMIN_SCOPE) === true; const missingScope = scopes ? requiredScopes.find((scope) => !hasAdmin && !scopes.has(scope)) : requiredScopes[0]; if (missingScope && (scopes !== void 0 || params.senderIsOwner !== true)) { logVerbose(`Plugin command /${command.name} blocked: missing gateway scope ${missingScope}`); return { text: `⚠️ This command requires gateway scope: ${missingScope}.` }; } } const bindingConversation = resolveBindingConversation({ registry, config, channel, senderId, from: params.from, to: params.to, originatingTo: params.originatingTo, accountId: params.accountId, messageThreadId: params.messageThreadId, threadParentId: params.threadParentId }); const trustedReservedOwner = isTrustedReservedCommandOwner(command) && command.ownership === "reserved" && isReservedCommandName(command.name) && command.pluginId === normalizeLowercaseStringOrEmpty(command.name); const senderIsOwner = canExposeSenderIsOwner(command) || trustedReservedOwner ? params.senderIsOwner : void 0; const commandInvocationAbort = new AbortController(); const assertOwnerCurrent = senderIsOwner === true ? () => { if (commandInvocationAbort.signal.aborted) throw new Error("Plugin command invocation closed."); assertAdmittedOwner?.(); } : void 0; const ctx = { senderId, channel, channelId: params.channelId, isAuthorizedSender, ...senderIsOwner === void 0 ? {} : { senderIsOwner }, ...assertOwnerCurrent ? { assertOwnerCurrent } : {}, gatewayClientScopes: params.gatewayClientScopes, agentId: params.agentId, sessionKey: params.sessionKey, sessionId: params.sessionId, sessionTarget: params.sessionTarget, sessionFile: params.sessionFile, args: sanitizeArgs(args), commandBody, config, from: params.from, to: params.to, accountId: bindingConversation?.accountId ?? params.accountId, messageThreadId: params.messageThreadId, threadParentId: params.threadParentId, diagnosticsSessions: params.diagnosticsSessions, runtimeContext: buildRuntimeContext(command, params, commandInvocationAbort.signal, assertOwnerCurrent), ...trustedReservedOwner && params.diagnosticsUploadApproved !== void 0 ? { diagnosticsUploadApproved: params.diagnosticsUploadApproved } : {}, ...trustedReservedOwner && params.diagnosticsPreviewOnly !== void 0 ? { diagnosticsPreviewOnly: params.diagnosticsPreviewOnly } : {}, ...trustedReservedOwner && params.diagnosticsPrivateRouted !== void 0 ? { diagnosticsPrivateRouted: params.diagnosticsPrivateRouted } : {}, requestConversationBinding: async (bindingParams) => { if (!command.pluginRoot || !bindingConversation) return { status: "error", message: "This command cannot bind the current conversation." }; return requestPluginConversationBinding({ pluginId: command.pluginId, pluginName: command.pluginName, pluginRoot: command.pluginRoot, requestedBySenderId: senderId, conversation: bindingConversation, binding: bindingParams, assertCurrent: assertOwnerCurrent }); }, detachConversationBinding: async () => command.pluginRoot && bindingConversation ? detachPluginConversationBinding({ pluginRoot: command.pluginRoot, conversation: bindingConversation }) : { removed: false }, getCurrentConversationBinding: async () => command.pluginRoot && bindingConversation ? getCurrentPluginConversationBinding({ pluginRoot: command.pluginRoot, conversation: bindingConversation }) : null }; try { if (requiredScopes.length > 0 && !Array.isArray(params.gatewayClientScopes)) assertOwnerCurrent?.(); const execution = await withPluginCommandExecution(registry, () => command.handler(ctx)); if (!execution.admitted) return { text: "⚠️ This command is no longer available after the plugin registry changed. Please try again." }; const result = execution.value; logVerbose(`Plugin command /${command.name} executed successfully for ${senderId || "unknown"}`); if (!result || typeof result !== "object") { logVerbose(`Plugin command /${command.name} returned no reply payload`); return {}; } return result; } catch (error) { logVerbose(`Plugin command /${command.name} error: ${error.message}`); return { text: "⚠️ Command failed. Please try again later." }; } finally { commandInvocationAbort.abort("command invocation closed"); } } //#endregion export { executeRegisteredPluginCommand as t };