UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

417 lines (416 loc) 19.6 kB
import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js"; import { c as resolveUserPath } from "./home-dir-BPhrG-aM.js"; import { m as shortenHomePath } from "./utils-P__uGsPB.js"; import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js"; import { a as buildAgentMainSessionKey } from "./session-key-BnWWjqNc.js"; import { r as isReservedSystemAgentId } from "./agent-id-D7twbNRG.js"; import { n as t } from "./i18n-hynzGFbD.js"; import { r as SYSTEM_AGENT_AUDIT_STORE_LABEL } from "./audit-D3_LINzS.js"; import { s as validateSystemAgentPluginInstallSpec, u as redactSystemAgentConfig } from "./operations-parse-DIiMdt6S.js"; import { _ as runGatewayLifecycle, a as executeSetDefaultModel, c as formatConfigValidationLine, d as loadOverviewForOperation, f as readConfigFileSnapshotLazy, g as runConfigSetOperation, h as resolveTuiAgentId, i as createNoExitRuntime, l as formatGatewayStatusLine, m as resolveChannelSetupState, n as applyPersistentOperation, o as executeSetup, p as readConfigValueAtPath, r as assertConfigWriteDoesNotBypassInferenceVerification, s as formatChannelDocsUrl, t as CONFIG_GET_OUTPUT_MAX_CHARS, u as isPluginBackingDefaultInferenceRoute } from "./operations-execution-helpers-BpievPkg.js"; //#region src/system-agent/plugin-install.ts async function executePluginInstall(operation, runtime, opts) { const validationError = validateSystemAgentPluginInstallSpec(operation.spec); if (validationError) throw new Error(validationError); const { runPluginInstallCommand } = await import("./plugins-install-command-CEmIHzqR.js"); const result = await applyPersistentOperation({ auditOperation: "plugin.install", operation, runtime, opts, run: async (ctx) => { await ctx.commit(() => runPluginInstallCommand({ raw: operation.spec, opts: {}, runtime: createNoExitRuntime(ctx.runtime), allowInstallPolicyWarningPrompt: false, ...ctx.assertPersistentApply ? { beforePersistentApply: ctx.assertPersistentApply } : {} })); return { summary: `Installed plugin ${operation.spec}`, details: { spec: operation.spec } }; } }); if (result.applied) runtime.log("Restart the Gateway to apply installed plugin changes."); return result; } //#endregion //#region src/system-agent/operations-execute.ts const loadOverviewModule = async () => await import("./overview-BVzJyALQ.js"); /** Execute a parsed OpenClaw operation after applying approval gates and audit logging. */ async function executeSystemAgentOperation(operation, runtime, opts = {}) { switch (operation.kind) { case "none": runtime.log(operation.message); return { applied: false, exitsInteractive: operation.message.includes("Bye.") }; case "overview": { const overview = await loadOverviewForOperation(opts.deps); if (opts.deps?.formatOverview) runtime.log(opts.deps.formatOverview(overview)); else { const { formatSystemAgentOverview } = await loadOverviewModule(); runtime.log(formatSystemAgentOverview(overview)); } return { applied: false }; } case "agents": { const overview = await loadOverviewForOperation(opts.deps); runtime.log(["Agents:", ...overview.agents.map((agent) => { return ` - ${[ agent.id, agent.isDefault ? "default" : void 0, agent.name ? `name=${agent.name}` : void 0, agent.workspace ? `workspace=${shortenHomePath(resolveUserPath(agent.workspace))}` : void 0 ].filter(Boolean).join(" | ")}`; })].join("\n")); return { applied: false }; } case "models": { const overview = await loadOverviewForOperation(opts.deps); runtime.log([ `Default model: ${overview.defaultModel ?? "not configured"}`, `Codex: ${overview.tools.codex.found ? "found" : "not found"}`, `Claude Code: ${overview.tools.claude.found ? "found" : "not found"}`, `Gemini CLI: ${overview.tools.gemini.found ? "found" : "not found"}`, `OpenAI key: ${overview.tools.apiKeys.openai ? "found" : "not found"}`, `Anthropic key: ${overview.tools.apiKeys.anthropic ? "found" : "not found"}` ].join("\n")); return { applied: false }; } case "plugin-list": await (opts.deps?.runPluginsList ?? (async (pluginRuntime) => { const { runPluginsListCommand } = await import("./plugins-list-command-DgacKC5o.js"); await runPluginsListCommand({}, pluginRuntime); }))(runtime); return { applied: false }; case "plugin-search": await (opts.deps?.runPluginsSearch ?? (async (query, pluginRuntime) => { const { runPluginsSearchCommand } = await import("./plugins-search-command-WrRUFbgZ.js"); await runPluginsSearchCommand(query, {}, pluginRuntime); }))(operation.query, runtime); return { applied: false }; case "audit": runtime.log(`Audit state: ${SYSTEM_AGENT_AUDIT_STORE_LABEL}`); runtime.log("Only applied writes/actions are recorded; discovery stays quiet."); return { applied: false }; case "config-validate": { const snapshot = await readConfigFileSnapshotLazy(); runtime.log(formatConfigValidationLine(snapshot)); return { applied: false }; } case "config-get": { const snapshot = await readConfigFileSnapshotLazy(); if (!snapshot.exists) { runtime.log(`Config missing: ${shortenHomePath(snapshot.path)}`); return { applied: false }; } const cfg = snapshot.sourceConfig; const lookup = readConfigValueAtPath(redactSystemAgentConfig(cfg, { config: cfg, valid: snapshot.valid }), operation.path); if (!lookup.found) { runtime.log(`${operation.path}: not set. Use \`config schema ${operation.path}\` to see what is allowed.`); return { applied: false }; } const rendered = JSON.stringify(lookup.value, null, 2) ?? "null"; runtime.log(rendered.length > 2e3 ? `${operation.path} = ${truncateUtf16Safe(rendered, CONFIG_GET_OUTPUT_MAX_CHARS)}\n… (truncated)` : `${operation.path} = ${rendered}`); return { applied: false }; } case "config-schema": { const { buildConfigSchemaCore, lookupConfigSchema } = await import("./schema-BZBkNMTR.js"); const response = buildConfigSchemaCore(); const path = operation.path ?? "."; const result = lookupConfigSchema(response, path); if (!result) { runtime.log(`No config schema at "${path}". Try \`config schema .\` for the root keys.`); return { applied: false }; } const schema = result.schema; const childLines = result.children.slice(0, 40).map((child) => { const bits = [ Array.isArray(child.type) ? child.type.join("|") : child.type ?? "object", child.required ? "required" : void 0, child.hasChildren ? "…" : void 0 ].filter(Boolean).join(", "); return ` - ${child.path} (${bits})`; }); runtime.log([ `Schema for ${result.path === "" ? "." : result.path}:`, schema.type ? `type: ${Array.isArray(schema.type) ? schema.type.join("|") : schema.type}` : void 0, schema.description ? `description: ${schema.description}` : void 0, schema.enum ? `allowed values: ${schema.enum.map((v) => JSON.stringify(v)).join(", ")}` : void 0, schema.default !== void 0 ? `default: ${JSON.stringify(schema.default)}` : void 0, ...childLines.length > 0 ? ["keys:", ...childLines] : [], result.children.length > 40 ? `… +${result.children.length - 40} more keys` : void 0 ].filter((line) => line !== void 0).join("\n")); return { applied: false }; } case "channel-list": { const { resolved } = await resolveChannelSetupState(opts.deps); const entries = resolved.entries.toSorted((a, b) => a.id.localeCompare(b.id)); runtime.log([ "Channels:", ...entries.map((entry) => ` - ${entry.id}${entry.meta.label ? ` (${entry.meta.label})` : ""}`), "", "Say `connect <channel>` to walk through setup (for example `connect telegram`)." ].join("\n")); return { applied: false }; } case "channel-info": { const { cfg, installedPlugins, resolved, isConfigured } = await resolveChannelSetupState(opts.deps); const channel = operation.channel.toLowerCase(); const entry = resolved.entries.find((candidate) => candidate.id === channel); if (!entry) { const knownIds = resolved.entries.map((candidate) => candidate.id).toSorted(); runtime.log([`Unknown channel: ${channel}`, `Known channels: ${knownIds.length > 0 ? knownIds.join(", ") : "none"}`].join("\n")); return { applied: false }; } const installed = installedPlugins.some((plugin) => plugin.id === entry.id) || resolved.installedCatalogById.has(entry.id); runtime.log([ `${entry.meta.label} (${entry.id})`, entry.meta.blurb, `Configured: ${isConfigured(cfg, entry.id) ? "yes" : "no"}`, `Installed: ${installed ? "yes" : "no"}`, `Docs: ${formatChannelDocsUrl(entry.meta.docsPath)}`, "", `Say \`connect ${entry.id}\` to set it up here, or \`open channel wizard for ${entry.id}\` for the masked terminal wizard.` ].join("\n")); return { applied: false }; } case "channel-setup": runtime.log([ `Connecting ${operation.channel} needs an interactive session.`, "Run `openclaw setup` and say `connect " + operation.channel + "`,", "or run `openclaw channels add` for the terminal wizard." ].join("\n")); return { applied: false }; case "skills-setup": runtime.log([ "Skills setup needs an interactive session.", "Run `openclaw setup` and say `configure skills`,", "or run `openclaw configure --section skills` for the terminal wizard." ].join("\n")); return { applied: false }; case "search-setup": runtime.log([ "Web search setup needs an interactive session.", "Run `openclaw setup` and say `configure search`,", "or run `openclaw configure --section web` for the masked terminal wizard." ].join("\n")); return { applied: false }; case "gateway-config-setup": runtime.log([ "Gateway configuration needs an interactive session.", "Run `openclaw setup` and say `configure gateway`,", "or run `openclaw configure --section gateway` for the masked terminal wizard." ].join("\n")); return { applied: false }; case "memory-import": runtime.log([ "Memory import needs an interactive session.", "Open the Memory page in the Control UI,", "or run `openclaw onboard` for the terminal wizard." ].join("\n")); return { applied: false }; case "model-setup": runtime.log(["Changing model providers must happen outside the inference session that powers OpenClaw.", "Stop the OpenClaw host through whatever started it. Run `openclaw onboard` on the machine running OpenClaw: it stages credentials, live-tests the candidate route, and saves only a passing setup. Then restart the host."].join("\n")); return { applied: false }; case "model-accounts": runtime.log("Manage your personal accounts in Settings → Profile → Connected accounts, or run `openclaw models accounts list` / `openclaw models accounts login <provider>`. Check the Gateway, person, and Personal scope before signing in. Nothing has changed. Enter credentials only in the protected sign-in controls, never in chat."); return { applied: false }; case "open-setup": { const command = operation.target === "guided" ? "openclaw onboard" : operation.target === "classic" ? "openclaw onboard --classic" : operation.target === "channels" ? `openclaw channels add${operation.channel ? ` --channel ${operation.channel}` : ""}` : operation.target === "search" ? "openclaw configure --section web" : "openclaw configure --section gateway"; runtime.log(`This session cannot host an interactive wizard. Run \`${command}\` on the machine running OpenClaw.`); return { applied: false }; } case "setup": return await executeSetup(operation, runtime, opts); case "config-set": await assertConfigWriteDoesNotBypassInferenceVerification(operation); return await applyPersistentOperation({ auditOperation: "config.set", operation, runtime, opts, run: async (ctx) => { await runConfigSetOperation({ operation, ctx }); return { summary: `Set config ${operation.path}`, details: { path: operation.path } }; } }); case "config-set-ref": await assertConfigWriteDoesNotBypassInferenceVerification(operation); return await applyPersistentOperation({ auditOperation: "config.setRef", operation, runtime, opts, run: async (ctx) => { await runConfigSetOperation({ operation, ctx }); return { summary: `Set config ${operation.path} SecretRef`, details: { path: operation.path, source: operation.source, provider: operation.provider ?? "default" } }; } }); case "plugin-install": return await executePluginInstall(operation, runtime, opts); case "plugin-activate-artifact": { const { executePluginArtifactActivation } = await import("./plugin-artifact-C40a1THm.js"); return await executePluginArtifactActivation(operation, runtime, opts); } case "plugin-uninstall": { if (await isPluginBackingDefaultInferenceRoute(operation.pluginId)) { const message = [`Uninstalling ${operation.pluginId} could remove the provider behind OpenClaw's own active inference route.`, `Removing it has to happen with OpenClaw stopped: run \`openclaw plugins uninstall ${operation.pluginId}\` on the machine running it.`].join("\n"); runtime.log(message); return { applied: false, message }; } const result = await applyPersistentOperation({ auditOperation: "plugin.uninstall", operation, runtime, opts, run: async (ctx) => { const runPluginUninstall = ctx.deps?.runPluginUninstall ?? (async (pluginId, pluginRuntime, options) => { const { runPluginUninstallCommand } = await import("./plugins-uninstall-command-CuwMzcMI.js"); await runPluginUninstallCommand(pluginId, options, pluginRuntime); }); if (await isPluginBackingDefaultInferenceRoute(operation.pluginId)) throw new Error(`Uninstall aborted: ${operation.pluginId} now backs the active inference route. Removing it has to happen with OpenClaw stopped: run \`openclaw plugins uninstall ${operation.pluginId}\` on the machine running it.`); await ctx.commit(() => runPluginUninstall(operation.pluginId, createNoExitRuntime(ctx.runtime), ctx.assertPersistentApply ? { beforePersistentApply: ctx.assertPersistentApply } : void 0)); return { summary: `Uninstalled plugin ${operation.pluginId}`, details: { pluginId: operation.pluginId } }; } }); if (result.applied) runtime.log("Restart the Gateway to apply plugin changes."); return result; } case "create-agent": if (isReservedSystemAgentId(operation.agentId)) throw new Error(`Agent id "${normalizeAgentId(operation.agentId)}" is reserved for the system agent. Choose a different agent id.`); if (operation.model?.trim()) throw new Error("OpenClaw cannot save an explicit per-agent model until that new route can be live-tested. Retry without `model`; the new agent inherits the verified default, then use `set_default_model` with agentId to live-test and save its own model."); return await applyPersistentOperation({ auditOperation: "agents.create", operation, runtime, opts, run: async (ctx) => { const createAgentForOperation = ctx.deps?.createAgent ?? (await import("./agent-create-D0HXzoaY.js")).createAgent; const result = await ctx.commit(async () => { return await createAgentForOperation({ name: operation.agentId, ...operation.workspace ? { workspace: operation.workspace } : {}, ...ctx.assertPersistentApply ? { beforePersistentApply: ctx.assertPersistentApply } : {}, provenance: { createdVia: "agent", creatorAgentId: operation.requesterAgentId ?? "openclaw" } }); }); if (result.status === "error") throw new Error(result.message); return { summary: `Created agent ${result.agentId}`, bootstrapPending: result.bootstrapPending, agentId: result.agentId, details: { agentId: result.agentId, workspace: result.workspace } }; } }); case "doctor": await (opts.deps?.runDoctor ?? (await import("./doctor-90GwF_jO.js")).doctorCommand)(runtime, { nonInteractive: true }); return { applied: false }; case "doctor-fix": runtime.log("Doctor repairs can change the inference route that powers this session, so they run with OpenClaw stopped: `openclaw doctor --fix` on the machine running it."); return { applied: false }; case "status": { const { statusCommand } = await import("./status.command-Bb72QlU4.js"); await statusCommand({ timeoutMs: 1e4 }, runtime); return { applied: false }; } case "health": { const { healthCommand } = await import("./health-YMSl1wN1.js"); await healthCommand({ timeoutMs: 1e4 }, runtime); return { applied: false }; } case "gateway-status": { const overview = await loadOverviewForOperation(opts.deps); runtime.log(formatGatewayStatusLine(overview)); return { applied: false }; } case "gateway-start": case "gateway-stop": case "gateway-restart": return await applyPersistentOperation({ auditOperation: operation.kind.replace("-", "."), operation, runtime, opts, run: async (ctx) => { const action = operation.kind === "gateway-start" ? "start" : operation.kind === "gateway-stop" ? "stop" : "restart"; if (ctx.deps?.setupSurface === "gateway") { const host = ctx.deps.gatewayHostLifecycle; if (!host) throw new Error("Gateway host lifecycle is unavailable. Use the service manager on the Gateway host."); const result = await host.request(action, () => ctx.assertPersistentApply?.()); if (!result.ok) throw new Error(result.error); const summary = result.value.outcome === "already-running" ? "Gateway already running" : `Scheduled Gateway ${action}`; ctx.runtime.log(summary); return { summary }; } const run = action === "start" ? ctx.deps?.runGatewayStart : action === "stop" ? ctx.deps?.runGatewayStop : ctx.deps?.runGatewayRestart; if (await ctx.commit(run ?? (() => runGatewayLifecycle(action))) === false) throw new Error("Gateway restart did not complete"); return { summary: action === "start" ? "Started Gateway" : action === "stop" ? "Stopped Gateway" : "Restarted Gateway" }; } }); case "open-tui": { const overview = await loadOverviewForOperation(opts.deps); const agentId = resolveTuiAgentId({ requestedAgentId: operation.agentId, requestedWorkspace: operation.workspace, overview }); const session = agentId ? buildAgentMainSessionKey({ agentId }) : void 0; const result = await (opts.deps?.runTui ?? (await import("./tui-Cjf5Sw9V.js")).runTui)({ local: !overview.gateway.reachable, session, deliver: false, historyLimit: 200, ...operation.agentDraft === "hatch" ? { message: t("wizard.finalize.bootstrapHatchMessage") } : {} }); if (result?.exitReason === "return-to-system-agent") { runtime.log(result.systemAgentMessage ? `[openclaw] returned from agent with request: ${result.systemAgentMessage}` : "[openclaw] returned from agent"); return { applied: false, returnToShell: true, nextInput: result.systemAgentMessage }; } return { applied: false, exitsInteractive: true }; } case "set-default-model": return await executeSetDefaultModel(operation, runtime, opts); default: return { applied: false }; } } //#endregion export { executeSystemAgentOperation as t };