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