import { exec } from 'child_process'; import { promisify } from 'util'; import fs from 'fs/promises'; import path from 'path'; const execAsync = promisify(exec); const OPENCLAW_CONFIG_PATH = '/home/ai/.openclaw/openclaw.json'; const OPENCLAW_BIN = 'openclaw'; export interface OpenClawConfig { meta?: { lastTouchedVersion?: string; lastTouchedAt?: string; }; auth?: { profiles?: Record; }; models?: { mode?: string; providers?: Record; defaults?: { model?: { primary?: string; }; }; }; gateway?: { port?: number; mode?: string; bind?: string; controlUi?: { enabled?: boolean; }; auth?: { token?: string; mode?: string; }; }; plugins?: { entries?: Record; }; tools?: { web?: { search?: { apiKey?: string; }; }; }; [key: string]: any; } export interface GatewayStatus { running: boolean; pid?: number; port?: number; uptime?: number; mode?: string; } export interface InstanceStatus { id: string; name: string; type: 'openclaw' | 'opencode' | 'gateway'; status: 'online' | 'offline' | 'unknown' | 'starting' | 'stopping'; endpoint?: string; model?: string; lastCheck: number; } /** * Get the OpenClaw configuration */ export async function getOpenClawConfig(): Promise { try { const content = await fs.readFile(OPENCLAW_CONFIG_PATH, 'utf-8'); return JSON.parse(content); } catch (error) { throw new Error(`Failed to read OpenClaw config: ${(error as Error).message}`); } } /** * Save the OpenClaw configuration */ export async function saveOpenClawConfig(config: OpenClawConfig): Promise { try { await fs.writeFile( OPENCLAW_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8' ); } catch (error) { throw new Error(`Failed to save OpenClaw config: ${(error as Error).message}`); } } /** * Get Gateway status */ export async function getGatewayStatus(): Promise { try { const { stdout } = await execAsync('ps aux | grep "[o]penclaw gateway"'); const running = stdout.trim().length > 0; if (!running) { return { running: false }; } // Try to get more details from the running process try { const { stdout: statusOutput } = await execAsync('openclaw gateway status --json', { timeout: 5000, }); const statusData = JSON.parse(statusOutput); return { running: true, pid: statusData.pid, port: statusData.port, uptime: statusData.uptime, mode: statusData.mode, }; } catch { return { running: true }; } } catch { return { running: false }; } } /** * Start the Gateway */ export async function startGateway(): Promise<{ success: boolean; message: string }> { try { await execAsync('openclaw gateway start', { timeout: 10000 }); return { success: true, message: 'Gateway started successfully' }; } catch (error) { return { success: false, message: `Failed to start Gateway: ${(error as Error).message}` }; } } /** * Stop the Gateway */ export async function stopGateway(): Promise<{ success: boolean; message: string }> { try { await execAsync('openclaw gateway stop', { timeout: 10000 }); return { success: true, message: 'Gateway stopped successfully' }; } catch (error) { return { success: false, message: `Failed to stop Gateway: ${(error as Error).message}` }; } } /** * Restart the Gateway */ export async function restartGateway(): Promise<{ success: boolean; message: string }> { try { await execAsync('openclaw gateway restart', { timeout: 15000 }); return { success: true, message: 'Gateway restarted successfully' }; } catch (error) { return { success: false, message: `Failed to restart Gateway: ${(error as Error).message}` }; } } /** * Get recent logs from Gateway */ export async function getGatewayLogs(lines: number = 100): Promise { try { const { stdout } = await execAsync(`journalctl -u openclaw -n ${lines} --no-pager --output=cat`, { timeout: 5000, }); return stdout.trim().split('\n'); } catch { // Fallback: Try to read from common log locations const logPaths = [ '/var/log/openclaw/gateway.log', '/root/.openclaw/logs/gateway.log', '/tmp/openclaw-gateway.log', ]; for (const logPath of logPaths) { try { const content = await fs.readFile(logPath, 'utf-8'); return content.trim().split('\n').slice(-lines); } catch { continue; } } return []; } } /** * Get current active model */ export async function getActiveModel(): Promise { try { const config = await getOpenClawConfig(); return config.models?.defaults?.model?.primary || null; } catch { return null; } } /** * Set active model */ export async function setActiveModel(modelId: string): Promise<{ success: boolean; message: string }> { try { const config = await getOpenClawConfig(); if (!config.models) config.models = {}; if (!config.models.defaults) config.models.defaults = {}; if (!config.models.defaults.model) config.models.defaults.model = {}; config.models.defaults.model.primary = modelId; await saveOpenClawConfig(config); // Restart gateway to apply changes await restartGateway(); return { success: true, message: `Model set to ${modelId}` }; } catch (error) { return { success: false, message: `Failed to set model: ${(error as Error).message}` }; } } /** * Get available models */ export async function getAvailableModels(): Promise> { try { const config = await getOpenClawConfig(); const models: Array<{ id: string; name: string }> = []; // Collect models from all providers if (config.models?.providers) { for (const [providerName, providerData] of Object.entries(config.models.providers)) { if (providerData && typeof providerData === 'object' && 'models' in providerData) { const providerModels = (providerData as any).models || []; for (const model of providerModels) { models.push({ id: `${providerName}/${model.id}`, name: model.name || model.id, }); } } } } return models; } catch { return []; } } /** * Get all providers with their API keys */ export async function getProviders(): Promise> { try { const config = await getOpenClawConfig(); const providers: Array<{ id: string; name: string; type: string; hasKey: boolean; key?: string; }> = []; if (config.auth?.profiles) { for (const [id, profile] of Object.entries(config.auth.profiles)) { const providerData = profile as any; providers.push({ id, name: providerData.email || id, type: providerData.provider || id.split(':')[0], hasKey: !!providerData.apiKey, key: providerData.apiKey, }); } } return providers; } catch { return []; } } /** * Add or update a provider's API key */ export async function setProviderApiKey( providerId: string, apiKey: string ): Promise<{ success: boolean; message: string }> { try { const config = await getOpenClawConfig(); if (!config.auth) config.auth = {}; if (!config.auth.profiles) config.auth.profiles = {}; if (!config.auth.profiles[providerId]) { // Create new provider profile const [providerName, ...rest] = providerId.split(':'); config.auth.profiles[providerId] = { provider: providerName, mode: 'api_key', apiKey, }; } else { config.auth.profiles[providerId].apiKey = apiKey; } await saveOpenClawConfig(config); return { success: true, message: `API key updated for ${providerId}` }; } catch (error) { return { success: false, message: `Failed to update API key: ${(error as Error).message}` }; } } /** * Get system status summary */ export async function getSystemStatus(): Promise<{ gateway: GatewayStatus; activeModel: string | null; instanceCount: number; uptime: number; }> { const [gatewayStatus, activeModel] = await Promise.all([ getGatewayStatus(), getActiveModel(), ]); // Get system uptime const uptime = process.uptime(); return { gateway: gatewayStatus, activeModel, instanceCount: 1, // Will be expanded for multiple instances uptime, }; } /** * Apply config changes and restart gateway */ export async function applyConfig(config: OpenClawConfig): Promise<{ success: boolean; message: string; }> { try { // Validate config (basic) if (!config || typeof config !== 'object') { throw new Error('Invalid configuration'); } await saveOpenClawConfig(config); // Restart gateway to apply changes const restartResult = await restartGateway(); if (!restartResult.success) { return { success: false, message: `Config saved but failed to restart Gateway: ${restartResult.message}`, }; } return { success: true, message: 'Configuration applied and Gateway restarted' }; } catch (error) { return { success: false, message: `Failed to apply config: ${(error as Error).message}` }; } }