@mmmbuto/nexuscli 0.5.3 → 0.5.5

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 CHANGED
@@ -1,48 +1,8 @@
1
- # NexusCLI — AI Terminal Cockpit
1
+ # NexusCLI
2
2
 
3
- <p align="center">
4
- <img src=".github/header/header.png" width="900" />
5
- </p>
3
+ **TRI CLI v0.4.0** - Termux Mobile First AI Control Plane
6
4
 
7
-
8
- ---
9
-
10
- ## Overview
11
-
12
- NexusCLI is an experimental, ultra-light terminal cockpit designed for
13
- AI-assisted development workflows on Termux (Android).
14
-
15
- **v0.5.3** - Mobile-First AI Control Plane
16
-
17
- Web UI wrapper for Claude Code, Codex CLI, and Gemini CLI with voice input support.
18
-
19
- ---
20
-
21
- [![npm](https://img.shields.io/npm/v/@mmmbuto/nexuscli?style=flat-square&logo=npm)](https://www.npmjs.com/package/@mmmbuto/nexuscli)
22
- [![downloads](https://img.shields.io/npm/dt/@mmmbuto/nexuscli?style=flat-square)](https://www.npmjs.com/package/@mmmbuto/nexuscli)
23
- [![ko-fi](https://img.shields.io/badge/☕_Support-Ko--fi-FF5E5B?style=flat-square&logo=ko-fi)](https://ko-fi.com/dionanos)
24
-
25
- ---
26
-
27
- ## Screenshots
28
-
29
- <p align="center">
30
- <img src="docs/assets/screenshots/nexuscli-multilang-preview.png" width="45%" />
31
- <img src="docs/assets/screenshots/nexuscli-mobile-terminal.png" width="45%" />
32
- </p>
33
-
34
- ---
35
-
36
- ## Features
37
-
38
- - Multi-engine support (Claude, Codex, Gemini)
39
- - **Voice input** (OpenAI Whisper STT)
40
- - **Auto HTTPS** for remote microphone access
41
- - Mobile-first responsive UI
42
- - SSE streaming responses
43
- - Workspace management
44
- - Conversation history
45
- - Model selector with think mode toggle
5
+ Web UI for Claude Code, Codex CLI, and Gemini CLI.
46
6
 
47
7
  ## Supported Engines
48
8
 
@@ -52,16 +12,23 @@ Web UI wrapper for Claude Code, Codex CLI, and Gemini CLI with voice input suppo
52
12
  | **Codex** | GPT-5.1, GPT-5.1 Codex (Mini/Max) | OpenAI |
53
13
  | **Gemini** | Gemini 3 Pro Preview | Google |
54
14
 
55
- ---
56
-
57
15
  ## Install
58
16
 
17
+ **Private package** - Install via git:
18
+
59
19
  ```bash
60
- # From npm
61
- npm install -g @mmmbuto/nexuscli
20
+ # From VPS (recommended)
21
+ npm install -g git+ssh://dag@cloud.alpacalibre.com/home/dag/git_repos/NexusCLI.git
62
22
 
63
- # From GitHub
64
- npm install -g github:DioNanos/nexuscli
23
+ # From VPSHome backup
24
+ npm install -g git+ssh://calner@192.168.10.10/home/calner/git_repos/NexusCLI.git
25
+
26
+ # Local clone install
27
+ cd ~/Dev/NexusCLI && npm install -g .
28
+
29
+ # Termux manual install
30
+ cd ~/Dev/NexusCLI
31
+ ./scripts/install-termux.sh
65
32
  ```
66
33
 
67
34
  ## Setup
@@ -78,8 +45,6 @@ nexuscli start
78
45
 
79
46
  Open browser: `http://localhost:41800`
80
47
 
81
- ---
82
-
83
48
  ## Commands
84
49
 
85
50
  | Command | Description |
@@ -91,28 +56,6 @@ Open browser: `http://localhost:41800`
91
56
  | `nexuscli engines` | Manage AI engines |
92
57
  | `nexuscli workspaces` | Manage workspaces |
93
58
  | `nexuscli config` | Configuration |
94
- | `nexuscli api` | Manage API keys |
95
- | `nexuscli users` | User management |
96
- | `nexuscli setup-termux` | Bootstrap Termux (SSH, packages) |
97
-
98
- ---
99
-
100
- ## API Keys
101
-
102
- Configure API keys for additional providers:
103
-
104
- ```bash
105
- nexuscli api list # List configured keys
106
- nexuscli api set deepseek <key> # DeepSeek models
107
- nexuscli api set openai <key> # Voice input (Whisper STT)
108
- nexuscli api set openrouter <key> # Future: Multi-provider gateway
109
- nexuscli api delete <provider> # Remove key
110
- ```
111
-
112
- > **Note**: Claude/Codex/Gemini keys are managed by their respective CLIs.
113
- > OpenAI key enables voice input via Whisper. HTTPS auto-generated for remote mic access.
114
-
115
- ---
116
59
 
117
60
  ## Requirements
118
61
 
@@ -121,31 +64,7 @@ nexuscli api delete <provider> # Remove key
121
64
  - Claude Code CLI (`claude`)
122
65
  - Codex CLI (`codex`)
123
66
  - Gemini CLI (`gemini`)
124
-
125
- ---
126
-
127
- ## Termux-First Architecture
128
-
129
- NexusCLI is designed primarily for **Termux** on Android devices.
130
-
131
- ### Stack
132
-
133
- - **Termux** - primary runtime environment
134
- - **tmux** - session management
135
- - **Node.js + SSE** - lightweight backend
136
- - **React** - minimal UI
137
-
138
- ### Purpose
139
-
140
- This project exists to study:
141
-
142
- - terminal-driven AI orchestration
143
- - ultra-light architectures for constrained devices
144
- - mobile development workflows
145
-
146
- It is a **research and learning tool**.
147
-
148
- ---
67
+ - SSH access to git repos (for installation)
149
68
 
150
69
  ## API Endpoints
151
70
 
@@ -157,14 +76,12 @@ It is a **research and learning tool**.
157
76
  | `GET /api/v1/models` | All | List available models |
158
77
  | `GET /health` | - | Health check |
159
78
 
160
- ---
161
-
162
79
  ## Development
163
80
 
164
81
  ```bash
165
82
  # Clone
166
- git clone https://github.com/DioNanos/nexuscli.git
167
- cd nexuscli
83
+ git clone dag@cloud.alpacalibre.com:git_repos/NexusCLI.git
84
+ cd NexusCLI
168
85
 
169
86
  # Install deps
170
87
  npm install
@@ -174,9 +91,6 @@ cd frontend && npm install && npm run build && cd ..
174
91
  npm run dev
175
92
  ```
176
93
 
177
- ---
178
-
179
94
  ## License
180
95
 
181
- MIT License.
182
- See `LICENSE` for details.
96
+ MIT
package/bin/nexuscli.js CHANGED
@@ -103,18 +103,18 @@ program
103
103
  .option('-a, --admin', 'Create as admin user')
104
104
  .action(usersCommand);
105
105
 
106
+ // nexuscli setup-termux
107
+ program
108
+ .command('setup-termux')
109
+ .description('Bootstrap Termux: install packages, setup SSH, show connection info')
110
+ .action(setupTermuxCommand);
111
+
106
112
  // nexuscli uninstall
107
113
  program
108
114
  .command('uninstall')
109
115
  .description('Prepare for uninstallation (optional data removal)')
110
116
  .action(uninstallCommand);
111
117
 
112
- // nexuscli setup-termux
113
- program
114
- .command('setup-termux')
115
- .description('Bootstrap Termux for remote development (SSH, packages)')
116
- .action(setupTermuxCommand);
117
-
118
118
  // Parse arguments
119
119
  program.parse();
120
120
 
@@ -14,7 +14,7 @@ PORT=41800
14
14
  NODE_ENV=production
15
15
 
16
16
  # Workspace directory for CLI execution
17
- WORKSPACE_DIR=/home/user/myproject
17
+ WORKSPACE_DIR=/var/www/cli.wellanet.dev
18
18
 
19
19
  # Timeout for CLI commands (ms)
20
20
  DEFAULT_TIMEOUT=30000
@@ -0,0 +1,225 @@
1
+ const initSqlJs = require('sql.js');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+
5
+ // Detect environment (Linux vs Termux)
6
+ const isTermux = process.env.PREFIX?.includes('com.termux');
7
+
8
+ // Database directory
9
+ const dbDir = isTermux
10
+ ? path.join(process.env.HOME, '.nexuscli')
11
+ : process.env.NEXUSCLI_DB_DIR || '/var/lib/nexuscli';
12
+
13
+ // Database file path
14
+ const dbPath = path.join(dbDir, 'nexuscli.db');
15
+
16
+ // Ensure directory exists
17
+ if (!fs.existsSync(dbDir)) {
18
+ fs.mkdirSync(dbDir, { recursive: true });
19
+ console.log(`✅ Created database directory: ${dbDir}`);
20
+ }
21
+
22
+ let db = null;
23
+
24
+ // Initialize database
25
+ async function initDb() {
26
+ const SQL = await initSqlJs();
27
+
28
+ // Load existing database or create new
29
+ if (fs.existsSync(dbPath)) {
30
+ const buffer = fs.readFileSync(dbPath);
31
+ db = new SQL.Database(buffer);
32
+ console.log(`✅ Database loaded: ${dbPath}`);
33
+ } else {
34
+ db = new SQL.Database();
35
+ console.log(`✅ Database created: ${dbPath}`);
36
+ }
37
+
38
+ // Initialize schema
39
+ initSchema();
40
+
41
+ // Auto-save every 5 seconds
42
+ setInterval(saveDb, 5000);
43
+
44
+ return db;
45
+ }
46
+
47
+ // Save database to file
48
+ function saveDb() {
49
+ if (!db) return;
50
+ const data = db.export();
51
+ const buffer = Buffer.from(data);
52
+ fs.writeFileSync(dbPath, buffer);
53
+ }
54
+
55
+ // Initialize schema
56
+ function initSchema() {
57
+ db.run(`
58
+ -- Conversations table
59
+ CREATE TABLE IF NOT EXISTS conversations (
60
+ id TEXT PRIMARY KEY,
61
+ title TEXT NOT NULL,
62
+ created_at INTEGER NOT NULL,
63
+ updated_at INTEGER NOT NULL,
64
+ metadata TEXT
65
+ );
66
+
67
+ CREATE INDEX IF NOT EXISTS idx_conversations_updated_at
68
+ ON conversations(updated_at DESC);
69
+
70
+ -- Messages table
71
+ CREATE TABLE IF NOT EXISTS messages (
72
+ id TEXT PRIMARY KEY,
73
+ conversation_id TEXT NOT NULL,
74
+ role TEXT NOT NULL CHECK(role IN ('user', 'assistant', 'system')),
75
+ content TEXT NOT NULL,
76
+ created_at INTEGER NOT NULL,
77
+ metadata TEXT,
78
+ FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
79
+ );
80
+
81
+ CREATE INDEX IF NOT EXISTS idx_messages_conversation_id
82
+ ON messages(conversation_id);
83
+
84
+ CREATE INDEX IF NOT EXISTS idx_messages_created_at
85
+ ON messages(created_at ASC);
86
+
87
+ -- Jobs table
88
+ CREATE TABLE IF NOT EXISTS jobs (
89
+ id TEXT PRIMARY KEY,
90
+ conversation_id TEXT,
91
+ message_id TEXT,
92
+ node_id TEXT NOT NULL,
93
+ tool TEXT NOT NULL,
94
+ command TEXT NOT NULL,
95
+ status TEXT NOT NULL CHECK(status IN ('queued', 'executing', 'completed', 'failed', 'cancelled')),
96
+ exit_code INTEGER,
97
+ stdout TEXT,
98
+ stderr TEXT,
99
+ duration INTEGER,
100
+ created_at INTEGER NOT NULL,
101
+ started_at INTEGER,
102
+ completed_at INTEGER,
103
+ FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE SET NULL,
104
+ FOREIGN KEY (message_id) REFERENCES messages(id) ON DELETE SET NULL
105
+ );
106
+
107
+ CREATE INDEX IF NOT EXISTS idx_jobs_conversation_id
108
+ ON jobs(conversation_id);
109
+
110
+ CREATE INDEX IF NOT EXISTS idx_jobs_status
111
+ ON jobs(status);
112
+
113
+ CREATE INDEX IF NOT EXISTS idx_jobs_created_at
114
+ ON jobs(created_at DESC);
115
+
116
+ -- Users table
117
+ CREATE TABLE IF NOT EXISTS users (
118
+ id TEXT PRIMARY KEY,
119
+ username TEXT UNIQUE NOT NULL,
120
+ password_hash TEXT NOT NULL,
121
+ role TEXT NOT NULL DEFAULT 'user' CHECK(role IN ('admin', 'user')),
122
+ is_locked INTEGER NOT NULL DEFAULT 0,
123
+ failed_attempts INTEGER NOT NULL DEFAULT 0,
124
+ last_failed_attempt INTEGER,
125
+ locked_until INTEGER,
126
+ created_at INTEGER NOT NULL,
127
+ last_login INTEGER
128
+ );
129
+
130
+ CREATE INDEX IF NOT EXISTS idx_users_username
131
+ ON users(username);
132
+
133
+ -- Login attempts table (for rate limiting)
134
+ CREATE TABLE IF NOT EXISTS login_attempts (
135
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
136
+ ip_address TEXT NOT NULL,
137
+ username TEXT,
138
+ success INTEGER NOT NULL DEFAULT 0,
139
+ timestamp INTEGER NOT NULL
140
+ );
141
+
142
+ CREATE INDEX IF NOT EXISTS idx_login_attempts_ip
143
+ ON login_attempts(ip_address, timestamp DESC);
144
+
145
+ CREATE INDEX IF NOT EXISTS idx_login_attempts_timestamp
146
+ ON login_attempts(timestamp DESC);
147
+
148
+ -- Nodes table (optional - for multi-node setups)
149
+ CREATE TABLE IF NOT EXISTS nodes (
150
+ id TEXT PRIMARY KEY,
151
+ hostname TEXT NOT NULL,
152
+ ip_address TEXT,
153
+ status TEXT NOT NULL CHECK(status IN ('online', 'offline', 'error')),
154
+ capabilities TEXT,
155
+ last_heartbeat INTEGER,
156
+ created_at INTEGER NOT NULL
157
+ );
158
+
159
+ CREATE INDEX IF NOT EXISTS idx_nodes_status
160
+ ON nodes(status);
161
+ `);
162
+
163
+ console.log('✅ Database schema initialized');
164
+ }
165
+
166
+ // Prepare statement (sql.js uses db.prepare)
167
+ function prepare(sql) {
168
+ const stmt = db.prepare(sql);
169
+ return {
170
+ run: (...params) => {
171
+ stmt.bind(params);
172
+ stmt.step();
173
+ stmt.reset();
174
+ saveDb(); // Auto-save on write
175
+ },
176
+ get: (...params) => {
177
+ stmt.bind(params);
178
+ const result = stmt.step() ? stmt.getAsObject() : null;
179
+ stmt.reset();
180
+ return result;
181
+ },
182
+ all: (...params) => {
183
+ stmt.bind(params);
184
+ const results = [];
185
+ while (stmt.step()) {
186
+ results.push(stmt.getAsObject());
187
+ }
188
+ stmt.reset();
189
+ return results;
190
+ }
191
+ };
192
+ }
193
+
194
+ // Graceful shutdown
195
+ process.on('exit', () => {
196
+ if (db) {
197
+ saveDb();
198
+ db.close();
199
+ console.log('✅ Database connection closed');
200
+ }
201
+ });
202
+
203
+ process.on('SIGINT', () => {
204
+ if (db) {
205
+ saveDb();
206
+ db.close();
207
+ console.log('✅ Database connection closed (SIGINT)');
208
+ }
209
+ process.exit(0);
210
+ });
211
+
212
+ process.on('SIGTERM', () => {
213
+ if (db) {
214
+ saveDb();
215
+ db.close();
216
+ }
217
+ });
218
+
219
+ // Export db object (initialized async)
220
+ module.exports = {
221
+ initDb,
222
+ getDb: () => db,
223
+ prepare,
224
+ saveDb
225
+ };