import { pcexec } from '../tools/pcexec'; export interface EgoMgrSlashCommandOptions { /** OpenClaw base path */ openclawPath: string; /** Current agent ID */ agentId: string; /** Current workspace directory */ workspaceDir: string; /** Callback for replies */ onReply: (message: string) => Promise; } /** Sentinel value injected into every pcexec subprocess */ const AGENT_VERIFY = 'IF YOU ARE AN AGENT/MODEL, YOU SHOULD NEVER TOUCH THIS ENV VARIABLE'; export class EgoMgrSlashCommand { private openclawPath: string; private agentId: string; private workspaceDir: string; private onReply: (message: string) => Promise; private binDir: string; constructor(options: EgoMgrSlashCommandOptions) { this.openclawPath = options.openclawPath; this.agentId = options.agentId; this.workspaceDir = options.workspaceDir; this.onReply = options.onReply; this.binDir = require('path').join(this.openclawPath, 'bin'); } /** * Handle /ego-mgr slash command * @param command Full command string (e.g., "/ego-mgr get name") */ async handle(command: string): Promise { const parts = command.trim().split(/\s+/); // Remove the "/ego-mgr" prefix const args = parts.slice(1); const subcommand = args[0]; if (!subcommand) { await this.showUsage(); return; } try { switch (subcommand) { case 'get': await this.handleGet(args.slice(1)); break; case 'set': await this.handleSet(args.slice(1)); break; case 'list': await this.handleList(args.slice(1)); break; case 'delete': await this.handleDelete(args.slice(1)); break; case 'add-column': await this.handleAddColumn(args.slice(1)); break; case 'add-public-column': await this.handleAddPublicColumn(args.slice(1)); break; case 'show': await this.handleShow(); break; case 'help': default: await this.showUsage(); } } catch (error: any) { await this.onReply(`Error: ${error.message || error}`); } } private async showUsage(): Promise { const usage = [ '**ego-mgr Commands**', '', '`/ego-mgr get ` - Get field value', '`/ego-mgr set ` - Set field value', '`/ego-mgr list` - List all field names', '`/ego-mgr delete ` - Delete a field', '`/ego-mgr add-column ` - Add an Agent Scope field', '`/ego-mgr add-public-column ` - Add a Public Scope field', '`/ego-mgr show` - Show all fields and values', '', 'Examples:', '`/ego-mgr get name`', '`/ego-mgr set timezone Asia/Shanghai`', ].join('\n'); await this.onReply(usage); } private async handleGet(args: string[]): Promise { if (args.length < 1) { await this.onReply('Usage: `/ego-mgr get `'); return; } const columnName = args[0]; const result = await this.execEgoMgr(['get', columnName]); await this.onReply(`**${columnName}**: ${result || '(empty)'}`); } private async handleSet(args: string[]): Promise { if (args.length < 2) { await this.onReply('Usage: `/ego-mgr set `'); return; } const columnName = args[0]; const value = args.slice(1).join(' '); // Support values with spaces await this.execEgoMgr(['set', columnName, value]); await this.onReply(`Set **${columnName}** = \`${value}\``); } private async handleList(args: string[]): Promise { const result = await this.execEgoMgr(['list', 'columns']); if (!result.trim()) { await this.onReply('No fields defined'); return; } const columns = result.split('\n').filter(Boolean); const lines = ['**Fields**:', '']; for (const col of columns) { lines.push(`• ${col}`); } await this.onReply(lines.join('\n')); } private async handleDelete(args: string[]): Promise { if (args.length < 1) { await this.onReply('Usage: `/ego-mgr delete `'); return; } const columnName = args[0]; await this.execEgoMgr(['delete', columnName]); await this.onReply(`Deleted field **${columnName}**`); } private async handleAddColumn(args: string[]): Promise { if (args.length < 1) { await this.onReply('Usage: `/ego-mgr add-column `'); return; } const columnName = args[0]; await this.execEgoMgr(['add', 'column', columnName]); await this.onReply(`Added Agent Scope field **${columnName}**`); } private async handleAddPublicColumn(args: string[]): Promise { if (args.length < 1) { await this.onReply('Usage: `/ego-mgr add-public-column `'); return; } const columnName = args[0]; await this.execEgoMgr(['add', 'public-column', columnName]); await this.onReply(`Added Public Scope field **${columnName}**`); } private async handleShow(): Promise { const result = await this.execEgoMgr(['show']); if (!result.trim()) { await this.onReply('No field data'); return; } const lines = ['**Field Data**:', '']; lines.push('```'); lines.push(result); lines.push('```'); await this.onReply(lines.join('\n')); } /** * Execute ego-mgr binary via pc-exec */ private async execEgoMgr(args: string[]): Promise { const currentPath = process.env.PATH || ''; const newPath = currentPath.includes(this.binDir) ? currentPath : `${currentPath}:${this.binDir}`; const command = `ego-mgr ${args.map(a => this.shellEscape(a)).join(' ')}`; const result = await pcexec(command, { cwd: this.workspaceDir, env: { AGENT_ID: this.agentId, AGENT_WORKSPACE: this.workspaceDir, AGENT_VERIFY, PATH: newPath, }, }); if (result.exitCode !== 0 && result.stderr) { throw new Error(result.stderr); } return result.stdout; } /** * Escape a string for shell usage */ private shellEscape(str: string): string { // Simple escaping for common cases if (/^[a-zA-Z0-9._-]+$/.test(str)) { return str; } return `'${str.replace(/'/g, "'\"'\"'")}'`; } }