Refine desktop setup and remove bundled app center apps
This commit is contained in:
249
electron/utils/agent-system-documents.ts
Normal file
249
electron/utils/agent-system-documents.ts
Normal file
@@ -0,0 +1,249 @@
|
||||
import { access, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
||||
import { constants } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { listAgentsSnapshot, type AgentSummary } from './agent-config';
|
||||
import { expandPath, getOpenClawDir } from './paths';
|
||||
|
||||
export const AGENT_SYSTEM_DOCUMENTS = [
|
||||
{
|
||||
kind: 'soul',
|
||||
fileName: 'SOUL.md',
|
||||
},
|
||||
{
|
||||
kind: 'identity',
|
||||
fileName: 'IDENTITY.md',
|
||||
},
|
||||
{
|
||||
kind: 'user',
|
||||
fileName: 'USER.md',
|
||||
},
|
||||
{
|
||||
kind: 'agent',
|
||||
fileName: 'AGENTS.md',
|
||||
},
|
||||
{
|
||||
kind: 'tool',
|
||||
fileName: 'TOOLS.md',
|
||||
},
|
||||
{
|
||||
kind: 'heartbeat',
|
||||
fileName: 'HEARTBEAT.md',
|
||||
},
|
||||
{
|
||||
kind: 'boot',
|
||||
fileName: 'BOOT.md',
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type AgentSystemDocumentKind = typeof AGENT_SYSTEM_DOCUMENTS[number]['kind'];
|
||||
export type AgentSystemDocumentSource = 'workspace' | 'template' | 'empty';
|
||||
|
||||
export interface AgentSystemDocumentAgent {
|
||||
id: string;
|
||||
name: string;
|
||||
isDefault: boolean;
|
||||
workspace: string;
|
||||
}
|
||||
|
||||
export interface AgentSystemDocument {
|
||||
kind: AgentSystemDocumentKind;
|
||||
fileName: string;
|
||||
path: string;
|
||||
exists: boolean;
|
||||
source: AgentSystemDocumentSource;
|
||||
content: string;
|
||||
size: number;
|
||||
updatedAt: number | null;
|
||||
templateAvailable: boolean;
|
||||
templatePath: string;
|
||||
}
|
||||
|
||||
export interface AgentSystemDocumentsSnapshot {
|
||||
success: true;
|
||||
selectedAgentId: string;
|
||||
defaultAgentId: string;
|
||||
agents: AgentSystemDocumentAgent[];
|
||||
documents: AgentSystemDocument[];
|
||||
paths: {
|
||||
workspace: string;
|
||||
templateDir: string;
|
||||
};
|
||||
}
|
||||
|
||||
const MAX_DOCUMENT_BYTES = 512 * 1024;
|
||||
|
||||
function getDescriptor(kind: string): typeof AGENT_SYSTEM_DOCUMENTS[number] | null {
|
||||
return AGENT_SYSTEM_DOCUMENTS.find((document) => document.kind === kind) ?? null;
|
||||
}
|
||||
|
||||
export function isAgentSystemDocumentKind(kind: string): kind is AgentSystemDocumentKind {
|
||||
return getDescriptor(kind) !== null;
|
||||
}
|
||||
|
||||
async function fileExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
await access(path, constants.F_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveTemplateDir(): string {
|
||||
return join(getOpenClawDir(), 'docs', 'reference', 'templates');
|
||||
}
|
||||
|
||||
function resolveTemplatePath(fileName: string): string {
|
||||
return join(resolveTemplateDir(), fileName);
|
||||
}
|
||||
|
||||
function toDocumentAgent(agent: AgentSummary): AgentSystemDocumentAgent {
|
||||
return {
|
||||
id: agent.id,
|
||||
name: agent.name,
|
||||
isDefault: agent.isDefault,
|
||||
workspace: agent.workspace,
|
||||
};
|
||||
}
|
||||
|
||||
async function resolveAgent(agentId?: string): Promise<{
|
||||
selected: AgentSystemDocumentAgent;
|
||||
defaultAgentId: string;
|
||||
agents: AgentSystemDocumentAgent[];
|
||||
}> {
|
||||
const snapshot = await listAgentsSnapshot();
|
||||
const agents = snapshot.agents.map(toDocumentAgent);
|
||||
if (agents.length === 0) {
|
||||
throw new Error('No OpenClaw agents are configured');
|
||||
}
|
||||
|
||||
const requestedAgentId = typeof agentId === 'string' ? agentId.trim() : '';
|
||||
const selected = requestedAgentId
|
||||
? agents.find((agent) => agent.id === requestedAgentId)
|
||||
: agents.find((agent) => agent.id === snapshot.defaultAgentId) ?? agents[0];
|
||||
|
||||
if (!selected) {
|
||||
throw new Error(`Agent "${requestedAgentId}" not found`);
|
||||
}
|
||||
|
||||
return {
|
||||
selected,
|
||||
defaultAgentId: snapshot.defaultAgentId,
|
||||
agents,
|
||||
};
|
||||
}
|
||||
|
||||
async function readTemplate(fileName: string): Promise<string | null> {
|
||||
const templatePath = resolveTemplatePath(fileName);
|
||||
if (!(await fileExists(templatePath))) return null;
|
||||
return readFile(templatePath, 'utf8');
|
||||
}
|
||||
|
||||
async function readDocument(agent: AgentSystemDocumentAgent, kind: AgentSystemDocumentKind): Promise<AgentSystemDocument> {
|
||||
const descriptor = getDescriptor(kind);
|
||||
if (!descriptor) {
|
||||
throw new Error(`Unsupported system document kind "${kind}"`);
|
||||
}
|
||||
|
||||
const workspace = expandPath(agent.workspace);
|
||||
const documentPath = join(workspace, descriptor.fileName);
|
||||
const templatePath = resolveTemplatePath(descriptor.fileName);
|
||||
const template = await readTemplate(descriptor.fileName);
|
||||
|
||||
if (await fileExists(documentPath)) {
|
||||
const [content, stats] = await Promise.all([
|
||||
readFile(documentPath, 'utf8'),
|
||||
stat(documentPath),
|
||||
]);
|
||||
return {
|
||||
kind,
|
||||
fileName: descriptor.fileName,
|
||||
path: documentPath,
|
||||
exists: true,
|
||||
source: 'workspace',
|
||||
content,
|
||||
size: stats.size,
|
||||
updatedAt: stats.mtimeMs,
|
||||
templateAvailable: template !== null,
|
||||
templatePath,
|
||||
};
|
||||
}
|
||||
|
||||
const content = template ?? '';
|
||||
return {
|
||||
kind,
|
||||
fileName: descriptor.fileName,
|
||||
path: documentPath,
|
||||
exists: false,
|
||||
source: template === null ? 'empty' : 'template',
|
||||
content,
|
||||
size: 0,
|
||||
updatedAt: null,
|
||||
templateAvailable: template !== null,
|
||||
templatePath,
|
||||
};
|
||||
}
|
||||
|
||||
export async function readAgentSystemDocuments(agentId?: string): Promise<AgentSystemDocumentsSnapshot> {
|
||||
const { selected, defaultAgentId, agents } = await resolveAgent(agentId);
|
||||
const documents = await Promise.all(
|
||||
AGENT_SYSTEM_DOCUMENTS.map((document) => readDocument(selected, document.kind)),
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
selectedAgentId: selected.id,
|
||||
defaultAgentId,
|
||||
agents,
|
||||
documents,
|
||||
paths: {
|
||||
workspace: expandPath(selected.workspace),
|
||||
templateDir: resolveTemplateDir(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function saveAgentSystemDocument(
|
||||
agentId: string | undefined,
|
||||
kind: AgentSystemDocumentKind,
|
||||
content: string,
|
||||
): Promise<AgentSystemDocumentsSnapshot> {
|
||||
if (typeof content !== 'string') {
|
||||
throw new Error('Document content must be a string');
|
||||
}
|
||||
const byteLength = Buffer.byteLength(content, 'utf8');
|
||||
if (byteLength > MAX_DOCUMENT_BYTES) {
|
||||
throw new Error(`Document is too large (${byteLength} bytes, max ${MAX_DOCUMENT_BYTES})`);
|
||||
}
|
||||
|
||||
const { selected } = await resolveAgent(agentId);
|
||||
const descriptor = getDescriptor(kind);
|
||||
if (!descriptor) {
|
||||
throw new Error(`Unsupported system document kind "${kind}"`);
|
||||
}
|
||||
|
||||
const workspace = expandPath(selected.workspace);
|
||||
await mkdir(workspace, { recursive: true });
|
||||
await writeFile(join(workspace, descriptor.fileName), content, 'utf8');
|
||||
return readAgentSystemDocuments(selected.id);
|
||||
}
|
||||
|
||||
export async function resetAgentSystemDocument(
|
||||
agentId: string | undefined,
|
||||
kind: AgentSystemDocumentKind,
|
||||
): Promise<AgentSystemDocumentsSnapshot> {
|
||||
const { selected } = await resolveAgent(agentId);
|
||||
const descriptor = getDescriptor(kind);
|
||||
if (!descriptor) {
|
||||
throw new Error(`Unsupported system document kind "${kind}"`);
|
||||
}
|
||||
|
||||
const template = await readTemplate(descriptor.fileName);
|
||||
if (template === null) {
|
||||
throw new Error(`Template for ${descriptor.fileName} is not available`);
|
||||
}
|
||||
|
||||
const workspace = expandPath(selected.workspace);
|
||||
await mkdir(workspace, { recursive: true });
|
||||
await writeFile(join(workspace, descriptor.fileName), template, 'utf8');
|
||||
return readAgentSystemDocuments(selected.id);
|
||||
}
|
||||
Reference in New Issue
Block a user