navada-edge-cli 4.0.0 → 4.2.0
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/README.md +297 -523
- package/lib/agent.js +392 -284
- package/lib/commands/ai.js +8 -9
- package/lib/commands/audit.js +1 -1
- package/lib/commands/compute.js +144 -165
- package/lib/commands/edge.js +139 -14
- package/lib/commands/index.js +1 -1
- package/lib/commands/lucas.js +6 -34
- package/lib/commands/mcp.js +6 -29
- package/lib/commands/nvidia.js +4 -4
- package/lib/commands/setup.js +271 -59
- package/lib/commands/skills.js +209 -0
- package/lib/commands/system.js +173 -0
- package/lib/memory.js +432 -0
- package/lib/skills.js +222 -0
- package/package.json +14 -12
- package/lib/commands/files.js +0 -164
- package/lib/knowledge.py +0 -197
package/lib/commands/ai.js
CHANGED
|
@@ -13,12 +13,11 @@ module.exports = function(reg) {
|
|
|
13
13
|
|
|
14
14
|
const hasKey = config.getApiKey() || config.get('anthropicKey') || process.env.ANTHROPIC_API_KEY;
|
|
15
15
|
|
|
16
|
-
//
|
|
17
|
-
sessionState._lastStreamed = false;
|
|
18
|
-
|
|
19
|
-
// Show thinking indicator
|
|
16
|
+
// Show a brief "thinking" indicator, then clear it when streaming starts
|
|
20
17
|
let spinner;
|
|
21
|
-
if (hasKey) {
|
|
18
|
+
if (!hasKey) {
|
|
19
|
+
process.stdout.write(ui.dim(' NAVADA > '));
|
|
20
|
+
} else {
|
|
22
21
|
const ora = require('ora');
|
|
23
22
|
spinner = ora({ text: ' NAVADA thinking...', color: 'white' }).start();
|
|
24
23
|
}
|
|
@@ -32,9 +31,9 @@ module.exports = function(reg) {
|
|
|
32
31
|
addToHistory('user', msg);
|
|
33
32
|
addToHistory('assistant', response);
|
|
34
33
|
|
|
35
|
-
// Only print if not already streamed
|
|
36
|
-
if (response
|
|
37
|
-
console.log('');
|
|
34
|
+
// Only print if not already streamed
|
|
35
|
+
if (!response._streamed) {
|
|
36
|
+
console.log(ui.header('NAVADA'));
|
|
38
37
|
console.log(` ${response}`);
|
|
39
38
|
}
|
|
40
39
|
|
|
@@ -43,7 +42,7 @@ module.exports = function(reg) {
|
|
|
43
42
|
} catch (e) {
|
|
44
43
|
if (spinner) spinner.stop();
|
|
45
44
|
console.log(ui.error(e.message));
|
|
46
|
-
console.log(ui.dim('/config to
|
|
45
|
+
console.log(ui.dim('Check: /config to see which providers are set, or /setup to configure.'));
|
|
47
46
|
}
|
|
48
47
|
}, { category: 'AI', aliases: ['ask'] });
|
|
49
48
|
|
package/lib/commands/audit.js
CHANGED
|
@@ -172,7 +172,7 @@ CURRENT STATE (March 2026):
|
|
|
172
172
|
- Azure Key Vault: navada-edge-vault stores all secrets, user keys synced on create/revoke
|
|
173
173
|
|
|
174
174
|
NOT YET BUILT:
|
|
175
|
-
-
|
|
175
|
+
- Automation queue API (/automate, /requests)
|
|
176
176
|
- Agent customisation (agent.md, sub-agents)
|
|
177
177
|
- Azure compute node
|
|
178
178
|
- Billing/metering
|
package/lib/commands/compute.js
CHANGED
|
@@ -5,31 +5,32 @@ const ui = require('../ui');
|
|
|
5
5
|
const config = require('../config');
|
|
6
6
|
const https = require('https');
|
|
7
7
|
const http = require('http');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const crypto = require('crypto');
|
|
8
11
|
|
|
9
|
-
const
|
|
12
|
+
const QUEUE_ENDPOINT = 'https://api.navada-edge-server.uk';
|
|
10
13
|
|
|
11
14
|
function getEdgeKey() {
|
|
12
15
|
return config.get('edgeKey') || '';
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
function
|
|
18
|
+
async function submitRequest(body) {
|
|
16
19
|
const key = getEdgeKey();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const headers = {
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
'X-Request-ID': `nv_${crypto.randomUUID()}`,
|
|
23
|
+
'X-Client-Version': require('../package.json').version,
|
|
24
|
+
};
|
|
25
|
+
if (key) headers['Authorization'] = `Bearer ${key}`;
|
|
22
26
|
|
|
23
27
|
return new Promise((resolve, reject) => {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
},
|
|
31
|
-
timeout: 30000,
|
|
32
|
-
}, (res) => {
|
|
28
|
+
const url = new URL(QUEUE_ENDPOINT + '/api/v1/queue/automation');
|
|
29
|
+
const transport = url.protocol === 'https:' ? https : http;
|
|
30
|
+
const payload = JSON.stringify(body);
|
|
31
|
+
headers['Content-Length'] = Buffer.byteLength(payload);
|
|
32
|
+
|
|
33
|
+
const req = transport.request(url, { method: 'POST', headers, timeout: 15000 }, (res) => {
|
|
33
34
|
let data = '';
|
|
34
35
|
res.on('data', c => data += c);
|
|
35
36
|
res.on('end', () => {
|
|
@@ -39,187 +40,165 @@ function request(method, path, body) {
|
|
|
39
40
|
});
|
|
40
41
|
req.on('error', reject);
|
|
41
42
|
req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
|
|
42
|
-
|
|
43
|
+
req.write(payload);
|
|
43
44
|
req.end();
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
module.exports = function(reg) {
|
|
48
49
|
|
|
49
|
-
// ── /
|
|
50
|
-
reg('
|
|
50
|
+
// ── /automate ── Submit automation request
|
|
51
|
+
reg('automate', 'Submit an automation request to NAVADA Edge', async (args) => {
|
|
51
52
|
if (!args.length) {
|
|
52
|
-
console.log(ui.header('EDGE
|
|
53
|
-
console.log(ui.
|
|
54
|
-
console.log(
|
|
55
|
-
console.log(ui.cmd('
|
|
56
|
-
console.log(ui.cmd('
|
|
53
|
+
console.log(ui.header('NAVADA EDGE — AUTOMATION'));
|
|
54
|
+
console.log(ui.dim(' Submit a request and we\'ll set it up for you on 24/7 cloud.'));
|
|
55
|
+
console.log('');
|
|
56
|
+
console.log(ui.cmd('automate <description>', 'Quick automation request'));
|
|
57
|
+
console.log(ui.cmd('automate --type email', 'Specify type'));
|
|
58
|
+
console.log(ui.cmd('automate --schedule weekly', 'Specify schedule'));
|
|
59
|
+
console.log('');
|
|
60
|
+
console.log(ui.dim('Types: email, marketing, build, data, schedule, custom'));
|
|
61
|
+
console.log(ui.dim('Schedules: daily, weekly, monthly, cron, one-time'));
|
|
57
62
|
console.log('');
|
|
58
63
|
console.log(ui.dim('Examples:'));
|
|
59
|
-
console.log(ui.dim(' /
|
|
60
|
-
console.log(ui.dim(' /
|
|
61
|
-
console.log(ui.dim(' /
|
|
64
|
+
console.log(ui.dim(' /automate Send weekly newsletter to my leads'));
|
|
65
|
+
console.log(ui.dim(' /automate --type build Deploy my React app nightly'));
|
|
66
|
+
console.log(ui.dim(' /automate --type data --schedule daily Scrape competitor prices'));
|
|
62
67
|
console.log('');
|
|
63
|
-
console.log(ui.dim('
|
|
68
|
+
console.log(ui.dim('Or describe what you want to the AI agent — it will submit for you.'));
|
|
64
69
|
return;
|
|
65
70
|
}
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
let filteredArgs = [...args];
|
|
72
|
+
// Parse flags
|
|
73
|
+
let type = 'custom';
|
|
74
|
+
let schedule = 'on-demand';
|
|
75
|
+
const filtered = [];
|
|
72
76
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
filteredArgs = filteredArgs.filter(a => a !== '--python' && a !== '--js');
|
|
78
|
-
|
|
79
|
-
const body = {};
|
|
80
|
-
if (isPython) {
|
|
81
|
-
body.code = filteredArgs.join(' ');
|
|
82
|
-
body.language = 'python';
|
|
83
|
-
} else if (isJs) {
|
|
84
|
-
body.code = filteredArgs.join(' ');
|
|
85
|
-
body.language = 'javascript';
|
|
86
|
-
} else {
|
|
87
|
-
body.command = filteredArgs.join(' ');
|
|
77
|
+
for (let i = 0; i < args.length; i++) {
|
|
78
|
+
if (args[i] === '--type' && args[i + 1]) { type = args[++i]; }
|
|
79
|
+
else if (args[i] === '--schedule' && args[i + 1]) { schedule = args[++i]; }
|
|
80
|
+
else { filtered.push(args[i]); }
|
|
88
81
|
}
|
|
89
|
-
|
|
82
|
+
|
|
83
|
+
const description = filtered.join(' ');
|
|
84
|
+
if (!description) { console.log(ui.error('Please describe what you want automated.')); return; }
|
|
85
|
+
|
|
86
|
+
const requestId = `req_${crypto.randomUUID().slice(0, 8)}`;
|
|
87
|
+
const userId = config.get('edgeUserId') || 'anonymous';
|
|
88
|
+
const email = config.get('edgeEmail') || '';
|
|
89
|
+
const name = config.get('edgeName') || '';
|
|
90
|
+
|
|
91
|
+
const request = {
|
|
92
|
+
id: requestId,
|
|
93
|
+
title: description.slice(0, 80),
|
|
94
|
+
description,
|
|
95
|
+
type,
|
|
96
|
+
schedule,
|
|
97
|
+
userId,
|
|
98
|
+
email,
|
|
99
|
+
name,
|
|
100
|
+
status: 'pending',
|
|
101
|
+
submittedAt: new Date().toISOString(),
|
|
102
|
+
};
|
|
90
103
|
|
|
91
104
|
const ora = require('ora');
|
|
92
|
-
const spinner = ora({ text: '
|
|
105
|
+
const spinner = ora({ text: ' Submitting automation request...', color: 'white' }).start();
|
|
93
106
|
|
|
94
107
|
try {
|
|
95
|
-
const r = await request
|
|
108
|
+
const r = await submitRequest(request);
|
|
96
109
|
spinner.stop();
|
|
97
110
|
|
|
98
|
-
if (r.status === 201) {
|
|
99
|
-
console.log(ui.success(
|
|
100
|
-
console.log(
|
|
101
|
-
console.log(ui.label('
|
|
102
|
-
console.log(ui.label('
|
|
111
|
+
if (r.status === 201 || r.status === 200) {
|
|
112
|
+
console.log(ui.success('Automation request submitted!'));
|
|
113
|
+
console.log('');
|
|
114
|
+
console.log(ui.label('Request ID', requestId));
|
|
115
|
+
console.log(ui.label('Title', request.title));
|
|
116
|
+
console.log(ui.label('Type', type));
|
|
117
|
+
console.log(ui.label('Schedule', schedule));
|
|
118
|
+
console.log(ui.label('Status', 'Pending review'));
|
|
103
119
|
console.log('');
|
|
104
|
-
console.log(ui.dim(
|
|
105
|
-
console.log(ui.dim(
|
|
106
|
-
} else if (r.status === 401) {
|
|
107
|
-
console.log(ui.error('Invalid API key. Run /edge login <key>'));
|
|
108
|
-
} else if (r.status === 429) {
|
|
109
|
-
console.log(ui.warn(r.data.error || 'Concurrent task limit reached'));
|
|
120
|
+
console.log(ui.dim('You\'ll receive an email once your automation is set up.'));
|
|
121
|
+
console.log(ui.dim('Track: /requests'));
|
|
110
122
|
} else {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
} catch (e) {
|
|
114
|
-
spinner.stop();
|
|
115
|
-
console.log(ui.error(`Edge Compute unreachable: ${e.message}`));
|
|
116
|
-
console.log(ui.dim('The Edge Network may be offline. Try /doctor'));
|
|
117
|
-
}
|
|
118
|
-
}, { category: 'EDGE' });
|
|
119
|
-
|
|
120
|
-
// ── /sessions ── List cloud sessions
|
|
121
|
-
reg('sessions', 'View Edge Network task sessions', async () => {
|
|
122
|
-
const ora = require('ora');
|
|
123
|
-
const spinner = ora({ text: ' Loading sessions...', color: 'white' }).start();
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
const r = await request('GET', '/sessions');
|
|
127
|
-
spinner.stop();
|
|
128
|
-
|
|
129
|
-
if (r.status === 401) { console.log(ui.error('Not connected. /edge login <key>')); return; }
|
|
130
|
-
if (r.status !== 200) { console.log(ui.error(r.data.error || `HTTP ${r.status}`)); return; }
|
|
131
|
-
|
|
132
|
-
console.log(ui.header('EDGE SESSIONS'));
|
|
133
|
-
const sessions = r.data.sessions || [];
|
|
134
|
-
|
|
135
|
-
if (sessions.length === 0) {
|
|
136
|
-
console.log(ui.dim('No sessions yet. /offload <command> to start one.'));
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
for (const s of sessions) {
|
|
141
|
-
const statusColor = s.status === 'completed' ? '\x1b[32m' : s.status === 'running' ? '\x1b[33m' : '\x1b[31m';
|
|
142
|
-
const statusLabel = `${statusColor}${s.status.toUpperCase()}\x1b[0m`;
|
|
143
|
-
const age = s.completedAt ? timeSince(s.completedAt) : timeSince(s.createdAt);
|
|
144
|
-
console.log(` ${statusLabel} ${s.id.slice(0, 16)} ${(s.task || '').padEnd(20)} ${age}`);
|
|
123
|
+
spinner.stop();
|
|
124
|
+
console.log(ui.error(r.data?.error || `HTTP ${r.status}`));
|
|
145
125
|
}
|
|
146
|
-
|
|
147
|
-
console.log('');
|
|
148
|
-
console.log(ui.label('Total', String(r.data.total)));
|
|
149
|
-
console.log(ui.label('Limits', `${r.data.limits.concurrent} concurrent, ${Math.round(r.data.limits.maxRuntime / 60000)}min max`));
|
|
150
|
-
console.log('');
|
|
151
|
-
console.log(ui.dim('Details: /attach <session-id>'));
|
|
152
126
|
} catch (e) {
|
|
153
127
|
spinner.stop();
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
console.log(ui.dim('
|
|
163
|
-
console.log(ui.dim('List sessions: /sessions'));
|
|
164
|
-
return;
|
|
128
|
+
// Save locally if API unavailable
|
|
129
|
+
const reqDir = path.join(config.CONFIG_DIR, 'requests');
|
|
130
|
+
if (!fs.existsSync(reqDir)) fs.mkdirSync(reqDir, { recursive: true });
|
|
131
|
+
fs.writeFileSync(path.join(reqDir, `${requestId}.json`), JSON.stringify(request, null, 2));
|
|
132
|
+
|
|
133
|
+
console.log(ui.warn('Could not reach NAVADA Edge — request saved locally.'));
|
|
134
|
+
console.log(ui.label('Request ID', requestId));
|
|
135
|
+
console.log(ui.dim('It will sync automatically when connected.'));
|
|
136
|
+
console.log(ui.dim('Track: /requests'));
|
|
165
137
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
138
|
+
}, { category: 'AUTOMATION', aliases: ['auto'] });
|
|
139
|
+
|
|
140
|
+
// ── /requests ── View automation requests
|
|
141
|
+
reg('requests', 'View your automation requests', async (args) => {
|
|
142
|
+
const key = getEdgeKey();
|
|
143
|
+
|
|
144
|
+
// Try API first
|
|
145
|
+
if (key) {
|
|
146
|
+
try {
|
|
147
|
+
const headers = {
|
|
148
|
+
'Content-Type': 'application/json',
|
|
149
|
+
'Authorization': `Bearer ${key}`,
|
|
150
|
+
};
|
|
151
|
+
const url = new URL(QUEUE_ENDPOINT + '/api/v1/queue/requests');
|
|
152
|
+
const transport = url.protocol === 'https:' ? https : http;
|
|
153
|
+
|
|
154
|
+
const r = await new Promise((resolve, reject) => {
|
|
155
|
+
const req = transport.request(url, { method: 'GET', headers, timeout: 10000 }, (res) => {
|
|
156
|
+
let data = '';
|
|
157
|
+
res.on('data', c => data += c);
|
|
158
|
+
res.on('end', () => {
|
|
159
|
+
try { resolve({ status: res.statusCode, data: JSON.parse(data) }); }
|
|
160
|
+
catch { resolve({ status: res.statusCode, data }); }
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
req.on('error', reject);
|
|
164
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
|
|
165
|
+
req.end();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (r.status === 200 && r.data?.requests) {
|
|
169
|
+
console.log(ui.header('AUTOMATION REQUESTS'));
|
|
170
|
+
const reqs = r.data.requests;
|
|
171
|
+
if (reqs.length === 0) {
|
|
172
|
+
console.log(ui.dim('No automation requests yet. Submit one: /automate'));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
for (const req of reqs) {
|
|
176
|
+
const statusColor = req.status === 'active' ? '\x1b[32m' : req.status === 'pending' ? '\x1b[33m' : '\x1b[37m';
|
|
177
|
+
console.log(` ${statusColor}${(req.status || '').toUpperCase().padEnd(10)}\x1b[0m ${req.id.padEnd(14)} ${(req.title || '').slice(0, 40)}`);
|
|
178
|
+
}
|
|
179
|
+
console.log('');
|
|
180
|
+
console.log(ui.dim(`${reqs.length} request(s)`));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
} catch {}
|
|
197
184
|
}
|
|
198
|
-
}, { category: 'EDGE' });
|
|
199
|
-
|
|
200
|
-
// ── /kill ── Kill a running session
|
|
201
|
-
reg('kill', 'Kill a running Edge session', async (args) => {
|
|
202
|
-
const id = args[0];
|
|
203
|
-
if (!id) { console.log(ui.dim('Usage: /kill <session-id>')); return; }
|
|
204
|
-
const sessionId = id.startsWith('ses_') ? id : `ses_${id}`;
|
|
205
185
|
|
|
186
|
+
// Fallback: show local requests
|
|
187
|
+
const reqDir = path.join(config.CONFIG_DIR, 'requests');
|
|
188
|
+
console.log(ui.header('AUTOMATION REQUESTS (local)'));
|
|
206
189
|
try {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
190
|
+
if (!fs.existsSync(reqDir)) { console.log(ui.dim('No requests yet. Submit one: /automate')); return; }
|
|
191
|
+
const files = fs.readdirSync(reqDir).filter(f => f.endsWith('.json'));
|
|
192
|
+
if (files.length === 0) { console.log(ui.dim('No requests yet. Submit one: /automate')); return; }
|
|
193
|
+
for (const f of files) {
|
|
194
|
+
const req = JSON.parse(fs.readFileSync(path.join(reqDir, f), 'utf-8'));
|
|
195
|
+
const statusColor = req.status === 'active' ? '\x1b[32m' : req.status === 'pending' ? '\x1b[33m' : '\x1b[37m';
|
|
196
|
+
console.log(` ${statusColor}${(req.status || '').toUpperCase().padEnd(10)}\x1b[0m ${req.id.padEnd(14)} ${(req.title || '').slice(0, 40)}`);
|
|
212
197
|
}
|
|
198
|
+
console.log('');
|
|
199
|
+
console.log(ui.dim(`${files.length} request(s) — saved locally, will sync when connected`));
|
|
213
200
|
} catch (e) {
|
|
214
201
|
console.log(ui.error(e.message));
|
|
215
202
|
}
|
|
216
|
-
}, { category: '
|
|
203
|
+
}, { category: 'AUTOMATION', aliases: ['reqs'] });
|
|
217
204
|
};
|
|
218
|
-
|
|
219
|
-
function timeSince(iso) {
|
|
220
|
-
const ms = Date.now() - new Date(iso).getTime();
|
|
221
|
-
if (ms < 60000) return `${Math.round(ms / 1000)}s ago`;
|
|
222
|
-
if (ms < 3600000) return `${Math.round(ms / 60000)}m ago`;
|
|
223
|
-
if (ms < 86400000) return `${Math.round(ms / 3600000)}h ago`;
|
|
224
|
-
return `${Math.round(ms / 86400000)}d ago`;
|
|
225
|
-
}
|
package/lib/commands/edge.js
CHANGED
|
@@ -13,6 +13,78 @@ const { sessionState, listSubAgents } = require('../agent');
|
|
|
13
13
|
|
|
14
14
|
module.exports = function(reg) {
|
|
15
15
|
|
|
16
|
+
// /register — self-service registration from CLI
|
|
17
|
+
reg('register', 'Create a NAVADA Edge Network account', async () => {
|
|
18
|
+
const readline = require('readline');
|
|
19
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
20
|
+
const ask = (q) => new Promise(resolve => rl.question(q, resolve));
|
|
21
|
+
|
|
22
|
+
console.log(ui.header('NAVADA EDGE — REGISTRATION'));
|
|
23
|
+
console.log(ui.dim(' Create your account to access cloud compute, storage, and AI.'));
|
|
24
|
+
console.log('');
|
|
25
|
+
|
|
26
|
+
const name = await ask(' Name: ');
|
|
27
|
+
const email = await ask(' Email: ');
|
|
28
|
+
|
|
29
|
+
if (!name.trim() || !email.trim()) {
|
|
30
|
+
console.log(ui.error('Name and email are required.'));
|
|
31
|
+
rl.close();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim())) {
|
|
36
|
+
console.log(ui.error('Invalid email format.'));
|
|
37
|
+
rl.close();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const ora = require('ora');
|
|
42
|
+
const spinner = ora({ text: ' Creating account...', color: 'white' }).start();
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const r = await navada.request('https://api.navada-edge-server.uk/api/v1/register', {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
body: { name: name.trim(), email: email.trim() },
|
|
48
|
+
timeout: 15000,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
spinner.stop();
|
|
52
|
+
|
|
53
|
+
if (r.status === 201 && r.data?.key) {
|
|
54
|
+
config.set('edgeKey', r.data.key);
|
|
55
|
+
config.set('edgeTier', r.data.tier || 'free');
|
|
56
|
+
config.set('edgeUserId', r.data.userId || '');
|
|
57
|
+
config.set('edgeName', name.trim());
|
|
58
|
+
config.set('edgeEmail', email.trim());
|
|
59
|
+
config.set('edgeConnected', true);
|
|
60
|
+
|
|
61
|
+
console.log(ui.success('Account created!'));
|
|
62
|
+
console.log('');
|
|
63
|
+
console.log(ui.label('API Key', r.data.key));
|
|
64
|
+
console.log(ui.label('Tier', (r.data.tier || 'FREE').toUpperCase()));
|
|
65
|
+
console.log(ui.label('User ID', r.data.userId || 'assigned'));
|
|
66
|
+
console.log(ui.label('Namespace', r.data.namespace || 'user_' + (r.data.userId || '').slice(0, 8)));
|
|
67
|
+
console.log('');
|
|
68
|
+
console.log(ui.dim('Your key has been saved. You\'re ready to go!'));
|
|
69
|
+
console.log(ui.dim('Try: /automate Send weekly newsletter to my leads'));
|
|
70
|
+
} else if (r.status === 409) {
|
|
71
|
+
spinner.stop();
|
|
72
|
+
console.log(ui.warn('An account with this email already exists.'));
|
|
73
|
+
console.log(ui.dim('Use /edge login <key> if you already have a key.'));
|
|
74
|
+
console.log(ui.dim('Or /onboard to reset your key via the portal.'));
|
|
75
|
+
} else {
|
|
76
|
+
spinner.stop();
|
|
77
|
+
console.log(ui.error(r.data?.error || `Registration failed (HTTP ${r.status})`));
|
|
78
|
+
}
|
|
79
|
+
} catch (e) {
|
|
80
|
+
spinner.stop();
|
|
81
|
+
console.log(ui.error(`Could not reach NAVADA Edge Network: ${e.message}`));
|
|
82
|
+
console.log(ui.dim('Try again later or register at /onboard'));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
rl.close();
|
|
86
|
+
}, { category: 'EDGE' });
|
|
87
|
+
|
|
16
88
|
// /onboard — open portal in browser
|
|
17
89
|
reg('onboard', 'Open NAVADA Edge Portal to create account and get API key', async () => {
|
|
18
90
|
console.log(ui.header('NAVADA EDGE NETWORK — ONBOARDING'));
|
|
@@ -39,7 +111,7 @@ module.exports = function(reg) {
|
|
|
39
111
|
console.log('');
|
|
40
112
|
console.log(ui.dim('After signing up and generating a key, run:'));
|
|
41
113
|
console.log(ui.dim(' /edge login nv_edge_your_key_here'));
|
|
42
|
-
}, { category: 'EDGE', aliases: ['signup'
|
|
114
|
+
}, { category: 'EDGE', aliases: ['signup'] });
|
|
43
115
|
|
|
44
116
|
// /edge — edge network commands
|
|
45
117
|
reg('edge', 'NAVADA Edge Network commands', async (args) => {
|
|
@@ -53,8 +125,10 @@ module.exports = function(reg) {
|
|
|
53
125
|
console.log(ui.cmd('edge status', 'Check your Edge Network connection'));
|
|
54
126
|
console.log(ui.cmd('edge logout', 'Disconnect from Edge Network'));
|
|
55
127
|
console.log(ui.cmd('edge tier', 'Show your current tier and limits'));
|
|
128
|
+
console.log(ui.cmd('edge usage', 'View your usage and limits'));
|
|
56
129
|
console.log(ui.cmd('edge setup', 'Create agent.md and sub-agents directory'));
|
|
57
|
-
console.log(ui.cmd('
|
|
130
|
+
console.log(ui.cmd('register', 'Create account from CLI'));
|
|
131
|
+
console.log(ui.cmd('onboard', 'Create account via portal'));
|
|
58
132
|
console.log('');
|
|
59
133
|
console.log(ui.dim('Get started: /onboard | Customise: /edge setup'));
|
|
60
134
|
return;
|
|
@@ -100,10 +174,10 @@ module.exports = function(reg) {
|
|
|
100
174
|
console.log(ui.label('User', r.data.name || r.data.userId || 'connected'));
|
|
101
175
|
console.log('');
|
|
102
176
|
console.log(ui.dim('You now have access to:'));
|
|
103
|
-
console.log(ui.dim(' /
|
|
104
|
-
console.log(ui.dim(' /
|
|
105
|
-
console.log(ui.dim(' /
|
|
106
|
-
console.log(ui.dim(' /
|
|
177
|
+
console.log(ui.dim(' /automate — submit automation requests'));
|
|
178
|
+
console.log(ui.dim(' /requests — track your requests'));
|
|
179
|
+
console.log(ui.dim(' /tools — available agent tools'));
|
|
180
|
+
console.log(ui.dim(' /skills — what the agent can do'));
|
|
107
181
|
validated = true;
|
|
108
182
|
break;
|
|
109
183
|
} else if (r.status === 401) {
|
|
@@ -170,14 +244,65 @@ module.exports = function(reg) {
|
|
|
170
244
|
console.log(ui.header('EDGE NETWORK TIER'));
|
|
171
245
|
console.log(ui.label('Current', tier));
|
|
172
246
|
console.log('');
|
|
247
|
+
|
|
248
|
+
const tiers = {
|
|
249
|
+
FREE: {
|
|
250
|
+
limits: ['100 requests/day', '10MB cloud storage', '5 min max compute', '3 concurrent tasks', 'Read-only shared datasets'],
|
|
251
|
+
tools: ['navada_compute', 'navada_storage (read)', 'web_search', 'memory tools'],
|
|
252
|
+
},
|
|
253
|
+
PRO: {
|
|
254
|
+
limits: ['1,000 requests/day', '1GB cloud storage', '30 min max compute', '10 concurrent tasks', 'Read/write own namespace'],
|
|
255
|
+
tools: ['All FREE tools', 'navada_storage (write)', 'perception tools', 'priority compute'],
|
|
256
|
+
},
|
|
257
|
+
TEAM: {
|
|
258
|
+
limits: ['Unlimited requests', '10GB shared storage', '60 min max compute', 'Unlimited concurrent', 'Shared team namespace'],
|
|
259
|
+
tools: ['All PRO tools', 'shared namespaces', 'custom agents', 'dedicated compute'],
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const tierInfo = tiers[tier] || tiers.FREE;
|
|
264
|
+
console.log(ui.dim('Limits:'));
|
|
265
|
+
tierInfo.limits.forEach(l => console.log(ui.dim(` - ${l}`)));
|
|
266
|
+
console.log('');
|
|
267
|
+
console.log(ui.dim('Available tools:'));
|
|
268
|
+
tierInfo.tools.forEach(t => console.log(ui.dim(` - ${t}`)));
|
|
269
|
+
console.log('');
|
|
270
|
+
|
|
173
271
|
if (tier === 'FREE') {
|
|
174
|
-
console.log(ui.dim('
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
272
|
+
console.log(ui.dim('Upgrade: https://portal.navada-edge-server.uk/pricing'));
|
|
273
|
+
}
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// /edge usage — show current usage stats
|
|
278
|
+
if (sub === 'usage') {
|
|
279
|
+
const ora = require('ora');
|
|
280
|
+
const spinner = ora({ text: ' Fetching usage...', color: 'white' }).start();
|
|
281
|
+
try {
|
|
282
|
+
const edgeKey = config.get('edgeKey');
|
|
283
|
+
if (!edgeKey) { spinner.stop(); console.log(ui.error('Not connected. /edge login or /register')); return; }
|
|
284
|
+
|
|
285
|
+
const r = await navada.request('https://api.navada-edge-server.uk/api/v1/usage', {
|
|
286
|
+
method: 'GET',
|
|
287
|
+
headers: { 'Authorization': `Bearer ${edgeKey}` },
|
|
288
|
+
timeout: 10000,
|
|
289
|
+
});
|
|
290
|
+
spinner.stop();
|
|
291
|
+
|
|
292
|
+
if (r.status === 200) {
|
|
293
|
+
const u = r.data;
|
|
294
|
+
console.log(ui.header('EDGE USAGE'));
|
|
295
|
+
console.log(ui.label('Requests today', `${u.requestsToday || 0} / ${u.requestLimit || 100}`));
|
|
296
|
+
console.log(ui.label('Storage used', `${u.storageMB || 0} MB / ${u.storageLimit || 10} MB`));
|
|
297
|
+
console.log(ui.label('Compute tasks', `${u.tasksToday || 0} / ${u.taskLimit || 3}`));
|
|
298
|
+
console.log(ui.label('Namespace', u.namespace || 'user_' + (config.get('edgeUserId') || 'unknown')));
|
|
299
|
+
console.log(ui.label('Tier', (config.get('edgeTier') || 'FREE').toUpperCase()));
|
|
300
|
+
} else {
|
|
301
|
+
console.log(ui.error(r.data?.error || `HTTP ${r.status}`));
|
|
302
|
+
}
|
|
303
|
+
} catch (e) {
|
|
304
|
+
spinner.stop();
|
|
305
|
+
console.log(ui.error(e.message));
|
|
181
306
|
}
|
|
182
307
|
return;
|
|
183
308
|
}
|
|
@@ -249,7 +374,7 @@ Be methodical. Confirm each step before proceeding. Report any failures immediat
|
|
|
249
374
|
|
|
250
375
|
console.log(ui.dim('Unknown subcommand. Try /edge help'));
|
|
251
376
|
|
|
252
|
-
}, { category: 'EDGE', subs: ['login', 'status', 'logout', 'tier', 'setup', 'help'] });
|
|
377
|
+
}, { category: 'EDGE', subs: ['login', 'status', 'logout', 'tier', 'usage', 'setup', 'help'] });
|
|
253
378
|
|
|
254
379
|
// ── /agent ── Manage sub-agents
|
|
255
380
|
reg('agent', 'Manage NAVADA sub-agents', (args) => {
|
package/lib/commands/index.js
CHANGED
|
@@ -6,7 +6,7 @@ const { register } = require('../registry');
|
|
|
6
6
|
const moduleNames = [
|
|
7
7
|
'network', 'mcp', 'lucas', 'docker', 'database', 'cloudflare',
|
|
8
8
|
'ai', 'azure', 'agents', 'tasks', 'keys', 'setup', 'system',
|
|
9
|
-
'learn', 'sandbox', 'nvidia', 'edge', 'conversations', 'audit', 'compute', '
|
|
9
|
+
'learn', 'sandbox', 'nvidia', 'edge', 'conversations', 'audit', 'compute', 'skills',
|
|
10
10
|
];
|
|
11
11
|
|
|
12
12
|
function loadAll() {
|