Files
openclaw/src/gateway/control-ui-shared.ts
LZY3538 4af1d53766 fix(agent): return data URIs for workspace-relative identity card avatars (#97602) (#102892)
* fix(agent): return data URIs for workspace-relative identity card avatars (#97602)

agent.identity.get resolved workspace-relative agent avatars via
resolveAssistantAvatarUrl to /avatar/<agentId> route URLs. <img> tags
in the Control UI Personal card cannot load these URLs because they
lack Bearer auth headers, showing broken images (401).

Read the validated local avatar file into a data URI inline before
constructing the identity response. The file-reading helper lives in
agent.ts as a private (non-exported) function so it does not leak onto
the public Plugin SDK surface. The workspace root is canonicalised with
realpathSync to match the already-resolved filePath from
resolveAgentAvatar, fixing containment checks for symlinked workspaces.

Fixes #97602

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(gateway): inline workspace avatars in bootstrap config Personal card (#97602)

agent.identity.get already returns inline data URIs for workspace-local
avatars, but the Control UI Personal card is initialized from the bootstrap
config (control-ui-config.json), which still mapped workspace avatars to the
auth-gated /avatar/<agentId> route. An <img> cannot carry Bearer auth, so the
linked Personal card 401/broken-image symptom persisted despite the RPC fix.

Consolidate the workspace-safe avatar reader into a single gateway-internal
helper (readLocalAvatarDataUrl in session-utils) and use it in both
agent.identity.get and the bootstrap config handler, so the identity and
bootstrap projections produce identical data URIs. Not re-exported on the
Plugin SDK surface. Adds a bootstrap-config regression test.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(control-ui): inline workspace assistant avatars

Project workspace-local assistant avatars into browser-safe data URLs across bootstrap and agent identity RPCs while preserving same-origin avatar routes and shared size limits.

Co-authored-by: LZY3538 <LZY3538@users.noreply.github.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: LZY3538 <LZY3538@users.noreply.github.com>
2026-07-10 11:03:08 +01:00

36 lines
1.0 KiB
TypeScript

// Control UI shared URL helpers.
// Normalizes base paths and avatar URLs for browser/gateway surfaces.
const CONTROL_UI_AVATAR_PREFIX = "/avatar";
/** Normalizes a Control UI base path to either "" or a leading-slash path without trailing slash. */
export function normalizeControlUiBasePath(basePath?: string): string {
if (!basePath) {
return "";
}
let normalized = basePath.trim();
if (!normalized) {
return "";
}
if (!normalized.startsWith("/")) {
normalized = `/${normalized}`;
}
if (normalized === "/") {
return "";
}
if (normalized.endsWith("/")) {
normalized = normalized.slice(0, -1);
}
return normalized;
}
/** Builds the gateway-served avatar URL for an agent under the provided base path. */
export function buildControlUiAvatarUrl(basePath: string, agentId: string): string {
return basePath
? `${basePath}${CONTROL_UI_AVATAR_PREFIX}/${agentId}`
: `${CONTROL_UI_AVATAR_PREFIX}/${agentId}`;
}
/** URL prefix for gateway-served Control UI avatar assets. */
export { CONTROL_UI_AVATAR_PREFIX };