automagik-genie
Version:
Self-evolving AI agent orchestration framework with Model Context Protocol support
271 lines (270 loc) • 10.9 kB
JavaScript
"use strict";
/**
* Run Command - Unified browser + monitoring workflow
*
* Creates a Forge task, opens browser, and monitors completion via WebSocket.
* This is the primary user-facing command for interactive task execution.
*
* Behavior:
* 1. Create Forge task with agent
* 2. Open browser in fullscreen task view
* 3. Attach WebSocket monitor
* 4. Wait for completion (event-driven)
* 5. Output JSON with results
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runRun = runRun;
const service_config_js_1 = require("../lib/service-config.js");
const forge_manager_1 = require("../lib/forge-manager");
const agent_resolver_1 = require("../lib/agent-resolver");
const forge_executor_1 = require("../lib/forge-executor");
const forge_helpers_1 = require("../lib/forge-helpers");
const executor_registry_1 = require("../lib/executor-registry");
const task_monitor_1 = require("../lib/task-monitor");
const task_service_js_1 = require("../cli-core/task-service.js");
const path_1 = __importDefault(require("path"));
const child_process_1 = require("child_process");
const gradient_string_1 = __importDefault(require("gradient-string"));
const fs_1 = __importDefault(require("fs"));
const successGradient = (0, gradient_string_1.default)(['#00ff88', '#00ccff', '#0099ff']);
async function runRun(parsed, config, paths) {
const [agentName, ...promptParts] = parsed.commandArgs;
if (!agentName) {
console.error('Usage: genie run <agent> "<prompt>"');
process.exit(1);
}
const prompt = promptParts.join(' ').trim();
if (!prompt) {
console.error('Error: Prompt is required');
console.error('Usage: genie run <agent> "<prompt>"');
process.exit(1);
}
if (parsed.options.background === true) {
const { runTask } = await import('./task.js');
return runTask(parsed, config, paths);
}
const resolvedAgentName = (0, agent_resolver_1.resolveAgentIdentifier)(agentName);
const agentSpec = (0, agent_resolver_1.loadAgentSpec)(resolvedAgentName);
const agentGenie = agentSpec.meta?.genie || {};
// Resolve executor configuration (CLI flags override agent/config defaults)
const executorKey = (0, executor_registry_1.normalizeExecutorKeyOrDefault)(parsed.options.executor || (0, executor_registry_1.normalizeExecutorValue)(agentGenie.executor) || config.defaults?.executor);
const executorVariant = (agentGenie.executorVariant ||
agentGenie.variant ||
config.defaults?.executorVariant ||
'DEFAULT').trim().toUpperCase();
const model = parsed.options.model || agentGenie.model || config.defaults?.model;
const { baseUrl, token } = (0, service_config_js_1.getForgeConfig)();
const logDir = path_1.default.join(process.cwd(), '.genie', 'state');
const quiet = parsed.options.quiet || false;
const raw = parsed.options.raw || false;
const sessionName = parsed.options.name;
// Start Forge if not running
const forgeRunning = await (0, forge_manager_1.isForgeRunning)(baseUrl);
if (!forgeRunning) {
if (!quiet) {
console.log('');
process.stderr.write('Starting Forge... ');
}
const startTime = Date.now();
const result = (0, forge_manager_1.startForgeInBackground)({ baseUrl, logDir });
if (!result.ok) {
const error = 'error' in result ? result.error : new Error('Unknown error');
console.error('');
console.error('❌ Failed to start Forge');
console.error(` ${error.message}`);
console.error(` Check logs at ${logDir}/forge.log`);
process.exit(1);
}
const ready = await (0, forge_manager_1.waitForForgeReady)(baseUrl, 60000, 500, false);
if (!ready) {
console.error('');
console.error('❌ Forge did not start in time (60s)');
console.error(` Check logs at ${logDir}/forge.log`);
process.exit(1);
}
if (!quiet) {
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
process.stderr.write(`ready (${elapsed}s)\n`);
}
}
// Create Forge session
const forgeExecutor = (0, forge_executor_1.createForgeExecutor)();
let sessionResult;
try {
sessionResult = await forgeExecutor.createTask({
agentName: resolvedAgentName,
prompt,
executorKey,
executorVariant,
executionMode: 'interactive',
model,
...(sessionName && { name: sessionName })
});
}
catch (error) {
const reason = (0, forge_helpers_1.describeForgeError)(error);
console.error(`❌ Failed to create session: ${reason}`);
console.error(` ${forge_helpers_1.FORGE_RECOVERY_HINT}`);
process.exit(1);
}
const attemptId = sessionResult.attemptId;
const taskUrl = sessionResult.forgeUrl;
const sessionService = new task_service_js_1.TaskService({
paths: {
tasksFile: paths.tasksFile
}
});
const store = sessionService.load();
const now = new Date().toISOString();
store.sessions[attemptId] = {
agent: resolvedAgentName,
taskId: sessionResult.taskId,
projectId: sessionResult.projectId,
executor: executorKey,
executorVariant,
model: model || undefined,
status: 'running',
created: now,
lastUsed: now,
lastPrompt: prompt.slice(0, 200),
mode: 'interactive',
forgeUrl: sessionResult.forgeUrl,
background: false
};
await sessionService.save(store);
if (!quiet) {
console.log('');
console.log(successGradient('━'.repeat(60)));
console.log(successGradient(`✨ ${resolvedAgentName} task started! ✨`));
console.log(successGradient('━'.repeat(60)));
console.log('');
console.log(`📊 Task ID: ${attemptId}`);
console.log(`🌐 Opening browser...`);
console.log('');
}
openBrowserCrossPlatform(taskUrl);
if (!quiet) {
console.log('📡 Monitoring task completion...');
console.log('');
}
try {
const result = await (0, task_monitor_1.monitorTaskCompletion)({
attemptId,
baseUrl,
token,
taskUrl,
onStatus: (status) => {
if (!quiet && status !== 'running') {
process.stderr.write(`Status: ${status}\n`);
}
}
});
// Persist final task status to local store
const updatedStore = sessionService.load();
if (updatedStore.sessions[attemptId]) {
updatedStore.sessions[attemptId].status = result.status;
updatedStore.sessions[attemptId].lastUsed = new Date().toISOString();
await sessionService.save(updatedStore);
}
if (raw) {
console.log(result.output);
}
else {
const jsonOutput = JSON.stringify({
task_url: taskUrl,
result: result.output,
status: result.status,
duration_ms: result.duration_ms,
attempt_id: attemptId,
...(result.error && { error: result.error })
}, null, 2);
if (!quiet) {
console.log('');
console.log('━'.repeat(60));
if (result.status === 'completed') {
console.log(successGradient('✅ Task Completed'));
}
else if (result.status === 'failed') {
console.log('❌ Task Failed');
}
else if (result.status === 'timeout') {
console.log('⏱️ Task Timeout');
}
console.log('━'.repeat(60));
console.log('');
}
console.log(jsonOutput);
if (!quiet) {
console.log('');
}
}
process.exitCode = result.status === 'completed' ? 0 : 1;
}
catch (error) {
// Update status to error on monitoring failure
const errorStore = sessionService.load();
if (errorStore.sessions[attemptId]) {
errorStore.sessions[attemptId].status = 'error';
errorStore.sessions[attemptId].lastUsed = new Date().toISOString();
await sessionService.save(errorStore);
}
console.error('');
console.error('❌ Monitoring failed:', error);
console.error('');
console.error('💡 View task manually at:', taskUrl);
process.exitCode = 1;
}
}
/**
* Open URL in browser using cross-platform logic (including WSL support)
* Based on Forge's browser opening strategy
*/
function openBrowserCrossPlatform(url) {
try {
const platform = process.platform;
if (platform === 'darwin') {
// macOS
(0, child_process_1.execSync)(`open "${url}"`, { stdio: 'ignore' });
}
else if (platform === 'win32') {
// Windows
(0, child_process_1.spawn)('cmd', ['/c', 'start', '', url], { detached: true, stdio: 'ignore' }).unref();
}
else if (platform === 'linux') {
// Check if running in WSL
const isWSL = fs_1.default.existsSync('/proc/version') &&
fs_1.default.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft');
if (isWSL) {
// WSL: Use Windows browser via cmd.exe
try {
(0, child_process_1.execSync)(`cmd.exe /c start "" "${url}"`, { stdio: 'ignore' });
}
catch {
// Fallback to wslview if cmd.exe fails
try {
(0, child_process_1.execSync)(`wslview "${url}"`, { stdio: 'ignore' });
}
catch {
// Last resort: Linux browser
(0, child_process_1.execSync)(`xdg-open "${url}"`, { stdio: 'ignore' });
}
}
}
else {
// Native Linux
(0, child_process_1.execSync)(`xdg-open "${url}"`, { stdio: 'ignore' });
}
}
else {
// Unknown platform, try xdg-open
(0, child_process_1.execSync)(`xdg-open "${url}"`, { stdio: 'ignore' });
}
}
catch (error) {
console.log(`⚠️ Failed to open browser automatically.`);
console.log(` Visit: ${url}`);
}
}