Files
openclaw/src/agents/identity-file.ts

108 lines
2.9 KiB
TypeScript

import fs from "node:fs";
import path from "node:path";
import { DEFAULT_IDENTITY_FILENAME } from "./workspace.js";
export type AgentIdentityFile = {
name?: string;
emoji?: string;
theme?: string;
creature?: string;
vibe?: string;
avatar?: string;
};
const IDENTITY_PLACEHOLDER_VALUES = new Set([
"pick something you like",
"ai? robot? familiar? ghost in the machine? something weirder?",
"how do you come across? sharp? warm? chaotic? calm?",
"your signature - pick one that feels right",
"workspace-relative path, http(s) url, or data uri",
]);
function normalizeIdentityValue(value: string): string {
let normalized = value.trim();
normalized = normalized.replace(/^[*_]+|[*_]+$/g, "").trim();
if (normalized.startsWith("(") && normalized.endsWith(")")) {
normalized = normalized.slice(1, -1).trim();
}
normalized = normalized.replace(/[\u2013\u2014]/g, "-");
normalized = normalized.replace(/\s+/g, " ").toLowerCase();
return normalized;
}
function isIdentityPlaceholder(value: string): boolean {
const normalized = normalizeIdentityValue(value);
return IDENTITY_PLACEHOLDER_VALUES.has(normalized);
}
export function parseIdentityMarkdown(content: string): AgentIdentityFile {
const identity: AgentIdentityFile = {};
const lines = content.split(/\r?\n/);
for (const line of lines) {
const cleaned = line.trim().replace(/^\s*-\s*/, "");
const colonIndex = cleaned.indexOf(":");
if (colonIndex === -1) {
continue;
}
const label = cleaned.slice(0, colonIndex).replace(/[*_]/g, "").trim().toLowerCase();
const value = cleaned
.slice(colonIndex + 1)
.replace(/^[*_]+|[*_]+$/g, "")
.trim();
if (!value) {
continue;
}
if (isIdentityPlaceholder(value)) {
continue;
}
if (label === "name") {
identity.name = value;
}
if (label === "emoji") {
identity.emoji = value;
}
if (label === "creature") {
identity.creature = value;
}
if (label === "vibe") {
identity.vibe = value;
}
if (label === "theme") {
identity.theme = value;
}
if (label === "avatar") {
identity.avatar = value;
}
}
return identity;
}
export function identityHasValues(identity: AgentIdentityFile): boolean {
return Boolean(
identity.name ||
identity.emoji ||
identity.theme ||
identity.creature ||
identity.vibe ||
identity.avatar,
);
}
export function loadIdentityFromFile(identityPath: string): AgentIdentityFile | null {
try {
const content = fs.readFileSync(identityPath, "utf-8");
const parsed = parseIdentityMarkdown(content);
if (!identityHasValues(parsed)) {
return null;
}
return parsed;
} catch {
return null;
}
}
export function loadAgentIdentityFromWorkspace(workspace: string): AgentIdentityFile | null {
const identityPath = path.join(workspace, DEFAULT_IDENTITY_FILENAME);
return loadIdentityFromFile(identityPath);
}