tasq/node_modules/agentic-flow/dist/cli/config-wizard.js

369 lines
13 KiB
JavaScript

#!/usr/bin/env node
/**
* Interactive CLI wizard for agentic-flow configuration
* Supports both interactive mode and direct CLI arguments
*/
import { readFileSync, writeFileSync, existsSync } from 'fs';
import { resolve } from 'path';
import { createInterface } from 'readline';
const CONFIG_DEFINITIONS = [
{
key: 'ANTHROPIC_API_KEY',
value: '',
description: 'Anthropic API key for Claude models (sk-ant-...)',
required: false,
validation: (val) => val.startsWith('sk-ant-') || 'Must start with sk-ant-'
},
{
key: 'OPENROUTER_API_KEY',
value: '',
description: 'OpenRouter API key for alternative models (sk-or-v1-...)',
required: false,
validation: (val) => val.startsWith('sk-or-') || 'Must start with sk-or-'
},
{
key: 'GOOGLE_GEMINI_API_KEY',
value: '',
description: 'Google Gemini API key for Gemini models',
required: false
},
{
key: 'COMPLETION_MODEL',
value: 'claude-sonnet-4-5-20250929',
description: 'Default model for completions',
required: false
},
{
key: 'PROVIDER',
value: 'anthropic',
description: 'Default provider (anthropic, openrouter, gemini, onnx)',
required: false,
validation: (val) => ['anthropic', 'openrouter', 'gemini', 'onnx'].includes(val) || 'Must be anthropic, openrouter, gemini, or onnx'
},
{
key: 'AGENTS_DIR',
value: '',
description: 'Custom agents directory path (optional)',
required: false
},
{
key: 'PROXY_PORT',
value: '3000',
description: 'Proxy server port for OpenRouter',
required: false,
validation: (val) => !isNaN(parseInt(val)) || 'Must be a number'
},
{
key: 'USE_OPENROUTER',
value: 'false',
description: 'Force OpenRouter usage (true/false)',
required: false,
validation: (val) => ['true', 'false'].includes(val) || 'Must be true or false'
},
{
key: 'USE_ONNX',
value: 'false',
description: 'Use ONNX local inference (true/false)',
required: false,
validation: (val) => ['true', 'false'].includes(val) || 'Must be true or false'
}
];
export class ConfigWizard {
envPath;
currentConfig = new Map();
constructor(envPath) {
this.envPath = envPath || resolve(process.cwd(), '.env');
this.loadExistingConfig();
}
loadExistingConfig() {
if (existsSync(this.envPath)) {
const content = readFileSync(this.envPath, 'utf-8');
content.split('\n').forEach(line => {
const trimmed = line.trim();
if (trimmed && !trimmed.startsWith('#')) {
const [key, ...valueParts] = trimmed.split('=');
const value = valueParts.join('=');
if (key && value) {
this.currentConfig.set(key, value);
}
}
});
}
}
saveConfig() {
const lines = [
'# Agentic Flow Configuration',
'# Generated by agentic-flow config wizard',
`# Created: ${new Date().toISOString()}`,
''
];
CONFIG_DEFINITIONS.forEach(def => {
const value = this.currentConfig.get(def.key) || def.value;
if (value) {
lines.push(`# ${def.description}`);
lines.push(`${def.key}=${value}`);
lines.push('');
}
});
// Preserve other env vars not in CONFIG_DEFINITIONS
this.currentConfig.forEach((value, key) => {
if (!CONFIG_DEFINITIONS.find(d => d.key === key)) {
lines.push(`${key}=${value}`);
}
});
writeFileSync(this.envPath, lines.join('\n'), 'utf-8');
}
// Interactive wizard mode
async runInteractive() {
console.log('\n🤖 Agentic Flow Configuration Wizard\n');
console.log('Configure your environment variables for agentic-flow.');
console.log('Press Enter to keep current value, or type new value.\n');
const rl = createInterface({
input: process.stdin,
output: process.stdout
});
const question = (prompt) => {
return new Promise((resolve) => {
rl.question(prompt, resolve);
});
};
try {
for (const def of CONFIG_DEFINITIONS) {
const currentValue = this.currentConfig.get(def.key) || def.value;
const displayValue = currentValue
? (def.key.includes('KEY') ? `${currentValue.substring(0, 15)}...` : currentValue)
: '(not set)';
console.log(`\n📝 ${def.key}`);
console.log(` ${def.description}`);
let input;
let isValid = false;
while (!isValid) {
input = await question(` Current: ${displayValue}\n New value: `);
// Keep current if empty
if (!input.trim()) {
input = currentValue;
}
// Validate if validator exists
if (input && def.validation) {
const validationResult = def.validation(input);
if (validationResult === true) {
isValid = true;
}
else {
console.log(` ❌ Invalid: ${validationResult}`);
}
}
else {
isValid = true;
}
if (isValid && input) {
this.currentConfig.set(def.key, input);
}
else if (isValid && !input) {
// Remove if empty
this.currentConfig.delete(def.key);
}
}
}
console.log('\n💾 Saving configuration...');
this.saveConfig();
console.log(`✅ Configuration saved to: ${this.envPath}\n`);
// Show summary
this.showSummary();
}
finally {
rl.close();
}
}
// Direct CLI mode - set single value
set(key, value) {
const def = CONFIG_DEFINITIONS.find(d => d.key === key);
if (!def) {
throw new Error(`Unknown configuration key: ${key}\nAvailable keys: ${CONFIG_DEFINITIONS.map(d => d.key).join(', ')}`);
}
// Validate
if (def.validation) {
const result = def.validation(value);
if (result !== true) {
throw new Error(`Invalid value for ${key}: ${result}`);
}
}
this.currentConfig.set(key, value);
this.saveConfig();
console.log(`✅ Set ${key}=${value.substring(0, 20)}${value.length > 20 ? '...' : ''}`);
}
// Get single value
get(key) {
const value = this.currentConfig.get(key);
if (value) {
// Mask API keys
if (key.includes('KEY')) {
console.log(`${key}=${value.substring(0, 15)}...`);
}
else {
console.log(`${key}=${value}`);
}
}
else {
console.log(`${key} is not set`);
}
}
// Delete value
delete(key) {
if (this.currentConfig.has(key)) {
this.currentConfig.delete(key);
this.saveConfig();
console.log(`✅ Deleted ${key}`);
}
else {
console.log(`${key} is not set`);
}
}
// List all values
list() {
console.log('\n📋 Current Configuration:\n');
CONFIG_DEFINITIONS.forEach(def => {
const value = this.currentConfig.get(def.key);
const displayValue = value
? (def.key.includes('KEY') ? `${value.substring(0, 15)}...` : value)
: '(not set)';
console.log(`${def.key.padEnd(25)} = ${displayValue}`);
console.log(` ${def.description}`);
console.log('');
});
}
// Show configuration summary
showSummary() {
console.log('📊 Configuration Summary:\n');
const hasAnthropic = this.currentConfig.has('ANTHROPIC_API_KEY');
const hasOpenRouter = this.currentConfig.has('OPENROUTER_API_KEY');
const hasGemini = this.currentConfig.has('GOOGLE_GEMINI_API_KEY');
const provider = this.currentConfig.get('PROVIDER') || 'anthropic';
console.log('Providers configured:');
console.log(` ${hasAnthropic ? '✅' : '❌'} Anthropic (Claude)`);
console.log(` ${hasOpenRouter ? '✅' : '❌'} OpenRouter (Alternative models)`);
console.log(` ${hasGemini ? '✅' : '❌'} Gemini (Google AI)`);
console.log(` ⚙️ ONNX (Local inference) - always available`);
console.log('');
console.log(`Default provider: ${provider}`);
console.log('');
if (!hasAnthropic && !hasOpenRouter && !hasGemini) {
console.log('⚠️ Warning: No API keys configured!');
console.log(' You can use ONNX local inference, but quality may be limited.');
console.log(' Run with --provider onnx to use local inference.\n');
}
console.log('Next steps:');
console.log(' npx agentic-flow --list # List available agents');
console.log(' npx agentic-flow --agent coder --task "Your task" # Run agent');
console.log('');
}
// Reset to defaults
reset() {
console.log('⚠️ Resetting configuration to defaults...');
this.currentConfig.clear();
CONFIG_DEFINITIONS.forEach(def => {
if (def.value) {
this.currentConfig.set(def.key, def.value);
}
});
this.saveConfig();
console.log('✅ Configuration reset to defaults');
}
}
// CLI handler
export async function handleConfigCommand(args) {
const command = args[0];
const wizard = new ConfigWizard();
switch (command) {
case undefined:
case 'wizard':
case 'interactive':
await wizard.runInteractive();
break;
case 'set':
if (args.length < 3) {
console.error('Usage: config set <KEY> <VALUE>');
process.exit(1);
}
wizard.set(args[1], args[2]);
break;
case 'get':
if (args.length < 2) {
console.error('Usage: config get <KEY>');
process.exit(1);
}
wizard.get(args[1]);
break;
case 'delete':
case 'remove':
if (args.length < 2) {
console.error('Usage: config delete <KEY>');
process.exit(1);
}
wizard.delete(args[1]);
break;
case 'list':
wizard.list();
break;
case 'reset':
wizard.reset();
break;
case 'help':
printConfigHelp();
break;
default:
console.error(`Unknown config command: ${command}`);
printConfigHelp();
process.exit(1);
}
}
function printConfigHelp() {
console.log(`
🤖 Agentic Flow Configuration Manager
USAGE:
npx agentic-flow config [COMMAND] [OPTIONS]
COMMANDS:
(none), wizard, interactive Launch interactive configuration wizard
set <KEY> <VALUE> Set a configuration value
get <KEY> Get a configuration value
delete <KEY> Delete a configuration value
list List all configuration values
reset Reset to default configuration
help Show this help message
EXAMPLES:
# Interactive wizard
npx agentic-flow config
# Direct commands
npx agentic-flow config set ANTHROPIC_API_KEY sk-ant-xxxxx
npx agentic-flow config set PROVIDER openrouter
npx agentic-flow config get PROVIDER
npx agentic-flow config list
npx agentic-flow config delete OPENROUTER_API_KEY
AVAILABLE CONFIGURATION KEYS:
ANTHROPIC_API_KEY - Anthropic API key (sk-ant-...)
OPENROUTER_API_KEY - OpenRouter API key (sk-or-v1-...)
GOOGLE_GEMINI_API_KEY - Google Gemini API key
COMPLETION_MODEL - Default model name
PROVIDER - Default provider (anthropic, openrouter, gemini, onnx)
AGENTS_DIR - Custom agents directory
PROXY_PORT - Proxy server port (default: 3000)
USE_OPENROUTER - Force OpenRouter (true/false)
USE_ONNX - Use ONNX local inference (true/false)
For more information: https://github.com/ruvnet/agentic-flow
`);
}
// If run directly
if (import.meta.url === `file://${process.argv[1]}`) {
const args = process.argv.slice(2);
handleConfigCommand(args).catch(err => {
console.error(err.message);
process.exit(1);
});
}
//# sourceMappingURL=config-wizard.js.map