openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
417 lines (416 loc) • 19.6 kB
JavaScript
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 };