// OpenClaw Gateway client
import { exec } from 'child_process';
import { promisify } from 'util';
import type {
SendMessageArgs,
SendMessageResult,
SpawnTaskArgs,
SpawnTaskResult,
CheckStatusArgs,
CheckStatusResult,
ListSessionsArgs,
ListSessionsResult,
OpenClawConfig
} from './types.js';
const execAsync = promisify(exec);
export class OpenClawClient {
private config: OpenClawConfig;
constructor(config: OpenClawConfig) {
this.config = config;
}
async sendMessage(args: SendMessageArgs): Promise<SendMessageResult> {
const { agent, message, timeout = 60 } = args;
const sessionKey = `agent:${agent}:main`;
try {
const cmd = [
'openclaw sessions send',
`--agent ${agent}`,
`--session ${sessionKey}`,
`--message "${message.replace(/"/g, '\\"')}"`,
`--timeout ${timeout}`
].join(' ');
const { stdout, stderr } = await execAsync(cmd);
if (stderr && !stderr.includes('✅')) {
throw new Error(`OpenClaw error: ${stderr}`);
}
return {
response: stdout.trim() || 'Message sent',
sessionKey
};
} catch (error) {
throw new Error(`Failed to send message: ${error}`);
}
}
async spawnTask(args: SpawnTaskArgs): Promise<SpawnTaskResult> {
const {
agent,
task,
label,
cleanup = 'keep',
model,
timeout = 300
} = args;
try {
const cmdParts = [
'openclaw sessions spawn',
`--agent ${agent}`,
`--task "${task.replace(/"/g, '\\"')}"`,
`--timeout ${timeout}`,
`--cleanup ${cleanup}`
];
if (label) {
cmdParts.push(`--label ${label}`);
}
if (model) {
cmdParts.push(`--model ${model}`);
}
const cmd = cmdParts.join(' ');
const { stdout, stderr } = await execAsync(cmd);
if (stderr && !stderr.includes('🚀')) {
throw new Error(`OpenClaw error: ${stderr}`);
}
// Parse task ID from output (format: "spawn-abc123")
const taskIdMatch = stdout.match(/spawn-[a-z0-9]+/);
const taskId = taskIdMatch ? taskIdMatch[0] : `spawn-${Date.now()}`;
const sessionKey = `agent:${agent}:isolated:${taskId}`;
return {
taskId,
sessionKey,
status: 'running'
};
} catch (error) {
throw new Error(`Failed to spawn task: ${error}`);
}
}
async checkStatus(args: CheckStatusArgs): Promise<CheckStatusResult> {
// For MVP, we'll check session history
// In a full implementation, this would query the gateway API
const { taskId } = args;
try {
const cmd = `openclaw sessions list --active-minutes 1440 --limit 100`;
const { stdout } = await execAsync(cmd);
// Simple status check - in production, parse actual session state
if (stdout.includes(taskId)) {
return {
status: 'running',
startedAt: new Date().toISOString()
};
}
return {
status: 'completed',
result: 'Task completed (check session for details)',
completedAt: new Date().toISOString()
};
} catch (error) {
return {
status: 'failed',
error: `Failed to check status: ${error}`
};
}
}
async listSessions(args: ListSessionsArgs): Promise<ListSessionsResult> {
const { agent, activeMinutes = 60 } = args;
try {
const cmd = `openclaw sessions list --active-minutes ${activeMinutes} --limit 50`;
const { stdout } = await execAsync(cmd);
// Parse session list from output
// This is simplified - in production, parse JSON output
const sessions: any[] = [];
const lines = stdout.split('\n').filter(l => l.trim());
for (const line of lines) {
if (line.includes(agent)) {
sessions.push({
sessionKey: line.trim(),
kind: 'unknown',
lastUpdate: new Date().toISOString()
});
}
}
return { sessions };
} catch (error) {
throw new Error(`Failed to list sessions: ${error}`);
}
}
}