navada-edge-cli 1.0.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/LICENSE +21 -0
- package/README.md +142 -0
- package/bin/navada.js +5 -0
- package/lib/auth.js +46 -0
- package/lib/cli.js +97 -0
- package/lib/commands.js +468 -0
- package/lib/ui.js +58 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Leslie Akpareva / NAVADA
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# navada-edge-cli
|
|
2
|
+
|
|
3
|
+
Interactive CLI for the **NAVADA Edge Network**. Explore nodes, Cloudflare, AI services, Docker registry, MCP tools, and Postgres — all from your terminal.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npm install -g navada-edge-cli
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Launch interactive mode
|
|
13
|
+
navada
|
|
14
|
+
|
|
15
|
+
# Or run commands directly
|
|
16
|
+
navada status
|
|
17
|
+
navada mcp tools
|
|
18
|
+
navada registry
|
|
19
|
+
navada chat "What is ISA allowance for 2026?"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Install globally
|
|
26
|
+
npm install -g navada-edge-cli
|
|
27
|
+
|
|
28
|
+
# Set your API key
|
|
29
|
+
navada login <your-api-key>
|
|
30
|
+
|
|
31
|
+
# Configure your nodes
|
|
32
|
+
navada init asus 10.0.0.1
|
|
33
|
+
navada init mcp http://10.0.0.1:8811
|
|
34
|
+
navada init dashboard http://10.0.0.1:7900
|
|
35
|
+
navada init registry http://10.0.0.1:5000
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Config is saved to `~/.navada/config.json`. You can also use environment variables (see `navada-edge-sdk` docs).
|
|
39
|
+
|
|
40
|
+
## Interactive Mode
|
|
41
|
+
|
|
42
|
+
Run `navada` with no arguments to enter the interactive TUI:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
███╗ ██╗ █████╗ ██╗ ██╗ █████╗ ██████╗ █████╗
|
|
46
|
+
████╗ ██║██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔══██╗
|
|
47
|
+
██╔██╗ ██║███████║██║ ██║███████║██║ ██║███████║
|
|
48
|
+
██║╚██╗██║██╔══██║╚██╗ ██╔╝██╔══██║██║ ██║██╔══██║
|
|
49
|
+
██║ ╚████║██║ ██║ ╚████╔╝ ██║ ██║██████╔╝██║ ██║
|
|
50
|
+
╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝
|
|
51
|
+
─────────────────────────────────────────────────────
|
|
52
|
+
E D G E N E T W O R K v1.0.0
|
|
53
|
+
─────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
navada> /status
|
|
56
|
+
● ASUS ONLINE (HTTP 200)
|
|
57
|
+
● HP ONLINE (HTTP 401)
|
|
58
|
+
● EC2 ONLINE (HTTP 401)
|
|
59
|
+
● ORACLE ONLINE (HTTP 200)
|
|
60
|
+
○ AZURE OFFLINE (unreachable)
|
|
61
|
+
● CLOUDFLARE ONLINE (HTTP 200)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Commands
|
|
65
|
+
|
|
66
|
+
### Network
|
|
67
|
+
| Command | Description |
|
|
68
|
+
|---------|-------------|
|
|
69
|
+
| `/status` | Ping all nodes + cloud services |
|
|
70
|
+
| `/nodes` | Show node configuration |
|
|
71
|
+
| `/dashboard` | Get Command Dashboard status |
|
|
72
|
+
|
|
73
|
+
### MCP
|
|
74
|
+
| Command | Description |
|
|
75
|
+
|---------|-------------|
|
|
76
|
+
| `/mcp tools` | List MCP tools |
|
|
77
|
+
| `/mcp call <tool> [json]` | Call an MCP tool |
|
|
78
|
+
|
|
79
|
+
### Lucas CTO
|
|
80
|
+
| Command | Description |
|
|
81
|
+
|---------|-------------|
|
|
82
|
+
| `/lucas exec <cmd>` | Run bash on EC2 |
|
|
83
|
+
| `/lucas ssh <node> <cmd>` | SSH to node via Lucas |
|
|
84
|
+
| `/lucas docker <ctr> <cmd>` | Docker exec via Lucas |
|
|
85
|
+
| `/lucas status` | Lucas network status |
|
|
86
|
+
|
|
87
|
+
### Docker
|
|
88
|
+
| Command | Description |
|
|
89
|
+
|---------|-------------|
|
|
90
|
+
| `/registry` | List images in private registry |
|
|
91
|
+
| `/registry tags <image>` | List tags for an image |
|
|
92
|
+
|
|
93
|
+
### Database
|
|
94
|
+
| Command | Description |
|
|
95
|
+
|---------|-------------|
|
|
96
|
+
| `/db <sql>` | Run a Postgres query |
|
|
97
|
+
|
|
98
|
+
### Cloudflare
|
|
99
|
+
| Command | Description |
|
|
100
|
+
|---------|-------------|
|
|
101
|
+
| `/r2 ls [prefix]` | List R2 objects |
|
|
102
|
+
| `/r2 buckets` | List R2 buckets |
|
|
103
|
+
| `/r2 upload <key> <file>` | Upload file to R2 |
|
|
104
|
+
| `/dns` | List DNS records |
|
|
105
|
+
| `/dns create <type> <name> <content>` | Create DNS record |
|
|
106
|
+
| `/tunnel` | List Cloudflare tunnels |
|
|
107
|
+
| `/stream` | List Stream videos |
|
|
108
|
+
| `/flux <prompt>` | Generate image (FREE) |
|
|
109
|
+
| `/trace <url>` | Trace through Cloudflare WAF |
|
|
110
|
+
|
|
111
|
+
### AI
|
|
112
|
+
| Command | Description |
|
|
113
|
+
|---------|-------------|
|
|
114
|
+
| `/chat <message>` | Chat with GPT-4o |
|
|
115
|
+
| `/qwen <prompt>` | Qwen Coder (FREE) |
|
|
116
|
+
| `/yolo` | YOLO service status |
|
|
117
|
+
| `/yolo detect <image>` | Run object detection |
|
|
118
|
+
|
|
119
|
+
### Azure
|
|
120
|
+
| Command | Description |
|
|
121
|
+
|---------|-------------|
|
|
122
|
+
| `/n8n` | Check n8n health |
|
|
123
|
+
|
|
124
|
+
### System
|
|
125
|
+
| Command | Description |
|
|
126
|
+
|---------|-------------|
|
|
127
|
+
| `/config` | Show current configuration |
|
|
128
|
+
| `/login <api-key>` | Set API key |
|
|
129
|
+
| `/init <key> <value>` | Set a config value |
|
|
130
|
+
| `/clear` | Clear screen |
|
|
131
|
+
| `/exit` | Exit CLI |
|
|
132
|
+
|
|
133
|
+
## NAVADA Edge Ecosystem
|
|
134
|
+
|
|
135
|
+
| Package | Install | Purpose |
|
|
136
|
+
|---------|---------|---------|
|
|
137
|
+
| `navada-edge-sdk` | `npm i navada-edge-sdk` | SDK — use in your Node.js apps |
|
|
138
|
+
| `navada-edge-cli` | `npm i -g navada-edge-cli` | CLI — interactive terminal tool |
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT - Leslie Akpareva / NAVADA
|
package/bin/navada.js
ADDED
package/lib/auth.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const CONFIG_DIR = path.join(os.homedir(), '.navada');
|
|
8
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
9
|
+
|
|
10
|
+
function ensureDir() {
|
|
11
|
+
if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function load() {
|
|
15
|
+
try {
|
|
16
|
+
if (!fs.existsSync(CONFIG_FILE)) return {};
|
|
17
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
18
|
+
} catch { return {}; }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function save(config) {
|
|
22
|
+
ensureDir();
|
|
23
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getApiKey() {
|
|
27
|
+
return process.env.NAVADA_API_KEY || load().apiKey || '';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function setApiKey(key) {
|
|
31
|
+
const config = load();
|
|
32
|
+
config.apiKey = key;
|
|
33
|
+
save(config);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getConfig() {
|
|
37
|
+
return { ...load(), apiKey: getApiKey() };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function setConfig(key, value) {
|
|
41
|
+
const config = load();
|
|
42
|
+
config[key] = value;
|
|
43
|
+
save(config);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { getApiKey, setApiKey, getConfig, setConfig, CONFIG_FILE };
|
package/lib/cli.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const readline = require('readline');
|
|
4
|
+
const navada = require('navada-edge-sdk');
|
|
5
|
+
const ui = require('./ui');
|
|
6
|
+
const auth = require('./auth');
|
|
7
|
+
const { execute, commands } = require('./commands');
|
|
8
|
+
|
|
9
|
+
function applyConfig() {
|
|
10
|
+
const config = auth.getConfig();
|
|
11
|
+
const overrides = {};
|
|
12
|
+
if (config.apiKey) { overrides.mcpApiKey = config.apiKey; overrides.dashboardApiKey = config.apiKey; }
|
|
13
|
+
if (config.asus) overrides.asus = config.asus;
|
|
14
|
+
if (config.hp) overrides.hp = config.hp;
|
|
15
|
+
if (config.ec2) overrides.ec2 = config.ec2;
|
|
16
|
+
if (config.oracle) overrides.oracle = config.oracle;
|
|
17
|
+
if (config.mcp) overrides.mcp = config.mcp;
|
|
18
|
+
if (config.dashboard) overrides.dashboard = config.dashboard;
|
|
19
|
+
if (config.registry) overrides.registry = config.registry;
|
|
20
|
+
if (config.lucas) overrides.lucas = config.lucas;
|
|
21
|
+
if (Object.keys(overrides).length > 0) navada.init(overrides);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function showWelcome() {
|
|
25
|
+
console.clear();
|
|
26
|
+
console.log(ui.LOGO);
|
|
27
|
+
console.log(ui.dim('Type /help for commands. Tab to autocomplete. Ctrl+C to exit.'));
|
|
28
|
+
console.log('');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function startRepl() {
|
|
32
|
+
const rl = readline.createInterface({
|
|
33
|
+
input: process.stdin,
|
|
34
|
+
output: process.stdout,
|
|
35
|
+
prompt: ui.prompt(),
|
|
36
|
+
completer: (line) => {
|
|
37
|
+
const cmds = Object.keys(commands).map(c => '/' + c);
|
|
38
|
+
const hits = cmds.filter(c => c.startsWith(line));
|
|
39
|
+
return [hits.length ? hits : cmds, line];
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
rl.prompt();
|
|
44
|
+
|
|
45
|
+
rl.on('line', async (line) => {
|
|
46
|
+
const input = line.trim();
|
|
47
|
+
if (input) {
|
|
48
|
+
await execute(input);
|
|
49
|
+
}
|
|
50
|
+
console.log('');
|
|
51
|
+
rl.prompt();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
rl.on('close', () => {
|
|
55
|
+
console.log(ui.dim('\nGoodbye.'));
|
|
56
|
+
process.exit(0);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function runDirect(args) {
|
|
61
|
+
const input = args.join(' ');
|
|
62
|
+
await execute(input);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function run(argv) {
|
|
66
|
+
applyConfig();
|
|
67
|
+
|
|
68
|
+
if (argv.length === 0) {
|
|
69
|
+
// Interactive mode
|
|
70
|
+
showWelcome();
|
|
71
|
+
startRepl();
|
|
72
|
+
} else if (argv[0] === '--version' || argv[0] === '-v') {
|
|
73
|
+
const pkg = require('../package.json');
|
|
74
|
+
console.log(`navada-edge-cli v${pkg.version}`);
|
|
75
|
+
} else if (argv[0] === '--help' || argv[0] === '-h') {
|
|
76
|
+
console.log(ui.LOGO);
|
|
77
|
+
console.log(' Usage:');
|
|
78
|
+
console.log(' navada Interactive mode');
|
|
79
|
+
console.log(' navada status Ping all nodes');
|
|
80
|
+
console.log(' navada mcp tools List MCP tools');
|
|
81
|
+
console.log(' navada registry List Docker images');
|
|
82
|
+
console.log(' navada chat "question" Chat with GPT-4o');
|
|
83
|
+
console.log(' navada login <key> Set API key');
|
|
84
|
+
console.log(' navada --version Show version');
|
|
85
|
+
console.log('');
|
|
86
|
+
console.log(' Run `navada` with no args for interactive mode.');
|
|
87
|
+
console.log('');
|
|
88
|
+
} else {
|
|
89
|
+
// Direct command mode
|
|
90
|
+
runDirect(argv).then(() => process.exit(0)).catch(e => {
|
|
91
|
+
console.log(ui.error(e.message));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { run };
|
package/lib/commands.js
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const Table = require('cli-table3');
|
|
5
|
+
const navada = require('navada-edge-sdk');
|
|
6
|
+
const ui = require('./ui');
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Command registry
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
const commands = {};
|
|
12
|
+
|
|
13
|
+
function register(name, description, handler) {
|
|
14
|
+
commands[name] = { description, handler };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function execute(input) {
|
|
18
|
+
const parts = input.trim().replace(/^\//, '').split(/\s+/);
|
|
19
|
+
const name = parts[0]?.toLowerCase();
|
|
20
|
+
const args = parts.slice(1);
|
|
21
|
+
|
|
22
|
+
if (!name) return;
|
|
23
|
+
|
|
24
|
+
const cmd = commands[name];
|
|
25
|
+
if (!cmd) {
|
|
26
|
+
console.log(ui.error(`Unknown command: /${name}`));
|
|
27
|
+
console.log(ui.dim('Type /help for available commands'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await cmd.handler(args);
|
|
33
|
+
} catch (e) {
|
|
34
|
+
console.log(ui.error(e.message));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// /help
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
register('help', 'Show all commands', () => {
|
|
42
|
+
console.log(ui.header('COMMANDS'));
|
|
43
|
+
console.log('');
|
|
44
|
+
console.log(chalk.gray.dim(' NETWORK'));
|
|
45
|
+
console.log(ui.cmd('status', 'Ping all nodes + cloud services'));
|
|
46
|
+
console.log(ui.cmd('nodes', 'Show node configuration'));
|
|
47
|
+
console.log(ui.cmd('dashboard', 'Get Command Dashboard status'));
|
|
48
|
+
console.log('');
|
|
49
|
+
console.log(chalk.gray.dim(' MCP'));
|
|
50
|
+
console.log(ui.cmd('mcp tools', 'List MCP tools'));
|
|
51
|
+
console.log(ui.cmd('mcp call <tool> [json]', 'Call an MCP tool'));
|
|
52
|
+
console.log('');
|
|
53
|
+
console.log(chalk.gray.dim(' LUCAS CTO'));
|
|
54
|
+
console.log(ui.cmd('lucas exec <cmd>', 'Run bash on EC2'));
|
|
55
|
+
console.log(ui.cmd('lucas ssh <node> <cmd>', 'SSH to node via Lucas'));
|
|
56
|
+
console.log(ui.cmd('lucas docker <ctr> <cmd>', 'Docker exec via Lucas'));
|
|
57
|
+
console.log(ui.cmd('lucas status', 'Lucas network status'));
|
|
58
|
+
console.log('');
|
|
59
|
+
console.log(chalk.gray.dim(' DOCKER'));
|
|
60
|
+
console.log(ui.cmd('registry', 'List images in private registry'));
|
|
61
|
+
console.log(ui.cmd('registry tags <image>', 'List tags for an image'));
|
|
62
|
+
console.log('');
|
|
63
|
+
console.log(chalk.gray.dim(' DATABASE'));
|
|
64
|
+
console.log(ui.cmd('db <sql>', 'Run a Postgres query'));
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(chalk.gray.dim(' OPENCODE'));
|
|
67
|
+
console.log(ui.cmd('opencode', 'Check OpenCode on all nodes'));
|
|
68
|
+
console.log('');
|
|
69
|
+
console.log(chalk.gray.dim(' CLOUDFLARE'));
|
|
70
|
+
console.log(ui.cmd('r2 ls [prefix]', 'List R2 objects'));
|
|
71
|
+
console.log(ui.cmd('r2 buckets', 'List R2 buckets'));
|
|
72
|
+
console.log(ui.cmd('r2 upload <key> <file>', 'Upload file to R2'));
|
|
73
|
+
console.log(ui.cmd('r2 delete <key>', 'Delete R2 object'));
|
|
74
|
+
console.log(ui.cmd('dns', 'List DNS records'));
|
|
75
|
+
console.log(ui.cmd('dns create <type> <name> <content>', 'Create DNS record'));
|
|
76
|
+
console.log(ui.cmd('tunnel', 'List Cloudflare tunnels'));
|
|
77
|
+
console.log(ui.cmd('stream', 'List Stream videos'));
|
|
78
|
+
console.log(ui.cmd('flux <prompt>', 'Generate image (FREE)'));
|
|
79
|
+
console.log(ui.cmd('trace <url>', 'Trace request through Cloudflare'));
|
|
80
|
+
console.log('');
|
|
81
|
+
console.log(chalk.gray.dim(' AI'));
|
|
82
|
+
console.log(ui.cmd('chat <message>', 'Chat with GPT-4o'));
|
|
83
|
+
console.log(ui.cmd('qwen <prompt>', 'Qwen Coder (FREE)'));
|
|
84
|
+
console.log(ui.cmd('yolo health', 'YOLO service status'));
|
|
85
|
+
console.log(ui.cmd('yolo detect <image>', 'Run object detection'));
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log(chalk.gray.dim(' AZURE'));
|
|
88
|
+
console.log(ui.cmd('n8n', 'Check n8n health'));
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log(chalk.gray.dim(' SYSTEM'));
|
|
91
|
+
console.log(ui.cmd('config', 'Show current configuration'));
|
|
92
|
+
console.log(ui.cmd('login <api-key>', 'Set API key'));
|
|
93
|
+
console.log(ui.cmd('init <key> <value>', 'Set a config value'));
|
|
94
|
+
console.log(ui.cmd('clear', 'Clear screen'));
|
|
95
|
+
console.log(ui.cmd('exit', 'Exit CLI'));
|
|
96
|
+
console.log('');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// /status
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
register('status', 'Ping all nodes', async () => {
|
|
103
|
+
const ora = require('ora');
|
|
104
|
+
const spinner = ora({ text: ' Pinging network...', color: 'white' }).start();
|
|
105
|
+
const results = await navada.network.ping();
|
|
106
|
+
spinner.stop();
|
|
107
|
+
|
|
108
|
+
console.log(ui.header('NETWORK STATUS'));
|
|
109
|
+
for (const [node, info] of Object.entries(results)) {
|
|
110
|
+
console.log(ui.online(node.toUpperCase(), info.online, info.status ? `HTTP ${info.status}` : info.error));
|
|
111
|
+
}
|
|
112
|
+
const total = Object.keys(results).length;
|
|
113
|
+
const up = Object.values(results).filter(r => r.online).length;
|
|
114
|
+
console.log('');
|
|
115
|
+
console.log(ui.dim(`${up}/${total} endpoints online`));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// /nodes
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
register('nodes', 'Show node config', () => {
|
|
122
|
+
console.log(ui.header('NODES'));
|
|
123
|
+
const nodes = navada.network.nodes;
|
|
124
|
+
for (const [name, info] of Object.entries(nodes)) {
|
|
125
|
+
if (info.ip) {
|
|
126
|
+
console.log(ui.label(name.toUpperCase(), `${info.ip} ${chalk.gray(info.role)}`));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
console.log('');
|
|
130
|
+
console.log(ui.label('MCP', navada.config.mcp || chalk.gray('not set')));
|
|
131
|
+
console.log(ui.label('DASHBOARD', navada.config.dashboard || chalk.gray('not set')));
|
|
132
|
+
console.log(ui.label('REGISTRY', navada.config.registry || chalk.gray('not set')));
|
|
133
|
+
console.log(ui.label('LUCAS', navada.config.lucas || chalk.gray('not set')));
|
|
134
|
+
console.log(ui.label('PORTAINER', navada.config.portainer || chalk.gray('not set')));
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// /dashboard
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
register('dashboard', 'Command Dashboard status', async () => {
|
|
141
|
+
const data = await navada.network.status();
|
|
142
|
+
console.log(ui.header('DASHBOARD'));
|
|
143
|
+
console.log(JSON.stringify(data, null, 2));
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// /mcp
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
register('mcp', 'MCP tools', async (args) => {
|
|
150
|
+
const sub = args[0];
|
|
151
|
+
|
|
152
|
+
if (sub === 'tools' || !sub) {
|
|
153
|
+
const ora = require('ora');
|
|
154
|
+
const spinner = ora({ text: ' Fetching MCP tools...', color: 'white' }).start();
|
|
155
|
+
const tools = await navada.mcp.tools();
|
|
156
|
+
spinner.stop();
|
|
157
|
+
console.log(ui.header(`MCP TOOLS (${tools.length})`));
|
|
158
|
+
const t = new Table({ head: ['Tool', 'Description'], style: { head: ['white'], border: ['gray'] }, chars: tableChars() });
|
|
159
|
+
tools.forEach(tool => t.push([chalk.white(tool.name), chalk.gray(tool.description || '')]));
|
|
160
|
+
console.log(t.toString());
|
|
161
|
+
} else if (sub === 'call' && args[1]) {
|
|
162
|
+
const toolName = args[1];
|
|
163
|
+
let toolArgs = {};
|
|
164
|
+
if (args[2]) { try { toolArgs = JSON.parse(args.slice(2).join(' ')); } catch { toolArgs = { input: args.slice(2).join(' ') }; } }
|
|
165
|
+
const ora = require('ora');
|
|
166
|
+
const spinner = ora({ text: ` Calling ${toolName}...`, color: 'white' }).start();
|
|
167
|
+
const result = await navada.mcp.call(toolName, toolArgs);
|
|
168
|
+
spinner.stop();
|
|
169
|
+
console.log(ui.header(`MCP: ${toolName}`));
|
|
170
|
+
console.log(typeof result === 'object' ? JSON.stringify(result, null, 2) : result);
|
|
171
|
+
} else {
|
|
172
|
+
console.log(ui.dim('Usage: /mcp tools | /mcp call <tool> [json]'));
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// /lucas
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
register('lucas', 'Lucas CTO commands', async (args) => {
|
|
180
|
+
const sub = args[0];
|
|
181
|
+
if (sub === 'exec' && args[1]) {
|
|
182
|
+
const cmd = args.slice(1).join(' ');
|
|
183
|
+
const result = await navada.lucas.exec(cmd);
|
|
184
|
+
console.log(typeof result === 'object' ? JSON.stringify(result, null, 2) : result);
|
|
185
|
+
} else if (sub === 'ssh' && args[1] && args[2]) {
|
|
186
|
+
const result = await navada.lucas.ssh(args[1], args.slice(2).join(' '));
|
|
187
|
+
console.log(typeof result === 'object' ? JSON.stringify(result, null, 2) : result);
|
|
188
|
+
} else if (sub === 'docker' && args[1] && args[2]) {
|
|
189
|
+
const result = await navada.lucas.docker(args[1], args.slice(2).join(' '));
|
|
190
|
+
console.log(typeof result === 'object' ? JSON.stringify(result, null, 2) : result);
|
|
191
|
+
} else if (sub === 'status') {
|
|
192
|
+
const result = await navada.lucas.networkStatus();
|
|
193
|
+
console.log(ui.header('LUCAS NETWORK STATUS'));
|
|
194
|
+
console.log(typeof result === 'object' ? JSON.stringify(result, null, 2) : result);
|
|
195
|
+
} else {
|
|
196
|
+
console.log(ui.dim('Usage: /lucas exec <cmd> | /lucas ssh <node> <cmd> | /lucas docker <ctr> <cmd> | /lucas status'));
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// /registry
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
register('registry', 'Docker registry', async (args) => {
|
|
204
|
+
if (args[0] === 'tags' && args[1]) {
|
|
205
|
+
const tags = await navada.registry.tags(args[1]);
|
|
206
|
+
console.log(ui.header(`TAGS: ${args[1]}`));
|
|
207
|
+
tags.forEach(t => console.log(` ${chalk.white(t)}`));
|
|
208
|
+
} else {
|
|
209
|
+
const ora = require('ora');
|
|
210
|
+
const spinner = ora({ text: ' Querying registry...', color: 'white' }).start();
|
|
211
|
+
const images = await navada.registry.catalog();
|
|
212
|
+
spinner.stop();
|
|
213
|
+
console.log(ui.header(`DOCKER REGISTRY (${images.length} images)`));
|
|
214
|
+
images.forEach(img => console.log(` ${chalk.white(img)}`));
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
// /db
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
register('db', 'Query Postgres', async (args) => {
|
|
222
|
+
const sql = args.join(' ');
|
|
223
|
+
if (!sql) { console.log(ui.dim('Usage: /db SELECT NOW()')); return; }
|
|
224
|
+
const ora = require('ora');
|
|
225
|
+
const spinner = ora({ text: ' Querying...', color: 'white' }).start();
|
|
226
|
+
const rows = await navada.db.query(sql);
|
|
227
|
+
spinner.stop();
|
|
228
|
+
if (rows.length === 0) { console.log(ui.dim('No rows returned')); return; }
|
|
229
|
+
const t = new Table({ head: Object.keys(rows[0]), style: { head: ['white'], border: ['gray'] }, chars: tableChars() });
|
|
230
|
+
rows.slice(0, 50).forEach(row => t.push(Object.values(row).map(v => String(v ?? ''))));
|
|
231
|
+
console.log(t.toString());
|
|
232
|
+
if (rows.length > 50) console.log(ui.dim(`... ${rows.length - 50} more rows`));
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
// /opencode
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
register('opencode', 'OpenCode status', async () => {
|
|
239
|
+
const results = await navada.opencode.statusAll();
|
|
240
|
+
console.log(ui.header('OPENCODE'));
|
|
241
|
+
for (const [node, info] of Object.entries(results)) {
|
|
242
|
+
console.log(ui.online(node.toUpperCase(), info.online, info.status ? `HTTP ${info.status}` : info.error));
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
// /r2
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
register('r2', 'Cloudflare R2 storage', async (args) => {
|
|
250
|
+
const sub = args[0];
|
|
251
|
+
if (sub === 'buckets') {
|
|
252
|
+
const buckets = await navada.cloudflare.r2.buckets();
|
|
253
|
+
console.log(ui.header('R2 BUCKETS'));
|
|
254
|
+
if (Array.isArray(buckets)) buckets.forEach(b => console.log(` ${chalk.white(b.name || b)}`));
|
|
255
|
+
else console.log(JSON.stringify(buckets, null, 2));
|
|
256
|
+
} else if (sub === 'ls') {
|
|
257
|
+
const prefix = args[1] || '';
|
|
258
|
+
const objects = await navada.cloudflare.r2.list(prefix);
|
|
259
|
+
console.log(ui.header(`R2: ${prefix || '/'}`));
|
|
260
|
+
if (Array.isArray(objects)) objects.forEach(o => console.log(` ${chalk.white(o.key || o)}`));
|
|
261
|
+
else console.log(JSON.stringify(objects, null, 2));
|
|
262
|
+
} else if (sub === 'upload' && args[1] && args[2]) {
|
|
263
|
+
const result = await navada.cloudflare.r2.upload(args[1], args[2]);
|
|
264
|
+
console.log(ui.success(`Uploaded: ${result.key} (${result.size} bytes)`));
|
|
265
|
+
} else if (sub === 'delete' && args[1]) {
|
|
266
|
+
await navada.cloudflare.r2.delete(args[1]);
|
|
267
|
+
console.log(ui.success(`Deleted: ${args[1]}`));
|
|
268
|
+
} else {
|
|
269
|
+
console.log(ui.dim('Usage: /r2 ls [prefix] | /r2 buckets | /r2 upload <key> <file> | /r2 delete <key>'));
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
// /dns
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
register('dns', 'Cloudflare DNS', async (args) => {
|
|
277
|
+
if (args[0] === 'create' && args[1] && args[2] && args[3]) {
|
|
278
|
+
const result = await navada.cloudflare.dns.create(args[1], args[2], args[3]);
|
|
279
|
+
console.log(ui.success(`Created: ${args[1]} ${args[2]} -> ${args[3]}`));
|
|
280
|
+
} else {
|
|
281
|
+
const records = await navada.cloudflare.dns.list(args[0]);
|
|
282
|
+
console.log(ui.header('DNS RECORDS'));
|
|
283
|
+
const t = new Table({ head: ['Type', 'Name', 'Content', 'Proxied'], style: { head: ['white'], border: ['gray'] }, chars: tableChars() });
|
|
284
|
+
records.forEach(r => t.push([r.type, r.name, r.content?.slice(0, 40), r.proxied ? 'yes' : 'no']));
|
|
285
|
+
console.log(t.toString());
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// ---------------------------------------------------------------------------
|
|
290
|
+
// /tunnel
|
|
291
|
+
// ---------------------------------------------------------------------------
|
|
292
|
+
register('tunnel', 'Cloudflare tunnels', async () => {
|
|
293
|
+
const tunnels = await navada.cloudflare.tunnel.list();
|
|
294
|
+
console.log(ui.header('CLOUDFLARE TUNNELS'));
|
|
295
|
+
tunnels.forEach(t => console.log(ui.label(t.name, `${t.id} ${chalk.gray(t.status)}`)));
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
// /stream
|
|
300
|
+
// ---------------------------------------------------------------------------
|
|
301
|
+
register('stream', 'Cloudflare Stream videos', async () => {
|
|
302
|
+
const videos = await navada.cloudflare.stream.list();
|
|
303
|
+
console.log(ui.header(`STREAM VIDEOS (${videos.length})`));
|
|
304
|
+
videos.forEach(v => console.log(ui.label(v.meta?.name || 'Untitled', `${v.uid} ${chalk.gray(v.status?.state || '')}`)));
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
// /flux
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
register('flux', 'Generate image (FREE)', async (args) => {
|
|
311
|
+
const prompt = args.join(' ');
|
|
312
|
+
if (!prompt) { console.log(ui.dim('Usage: /flux a futuristic server room')); return; }
|
|
313
|
+
const ora = require('ora');
|
|
314
|
+
const spinner = ora({ text: ` Generating: "${prompt}"...`, color: 'white' }).start();
|
|
315
|
+
const { size } = await navada.cloudflare.flux.generate(prompt, { savePath: `navada-flux-${Date.now()}.png` });
|
|
316
|
+
spinner.stop();
|
|
317
|
+
console.log(ui.success(`Generated: ${(size / 1024).toFixed(1)} KB`));
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
// /trace
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
register('trace', 'Trace request through Cloudflare', async (args) => {
|
|
324
|
+
if (!args[0]) { console.log(ui.dim('Usage: /trace https://your-domain.com')); return; }
|
|
325
|
+
const result = await navada.cloudflare.trace(args[0]);
|
|
326
|
+
console.log(ui.header('CLOUDFLARE TRACE'));
|
|
327
|
+
console.log(JSON.stringify(result, null, 2));
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// ---------------------------------------------------------------------------
|
|
331
|
+
// /chat
|
|
332
|
+
// ---------------------------------------------------------------------------
|
|
333
|
+
register('chat', 'Chat with GPT-4o', async (args) => {
|
|
334
|
+
const msg = args.join(' ');
|
|
335
|
+
if (!msg) { console.log(ui.dim('Usage: /chat What is ISA allowance for 2026?')); return; }
|
|
336
|
+
const ora = require('ora');
|
|
337
|
+
const spinner = ora({ text: ' Thinking...', color: 'white' }).start();
|
|
338
|
+
const response = await navada.ai.openai.chat(msg);
|
|
339
|
+
spinner.stop();
|
|
340
|
+
console.log(ui.header('AI RESPONSE'));
|
|
341
|
+
console.log(` ${response}`);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// ---------------------------------------------------------------------------
|
|
345
|
+
// /qwen
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
register('qwen', 'Qwen Coder (FREE)', async (args) => {
|
|
348
|
+
const prompt = args.join(' ');
|
|
349
|
+
if (!prompt) { console.log(ui.dim('Usage: /qwen Write a function to validate UK postcodes')); return; }
|
|
350
|
+
const ora = require('ora');
|
|
351
|
+
const spinner = ora({ text: ' Qwen thinking...', color: 'white' }).start();
|
|
352
|
+
const result = await navada.ai.huggingface.qwen(prompt);
|
|
353
|
+
spinner.stop();
|
|
354
|
+
console.log(ui.header('QWEN CODER'));
|
|
355
|
+
console.log(typeof result === 'object' ? JSON.stringify(result, null, 2) : result);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// ---------------------------------------------------------------------------
|
|
359
|
+
// /yolo
|
|
360
|
+
// ---------------------------------------------------------------------------
|
|
361
|
+
register('yolo', 'YOLO object detection', async (args) => {
|
|
362
|
+
if (args[0] === 'detect' && args[1]) {
|
|
363
|
+
const ora = require('ora');
|
|
364
|
+
const spinner = ora({ text: ' Detecting...', color: 'white' }).start();
|
|
365
|
+
const result = await navada.ai.yolo.detect(args[1]);
|
|
366
|
+
spinner.stop();
|
|
367
|
+
console.log(ui.header(`DETECTIONS (${result.count || 0})`));
|
|
368
|
+
if (result.detections) {
|
|
369
|
+
const t = new Table({ head: ['Class', 'Confidence', 'BBox'], style: { head: ['white'], border: ['gray'] }, chars: tableChars() });
|
|
370
|
+
result.detections.forEach(d => t.push([d.class, `${(d.confidence * 100).toFixed(1)}%`, d.bbox?.join(', ') || '']));
|
|
371
|
+
console.log(t.toString());
|
|
372
|
+
}
|
|
373
|
+
console.log(ui.dim(`Inference: ${result.inference_ms}ms`));
|
|
374
|
+
} else {
|
|
375
|
+
const health = await navada.ai.yolo.health();
|
|
376
|
+
console.log(ui.header('YOLO'));
|
|
377
|
+
console.log(ui.online('YOLO Service', health.status === 'ok'));
|
|
378
|
+
if (health.model) console.log(ui.label('Model', health.model));
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// ---------------------------------------------------------------------------
|
|
383
|
+
// /n8n
|
|
384
|
+
// ---------------------------------------------------------------------------
|
|
385
|
+
register('n8n', 'Azure n8n health', async () => {
|
|
386
|
+
const result = await navada.azure.n8n.health();
|
|
387
|
+
console.log(ui.header('AZURE N8N'));
|
|
388
|
+
console.log(ui.online('n8n', result.ok, result.status ? `HTTP ${result.status}` : result.error));
|
|
389
|
+
console.log(ui.label('URL', result.url || 'not set'));
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// ---------------------------------------------------------------------------
|
|
393
|
+
// /config
|
|
394
|
+
// ---------------------------------------------------------------------------
|
|
395
|
+
register('config', 'Show config', () => {
|
|
396
|
+
console.log(ui.header('CONFIGURATION'));
|
|
397
|
+
const c = navada.config;
|
|
398
|
+
const show = (k, v) => console.log(ui.label(k, v ? chalk.white(v) : chalk.gray('not set')));
|
|
399
|
+
show('SERVICE', c.service);
|
|
400
|
+
show('NODE', c.node);
|
|
401
|
+
show('ASUS', c.asus);
|
|
402
|
+
show('HP', c.hp);
|
|
403
|
+
show('EC2', c.ec2);
|
|
404
|
+
show('ORACLE', c.oracle);
|
|
405
|
+
show('MCP', c.mcp);
|
|
406
|
+
show('DASHBOARD', c.dashboard);
|
|
407
|
+
show('REGISTRY', c.registry);
|
|
408
|
+
show('LUCAS', c.lucas);
|
|
409
|
+
show('PG_HOST', c.pgHost);
|
|
410
|
+
show('CF_ACCOUNT', c.cfAccountId ? c.cfAccountId.slice(0, 8) + '...' : '');
|
|
411
|
+
show('CF_DOMAIN', c.cfDomain);
|
|
412
|
+
show('YOLO', c.yoloUrl);
|
|
413
|
+
show('AZURE_N8N', c.azureN8nUrl);
|
|
414
|
+
show('HF_TOKEN', c.hfToken ? '****' + c.hfToken.slice(-4) : '');
|
|
415
|
+
show('OPENAI_KEY', c.openaiKey ? '****' + c.openaiKey.slice(-4) : '');
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// ---------------------------------------------------------------------------
|
|
419
|
+
// /login
|
|
420
|
+
// ---------------------------------------------------------------------------
|
|
421
|
+
register('login', 'Set API key', (args) => {
|
|
422
|
+
const auth = require('./auth');
|
|
423
|
+
if (!args[0]) { console.log(ui.dim('Usage: /login <api-key>')); return; }
|
|
424
|
+
auth.setApiKey(args[0]);
|
|
425
|
+
navada.init({ mcpApiKey: args[0], dashboardApiKey: args[0] });
|
|
426
|
+
console.log(ui.success(`API key saved to ${auth.CONFIG_FILE}`));
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// ---------------------------------------------------------------------------
|
|
430
|
+
// /init
|
|
431
|
+
// ---------------------------------------------------------------------------
|
|
432
|
+
register('init', 'Set config value', (args) => {
|
|
433
|
+
if (!args[0] || !args[1]) { console.log(ui.dim('Usage: /init asus 10.0.0.1')); return; }
|
|
434
|
+
navada.init({ [args[0]]: args[1] });
|
|
435
|
+
const auth = require('./auth');
|
|
436
|
+
auth.setConfig(args[0], args[1]);
|
|
437
|
+
console.log(ui.success(`${args[0]} = ${args[1]}`));
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// ---------------------------------------------------------------------------
|
|
441
|
+
// /clear
|
|
442
|
+
// ---------------------------------------------------------------------------
|
|
443
|
+
register('clear', 'Clear screen', () => {
|
|
444
|
+
console.clear();
|
|
445
|
+
console.log(ui.LOGO);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// ---------------------------------------------------------------------------
|
|
449
|
+
// /exit
|
|
450
|
+
// ---------------------------------------------------------------------------
|
|
451
|
+
register('exit', 'Exit CLI', () => {
|
|
452
|
+
console.log(ui.dim('Goodbye.'));
|
|
453
|
+
process.exit(0);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// ---------------------------------------------------------------------------
|
|
457
|
+
// Table style helper
|
|
458
|
+
// ---------------------------------------------------------------------------
|
|
459
|
+
function tableChars() {
|
|
460
|
+
return {
|
|
461
|
+
'top': '─', 'top-mid': '┬', 'top-left': '┌', 'top-right': '┐',
|
|
462
|
+
'bottom': '─', 'bottom-mid': '┴', 'bottom-left': '└', 'bottom-right': '┘',
|
|
463
|
+
'left': '│', 'left-mid': '├', 'mid': '─', 'mid-mid': '┼',
|
|
464
|
+
'right': '│', 'right-mid': '┤', 'middle': '│',
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
module.exports = { commands, execute, register };
|
package/lib/ui.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
const LOGO = `
|
|
6
|
+
${chalk.white.bold(' ███╗ ██╗ █████╗ ██╗ ██╗ █████╗ ██████╗ █████╗ ')}
|
|
7
|
+
${chalk.white.bold(' ████╗ ██║██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔══██╗')}
|
|
8
|
+
${chalk.white.bold(' ██╔██╗ ██║███████║██║ ██║███████║██║ ██║███████║')}
|
|
9
|
+
${chalk.gray(' ██║╚██╗██║██╔══██║╚██╗ ██╔╝██╔══██║██║ ██║██╔══██║')}
|
|
10
|
+
${chalk.gray(' ██║ ╚████║██║ ██║ ╚████╔╝ ██║ ██║██████╔╝██║ ██║')}
|
|
11
|
+
${chalk.gray.dim(' ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝')}
|
|
12
|
+
${chalk.gray(' ─────────────────────────────────────────────────────')}
|
|
13
|
+
${chalk.white(' E D G E N E T W O R K')} ${chalk.gray.dim('v1.0.0')}
|
|
14
|
+
${chalk.gray(' ─────────────────────────────────────────────────────')}
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
const DIVIDER = chalk.gray(' ─────────────────────────────────────────────────────');
|
|
18
|
+
|
|
19
|
+
function header(text) {
|
|
20
|
+
return `\n${chalk.white.bold(' ' + text)}\n${DIVIDER}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function label(key, val) {
|
|
24
|
+
return ` ${chalk.gray(key.padEnd(20))} ${chalk.white(val)}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function online(name, isOnline, detail = '') {
|
|
28
|
+
const dot = isOnline ? chalk.green('●') : chalk.red('○');
|
|
29
|
+
const status = isOnline ? chalk.green('ONLINE') : chalk.red('OFFLINE');
|
|
30
|
+
const d = detail ? chalk.gray(` (${detail})`) : '';
|
|
31
|
+
return ` ${dot} ${chalk.white(name.padEnd(14))} ${status}${d}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function cmd(name, desc) {
|
|
35
|
+
return ` ${chalk.white('/' + name.padEnd(16))} ${chalk.gray(desc)}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function error(msg) {
|
|
39
|
+
return chalk.red(` ✗ ${msg}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function success(msg) {
|
|
43
|
+
return chalk.green(` ✓ ${msg}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function warn(msg) {
|
|
47
|
+
return chalk.yellow(` ! ${msg}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function dim(msg) {
|
|
51
|
+
return chalk.gray(` ${msg}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function prompt() {
|
|
55
|
+
return chalk.gray(' navada') + chalk.white('> ');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { LOGO, DIVIDER, header, label, online, cmd, error, success, warn, dim, prompt };
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "navada-edge-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI for the NAVADA Edge Network — interactive TUI to explore nodes, Cloudflare, AI, Docker, and MCP services",
|
|
5
|
+
"main": "lib/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"navada": "./bin/navada.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "commonjs",
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18.0.0"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node bin/navada.js",
|
|
15
|
+
"test": "node -e \"require('./lib/cli'); console.log('OK');\""
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"navada",
|
|
19
|
+
"edge",
|
|
20
|
+
"cli",
|
|
21
|
+
"tui",
|
|
22
|
+
"distributed",
|
|
23
|
+
"cloudflare",
|
|
24
|
+
"docker",
|
|
25
|
+
"ai",
|
|
26
|
+
"mcp",
|
|
27
|
+
"sdk"
|
|
28
|
+
],
|
|
29
|
+
"author": {
|
|
30
|
+
"name": "Leslie Akpareva",
|
|
31
|
+
"email": "leeakpareva@hotmail.com",
|
|
32
|
+
"url": "https://www.navada-lab.space"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"homepage": "https://github.com/Navada25/edge-sdk",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/Navada25/edge-sdk.git"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"navada-edge-sdk": "^1.0.0",
|
|
42
|
+
"chalk": "^4.1.2",
|
|
43
|
+
"cli-table3": "^0.6.5",
|
|
44
|
+
"ora": "^5.4.1"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"bin/",
|
|
51
|
+
"lib/",
|
|
52
|
+
"README.md",
|
|
53
|
+
"LICENSE"
|
|
54
|
+
]
|
|
55
|
+
}
|