UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

119 lines (118 loc) 4.37 kB
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js"; import { p as resolveUserPath } from "./utils-CCC-BEJH.js"; import "./agent-scope-MrLta7Pq.js"; import { u as normalizeAgentId } from "./session-key-B_NoIfpX.js"; import { c as resolveDefaultAgentId, o as resolveAgentWorkspaceDir } from "./agent-scope-config-CgCYpZfK.js"; import { c as isWindowsAbsolutePath, i as isAvatarHttpUrl, n as hasAvatarUriScheme, o as isPathWithinRoot, r as isAvatarDataUrl, s as isSupportedLocalAvatarExtension } from "./avatar-policy-CLA5a0j8.js"; import { n as resolveAgentIdentity } from "./identity-Qcvvo6SY.js"; import { n as loadAgentIdentityFromWorkspace } from "./identity-file-DZ2jIsKe.js"; import fs from "node:fs"; import path from "node:path"; //#region src/agents/identity-avatar.ts /** * Resolves public avatar sources for configured agent identities. */ const PUBLIC_AVATAR_SOURCE_MAX_CHARS = 256; const PUBLIC_DATA_AVATAR_HEADER_MAX_CHARS = 64; function resolveAvatarSource(cfg, agentId, opts) { const normalizedAgentId = normalizeAgentId(agentId); const defaultAgentId = normalizeAgentId(resolveDefaultAgentId(cfg)); const fromUiConfig = normalizeOptionalString(cfg.ui?.assistant?.avatar) ?? null; if (opts?.includeUiOverride) { if (normalizedAgentId === defaultAgentId && fromUiConfig) return fromUiConfig; } const fromConfig = normalizeOptionalString(resolveAgentIdentity(cfg, normalizedAgentId)?.avatar) ?? null; if (fromConfig) return fromConfig; const fromIdentity = normalizeOptionalString(loadAgentIdentityFromWorkspace(resolveAgentWorkspaceDir(cfg, normalizedAgentId))?.avatar) ?? null; if (fromIdentity) return fromIdentity; return opts?.includeUiOverride ? fromUiConfig : null; } function resolveExistingPath(value) { try { return fs.realpathSync(value); } catch { return path.resolve(value); } } function resolveLocalAvatarPath(params) { const workspaceRoot = resolveExistingPath(params.workspaceDir); const raw = params.raw; const realPath = resolveExistingPath(raw.startsWith("~") || path.isAbsolute(raw) ? resolveUserPath(raw) : path.resolve(workspaceRoot, raw)); if (!isPathWithinRoot(workspaceRoot, realPath)) return { ok: false, reason: "outside_workspace" }; if (!isSupportedLocalAvatarExtension(realPath)) return { ok: false, reason: "unsupported_extension" }; try { const stat = fs.statSync(realPath); if (!stat.isFile()) return { ok: false, reason: "missing" }; if (stat.size > 2097152) return { ok: false, reason: "too_large" }; } catch { return { ok: false, reason: "missing" }; } return { ok: true, filePath: realPath }; } function isSafeRelativeAvatarSource(source) { if (source.length > PUBLIC_AVATAR_SOURCE_MAX_CHARS || source.startsWith("~") || path.isAbsolute(source) || isWindowsAbsolutePath(source) || hasAvatarUriScheme(source) && !isWindowsAbsolutePath(source) || source.includes("\0")) return false; return source.replace(/\\/g, "/").split("/").every((part) => part !== ".."); } /** Return a safe public description of the configured avatar source. */ function resolvePublicAgentAvatarSource(resolved) { const source = normalizeOptionalString(resolved.source) ?? null; if (!source) return; if (isAvatarDataUrl(source)) { const commaIndex = source.indexOf(","); return `${commaIndex > 0 ? source.slice(0, Math.min(commaIndex, PUBLIC_DATA_AVATAR_HEADER_MAX_CHARS)) : source.slice(0, PUBLIC_DATA_AVATAR_HEADER_MAX_CHARS)},...`; } if (isAvatarHttpUrl(source)) return "remote URL"; return isSafeRelativeAvatarSource(source) ? source : void 0; } /** Resolve the effective avatar for an agent, including config and IDENTITY.md. */ function resolveAgentAvatar(cfg, agentId, opts) { const source = resolveAvatarSource(cfg, agentId, opts); if (!source) return { kind: "none", reason: "missing" }; if (isAvatarHttpUrl(source)) return { kind: "remote", url: source, source }; if (isAvatarDataUrl(source)) return { kind: "data", url: source, source }; const resolved = resolveLocalAvatarPath({ raw: source, workspaceDir: resolveAgentWorkspaceDir(cfg, agentId) }); if (!resolved.ok) return { kind: "none", reason: resolved.reason, source }; return { kind: "local", filePath: resolved.filePath, source }; } //#endregion export { resolvePublicAgentAvatarSource as n, resolveAgentAvatar as t };