ruvector 0.1.90 ā 0.1.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +450 -0
- package/bin/mcp-server.js +193 -1
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -7376,6 +7376,456 @@ edgeNetCmd.command('status')
|
|
|
7376
7376
|
}
|
|
7377
7377
|
});
|
|
7378
7378
|
|
|
7379
|
+
// ============================================
|
|
7380
|
+
// REAL AGENT COMMANDS (Actual LLM Execution)
|
|
7381
|
+
// ============================================
|
|
7382
|
+
|
|
7383
|
+
const agentCmd = program.command('agent').description('Real AI agent execution - local ruvllm by default, or cloud APIs (Anthropic/OpenAI)');
|
|
7384
|
+
|
|
7385
|
+
agentCmd.command('run')
|
|
7386
|
+
.description('Execute a task with a real AI agent (local ruvllm by default, no API key needed)')
|
|
7387
|
+
.argument('<type>', 'Agent type: researcher, coder, reviewer, tester, analyst, optimizer, coordinator, embedder')
|
|
7388
|
+
.argument('<task>', 'Task description for the agent to execute')
|
|
7389
|
+
.option('-p, --provider <provider>', 'LLM provider: local (default), anthropic, openai', 'local')
|
|
7390
|
+
.option('-m, --model <model>', 'Model tier: fast, balanced, powerful', 'balanced')
|
|
7391
|
+
.option('-f, --files <files...>', 'Files to include as context')
|
|
7392
|
+
.option('-c, --context <context>', 'Additional context string')
|
|
7393
|
+
.option('--max-tokens <tokens>', 'Maximum tokens in response', '4096')
|
|
7394
|
+
.option('--relay <url>', 'Relay server URL for sync')
|
|
7395
|
+
.option('--json', 'Output as JSON')
|
|
7396
|
+
.action(async (type, task, opts) => {
|
|
7397
|
+
const spinner = ora('Initializing real agent...').start();
|
|
7398
|
+
|
|
7399
|
+
// Check for API key (only required for cloud providers)
|
|
7400
|
+
const apiKey = process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
|
|
7401
|
+
const isLocal = opts.provider === 'local' || opts.provider === 'ruvllm';
|
|
7402
|
+
|
|
7403
|
+
if (!isLocal && !apiKey && type !== 'embedder') {
|
|
7404
|
+
spinner.fail('No API key found for cloud provider');
|
|
7405
|
+
console.log(chalk.red('\nā Cloud provider requires an API key.\n'));
|
|
7406
|
+
console.log(chalk.bold('Options:'));
|
|
7407
|
+
console.log(chalk.green(' 1. Use local ruvllm (default, no key needed):'));
|
|
7408
|
+
console.log(chalk.cyan(' ruvector agent run coder "task" -p local\n'));
|
|
7409
|
+
console.log(chalk.yellow(' 2. Or set a cloud API key:'));
|
|
7410
|
+
console.log(chalk.cyan(' export ANTHROPIC_API_KEY=your-key-here'));
|
|
7411
|
+
console.log(chalk.cyan(' export OPENAI_API_KEY=your-key-here'));
|
|
7412
|
+
console.log(chalk.dim('\nGet an API key:'));
|
|
7413
|
+
console.log(chalk.dim(' Anthropic: https://console.anthropic.com/'));
|
|
7414
|
+
console.log(chalk.dim(' OpenAI: https://platform.openai.com/api-keys\n'));
|
|
7415
|
+
return;
|
|
7416
|
+
}
|
|
7417
|
+
|
|
7418
|
+
try {
|
|
7419
|
+
// Try to load real-agents module
|
|
7420
|
+
let RealAgentManager;
|
|
7421
|
+
try {
|
|
7422
|
+
const realAgents = await import('@ruvector/edge-net/real-agents');
|
|
7423
|
+
RealAgentManager = realAgents.RealAgentManager;
|
|
7424
|
+
} catch (e) {
|
|
7425
|
+
// Fallback to local path for development
|
|
7426
|
+
try {
|
|
7427
|
+
const realAgentsPath = path.resolve(__dirname, '../../../../examples/edge-net/pkg/real-agents.js');
|
|
7428
|
+
const realAgents = await import(realAgentsPath);
|
|
7429
|
+
RealAgentManager = realAgents.RealAgentManager;
|
|
7430
|
+
} catch (e2) {
|
|
7431
|
+
spinner.fail('Real agents module not found');
|
|
7432
|
+
console.log(chalk.yellow('\nInstall @ruvector/edge-net for real agent execution:'));
|
|
7433
|
+
console.log(chalk.cyan(' npm install @ruvector/edge-net\n'));
|
|
7434
|
+
return;
|
|
7435
|
+
}
|
|
7436
|
+
}
|
|
7437
|
+
|
|
7438
|
+
// Determine provider from API key if not specified
|
|
7439
|
+
const provider = opts.provider || (process.env.ANTHROPIC_API_KEY ? 'anthropic' : 'openai');
|
|
7440
|
+
|
|
7441
|
+
spinner.text = `Spawning ${type} agent (${provider})...`;
|
|
7442
|
+
|
|
7443
|
+
// Initialize manager
|
|
7444
|
+
const manager = new RealAgentManager({
|
|
7445
|
+
provider,
|
|
7446
|
+
apiKey,
|
|
7447
|
+
relayUrl: opts.relay,
|
|
7448
|
+
enableSync: !!opts.relay,
|
|
7449
|
+
});
|
|
7450
|
+
await manager.initialize();
|
|
7451
|
+
|
|
7452
|
+
// Spawn agent
|
|
7453
|
+
const agent = await manager.spawn(type, {
|
|
7454
|
+
provider,
|
|
7455
|
+
model: opts.model,
|
|
7456
|
+
});
|
|
7457
|
+
|
|
7458
|
+
spinner.text = `Executing task with ${type} agent...`;
|
|
7459
|
+
|
|
7460
|
+
// Build context
|
|
7461
|
+
const context = {
|
|
7462
|
+
model: opts.model,
|
|
7463
|
+
files: opts.files || [],
|
|
7464
|
+
additionalContext: opts.context,
|
|
7465
|
+
maxTokens: parseInt(opts.maxTokens) || 4096,
|
|
7466
|
+
};
|
|
7467
|
+
|
|
7468
|
+
// Execute task
|
|
7469
|
+
const startTime = Date.now();
|
|
7470
|
+
const result = await agent.execute(task, context);
|
|
7471
|
+
const duration = Date.now() - startTime;
|
|
7472
|
+
|
|
7473
|
+
spinner.succeed(`Task completed in ${(duration / 1000).toFixed(2)}s`);
|
|
7474
|
+
|
|
7475
|
+
if (opts.json) {
|
|
7476
|
+
console.log(JSON.stringify({
|
|
7477
|
+
success: true,
|
|
7478
|
+
agentId: agent.id,
|
|
7479
|
+
type,
|
|
7480
|
+
provider,
|
|
7481
|
+
model: result.model,
|
|
7482
|
+
duration,
|
|
7483
|
+
result,
|
|
7484
|
+
}, null, 2));
|
|
7485
|
+
} else {
|
|
7486
|
+
console.log(chalk.bold.cyan(`\nš¤ Real Agent Execution Complete\n`));
|
|
7487
|
+
console.log(`${chalk.cyan('Agent ID:')} ${agent.id}`);
|
|
7488
|
+
console.log(`${chalk.cyan('Type:')} ${type}`);
|
|
7489
|
+
console.log(`${chalk.cyan('Provider:')} ${provider}`);
|
|
7490
|
+
console.log(`${chalk.cyan('Model:')} ${result.model}`);
|
|
7491
|
+
console.log(`${chalk.cyan('Duration:')} ${(duration / 1000).toFixed(2)}s`);
|
|
7492
|
+
|
|
7493
|
+
if (agent.cost.inputTokens || agent.cost.outputTokens) {
|
|
7494
|
+
console.log(`${chalk.cyan('Tokens:')} ${agent.cost.inputTokens} in / ${agent.cost.outputTokens} out`);
|
|
7495
|
+
}
|
|
7496
|
+
|
|
7497
|
+
console.log(chalk.bold('\nš Response:\n'));
|
|
7498
|
+
console.log(result.content || JSON.stringify(result, null, 2));
|
|
7499
|
+
console.log();
|
|
7500
|
+
}
|
|
7501
|
+
|
|
7502
|
+
await manager.close();
|
|
7503
|
+
|
|
7504
|
+
} catch (e) {
|
|
7505
|
+
spinner.fail(`Agent execution failed: ${e.message}`);
|
|
7506
|
+
if (e.message.includes('401') || e.message.includes('Unauthorized')) {
|
|
7507
|
+
console.log(chalk.yellow('\nCheck your API key is valid and has sufficient credits.'));
|
|
7508
|
+
}
|
|
7509
|
+
}
|
|
7510
|
+
});
|
|
7511
|
+
|
|
7512
|
+
agentCmd.command('types')
|
|
7513
|
+
.description('List available agent types')
|
|
7514
|
+
.action(() => {
|
|
7515
|
+
console.log(chalk.bold.cyan('\nš¤ Available Real Agent Types\n'));
|
|
7516
|
+
|
|
7517
|
+
const types = [
|
|
7518
|
+
{ type: 'researcher', desc: 'Analyze, search, summarize information', cost: '~$0.01-0.05/task' },
|
|
7519
|
+
{ type: 'coder', desc: 'Write, refactor, debug code', cost: '~$0.02-0.10/task' },
|
|
7520
|
+
{ type: 'reviewer', desc: 'Review code quality and security', cost: '~$0.01-0.05/task' },
|
|
7521
|
+
{ type: 'tester', desc: 'Write tests and validate functionality', cost: '~$0.01-0.05/task' },
|
|
7522
|
+
{ type: 'analyst', desc: 'Analyze data and generate reports', cost: '~$0.01-0.05/task' },
|
|
7523
|
+
{ type: 'optimizer', desc: 'Profile and improve performance', cost: '~$0.02-0.10/task' },
|
|
7524
|
+
{ type: 'coordinator', desc: 'Orchestrate workflows and tasks', cost: '~$0.02-0.10/task' },
|
|
7525
|
+
{ type: 'embedder', desc: 'Generate semantic embeddings (no API key needed)', cost: 'Free (local)' },
|
|
7526
|
+
];
|
|
7527
|
+
|
|
7528
|
+
types.forEach(t => {
|
|
7529
|
+
console.log(` ${chalk.green('ā¢')} ${chalk.bold(t.type.padEnd(12))} ${t.desc}`);
|
|
7530
|
+
console.log(` ${chalk.dim('Estimated cost: ' + t.cost)}`);
|
|
7531
|
+
});
|
|
7532
|
+
|
|
7533
|
+
console.log(chalk.bold('\nš Model Tiers:\n'));
|
|
7534
|
+
console.log(` ${chalk.cyan('fast')} - Quick responses, lower cost (Haiku/GPT-4o-mini)`);
|
|
7535
|
+
console.log(` ${chalk.cyan('balanced')} - Good quality/speed balance (Sonnet/GPT-4o)`);
|
|
7536
|
+
console.log(` ${chalk.cyan('powerful')} - Best quality, higher cost (Opus/GPT-4-turbo)`);
|
|
7537
|
+
|
|
7538
|
+
console.log(chalk.bold('\nš§ Example:\n'));
|
|
7539
|
+
console.log(chalk.dim(' export ANTHROPIC_API_KEY=your-key'));
|
|
7540
|
+
console.log(chalk.cyan(' ruvector agent run coder "Write a function to validate emails" -m fast'));
|
|
7541
|
+
console.log();
|
|
7542
|
+
});
|
|
7543
|
+
|
|
7544
|
+
agentCmd.command('balance')
|
|
7545
|
+
.description('Show rUv balance and sync status')
|
|
7546
|
+
.option('--relay <url>', 'Relay server URL', 'ws://localhost:8080')
|
|
7547
|
+
.action(async (opts) => {
|
|
7548
|
+
const spinner = ora('Connecting to relay...').start();
|
|
7549
|
+
|
|
7550
|
+
try {
|
|
7551
|
+
let RelaySyncClient;
|
|
7552
|
+
try {
|
|
7553
|
+
const realAgents = await import('@ruvector/edge-net/real-agents');
|
|
7554
|
+
RelaySyncClient = realAgents.RelaySyncClient;
|
|
7555
|
+
} catch (e) {
|
|
7556
|
+
const realAgentsPath = path.resolve(__dirname, '../../../../examples/edge-net/pkg/real-agents.js');
|
|
7557
|
+
const realAgents = await import(realAgentsPath);
|
|
7558
|
+
RelaySyncClient = realAgents.RelaySyncClient;
|
|
7559
|
+
}
|
|
7560
|
+
|
|
7561
|
+
const client = new RelaySyncClient({ relayUrl: opts.relay });
|
|
7562
|
+
|
|
7563
|
+
await Promise.race([
|
|
7564
|
+
client.connect(),
|
|
7565
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Connection timeout')), 5000)),
|
|
7566
|
+
]);
|
|
7567
|
+
|
|
7568
|
+
spinner.succeed('Connected to relay');
|
|
7569
|
+
|
|
7570
|
+
console.log(chalk.bold.cyan('\nš° rUv Balance\n'));
|
|
7571
|
+
console.log(`${chalk.cyan('Balance:')} ${client.getBalance()} rUv`);
|
|
7572
|
+
console.log(`${chalk.cyan('Relay:')} ${opts.relay}`);
|
|
7573
|
+
console.log(`${chalk.cyan('Node ID:')} ${client.nodeId}`);
|
|
7574
|
+
console.log(`${chalk.cyan('Status:')} ${chalk.green('Connected')}`);
|
|
7575
|
+
console.log();
|
|
7576
|
+
|
|
7577
|
+
client.close();
|
|
7578
|
+
|
|
7579
|
+
} catch (e) {
|
|
7580
|
+
spinner.fail(`Connection failed: ${e.message}`);
|
|
7581
|
+
console.log(chalk.dim('\nMake sure the relay server is running:'));
|
|
7582
|
+
console.log(chalk.cyan(' cd examples/edge-net/relay && node index.js\n'));
|
|
7583
|
+
}
|
|
7584
|
+
});
|
|
7585
|
+
|
|
7586
|
+
// ============================================
|
|
7587
|
+
// REAL WORKER POOL COMMANDS
|
|
7588
|
+
// ============================================
|
|
7589
|
+
|
|
7590
|
+
const workerCmd = program.command('worker').description('Real worker pool execution with Node.js worker_threads');
|
|
7591
|
+
|
|
7592
|
+
workerCmd.command('create')
|
|
7593
|
+
.description('Create a real worker pool for parallel task execution')
|
|
7594
|
+
.option('-s, --size <size>', 'Pool size (number of workers)', String(require('os').cpus().length - 1))
|
|
7595
|
+
.option('--relay <url>', 'Relay server URL for distributed mode')
|
|
7596
|
+
.option('--json', 'Output as JSON')
|
|
7597
|
+
.action(async (opts) => {
|
|
7598
|
+
const spinner = ora('Creating worker pool...').start();
|
|
7599
|
+
|
|
7600
|
+
try {
|
|
7601
|
+
let RealWorkerPool;
|
|
7602
|
+
try {
|
|
7603
|
+
const realWorkers = await import('@ruvector/edge-net/real-workers');
|
|
7604
|
+
RealWorkerPool = realWorkers.RealWorkerPool;
|
|
7605
|
+
} catch (e) {
|
|
7606
|
+
const realWorkersPath = path.resolve(__dirname, '../../../../examples/edge-net/pkg/real-workers.js');
|
|
7607
|
+
const realWorkers = await import(realWorkersPath);
|
|
7608
|
+
RealWorkerPool = realWorkers.RealWorkerPool;
|
|
7609
|
+
}
|
|
7610
|
+
|
|
7611
|
+
const pool = new RealWorkerPool({
|
|
7612
|
+
size: parseInt(opts.size) || require('os').cpus().length - 1,
|
|
7613
|
+
});
|
|
7614
|
+
|
|
7615
|
+
await pool.initialize();
|
|
7616
|
+
|
|
7617
|
+
spinner.succeed(`Worker pool created: ${pool.id}`);
|
|
7618
|
+
|
|
7619
|
+
const info = {
|
|
7620
|
+
id: pool.id,
|
|
7621
|
+
size: pool.size,
|
|
7622
|
+
workers: pool.workers.map(w => ({ id: w.id, status: w.status })),
|
|
7623
|
+
ready: pool.workers.filter(w => w.status === 'ready').length,
|
|
7624
|
+
};
|
|
7625
|
+
|
|
7626
|
+
if (opts.json) {
|
|
7627
|
+
console.log(JSON.stringify(info, null, 2));
|
|
7628
|
+
} else {
|
|
7629
|
+
console.log(chalk.bold.cyan('\nā” Real Worker Pool Created\n'));
|
|
7630
|
+
console.log(`${chalk.cyan('Pool ID:')} ${info.id}`);
|
|
7631
|
+
console.log(`${chalk.cyan('Size:')} ${info.size} workers`);
|
|
7632
|
+
console.log(`${chalk.cyan('Ready:')} ${info.ready}/${info.size}`);
|
|
7633
|
+
console.log();
|
|
7634
|
+
}
|
|
7635
|
+
|
|
7636
|
+
if (pool.shutdown) await pool.shutdown();
|
|
7637
|
+
else if (pool.close) await pool.close();
|
|
7638
|
+
|
|
7639
|
+
} catch (e) {
|
|
7640
|
+
spinner.fail(`Failed to create pool: ${e.message}`);
|
|
7641
|
+
}
|
|
7642
|
+
});
|
|
7643
|
+
|
|
7644
|
+
workerCmd.command('execute')
|
|
7645
|
+
.description('Execute a task on worker pool')
|
|
7646
|
+
.argument('<type>', 'Task type: compute, analyze, transform, batch')
|
|
7647
|
+
.argument('<data>', 'Task data (JSON string or simple value)')
|
|
7648
|
+
.option('-s, --size <size>', 'Pool size', '4')
|
|
7649
|
+
.option('--timeout <ms>', 'Task timeout in milliseconds', '30000')
|
|
7650
|
+
.option('--json', 'Output as JSON')
|
|
7651
|
+
.action(async (type, data, opts) => {
|
|
7652
|
+
const spinner = ora(`Executing ${type} task...`).start();
|
|
7653
|
+
|
|
7654
|
+
try {
|
|
7655
|
+
let RealWorkerPool;
|
|
7656
|
+
try {
|
|
7657
|
+
const realWorkers = await import('@ruvector/edge-net/real-workers');
|
|
7658
|
+
RealWorkerPool = realWorkers.RealWorkerPool;
|
|
7659
|
+
} catch (e) {
|
|
7660
|
+
const realWorkersPath = path.resolve(__dirname, '../../../../examples/edge-net/pkg/real-workers.js');
|
|
7661
|
+
const realWorkers = await import(realWorkersPath);
|
|
7662
|
+
RealWorkerPool = realWorkers.RealWorkerPool;
|
|
7663
|
+
}
|
|
7664
|
+
|
|
7665
|
+
const pool = new RealWorkerPool({ size: parseInt(opts.size) || 4 });
|
|
7666
|
+
await pool.initialize();
|
|
7667
|
+
|
|
7668
|
+
let taskData;
|
|
7669
|
+
try {
|
|
7670
|
+
taskData = JSON.parse(data);
|
|
7671
|
+
} catch {
|
|
7672
|
+
taskData = { input: data };
|
|
7673
|
+
}
|
|
7674
|
+
|
|
7675
|
+
const startTime = Date.now();
|
|
7676
|
+
const result = await pool.execute(type, taskData, {
|
|
7677
|
+
timeout: parseInt(opts.timeout) || 30000,
|
|
7678
|
+
});
|
|
7679
|
+
const duration = Date.now() - startTime;
|
|
7680
|
+
|
|
7681
|
+
spinner.succeed(`Task completed in ${(duration / 1000).toFixed(2)}s`);
|
|
7682
|
+
|
|
7683
|
+
if (opts.json) {
|
|
7684
|
+
console.log(JSON.stringify({ success: true, duration, result }, null, 2));
|
|
7685
|
+
} else {
|
|
7686
|
+
console.log(chalk.bold.cyan('\nā” Worker Execution Complete\n'));
|
|
7687
|
+
console.log(`${chalk.cyan('Type:')} ${type}`);
|
|
7688
|
+
console.log(`${chalk.cyan('Duration:')} ${(duration / 1000).toFixed(2)}s`);
|
|
7689
|
+
console.log(`${chalk.cyan('Worker:')} ${result.workerId || 'pool'}`);
|
|
7690
|
+
console.log(chalk.bold('\nš Result:\n'));
|
|
7691
|
+
console.log(JSON.stringify(result, null, 2));
|
|
7692
|
+
console.log();
|
|
7693
|
+
}
|
|
7694
|
+
|
|
7695
|
+
if (pool.shutdown) await pool.shutdown();
|
|
7696
|
+
else if (pool.close) await pool.close();
|
|
7697
|
+
|
|
7698
|
+
} catch (e) {
|
|
7699
|
+
spinner.fail(`Execution failed: ${e.message}`);
|
|
7700
|
+
}
|
|
7701
|
+
});
|
|
7702
|
+
|
|
7703
|
+
workerCmd.command('info')
|
|
7704
|
+
.description('Show worker pool information')
|
|
7705
|
+
.action(() => {
|
|
7706
|
+
console.log(chalk.bold.cyan('\nā” Real Worker Pool System\n'));
|
|
7707
|
+
console.log(chalk.white('Execute parallel tasks using Node.js worker_threads.\n'));
|
|
7708
|
+
|
|
7709
|
+
console.log(chalk.bold('Task Types:'));
|
|
7710
|
+
console.log(chalk.dim(' compute - CPU-intensive computations'));
|
|
7711
|
+
console.log(chalk.dim(' analyze - Data analysis tasks'));
|
|
7712
|
+
console.log(chalk.dim(' transform - Data transformation'));
|
|
7713
|
+
console.log(chalk.dim(' batch - Batch processing'));
|
|
7714
|
+
|
|
7715
|
+
console.log(chalk.bold('\nš Examples:\n'));
|
|
7716
|
+
console.log(chalk.cyan(' ruvector worker create --size 8'));
|
|
7717
|
+
console.log(chalk.cyan(' ruvector worker execute compute \'{"n": 1000000}\''));
|
|
7718
|
+
console.log(chalk.cyan(' ruvector worker execute analyze \'{"data": [1,2,3,4,5]}\''));
|
|
7719
|
+
console.log();
|
|
7720
|
+
});
|
|
7721
|
+
|
|
7722
|
+
// ============================================
|
|
7723
|
+
// REAL WORKFLOW COMMANDS
|
|
7724
|
+
// ============================================
|
|
7725
|
+
|
|
7726
|
+
const workflowCmd = program.command('workflow').description('Real multi-agent workflow orchestration with LLM execution');
|
|
7727
|
+
|
|
7728
|
+
workflowCmd.command('run')
|
|
7729
|
+
.description('Run a multi-agent workflow (uses local ruvllm by default)')
|
|
7730
|
+
.argument('<template>', 'Workflow template: code-review, feature-dev, bug-fix, optimization, research')
|
|
7731
|
+
.argument('<input>', 'Input/context for the workflow')
|
|
7732
|
+
.option('-p, --provider <provider>', 'LLM provider: local (default), anthropic, openai', 'local')
|
|
7733
|
+
.option('-m, --model <model>', 'Model tier: fast, balanced, powerful', 'balanced')
|
|
7734
|
+
.option('--max-steps <n>', 'Maximum workflow steps', '10')
|
|
7735
|
+
.option('--json', 'Output as JSON')
|
|
7736
|
+
.action(async (template, input, opts) => {
|
|
7737
|
+
const spinner = ora(`Running ${template} workflow...`).start();
|
|
7738
|
+
|
|
7739
|
+
try {
|
|
7740
|
+
let RealWorkflowOrchestrator;
|
|
7741
|
+
try {
|
|
7742
|
+
const realWorkflows = await import('@ruvector/edge-net/real-workflows');
|
|
7743
|
+
RealWorkflowOrchestrator = realWorkflows.RealWorkflowOrchestrator;
|
|
7744
|
+
} catch (e) {
|
|
7745
|
+
const realWorkflowsPath = path.resolve(__dirname, '../../../../examples/edge-net/pkg/real-workflows.js');
|
|
7746
|
+
const realWorkflows = await import(realWorkflowsPath);
|
|
7747
|
+
RealWorkflowOrchestrator = realWorkflows.RealWorkflowOrchestrator;
|
|
7748
|
+
}
|
|
7749
|
+
|
|
7750
|
+
// Check for API key if using cloud provider
|
|
7751
|
+
const isLocal = opts.provider === 'local' || opts.provider === 'ruvllm';
|
|
7752
|
+
const apiKey = process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
|
|
7753
|
+
|
|
7754
|
+
if (!isLocal && !apiKey) {
|
|
7755
|
+
spinner.fail('No API key found for cloud provider');
|
|
7756
|
+
console.log(chalk.yellow('\n Use local provider (default) or set API key:'));
|
|
7757
|
+
console.log(chalk.cyan(' ruvector workflow run code-review "my code" -p local'));
|
|
7758
|
+
return;
|
|
7759
|
+
}
|
|
7760
|
+
|
|
7761
|
+
const orchestrator = new RealWorkflowOrchestrator({
|
|
7762
|
+
provider: isLocal ? 'local' : (process.env.ANTHROPIC_API_KEY ? 'anthropic' : 'openai'),
|
|
7763
|
+
apiKey: isLocal ? undefined : apiKey,
|
|
7764
|
+
maxConcurrency: 2,
|
|
7765
|
+
});
|
|
7766
|
+
|
|
7767
|
+
await orchestrator.initialize();
|
|
7768
|
+
|
|
7769
|
+
const startTime = Date.now();
|
|
7770
|
+
const result = await orchestrator.run(template, input, {
|
|
7771
|
+
maxSteps: parseInt(opts.maxSteps) || 10,
|
|
7772
|
+
modelTier: opts.model,
|
|
7773
|
+
});
|
|
7774
|
+
const duration = Date.now() - startTime;
|
|
7775
|
+
|
|
7776
|
+
spinner.succeed(`Workflow completed in ${(duration / 1000).toFixed(2)}s`);
|
|
7777
|
+
|
|
7778
|
+
if (opts.json) {
|
|
7779
|
+
console.log(JSON.stringify({ success: true, duration, ...result }, null, 2));
|
|
7780
|
+
} else {
|
|
7781
|
+
console.log(chalk.bold.cyan(`\nš Workflow: ${template}\n`));
|
|
7782
|
+
console.log(`${chalk.cyan('Duration:')} ${(duration / 1000).toFixed(2)}s`);
|
|
7783
|
+
console.log(`${chalk.cyan('Provider:')} ${opts.provider}`);
|
|
7784
|
+
const steps = result.steps || [];
|
|
7785
|
+
const completedSteps = steps.filter(s => s.status === 'completed').length;
|
|
7786
|
+
console.log(`${chalk.cyan('Steps:')} ${completedSteps}/${steps.length}`);
|
|
7787
|
+
|
|
7788
|
+
console.log(chalk.bold('\nš Results by Step:\n'));
|
|
7789
|
+
for (const [stepName, stepResult] of Object.entries(result.results || {})) {
|
|
7790
|
+
console.log(chalk.bold.green(` ${stepName}:`));
|
|
7791
|
+
const preview = typeof stepResult === 'string'
|
|
7792
|
+
? stepResult.slice(0, 200) + (stepResult.length > 200 ? '...' : '')
|
|
7793
|
+
: JSON.stringify(stepResult).slice(0, 200);
|
|
7794
|
+
console.log(chalk.dim(` ${preview}\n`));
|
|
7795
|
+
}
|
|
7796
|
+
}
|
|
7797
|
+
|
|
7798
|
+
await orchestrator.close();
|
|
7799
|
+
|
|
7800
|
+
} catch (e) {
|
|
7801
|
+
spinner.fail(`Workflow failed: ${e.message}`);
|
|
7802
|
+
}
|
|
7803
|
+
});
|
|
7804
|
+
|
|
7805
|
+
workflowCmd.command('templates')
|
|
7806
|
+
.description('List available workflow templates')
|
|
7807
|
+
.action(() => {
|
|
7808
|
+
console.log(chalk.bold.cyan('\nš Workflow Templates\n'));
|
|
7809
|
+
|
|
7810
|
+
const templates = [
|
|
7811
|
+
{ name: 'code-review', desc: 'Analyze, review, test coverage, and optimize code', steps: 4 },
|
|
7812
|
+
{ name: 'feature-dev', desc: 'Research, implement, test, and review a feature', steps: 4 },
|
|
7813
|
+
{ name: 'bug-fix', desc: 'Analyze, fix, and verify a bug', steps: 3 },
|
|
7814
|
+
{ name: 'optimization', desc: 'Profile, identify bottlenecks, optimize, benchmark', steps: 4 },
|
|
7815
|
+
{ name: 'research', desc: 'Research, analyze, and create knowledge embeddings', steps: 3 },
|
|
7816
|
+
];
|
|
7817
|
+
|
|
7818
|
+
templates.forEach(t => {
|
|
7819
|
+
console.log(` ${chalk.green('ā¢')} ${chalk.bold(t.name.padEnd(14))} ${t.desc}`);
|
|
7820
|
+
console.log(` ${chalk.dim(`Steps: ${t.steps}`)}`);
|
|
7821
|
+
});
|
|
7822
|
+
|
|
7823
|
+
console.log(chalk.bold('\nš Example:\n'));
|
|
7824
|
+
console.log(chalk.cyan(' ruvector workflow run code-review "Review my authentication module"'));
|
|
7825
|
+
console.log(chalk.cyan(' ruvector workflow run feature-dev "Add user profile page" -p anthropic'));
|
|
7826
|
+
console.log();
|
|
7827
|
+
});
|
|
7828
|
+
|
|
7379
7829
|
// MCP Server command
|
|
7380
7830
|
const mcpCmd = program.command('mcp').description('MCP (Model Context Protocol) server for Claude Code integration');
|
|
7381
7831
|
|
package/bin/mcp-server.js
CHANGED
|
@@ -294,7 +294,7 @@ class Intelligence {
|
|
|
294
294
|
const server = new Server(
|
|
295
295
|
{
|
|
296
296
|
name: 'ruvector',
|
|
297
|
-
version: '0.1.
|
|
297
|
+
version: '0.1.92',
|
|
298
298
|
},
|
|
299
299
|
{
|
|
300
300
|
capabilities: {
|
|
@@ -1124,6 +1124,37 @@ const TOOLS = [
|
|
|
1124
1124
|
properties: {},
|
|
1125
1125
|
required: []
|
|
1126
1126
|
}
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
name: 'agent_execute',
|
|
1130
|
+
description: 'REAL agent execution using local ruvllm (default, no API key), or cloud APIs (Anthropic/OpenAI). Local runs without needing any API key.',
|
|
1131
|
+
inputSchema: {
|
|
1132
|
+
type: 'object',
|
|
1133
|
+
properties: {
|
|
1134
|
+
type: {
|
|
1135
|
+
type: 'string',
|
|
1136
|
+
description: 'Agent type',
|
|
1137
|
+
enum: ['researcher', 'coder', 'reviewer', 'tester', 'analyst', 'optimizer', 'coordinator', 'embedder']
|
|
1138
|
+
},
|
|
1139
|
+
task: { type: 'string', description: 'Task for the agent to execute' },
|
|
1140
|
+
provider: { type: 'string', enum: ['local', 'ruvllm', 'anthropic', 'openai'], default: 'local', description: 'LLM provider (local by default, no API key needed)' },
|
|
1141
|
+
model: { type: 'string', enum: ['fast', 'balanced', 'powerful'], default: 'balanced', description: 'Model tier' },
|
|
1142
|
+
context: { type: 'string', description: 'Additional context for the agent' },
|
|
1143
|
+
max_tokens: { type: 'number', default: 4096, description: 'Maximum tokens in response' }
|
|
1144
|
+
},
|
|
1145
|
+
required: ['type', 'task']
|
|
1146
|
+
}
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
name: 'agent_balance',
|
|
1150
|
+
description: 'Get rUv credit balance from relay server with multi-device sync status',
|
|
1151
|
+
inputSchema: {
|
|
1152
|
+
type: 'object',
|
|
1153
|
+
properties: {
|
|
1154
|
+
relay_url: { type: 'string', description: 'Relay server URL', default: 'ws://localhost:8080' }
|
|
1155
|
+
},
|
|
1156
|
+
required: []
|
|
1157
|
+
}
|
|
1127
1158
|
}
|
|
1128
1159
|
];
|
|
1129
1160
|
|
|
@@ -2787,6 +2818,167 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2787
2818
|
return { content: [{ type: 'text', text: JSON.stringify(status, null, 2) }] };
|
|
2788
2819
|
}
|
|
2789
2820
|
|
|
2821
|
+
case 'agent_execute': {
|
|
2822
|
+
// REAL agent execution with local ruvllm by default, or cloud APIs
|
|
2823
|
+
const validTypes = ['researcher', 'coder', 'reviewer', 'tester', 'analyst', 'optimizer', 'coordinator', 'embedder'];
|
|
2824
|
+
const validModels = ['fast', 'balanced', 'powerful'];
|
|
2825
|
+
|
|
2826
|
+
const agentType = validTypes.includes(args.type) ? args.type : 'coder';
|
|
2827
|
+
const task = typeof args.task === 'string' ? args.task.slice(0, 10000).trim() : '';
|
|
2828
|
+
const model = validModels.includes(args.model) ? args.model : 'balanced';
|
|
2829
|
+
const maxTokens = Math.min(Math.max(parseInt(args.max_tokens) || 4096, 100), 8192);
|
|
2830
|
+
|
|
2831
|
+
// Determine provider (local by default)
|
|
2832
|
+
const provider = args.provider || 'local';
|
|
2833
|
+
const isLocal = provider === 'local' || provider === 'ruvllm';
|
|
2834
|
+
|
|
2835
|
+
// Check for API key (only required for cloud providers)
|
|
2836
|
+
const apiKey = process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
|
|
2837
|
+
if (!isLocal && !apiKey && agentType !== 'embedder') {
|
|
2838
|
+
return {
|
|
2839
|
+
content: [{
|
|
2840
|
+
type: 'text',
|
|
2841
|
+
text: JSON.stringify({
|
|
2842
|
+
success: false,
|
|
2843
|
+
error: 'No API key configured for cloud provider',
|
|
2844
|
+
message: 'Set ANTHROPIC_API_KEY or OPENAI_API_KEY, or use local provider (default)',
|
|
2845
|
+
options: {
|
|
2846
|
+
local: 'Use provider=local (no API key needed)',
|
|
2847
|
+
anthropic: 'https://console.anthropic.com/',
|
|
2848
|
+
openai: 'https://platform.openai.com/api-keys'
|
|
2849
|
+
}
|
|
2850
|
+
}, null, 2)
|
|
2851
|
+
}],
|
|
2852
|
+
isError: true
|
|
2853
|
+
};
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
try {
|
|
2857
|
+
// Try to load real-agents module
|
|
2858
|
+
let RealAgentManager;
|
|
2859
|
+
try {
|
|
2860
|
+
const realAgents = await import('@ruvector/edge-net/real-agents');
|
|
2861
|
+
RealAgentManager = realAgents.RealAgentManager;
|
|
2862
|
+
} catch (e) {
|
|
2863
|
+
// Fallback to local path for development
|
|
2864
|
+
const realAgentsPath = path.resolve(__dirname, '../../../../examples/edge-net/pkg/real-agents.js');
|
|
2865
|
+
const realAgents = await import(realAgentsPath);
|
|
2866
|
+
RealAgentManager = realAgents.RealAgentManager;
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2869
|
+
// Initialize manager with selected provider
|
|
2870
|
+
const manager = new RealAgentManager({
|
|
2871
|
+
provider: isLocal ? 'local' : (apiKey && process.env.ANTHROPIC_API_KEY ? 'anthropic' : 'openai'),
|
|
2872
|
+
apiKey: isLocal ? undefined : apiKey,
|
|
2873
|
+
});
|
|
2874
|
+
await manager.initialize();
|
|
2875
|
+
|
|
2876
|
+
// Spawn and execute
|
|
2877
|
+
const effectiveProvider = isLocal ? 'local' : (apiKey && process.env.ANTHROPIC_API_KEY ? 'anthropic' : 'openai');
|
|
2878
|
+
const agent = await manager.spawn(agentType, { provider: effectiveProvider, model });
|
|
2879
|
+
|
|
2880
|
+
const context = {
|
|
2881
|
+
model,
|
|
2882
|
+
additionalContext: args.context,
|
|
2883
|
+
maxTokens,
|
|
2884
|
+
};
|
|
2885
|
+
|
|
2886
|
+
const startTime = Date.now();
|
|
2887
|
+
const result = await agent.execute(task, context);
|
|
2888
|
+
const duration = Date.now() - startTime;
|
|
2889
|
+
|
|
2890
|
+
await manager.close();
|
|
2891
|
+
|
|
2892
|
+
return {
|
|
2893
|
+
content: [{
|
|
2894
|
+
type: 'text',
|
|
2895
|
+
text: JSON.stringify({
|
|
2896
|
+
success: true,
|
|
2897
|
+
real_execution: true,
|
|
2898
|
+
local_mode: isLocal,
|
|
2899
|
+
agent: {
|
|
2900
|
+
id: agent.id,
|
|
2901
|
+
type: agentType,
|
|
2902
|
+
provider: effectiveProvider,
|
|
2903
|
+
model: result.model || (isLocal ? 'ruvllm-balanced' : model)
|
|
2904
|
+
},
|
|
2905
|
+
execution: {
|
|
2906
|
+
duration_ms: duration,
|
|
2907
|
+
tokens: agent.cost,
|
|
2908
|
+
fallback: result.fallback || false
|
|
2909
|
+
},
|
|
2910
|
+
response: result.content
|
|
2911
|
+
}, null, 2)
|
|
2912
|
+
}]
|
|
2913
|
+
};
|
|
2914
|
+
|
|
2915
|
+
} catch (execError) {
|
|
2916
|
+
return {
|
|
2917
|
+
content: [{
|
|
2918
|
+
type: 'text',
|
|
2919
|
+
text: JSON.stringify({
|
|
2920
|
+
success: false,
|
|
2921
|
+
error: execError.message,
|
|
2922
|
+
real_execution: true,
|
|
2923
|
+
hint: execError.message.includes('401') ? 'Check your API key is valid' : 'Check error message for details'
|
|
2924
|
+
}, null, 2)
|
|
2925
|
+
}],
|
|
2926
|
+
isError: true
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2931
|
+
case 'agent_balance': {
|
|
2932
|
+
const relayUrl = args.relay_url || 'ws://localhost:8080';
|
|
2933
|
+
|
|
2934
|
+
try {
|
|
2935
|
+
// Try to load real-agents module
|
|
2936
|
+
let RelaySyncClient;
|
|
2937
|
+
try {
|
|
2938
|
+
const realAgents = await import('@ruvector/edge-net/real-agents');
|
|
2939
|
+
RelaySyncClient = realAgents.RelaySyncClient;
|
|
2940
|
+
} catch (e) {
|
|
2941
|
+
const realAgentsPath = path.resolve(__dirname, '../../../../examples/edge-net/pkg/real-agents.js');
|
|
2942
|
+
const realAgents = await import(realAgentsPath);
|
|
2943
|
+
RelaySyncClient = realAgents.RelaySyncClient;
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2946
|
+
const client = new RelaySyncClient({ relayUrl });
|
|
2947
|
+
|
|
2948
|
+
await Promise.race([
|
|
2949
|
+
client.connect(),
|
|
2950
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Connection timeout')), 5000)),
|
|
2951
|
+
]);
|
|
2952
|
+
|
|
2953
|
+
const balance = {
|
|
2954
|
+
success: true,
|
|
2955
|
+
balance: client.getBalance(),
|
|
2956
|
+
nodeId: client.nodeId,
|
|
2957
|
+
relayUrl,
|
|
2958
|
+
connected: true,
|
|
2959
|
+
ledgerState: client.ledgerState
|
|
2960
|
+
};
|
|
2961
|
+
|
|
2962
|
+
client.close();
|
|
2963
|
+
|
|
2964
|
+
return { content: [{ type: 'text', text: JSON.stringify(balance, null, 2) }] };
|
|
2965
|
+
|
|
2966
|
+
} catch (connError) {
|
|
2967
|
+
return {
|
|
2968
|
+
content: [{
|
|
2969
|
+
type: 'text',
|
|
2970
|
+
text: JSON.stringify({
|
|
2971
|
+
success: false,
|
|
2972
|
+
error: connError.message,
|
|
2973
|
+
relayUrl,
|
|
2974
|
+
hint: 'Start the relay server: cd examples/edge-net/relay && node index.js'
|
|
2975
|
+
}, null, 2)
|
|
2976
|
+
}],
|
|
2977
|
+
isError: true
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2790
2982
|
default:
|
|
2791
2983
|
return {
|
|
2792
2984
|
content: [{
|