navada-edge-cli 2.1.0 → 2.1.1

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/lib/agent.js CHANGED
@@ -98,10 +98,19 @@ const localTools = {
98
98
  // Anthropic Claude API — conversational agent
99
99
  // ---------------------------------------------------------------------------
100
100
  async function chat(userMessage, conversationHistory = []) {
101
- const anthropicKey = config.get('anthropicKey') || process.env.ANTHROPIC_API_KEY || '';
101
+ // Smart key detection check all possible locations
102
+ const anthropicKey = config.get('anthropicKey')
103
+ || config.getApiKey() // /setup saves here
104
+ || process.env.ANTHROPIC_API_KEY
105
+ || '';
102
106
 
103
- if (!anthropicKey) {
104
- // Fall back to other providers
107
+ // Auto-detect: if the key starts with sk-ant, it's Anthropic
108
+ const apiKey = config.getApiKey();
109
+ const effectiveKey = anthropicKey
110
+ || (apiKey && apiKey.startsWith('sk-ant') ? apiKey : '')
111
+ || '';
112
+
113
+ if (!effectiveKey) {
105
114
  return fallbackChat(userMessage);
106
115
  }
107
116
 
@@ -179,7 +188,7 @@ async function chat(userMessage, conversationHistory = []) {
179
188
  ];
180
189
 
181
190
  // Call Anthropic API
182
- let response = await callAnthropic(anthropicKey, messages, tools);
191
+ let response = await callAnthropic(effectiveKey, messages, tools);
183
192
 
184
193
  // Handle tool use loop
185
194
  let iterations = 0;
@@ -265,17 +274,30 @@ async function executeTool(name, input) {
265
274
  }
266
275
 
267
276
  async function fallbackChat(msg) {
268
- // Try MCP → Qwen → OpenAI → error
277
+ // Try MCP → Qwen → OpenAI → helpful error
269
278
  if (navada.config.mcp) {
270
- try { return JSON.stringify(await navada.mcp.call('chat', { message: msg })); } catch {}
279
+ try {
280
+ const r = await navada.mcp.call('chat', { message: msg });
281
+ return typeof r === 'object' ? JSON.stringify(r) : r;
282
+ } catch {}
271
283
  }
272
284
  if (navada.config.hfToken) {
273
- try { return await navada.ai.huggingface.qwen(msg); } catch {}
285
+ try {
286
+ const r = await navada.ai.huggingface.qwen(msg);
287
+ return typeof r === 'object' ? JSON.stringify(r) : r;
288
+ } catch {}
274
289
  }
275
290
  if (navada.config.openaiKey) {
276
291
  try { return await navada.ai.openai.chat(msg); } catch {}
277
292
  }
278
- return 'No AI provider configured. Run /setup or set ANTHROPIC_API_KEY.';
293
+ return `I need an API key to chat. Quick fix:
294
+
295
+ /login sk-ant-your-anthropic-key (recommended — enables full agent with tool use)
296
+ /login sk-your-openai-key (GPT-4o)
297
+ /init hfToken hf_your_token (Qwen Coder — FREE)
298
+ /setup (guided wizard)
299
+
300
+ /commands still work without a key — try /help`;
279
301
  }
280
302
 
281
303
  // ---------------------------------------------------------------------------
package/lib/cli.js CHANGED
@@ -85,17 +85,21 @@ async function run(argv) {
85
85
  applyConfig();
86
86
 
87
87
  if (argv.length === 0) {
88
- // Check first run
89
- if (config.isFirstRun()) {
90
- reportTelemetry('install');
91
- const { runSetup } = require('./commands/setup');
92
- await runSetup();
93
- return;
94
- }
95
- // Report session start
96
- reportTelemetry('session_start');
97
- // Interactive mode
88
+ // Report telemetry
89
+ if (config.isFirstRun()) reportTelemetry('install');
90
+ else reportTelemetry('session_start');
91
+
92
+ // Interactive mode — always start. User can /setup if needed.
98
93
  showWelcome();
94
+
95
+ // Hint if no key configured
96
+ const hasKey = config.getApiKey() || config.get('anthropicKey') || process.env.ANTHROPIC_API_KEY;
97
+ if (!hasKey) {
98
+ console.log(ui.warn('No API key set. Type /login <key> or /setup to configure.'));
99
+ console.log(ui.dim('You can still use /commands. Natural chat requires an API key.'));
100
+ console.log('');
101
+ }
102
+
99
103
  startRepl();
100
104
  } else if (argv[0] === '--version' || argv[0] === '-v') {
101
105
  const pkg = require('../package.json');
@@ -9,128 +9,97 @@ function ask(rl, question) {
9
9
  return new Promise(resolve => rl.question(question, resolve));
10
10
  }
11
11
 
12
- async function runSetup() {
13
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
12
+ async function runSetup(fromRepl = false) {
13
+ // Create a fresh readline don't conflict with the REPL
14
+ const rl = readline.createInterface({
15
+ input: process.stdin,
16
+ output: process.stdout,
17
+ terminal: true,
18
+ });
14
19
 
15
- console.log(ui.banner());
16
- console.log(ui.box('WELCOME', ' First-time setup. Let\'s connect you to the NAVADA Edge Network.'));
17
20
  console.log('');
18
-
19
- // 1. API Key
20
- const apiKey = await ask(rl, ' Enter your NAVADA Edge API key (or press Enter for FREE tier): ');
21
- if (apiKey.trim()) {
22
- config.setApiKey(apiKey.trim());
23
- navada.init({ mcpApiKey: apiKey.trim(), dashboardApiKey: apiKey.trim() });
24
- console.log(ui.success('API key saved'));
25
- } else {
26
- console.log(ui.dim('Continuing with FREE tier'));
27
- }
21
+ console.log(ui.box('SETUP', ' Configure your NAVADA Edge CLI.'));
28
22
  console.log('');
29
23
 
30
- // 2. Auto-detect network
31
- console.log(ui.dim('Scanning for NAVADA Edge nodes...'));
32
- // Ask for primary node IP to scan from
33
- const scanIp = await ask(rl, ' Enter your primary node IP to scan (or press Enter to skip): ');
34
- const defaultIps = {};
35
- if (scanIp.trim()) {
36
- defaultIps.asus = scanIp.trim();
37
- }
24
+ // 1. API Key — the only required thing
25
+ console.log(ui.dim(' Your API key powers the AI agent. Accepts:'));
26
+ console.log(ui.dim(' - Anthropic key (sk-ant-...)'));
27
+ console.log(ui.dim(' - NAVADA Edge API key (nv_edge_...)'));
28
+ console.log(ui.dim(' - OpenAI key (sk-...)'));
29
+ console.log(ui.dim(' - Or press Enter to skip (limited mode)'));
30
+ console.log('');
38
31
 
39
- let detected = {};
40
- for (const [node, ip] of Object.entries(defaultIps)) {
41
- try {
42
- const port = node === 'oracle' ? 9000 : 3200;
43
- await navada.request(`http://${ip}:${port}/`, { timeout: 3000 });
44
- detected[node] = ip;
45
- console.log(ui.online(node.toUpperCase(), true, ip));
46
- } catch {
47
- console.log(ui.online(node.toUpperCase(), false, 'not found'));
32
+ const apiKey = await ask(rl, ' API key: ');
33
+ if (apiKey.trim()) {
34
+ const key = apiKey.trim();
35
+ config.setApiKey(key);
36
+ // Auto-detect key type and save to right field
37
+ if (key.startsWith('sk-ant')) {
38
+ config.set('anthropicKey', key);
39
+ console.log(ui.success('Anthropic key saved — full agent mode enabled'));
40
+ } else if (key.startsWith('nv_edge')) {
41
+ console.log(ui.success('NAVADA Edge key saved — MCP access enabled'));
42
+ } else if (key.startsWith('sk-')) {
43
+ config.set('openaiKey', key);
44
+ navada.init({ openaiKey: key });
45
+ console.log(ui.success('OpenAI key saved'));
46
+ } else {
47
+ console.log(ui.success('Key saved'));
48
48
  }
49
+ } else {
50
+ console.log(ui.dim('Skipped — you can set this later with /login <key>'));
49
51
  }
50
52
  console.log('');
51
53
 
52
- // 3. Confirm or enter manually
53
- if (Object.keys(detected).length > 0) {
54
- const useDetected = await ask(rl, ' Use detected nodes? (Y/n): ');
55
- if (useDetected.toLowerCase() !== 'n') {
56
- for (const [node, ip] of Object.entries(detected)) {
57
- config.set(node, ip);
58
- }
59
- // Auto-derive service URLs from ASUS
60
- if (detected.asus) {
61
- config.set('mcp', `http://${detected.asus}:8811`);
62
- config.set('dashboard', `http://${detected.asus}:7900`);
63
- config.set('registry', `http://${detected.asus}:5000`);
64
- navada.init({
65
- asus: detected.asus, hp: detected.hp || '', ec2: detected.ec2 || '', oracle: detected.oracle || '',
66
- mcp: `http://${detected.asus}:8811`, dashboard: `http://${detected.asus}:7900`, registry: `http://${detected.asus}:5000`,
67
- });
68
- }
69
- if (detected.ec2) {
70
- config.set('lucas', `http://${detected.ec2}:8820`);
71
- navada.init({ lucas: `http://${detected.ec2}:8820` });
72
- }
73
- console.log(ui.success('Nodes configured'));
74
- }
75
- } else {
76
- console.log(ui.dim('No nodes auto-detected. Enter manually:'));
54
+ // 2. Node IPs optional
55
+ const configureNodes = await ask(rl, ' Configure network nodes? (y/N): ');
56
+ if (configureNodes.trim().toLowerCase() === 'y') {
57
+ console.log('');
58
+ console.log(ui.dim(' Enter Tailscale IPs for your NAVADA Edge nodes.'));
59
+ console.log(ui.dim(' Press Enter to skip any node.'));
60
+ console.log('');
61
+
77
62
  const asusIp = await ask(rl, ' ASUS IP (Production Engine): ');
78
63
  if (asusIp.trim()) {
79
64
  config.set('asus', asusIp.trim());
80
65
  config.set('mcp', `http://${asusIp.trim()}:8811`);
81
66
  config.set('dashboard', `http://${asusIp.trim()}:7900`);
82
67
  config.set('registry', `http://${asusIp.trim()}:5000`);
83
- navada.init({ asus: asusIp.trim(), mcp: `http://${asusIp.trim()}:8811`, dashboard: `http://${asusIp.trim()}:7900`, registry: `http://${asusIp.trim()}:5000` });
68
+ navada.init({
69
+ asus: asusIp.trim(),
70
+ mcp: `http://${asusIp.trim()}:8811`,
71
+ dashboard: `http://${asusIp.trim()}:7900`,
72
+ registry: `http://${asusIp.trim()}:5000`,
73
+ });
84
74
  }
85
- const hpIp = await ask(rl, ' HP IP (Database, press Enter to skip): ');
75
+ const hpIp = await ask(rl, ' HP IP (Database): ');
86
76
  if (hpIp.trim()) config.set('hp', hpIp.trim());
87
- const ec2Ip = await ask(rl, ' EC2 IP (Monitoring, press Enter to skip): ');
77
+ const ec2Ip = await ask(rl, ' EC2 IP (Monitoring): ');
88
78
  if (ec2Ip.trim()) {
89
79
  config.set('ec2', ec2Ip.trim());
90
80
  config.set('lucas', `http://${ec2Ip.trim()}:8820`);
91
81
  }
92
- const oracleIp = await ask(rl, ' Oracle IP (Docker Infra, press Enter to skip): ');
82
+ const oracleIp = await ask(rl, ' Oracle IP (Infrastructure): ');
93
83
  if (oracleIp.trim()) config.set('oracle', oracleIp.trim());
84
+ console.log(ui.success('Nodes configured'));
94
85
  }
95
86
  console.log('');
96
87
 
97
- // 4. Choose theme
98
- const theme = await ask(rl, ' Choose theme (dark/crow/matrix/light) [dark]: ');
88
+ // 3. Theme
89
+ const theme = await ask(rl, ' Theme (dark/crow/matrix/light) [dark]: ');
99
90
  config.setTheme(theme.trim() || 'dark');
100
- console.log(ui.success(`Theme: ${config.getTheme()}`));
101
- console.log('');
102
-
103
- // 5. Test connections
104
- console.log(ui.dim('Testing connections...'));
105
- const tests = [
106
- { name: 'MCP', url: navada.config.mcp ? navada.config.mcp + '/health' : '' },
107
- { name: 'Dashboard', url: navada.config.dashboard || '' },
108
- { name: 'Registry', url: navada.config.registry ? navada.config.registry + '/v2/_catalog' : '' },
109
- ];
110
- let pass = 0;
111
- for (const t of tests) {
112
- if (!t.url) { console.log(ui.online(t.name, false, 'not configured')); continue; }
113
- try {
114
- const r = await navada.request(t.url, { timeout: 5000 });
115
- console.log(ui.online(t.name, r.status < 500));
116
- pass++;
117
- } catch {
118
- console.log(ui.online(t.name, false, 'unreachable'));
119
- }
120
- }
121
91
  console.log('');
122
92
 
123
- // 6. Summary
124
- console.log(ui.box('SETUP COMPLETE', ` ${pass}/${tests.length} services connected\n Theme: ${config.getTheme()}\n Tier: ${config.getTier()}\n Config: ${config.CONFIG_FILE}`));
93
+ // 4. Done
94
+ console.log(ui.box('READY', ` Config saved: ${config.CONFIG_FILE}\n Type naturally to chat, or /help for commands.`));
125
95
  console.log('');
126
- console.log(ui.dim('Run `navada` to start, or `navada doctor` to test all connections.'));
127
96
 
128
97
  rl.close();
129
98
  }
130
99
 
131
100
  module.exports = function(reg) {
132
- reg('setup', 'Run onboarding wizard', async () => {
133
- await runSetup();
101
+ reg('setup', 'Run setup wizard', async () => {
102
+ await runSetup(true);
134
103
  }, { category: 'SYSTEM' });
135
104
  };
136
105
 
@@ -65,11 +65,32 @@ module.exports = function(reg) {
65
65
  }, { category: 'SYSTEM' });
66
66
 
67
67
  // --- /login ---
68
- reg('login', 'Set API key', (args) => {
69
- if (!args[0]) { console.log(ui.dim('Usage: /login <api-key>')); return; }
70
- config.setApiKey(args[0]);
71
- navada.init({ mcpApiKey: args[0], dashboardApiKey: args[0] });
72
- console.log(ui.success(`API key saved to ${config.CONFIG_FILE}`));
68
+ reg('login', 'Set API key (Anthropic, OpenAI, NAVADA Edge, or HuggingFace)', (args) => {
69
+ if (!args[0]) {
70
+ console.log(ui.dim('Usage: /login <api-key>'));
71
+ console.log(ui.dim('Accepts: sk-ant-... (Anthropic) | sk-... (OpenAI) | nv_edge_... (NAVADA) | hf_... (HuggingFace)'));
72
+ return;
73
+ }
74
+ const key = args[0].trim();
75
+ config.setApiKey(key);
76
+
77
+ if (key.startsWith('sk-ant')) {
78
+ config.set('anthropicKey', key);
79
+ console.log(ui.success('Anthropic key saved — full agent mode with tool use enabled'));
80
+ } else if (key.startsWith('hf_')) {
81
+ config.set('hfToken', key);
82
+ navada.init({ hfToken: key });
83
+ console.log(ui.success('HuggingFace key saved — Qwen Coder (FREE) enabled'));
84
+ } else if (key.startsWith('nv_edge')) {
85
+ navada.init({ mcpApiKey: key, dashboardApiKey: key });
86
+ console.log(ui.success('NAVADA Edge key saved — MCP + Dashboard access enabled'));
87
+ } else if (key.startsWith('sk-')) {
88
+ config.set('openaiKey', key);
89
+ navada.init({ openaiKey: key });
90
+ console.log(ui.success('OpenAI key saved — GPT-4o enabled'));
91
+ } else {
92
+ console.log(ui.success('Key saved'));
93
+ }
73
94
  }, { category: 'SYSTEM' });
74
95
 
75
96
  // --- /init ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "navada-edge-cli",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Interactive CLI for the NAVADA Edge Network — explore nodes, agents, Cloudflare, AI, Docker, and MCP from your terminal",
5
5
  "main": "lib/cli.js",
6
6
  "bin": {