UNPKG

@gguf/claw

Version:

WhatsApp gateway CLI (Baileys web) with Pi RPC agent

545 lines (539 loc) 20.6 kB
import { X as resolveStateDir } from "./entry.js"; import { m as resolveUserPath } from "./utils-PmTbZoD1.js"; import { t as runCommandWithTimeout } from "./exec-BIMFe4XS.js"; import { t as resolveOpenClawPackageRoot } from "./openclaw-root-Dean7Fp2.js"; import path from "node:path"; import os from "node:os"; import fs from "node:fs/promises"; import { fileURLToPath } from "node:url"; //#region src/sessions/session-key-utils.ts function parseAgentSessionKey(sessionKey) { const raw = (sessionKey ?? "").trim(); if (!raw) return null; const parts = raw.split(":").filter(Boolean); if (parts.length < 3) return null; if (parts[0] !== "agent") return null; const agentId = parts[1]?.trim(); const rest = parts.slice(2).join(":"); if (!agentId || !rest) return null; return { agentId, rest }; } function isSubagentSessionKey(sessionKey) { const raw = (sessionKey ?? "").trim(); if (!raw) return false; if (raw.toLowerCase().startsWith("subagent:")) return true; const parsed = parseAgentSessionKey(raw); return Boolean((parsed?.rest ?? "").toLowerCase().startsWith("subagent:")); } function isAcpSessionKey(sessionKey) { const raw = (sessionKey ?? "").trim(); if (!raw) return false; if (raw.toLowerCase().startsWith("acp:")) return true; const parsed = parseAgentSessionKey(raw); return Boolean((parsed?.rest ?? "").toLowerCase().startsWith("acp:")); } const THREAD_SESSION_MARKERS = [":thread:", ":topic:"]; function resolveThreadParentSessionKey(sessionKey) { const raw = (sessionKey ?? "").trim(); if (!raw) return null; const normalized = raw.toLowerCase(); let idx = -1; for (const marker of THREAD_SESSION_MARKERS) { const candidate = normalized.lastIndexOf(marker); if (candidate > idx) idx = candidate; } if (idx <= 0) return null; const parent = raw.slice(0, idx).trim(); return parent ? parent : null; } //#endregion //#region src/routing/session-key.ts const DEFAULT_AGENT_ID = "main"; const DEFAULT_MAIN_KEY = "main"; const DEFAULT_ACCOUNT_ID = "default"; const VALID_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i; const INVALID_CHARS_RE = /[^a-z0-9_-]+/g; const LEADING_DASH_RE = /^-+/; const TRAILING_DASH_RE = /-+$/; function normalizeToken(value) { return (value ?? "").trim().toLowerCase(); } function normalizeMainKey(value) { const trimmed = (value ?? "").trim(); return trimmed ? trimmed.toLowerCase() : DEFAULT_MAIN_KEY; } function toAgentRequestSessionKey(storeKey) { const raw = (storeKey ?? "").trim(); if (!raw) return; return parseAgentSessionKey(raw)?.rest ?? raw; } function toAgentStoreSessionKey(params) { const raw = (params.requestKey ?? "").trim(); if (!raw || raw === DEFAULT_MAIN_KEY) return buildAgentMainSessionKey({ agentId: params.agentId, mainKey: params.mainKey }); const lowered = raw.toLowerCase(); if (lowered.startsWith("agent:")) return lowered; if (lowered.startsWith("subagent:")) return `agent:${normalizeAgentId(params.agentId)}:${lowered}`; return `agent:${normalizeAgentId(params.agentId)}:${lowered}`; } function resolveAgentIdFromSessionKey(sessionKey) { return normalizeAgentId(parseAgentSessionKey(sessionKey)?.agentId ?? DEFAULT_AGENT_ID); } function normalizeAgentId(value) { const trimmed = (value ?? "").trim(); if (!trimmed) return DEFAULT_AGENT_ID; if (VALID_ID_RE.test(trimmed)) return trimmed.toLowerCase(); return trimmed.toLowerCase().replace(INVALID_CHARS_RE, "-").replace(LEADING_DASH_RE, "").replace(TRAILING_DASH_RE, "").slice(0, 64) || DEFAULT_AGENT_ID; } function sanitizeAgentId(value) { const trimmed = (value ?? "").trim(); if (!trimmed) return DEFAULT_AGENT_ID; if (VALID_ID_RE.test(trimmed)) return trimmed.toLowerCase(); return trimmed.toLowerCase().replace(INVALID_CHARS_RE, "-").replace(LEADING_DASH_RE, "").replace(TRAILING_DASH_RE, "").slice(0, 64) || DEFAULT_AGENT_ID; } function normalizeAccountId(value) { const trimmed = (value ?? "").trim(); if (!trimmed) return DEFAULT_ACCOUNT_ID; if (VALID_ID_RE.test(trimmed)) return trimmed.toLowerCase(); return trimmed.toLowerCase().replace(INVALID_CHARS_RE, "-").replace(LEADING_DASH_RE, "").replace(TRAILING_DASH_RE, "").slice(0, 64) || DEFAULT_ACCOUNT_ID; } function buildAgentMainSessionKey(params) { return `agent:${normalizeAgentId(params.agentId)}:${normalizeMainKey(params.mainKey)}`; } function buildAgentPeerSessionKey(params) { const peerKind = params.peerKind ?? "dm"; if (peerKind === "dm") { const dmScope = params.dmScope ?? "main"; let peerId = (params.peerId ?? "").trim(); const linkedPeerId = dmScope === "main" ? null : resolveLinkedPeerId({ identityLinks: params.identityLinks, channel: params.channel, peerId }); if (linkedPeerId) peerId = linkedPeerId; peerId = peerId.toLowerCase(); if (dmScope === "per-account-channel-peer" && peerId) { const channel = (params.channel ?? "").trim().toLowerCase() || "unknown"; const accountId = normalizeAccountId(params.accountId); return `agent:${normalizeAgentId(params.agentId)}:${channel}:${accountId}:dm:${peerId}`; } if (dmScope === "per-channel-peer" && peerId) { const channel = (params.channel ?? "").trim().toLowerCase() || "unknown"; return `agent:${normalizeAgentId(params.agentId)}:${channel}:dm:${peerId}`; } if (dmScope === "per-peer" && peerId) return `agent:${normalizeAgentId(params.agentId)}:dm:${peerId}`; return buildAgentMainSessionKey({ agentId: params.agentId, mainKey: params.mainKey }); } const channel = (params.channel ?? "").trim().toLowerCase() || "unknown"; const peerId = ((params.peerId ?? "").trim() || "unknown").toLowerCase(); return `agent:${normalizeAgentId(params.agentId)}:${channel}:${peerKind}:${peerId}`; } function resolveLinkedPeerId(params) { const identityLinks = params.identityLinks; if (!identityLinks) return null; const peerId = params.peerId.trim(); if (!peerId) return null; const candidates = /* @__PURE__ */ new Set(); const rawCandidate = normalizeToken(peerId); if (rawCandidate) candidates.add(rawCandidate); const channel = normalizeToken(params.channel); if (channel) { const scopedCandidate = normalizeToken(`${channel}:${peerId}`); if (scopedCandidate) candidates.add(scopedCandidate); } if (candidates.size === 0) return null; for (const [canonical, ids] of Object.entries(identityLinks)) { const canonicalName = canonical.trim(); if (!canonicalName) continue; if (!Array.isArray(ids)) continue; for (const id of ids) { const normalized = normalizeToken(id); if (normalized && candidates.has(normalized)) return canonicalName; } } return null; } function buildGroupHistoryKey(params) { const channel = normalizeToken(params.channel) || "unknown"; const accountId = normalizeAccountId(params.accountId); const peerId = params.peerId.trim().toLowerCase() || "unknown"; return `${channel}:${accountId}:${params.peerKind}:${peerId}`; } function resolveThreadSessionKeys(params) { const threadId = (params.threadId ?? "").trim(); if (!threadId) return { sessionKey: params.baseSessionKey, parentSessionKey: void 0 }; const normalizedThreadId = threadId.toLowerCase(); return { sessionKey: params.useSuffix ?? true ? `${params.baseSessionKey}:thread:${normalizedThreadId}` : params.baseSessionKey, parentSessionKey: params.parentSessionKey }; } //#endregion //#region src/agents/workspace-templates.ts const FALLBACK_TEMPLATE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../docs/reference/templates"); let cachedTemplateDir; let resolvingTemplateDir; async function pathExists(candidate) { try { await fs.access(candidate); return true; } catch { return false; } } async function resolveWorkspaceTemplateDir(opts) { if (cachedTemplateDir) return cachedTemplateDir; if (resolvingTemplateDir) return resolvingTemplateDir; resolvingTemplateDir = (async () => { const moduleUrl = opts?.moduleUrl ?? import.meta.url; const argv1 = opts?.argv1 ?? process.argv[1]; const cwd = opts?.cwd ?? process.cwd(); const packageRoot = await resolveOpenClawPackageRoot({ moduleUrl, argv1, cwd }); const candidates = [ packageRoot ? path.join(packageRoot, "docs", "reference", "templates") : null, cwd ? path.resolve(cwd, "docs", "reference", "templates") : null, FALLBACK_TEMPLATE_DIR ].filter(Boolean); for (const candidate of candidates) if (await pathExists(candidate)) { cachedTemplateDir = candidate; return candidate; } cachedTemplateDir = candidates[0] ?? FALLBACK_TEMPLATE_DIR; return cachedTemplateDir; })(); try { return await resolvingTemplateDir; } finally { resolvingTemplateDir = void 0; } } //#endregion //#region src/agents/workspace.ts function resolveDefaultAgentWorkspaceDir(env = process.env, homedir = os.homedir) { const profile = env.OPENCLAW_PROFILE?.trim(); if (profile && profile.toLowerCase() !== "default") return path.join(homedir(), ".openclaw", `workspace-${profile}`); return path.join(homedir(), ".openclaw", "workspace"); } const DEFAULT_AGENT_WORKSPACE_DIR = resolveDefaultAgentWorkspaceDir(); const DEFAULT_AGENTS_FILENAME = "AGENTS.md"; const DEFAULT_SOUL_FILENAME = "SOUL.md"; const DEFAULT_TOOLS_FILENAME = "TOOLS.md"; const DEFAULT_IDENTITY_FILENAME = "IDENTITY.md"; const DEFAULT_USER_FILENAME = "USER.md"; const DEFAULT_HEARTBEAT_FILENAME = "HEARTBEAT.md"; const DEFAULT_BOOTSTRAP_FILENAME = "BOOTSTRAP.md"; const DEFAULT_MEMORY_FILENAME = "MEMORY.md"; const DEFAULT_MEMORY_ALT_FILENAME = "memory.md"; function stripFrontMatter(content) { if (!content.startsWith("---")) return content; const endIndex = content.indexOf("\n---", 3); if (endIndex === -1) return content; const start = endIndex + 4; let trimmed = content.slice(start); trimmed = trimmed.replace(/^\s+/, ""); return trimmed; } async function loadTemplate(name) { const templateDir = await resolveWorkspaceTemplateDir(); const templatePath = path.join(templateDir, name); try { return stripFrontMatter(await fs.readFile(templatePath, "utf-8")); } catch { throw new Error(`Missing workspace template: ${name} (${templatePath}). Ensure docs/reference/templates are packaged.`); } } async function writeFileIfMissing(filePath, content) { try { await fs.writeFile(filePath, content, { encoding: "utf-8", flag: "wx" }); } catch (err) { if (err.code !== "EEXIST") throw err; } } async function hasGitRepo(dir) { try { await fs.stat(path.join(dir, ".git")); return true; } catch { return false; } } async function isGitAvailable() { try { return (await runCommandWithTimeout(["git", "--version"], { timeoutMs: 2e3 })).code === 0; } catch { return false; } } async function ensureGitRepo(dir, isBrandNewWorkspace) { if (!isBrandNewWorkspace) return; if (await hasGitRepo(dir)) return; if (!await isGitAvailable()) return; try { await runCommandWithTimeout(["git", "init"], { cwd: dir, timeoutMs: 1e4 }); } catch {} } async function ensureAgentWorkspace(params) { const dir = resolveUserPath(params?.dir?.trim() ? params.dir.trim() : DEFAULT_AGENT_WORKSPACE_DIR); await fs.mkdir(dir, { recursive: true }); if (!params?.ensureBootstrapFiles) return { dir }; const agentsPath = path.join(dir, DEFAULT_AGENTS_FILENAME); const soulPath = path.join(dir, DEFAULT_SOUL_FILENAME); const toolsPath = path.join(dir, DEFAULT_TOOLS_FILENAME); const identityPath = path.join(dir, DEFAULT_IDENTITY_FILENAME); const userPath = path.join(dir, DEFAULT_USER_FILENAME); const heartbeatPath = path.join(dir, DEFAULT_HEARTBEAT_FILENAME); const bootstrapPath = path.join(dir, DEFAULT_BOOTSTRAP_FILENAME); const isBrandNewWorkspace = await (async () => { const paths = [ agentsPath, soulPath, toolsPath, identityPath, userPath, heartbeatPath ]; return (await Promise.all(paths.map(async (p) => { try { await fs.access(p); return true; } catch { return false; } }))).every((v) => !v); })(); const agentsTemplate = await loadTemplate(DEFAULT_AGENTS_FILENAME); const soulTemplate = await loadTemplate(DEFAULT_SOUL_FILENAME); const toolsTemplate = await loadTemplate(DEFAULT_TOOLS_FILENAME); const identityTemplate = await loadTemplate(DEFAULT_IDENTITY_FILENAME); const userTemplate = await loadTemplate(DEFAULT_USER_FILENAME); const heartbeatTemplate = await loadTemplate(DEFAULT_HEARTBEAT_FILENAME); const bootstrapTemplate = await loadTemplate(DEFAULT_BOOTSTRAP_FILENAME); await writeFileIfMissing(agentsPath, agentsTemplate); await writeFileIfMissing(soulPath, soulTemplate); await writeFileIfMissing(toolsPath, toolsTemplate); await writeFileIfMissing(identityPath, identityTemplate); await writeFileIfMissing(userPath, userTemplate); await writeFileIfMissing(heartbeatPath, heartbeatTemplate); if (isBrandNewWorkspace) await writeFileIfMissing(bootstrapPath, bootstrapTemplate); await ensureGitRepo(dir, isBrandNewWorkspace); return { dir, agentsPath, soulPath, toolsPath, identityPath, userPath, heartbeatPath, bootstrapPath }; } async function resolveMemoryBootstrapEntries(resolvedDir) { const candidates = [DEFAULT_MEMORY_FILENAME, DEFAULT_MEMORY_ALT_FILENAME]; const entries = []; for (const name of candidates) { const filePath = path.join(resolvedDir, name); try { await fs.access(filePath); entries.push({ name, filePath }); } catch {} } if (entries.length <= 1) return entries; const seen = /* @__PURE__ */ new Set(); const deduped = []; for (const entry of entries) { let key = entry.filePath; try { key = await fs.realpath(entry.filePath); } catch {} if (seen.has(key)) continue; seen.add(key); deduped.push(entry); } return deduped; } async function loadWorkspaceBootstrapFiles(dir) { const resolvedDir = resolveUserPath(dir); const entries = [ { name: DEFAULT_AGENTS_FILENAME, filePath: path.join(resolvedDir, DEFAULT_AGENTS_FILENAME) }, { name: DEFAULT_SOUL_FILENAME, filePath: path.join(resolvedDir, DEFAULT_SOUL_FILENAME) }, { name: DEFAULT_TOOLS_FILENAME, filePath: path.join(resolvedDir, DEFAULT_TOOLS_FILENAME) }, { name: DEFAULT_IDENTITY_FILENAME, filePath: path.join(resolvedDir, DEFAULT_IDENTITY_FILENAME) }, { name: DEFAULT_USER_FILENAME, filePath: path.join(resolvedDir, DEFAULT_USER_FILENAME) }, { name: DEFAULT_HEARTBEAT_FILENAME, filePath: path.join(resolvedDir, DEFAULT_HEARTBEAT_FILENAME) }, { name: DEFAULT_BOOTSTRAP_FILENAME, filePath: path.join(resolvedDir, DEFAULT_BOOTSTRAP_FILENAME) } ]; entries.push(...await resolveMemoryBootstrapEntries(resolvedDir)); const result = []; for (const entry of entries) try { const content = await fs.readFile(entry.filePath, "utf-8"); result.push({ name: entry.name, path: entry.filePath, content, missing: false }); } catch { result.push({ name: entry.name, path: entry.filePath, missing: true }); } return result; } const SUBAGENT_BOOTSTRAP_ALLOWLIST = new Set([DEFAULT_AGENTS_FILENAME, DEFAULT_TOOLS_FILENAME]); function filterBootstrapFilesForSession(files, sessionKey) { if (!sessionKey || !isSubagentSessionKey(sessionKey)) return files; return files.filter((file) => SUBAGENT_BOOTSTRAP_ALLOWLIST.has(file.name)); } //#endregion //#region src/agents/agent-scope.ts let defaultAgentWarned = false; function listAgents(cfg) { const list = cfg.agents?.list; if (!Array.isArray(list)) return []; return list.filter((entry) => Boolean(entry && typeof entry === "object")); } function listAgentIds(cfg) { const agents = listAgents(cfg); if (agents.length === 0) return [DEFAULT_AGENT_ID]; const seen = /* @__PURE__ */ new Set(); const ids = []; for (const entry of agents) { const id = normalizeAgentId(entry?.id); if (seen.has(id)) continue; seen.add(id); ids.push(id); } return ids.length > 0 ? ids : [DEFAULT_AGENT_ID]; } function resolveDefaultAgentId(cfg) { const agents = listAgents(cfg); if (agents.length === 0) return DEFAULT_AGENT_ID; const defaults = agents.filter((agent) => agent?.default); if (defaults.length > 1 && !defaultAgentWarned) { defaultAgentWarned = true; console.warn("Multiple agents marked default=true; using the first entry as default."); } const chosen = (defaults[0] ?? agents[0])?.id?.trim(); return normalizeAgentId(chosen || DEFAULT_AGENT_ID); } function resolveSessionAgentIds(params) { const defaultAgentId = resolveDefaultAgentId(params.config ?? {}); const sessionKey = params.sessionKey?.trim(); const normalizedSessionKey = sessionKey ? sessionKey.toLowerCase() : void 0; const parsed = normalizedSessionKey ? parseAgentSessionKey(normalizedSessionKey) : null; return { defaultAgentId, sessionAgentId: parsed?.agentId ? normalizeAgentId(parsed.agentId) : defaultAgentId }; } function resolveSessionAgentId(params) { return resolveSessionAgentIds(params).sessionAgentId; } function resolveAgentEntry(cfg, agentId) { const id = normalizeAgentId(agentId); return listAgents(cfg).find((entry) => normalizeAgentId(entry.id) === id); } function resolveAgentConfig(cfg, agentId) { const entry = resolveAgentEntry(cfg, normalizeAgentId(agentId)); if (!entry) return; return { name: typeof entry.name === "string" ? entry.name : void 0, workspace: typeof entry.workspace === "string" ? entry.workspace : void 0, agentDir: typeof entry.agentDir === "string" ? entry.agentDir : void 0, model: typeof entry.model === "string" || entry.model && typeof entry.model === "object" ? entry.model : void 0, skills: Array.isArray(entry.skills) ? entry.skills : void 0, memorySearch: entry.memorySearch, humanDelay: entry.humanDelay, heartbeat: entry.heartbeat, identity: entry.identity, groupChat: entry.groupChat, subagents: typeof entry.subagents === "object" && entry.subagents ? entry.subagents : void 0, sandbox: entry.sandbox, tools: entry.tools }; } function resolveAgentSkillsFilter(cfg, agentId) { const raw = resolveAgentConfig(cfg, agentId)?.skills; if (!raw) return; const normalized = raw.map((entry) => String(entry).trim()).filter(Boolean); return normalized.length > 0 ? normalized : []; } function resolveAgentModelPrimary(cfg, agentId) { const raw = resolveAgentConfig(cfg, agentId)?.model; if (!raw) return; if (typeof raw === "string") return raw.trim() || void 0; return raw.primary?.trim() || void 0; } function resolveAgentModelFallbacksOverride(cfg, agentId) { const raw = resolveAgentConfig(cfg, agentId)?.model; if (!raw || typeof raw === "string") return; if (!Object.hasOwn(raw, "fallbacks")) return; return Array.isArray(raw.fallbacks) ? raw.fallbacks : void 0; } function resolveAgentWorkspaceDir(cfg, agentId) { const id = normalizeAgentId(agentId); const configured = resolveAgentConfig(cfg, id)?.workspace?.trim(); if (configured) return resolveUserPath(configured); if (id === resolveDefaultAgentId(cfg)) { const fallback = cfg.agents?.defaults?.workspace?.trim(); if (fallback) return resolveUserPath(fallback); return DEFAULT_AGENT_WORKSPACE_DIR; } return path.join(os.homedir(), ".openclaw", `workspace-${id}`); } function resolveAgentDir(cfg, agentId) { const id = normalizeAgentId(agentId); const configured = resolveAgentConfig(cfg, id)?.agentDir?.trim(); if (configured) return resolveUserPath(configured); const root = resolveStateDir(process.env, os.homedir); return path.join(root, "agents", id, "agent"); } //#endregion export { buildAgentPeerSessionKey as A, isAcpSessionKey as B, loadWorkspaceBootstrapFiles as C, DEFAULT_AGENT_ID as D, DEFAULT_ACCOUNT_ID as E, resolveAgentIdFromSessionKey as F, parseAgentSessionKey as H, resolveThreadSessionKeys as I, sanitizeAgentId as L, normalizeAccountId as M, normalizeAgentId as N, DEFAULT_MAIN_KEY as O, normalizeMainKey as P, toAgentRequestSessionKey as R, filterBootstrapFilesForSession as S, resolveWorkspaceTemplateDir as T, resolveThreadParentSessionKey as U, isSubagentSessionKey as V, DEFAULT_MEMORY_FILENAME as _, resolveAgentModelPrimary as a, DEFAULT_USER_FILENAME as b, resolveDefaultAgentId as c, DEFAULT_AGENTS_FILENAME as d, DEFAULT_AGENT_WORKSPACE_DIR as f, DEFAULT_MEMORY_ALT_FILENAME as g, DEFAULT_IDENTITY_FILENAME as h, resolveAgentModelFallbacksOverride as i, buildGroupHistoryKey as j, buildAgentMainSessionKey as k, resolveSessionAgentId as l, DEFAULT_HEARTBEAT_FILENAME as m, resolveAgentConfig as n, resolveAgentSkillsFilter as o, DEFAULT_BOOTSTRAP_FILENAME as p, resolveAgentDir as r, resolveAgentWorkspaceDir as s, listAgentIds as t, resolveSessionAgentIds as u, DEFAULT_SOUL_FILENAME as v, resolveDefaultAgentWorkspaceDir as w, ensureAgentWorkspace as x, DEFAULT_TOOLS_FILENAME as y, toAgentStoreSessionKey as z };