@mmmbuto/nexuscli 0.5.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 +172 -0
- package/bin/nexuscli.js +117 -0
- package/frontend/dist/apple-touch-icon.png +0 -0
- package/frontend/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/frontend/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/frontend/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/frontend/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/frontend/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/frontend/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/frontend/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/frontend/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/frontend/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/frontend/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/frontend/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/frontend/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/frontend/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/frontend/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/frontend/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/frontend/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/frontend/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/frontend/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/frontend/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/frontend/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/frontend/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/frontend/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/frontend/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/frontend/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/frontend/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/frontend/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/frontend/dist/assets/index-Bn_l1e6e.css +1 -0
- package/frontend/dist/assets/index-CikJbUR5.js +8617 -0
- package/frontend/dist/browserconfig.xml +12 -0
- package/frontend/dist/favicon-16x16.png +0 -0
- package/frontend/dist/favicon-32x32.png +0 -0
- package/frontend/dist/favicon-48x48.png +0 -0
- package/frontend/dist/favicon.ico +0 -0
- package/frontend/dist/icon-192.png +0 -0
- package/frontend/dist/icon-512.png +0 -0
- package/frontend/dist/icon-maskable-192.png +0 -0
- package/frontend/dist/icon-maskable-512.png +0 -0
- package/frontend/dist/index.html +79 -0
- package/frontend/dist/manifest.json +75 -0
- package/frontend/dist/sw.js +122 -0
- package/frontend/package.json +28 -0
- package/lib/cli/api.js +156 -0
- package/lib/cli/boot.js +172 -0
- package/lib/cli/config.js +185 -0
- package/lib/cli/engines.js +257 -0
- package/lib/cli/init.js +660 -0
- package/lib/cli/logs.js +72 -0
- package/lib/cli/start.js +220 -0
- package/lib/cli/status.js +187 -0
- package/lib/cli/stop.js +64 -0
- package/lib/cli/uninstall.js +194 -0
- package/lib/cli/users.js +295 -0
- package/lib/cli/workspaces.js +337 -0
- package/lib/config/manager.js +233 -0
- package/lib/server/.env.example +20 -0
- package/lib/server/db/adapter.js +314 -0
- package/lib/server/db/drivers/better-sqlite3.js +38 -0
- package/lib/server/db/drivers/sql-js.js +75 -0
- package/lib/server/db/migrate.js +174 -0
- package/lib/server/db/migrations/001_ultra_light_schema.sql +96 -0
- package/lib/server/db/migrations/002_session_conversation_mapping.sql +19 -0
- package/lib/server/db/migrations/003_message_engine_tracking.sql +18 -0
- package/lib/server/db/migrations/004_performance_indexes.sql +16 -0
- package/lib/server/db.js +2 -0
- package/lib/server/lib/cli-wrapper.js +164 -0
- package/lib/server/lib/output-parser.js +132 -0
- package/lib/server/lib/pty-adapter.js +57 -0
- package/lib/server/middleware/auth.js +103 -0
- package/lib/server/models/Conversation.js +259 -0
- package/lib/server/models/Message.js +228 -0
- package/lib/server/models/User.js +115 -0
- package/lib/server/package-lock.json +5895 -0
- package/lib/server/routes/auth.js +168 -0
- package/lib/server/routes/chat.js +206 -0
- package/lib/server/routes/codex.js +205 -0
- package/lib/server/routes/conversations.js +224 -0
- package/lib/server/routes/gemini.js +228 -0
- package/lib/server/routes/jobs.js +317 -0
- package/lib/server/routes/messages.js +60 -0
- package/lib/server/routes/models.js +198 -0
- package/lib/server/routes/sessions.js +285 -0
- package/lib/server/routes/upload.js +134 -0
- package/lib/server/routes/wake-lock.js +95 -0
- package/lib/server/routes/workspace.js +80 -0
- package/lib/server/routes/workspaces.js +142 -0
- package/lib/server/scripts/cleanup-ghost-sessions.js +71 -0
- package/lib/server/scripts/seed-users.js +37 -0
- package/lib/server/scripts/test-history-access.js +50 -0
- package/lib/server/server.js +227 -0
- package/lib/server/services/cache.js +85 -0
- package/lib/server/services/claude-wrapper.js +312 -0
- package/lib/server/services/cli-loader.js +384 -0
- package/lib/server/services/codex-output-parser.js +277 -0
- package/lib/server/services/codex-wrapper.js +224 -0
- package/lib/server/services/context-bridge.js +289 -0
- package/lib/server/services/gemini-output-parser.js +398 -0
- package/lib/server/services/gemini-wrapper.js +249 -0
- package/lib/server/services/history-sync.js +407 -0
- package/lib/server/services/output-parser.js +415 -0
- package/lib/server/services/session-manager.js +465 -0
- package/lib/server/services/summary-generator.js +259 -0
- package/lib/server/services/workspace-manager.js +516 -0
- package/lib/server/tests/history-sync.test.js +90 -0
- package/lib/server/tests/integration-session-sync.test.js +151 -0
- package/lib/server/tests/integration.test.js +76 -0
- package/lib/server/tests/performance.test.js +118 -0
- package/lib/server/tests/services.test.js +160 -0
- package/lib/setup/postinstall.js +216 -0
- package/lib/utils/paths.js +107 -0
- package/lib/utils/termux.js +145 -0
- package/package.json +82 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* NexusCLI Post-Install Script
|
|
4
|
+
* Termux-only: auto-run wizard if not configured
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { execSync, spawn } = require('child_process');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
// Check if running in Termux
|
|
12
|
+
const isTermux =
|
|
13
|
+
process.env.PREFIX?.includes('com.termux') ||
|
|
14
|
+
fs.existsSync('/data/data/com.termux') ||
|
|
15
|
+
process.env.TERMUX_VERSION !== undefined;
|
|
16
|
+
|
|
17
|
+
// ANSI colors (chalk-free for postinstall)
|
|
18
|
+
const colors = {
|
|
19
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
20
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
21
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
22
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
23
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
24
|
+
gray: (s) => `\x1b[90m${s}\x1b[0m`
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function log(msg) {
|
|
28
|
+
console.log(msg);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function success(msg) {
|
|
32
|
+
console.log(colors.green(` ✓ ${msg}`));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function warn(msg) {
|
|
36
|
+
console.log(colors.yellow(` ⚠ ${msg}`));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function error(msg) {
|
|
40
|
+
console.log(colors.red(` ✗ ${msg}`));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if NexusCLI is already configured
|
|
45
|
+
*/
|
|
46
|
+
function isConfigured() {
|
|
47
|
+
const configPath = path.join(process.env.HOME || '', '.nexuscli', 'config.json');
|
|
48
|
+
return fs.existsSync(configPath);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Run the setup wizard
|
|
53
|
+
*/
|
|
54
|
+
function runWizard() {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
// Find nexuscli binary
|
|
57
|
+
let nexuscliPath;
|
|
58
|
+
try {
|
|
59
|
+
nexuscliPath = execSync('which nexuscli', { encoding: 'utf8' }).trim();
|
|
60
|
+
} catch {
|
|
61
|
+
// Fallback to relative path
|
|
62
|
+
nexuscliPath = path.join(__dirname, '..', '..', 'bin', 'nexuscli.js');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(colors.cyan('Running setup wizard...'));
|
|
67
|
+
console.log('');
|
|
68
|
+
|
|
69
|
+
const child = spawn('node', [nexuscliPath, 'init'], {
|
|
70
|
+
stdio: 'inherit'
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
child.on('close', (code) => {
|
|
74
|
+
if (code === 0) {
|
|
75
|
+
resolve();
|
|
76
|
+
} else {
|
|
77
|
+
reject(new Error(`Wizard exited with code ${code}`));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
child.on('error', (err) => {
|
|
82
|
+
reject(err);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function installPackage(pkg) {
|
|
88
|
+
try {
|
|
89
|
+
// Check if already installed
|
|
90
|
+
try {
|
|
91
|
+
execSync(`which ${pkg}`, { stdio: 'ignore' });
|
|
92
|
+
success(`${pkg} already installed`);
|
|
93
|
+
return true;
|
|
94
|
+
} catch {
|
|
95
|
+
// Not installed, proceed
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
log(` Installing ${pkg}...`);
|
|
99
|
+
execSync(`pkg install -y ${pkg}`, { stdio: 'inherit' });
|
|
100
|
+
success(`${pkg} installed`);
|
|
101
|
+
return true;
|
|
102
|
+
} catch (err) {
|
|
103
|
+
warn(`${pkg} installation failed (may need manual install)`);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function main() {
|
|
109
|
+
console.log('');
|
|
110
|
+
|
|
111
|
+
// ============================================
|
|
112
|
+
// TERMUX CHECK - CRITICAL
|
|
113
|
+
// ============================================
|
|
114
|
+
if (!isTermux) {
|
|
115
|
+
console.log(colors.bold('╔═══════════════════════════════════════════════════╗'));
|
|
116
|
+
console.log(colors.bold('║ ') + colors.red('NOT COMPATIBLE') + colors.bold(' ║'));
|
|
117
|
+
console.log(colors.bold('╚═══════════════════════════════════════════════════╝'));
|
|
118
|
+
console.log('');
|
|
119
|
+
console.log(colors.yellow(' NexusCLI is designed for Termux on Android only.'));
|
|
120
|
+
console.log(colors.yellow(' Linux/macOS/Windows support is not available in this version.'));
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(colors.gray(' For more information: https://npmjs.com/package/@mmmbuto/nexuscli'));
|
|
123
|
+
console.log('');
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ============================================
|
|
128
|
+
// TERMUX INSTALLATION
|
|
129
|
+
// ============================================
|
|
130
|
+
console.log(colors.bold('╔═══════════════════════════════════════════════════╗'));
|
|
131
|
+
console.log(colors.bold('║ 📱 NexusCLI TRI CLI v0.4.0 ║'));
|
|
132
|
+
console.log(colors.bold('║ Claude • Codex • Gemini ║'));
|
|
133
|
+
console.log(colors.bold('╚═══════════════════════════════════════════════════╝'));
|
|
134
|
+
console.log('');
|
|
135
|
+
|
|
136
|
+
// Required packages
|
|
137
|
+
const packages = ['termux-api', 'termux-tools'];
|
|
138
|
+
|
|
139
|
+
log(colors.cyan('Installing Termux packages:'));
|
|
140
|
+
for (const pkg of packages) {
|
|
141
|
+
installPackage(pkg);
|
|
142
|
+
}
|
|
143
|
+
console.log('');
|
|
144
|
+
|
|
145
|
+
// Create directories
|
|
146
|
+
const HOME = process.env.HOME;
|
|
147
|
+
const dirs = [
|
|
148
|
+
path.join(HOME, '.nexuscli'),
|
|
149
|
+
path.join(HOME, '.nexuscli', 'data'),
|
|
150
|
+
path.join(HOME, '.nexuscli', 'logs'),
|
|
151
|
+
path.join(HOME, '.nexuscli', 'engines'),
|
|
152
|
+
path.join(HOME, '.termux', 'boot')
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
log(colors.cyan('Creating directories:'));
|
|
156
|
+
for (const dir of dirs) {
|
|
157
|
+
try {
|
|
158
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
159
|
+
success(dir.replace(HOME, '~'));
|
|
160
|
+
} catch (err) {
|
|
161
|
+
warn(`Failed to create ${dir}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
console.log('');
|
|
165
|
+
|
|
166
|
+
// Check Termux:API app
|
|
167
|
+
log(colors.cyan('Checking Termux apps:'));
|
|
168
|
+
try {
|
|
169
|
+
execSync('termux-battery-status', { timeout: 5000, stdio: 'ignore' });
|
|
170
|
+
success('Termux:API app working');
|
|
171
|
+
} catch {
|
|
172
|
+
warn('Termux:API app not detected');
|
|
173
|
+
console.log(' Install from F-Droid for notifications & wake-lock');
|
|
174
|
+
}
|
|
175
|
+
console.log('');
|
|
176
|
+
|
|
177
|
+
// ============================================
|
|
178
|
+
// AUTO-RUN WIZARD IF NOT CONFIGURED
|
|
179
|
+
// ============================================
|
|
180
|
+
if (!isConfigured()) {
|
|
181
|
+
console.log(colors.bold('╔═══════════════════════════════════════════════════╗'));
|
|
182
|
+
console.log(colors.bold('║ First time setup - Running wizard... ║'));
|
|
183
|
+
console.log(colors.bold('╚═══════════════════════════════════════════════════╝'));
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
await runWizard();
|
|
187
|
+
console.log('');
|
|
188
|
+
console.log(colors.green(' ✓ Setup complete!'));
|
|
189
|
+
console.log('');
|
|
190
|
+
console.log(` Run ${colors.cyan('nexuscli start')} to launch the server`);
|
|
191
|
+
console.log('');
|
|
192
|
+
} catch (err) {
|
|
193
|
+
console.log('');
|
|
194
|
+
warn('Setup wizard failed or was cancelled');
|
|
195
|
+
console.log(` Run ${colors.cyan('nexuscli init')} to complete setup manually`);
|
|
196
|
+
console.log('');
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
// Already configured
|
|
200
|
+
console.log(colors.bold('╔═══════════════════════════════════════════════════╗'));
|
|
201
|
+
console.log(colors.bold('║ ✅ NexusCLI updated! ║'));
|
|
202
|
+
console.log(colors.bold('╚═══════════════════════════════════════════════════╝'));
|
|
203
|
+
console.log('');
|
|
204
|
+
console.log(` Run ${colors.cyan('nexuscli start')} to launch the server`);
|
|
205
|
+
console.log('');
|
|
206
|
+
console.log(colors.yellow(' Optional apps (install from F-Droid):'));
|
|
207
|
+
console.log(' • Termux:API - notifications & wake-lock');
|
|
208
|
+
console.log(' • Termux:Boot - auto-start on device boot');
|
|
209
|
+
console.log('');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
main().catch(err => {
|
|
214
|
+
console.error('Postinstall error:', err.message);
|
|
215
|
+
process.exit(0); // Don't fail npm install
|
|
216
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard Paths for NexusCLI
|
|
3
|
+
* Termux Mobile First - all paths relative to $HOME
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
const HOME = process.env.HOME || os.homedir();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* NexusCLI standard paths
|
|
14
|
+
*/
|
|
15
|
+
const PATHS = {
|
|
16
|
+
// Main config directory
|
|
17
|
+
CONFIG_DIR: path.join(HOME, '.nexuscli'),
|
|
18
|
+
|
|
19
|
+
// Config file
|
|
20
|
+
CONFIG_FILE: path.join(HOME, '.nexuscli', 'config.json'),
|
|
21
|
+
|
|
22
|
+
// Data directory (database, cache)
|
|
23
|
+
DATA_DIR: path.join(HOME, '.nexuscli', 'data'),
|
|
24
|
+
|
|
25
|
+
// Database file
|
|
26
|
+
DATABASE: path.join(HOME, '.nexuscli', 'data', 'nexuscli.db'),
|
|
27
|
+
|
|
28
|
+
// Logs directory
|
|
29
|
+
LOGS_DIR: path.join(HOME, '.nexuscli', 'logs'),
|
|
30
|
+
|
|
31
|
+
// Server log
|
|
32
|
+
SERVER_LOG: path.join(HOME, '.nexuscli', 'logs', 'server.log'),
|
|
33
|
+
|
|
34
|
+
// PID file for daemon
|
|
35
|
+
PID_FILE: path.join(HOME, '.nexuscli', 'nexuscli.pid'),
|
|
36
|
+
|
|
37
|
+
// Engines config directory
|
|
38
|
+
ENGINES_DIR: path.join(HOME, '.nexuscli', 'engines'),
|
|
39
|
+
|
|
40
|
+
// Termux boot script
|
|
41
|
+
BOOT_SCRIPT: path.join(HOME, '.termux', 'boot', 'nexuscli-start.sh'),
|
|
42
|
+
|
|
43
|
+
// Claude CLI history (read-only)
|
|
44
|
+
CLAUDE_HISTORY: path.join(HOME, '.claude', 'history.jsonl'),
|
|
45
|
+
CLAUDE_PROJECTS: path.join(HOME, '.claude', 'projects'),
|
|
46
|
+
|
|
47
|
+
// Default workspace
|
|
48
|
+
DEFAULT_WORKSPACE: path.join(HOME, 'Dev')
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Ensure all required directories exist
|
|
53
|
+
*/
|
|
54
|
+
function ensureDirectories() {
|
|
55
|
+
const dirs = [
|
|
56
|
+
PATHS.CONFIG_DIR,
|
|
57
|
+
PATHS.DATA_DIR,
|
|
58
|
+
PATHS.LOGS_DIR,
|
|
59
|
+
PATHS.ENGINES_DIR
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
for (const dir of dirs) {
|
|
63
|
+
if (!fs.existsSync(dir)) {
|
|
64
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Ensure Termux boot directory exists
|
|
71
|
+
*/
|
|
72
|
+
function ensureBootDirectory() {
|
|
73
|
+
const bootDir = path.join(HOME, '.termux', 'boot');
|
|
74
|
+
if (!fs.existsSync(bootDir)) {
|
|
75
|
+
fs.mkdirSync(bootDir, { recursive: true });
|
|
76
|
+
}
|
|
77
|
+
return bootDir;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get path resolving ~ to HOME
|
|
82
|
+
*/
|
|
83
|
+
function resolvePath(p) {
|
|
84
|
+
if (p.startsWith('~')) {
|
|
85
|
+
return path.join(HOME, p.slice(1));
|
|
86
|
+
}
|
|
87
|
+
return path.resolve(p);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get relative path from HOME (for display)
|
|
92
|
+
*/
|
|
93
|
+
function toDisplayPath(p) {
|
|
94
|
+
if (p.startsWith(HOME)) {
|
|
95
|
+
return '~' + p.slice(HOME.length);
|
|
96
|
+
}
|
|
97
|
+
return p;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
PATHS,
|
|
102
|
+
HOME,
|
|
103
|
+
ensureDirectories,
|
|
104
|
+
ensureBootDirectory,
|
|
105
|
+
resolvePath,
|
|
106
|
+
toDisplayPath
|
|
107
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Termux Detection and Utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { execSync, spawn } = require('child_process');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Check if running in Termux environment
|
|
11
|
+
*/
|
|
12
|
+
function isTermux() {
|
|
13
|
+
return (
|
|
14
|
+
process.env.PREFIX?.includes('com.termux') ||
|
|
15
|
+
fs.existsSync('/data/data/com.termux') ||
|
|
16
|
+
process.env.TERMUX_VERSION !== undefined
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get Termux prefix path
|
|
22
|
+
*/
|
|
23
|
+
function getPrefix() {
|
|
24
|
+
return process.env.PREFIX || '/data/data/com.termux/files/usr';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if a Termux package is installed
|
|
29
|
+
*/
|
|
30
|
+
function isPackageInstalled(pkg) {
|
|
31
|
+
try {
|
|
32
|
+
execSync(`which ${pkg}`, { stdio: 'ignore' });
|
|
33
|
+
return true;
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Install a Termux package
|
|
41
|
+
*/
|
|
42
|
+
function installPackage(pkg) {
|
|
43
|
+
try {
|
|
44
|
+
execSync(`pkg install -y ${pkg}`, { stdio: 'inherit' });
|
|
45
|
+
return true;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if Termux:API app is working
|
|
53
|
+
*/
|
|
54
|
+
function isTermuxApiWorking() {
|
|
55
|
+
try {
|
|
56
|
+
execSync('termux-battery-status', { timeout: 5000, stdio: 'ignore' });
|
|
57
|
+
return true;
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if Termux:Boot is available
|
|
65
|
+
*/
|
|
66
|
+
function isTermuxBootAvailable() {
|
|
67
|
+
const bootDir = path.join(process.env.HOME, '.termux', 'boot');
|
|
68
|
+
return fs.existsSync(bootDir);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Acquire Termux wake lock
|
|
73
|
+
*/
|
|
74
|
+
function acquireWakeLock() {
|
|
75
|
+
if (!isTermux()) return false;
|
|
76
|
+
try {
|
|
77
|
+
spawn('termux-wake-lock', [], { detached: true, stdio: 'ignore' }).unref();
|
|
78
|
+
return true;
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Release Termux wake lock
|
|
86
|
+
*/
|
|
87
|
+
function releaseWakeLock() {
|
|
88
|
+
if (!isTermux()) return false;
|
|
89
|
+
try {
|
|
90
|
+
spawn('termux-wake-unlock', [], { detached: true, stdio: 'ignore' }).unref();
|
|
91
|
+
return true;
|
|
92
|
+
} catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Send Termux notification
|
|
99
|
+
*/
|
|
100
|
+
function sendNotification(title, content, id = 'nexuscli') {
|
|
101
|
+
if (!isTermux()) return false;
|
|
102
|
+
try {
|
|
103
|
+
execSync(`termux-notification --id ${id} --title "${title}" --content "${content}"`, {
|
|
104
|
+
stdio: 'ignore'
|
|
105
|
+
});
|
|
106
|
+
return true;
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get required Termux packages
|
|
114
|
+
*/
|
|
115
|
+
function getRequiredPackages() {
|
|
116
|
+
return ['termux-api', 'termux-tools'];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Check all required packages
|
|
121
|
+
*/
|
|
122
|
+
function checkRequiredPackages() {
|
|
123
|
+
const packages = getRequiredPackages();
|
|
124
|
+
const status = {};
|
|
125
|
+
|
|
126
|
+
for (const pkg of packages) {
|
|
127
|
+
status[pkg] = isPackageInstalled(pkg);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return status;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = {
|
|
134
|
+
isTermux,
|
|
135
|
+
getPrefix,
|
|
136
|
+
isPackageInstalled,
|
|
137
|
+
installPackage,
|
|
138
|
+
isTermuxApiWorking,
|
|
139
|
+
isTermuxBootAvailable,
|
|
140
|
+
acquireWakeLock,
|
|
141
|
+
releaseWakeLock,
|
|
142
|
+
sendNotification,
|
|
143
|
+
getRequiredPackages,
|
|
144
|
+
checkRequiredPackages
|
|
145
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mmmbuto/nexuscli",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Mobile-first AI Control Plane - Web UI for Claude/Codex/Gemini CLI",
|
|
6
|
+
"main": "lib/server/server.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"nexuscli": "./bin/nexuscli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node lib/server/server.js",
|
|
12
|
+
"dev": "node lib/server/server.js",
|
|
13
|
+
"postinstall": "node lib/setup/postinstall.js",
|
|
14
|
+
"test": "jest",
|
|
15
|
+
"test:watch": "jest --watch",
|
|
16
|
+
"test:coverage": "jest --coverage",
|
|
17
|
+
"build:frontend": "cd frontend && npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"nexuscli",
|
|
21
|
+
"termux",
|
|
22
|
+
"cli",
|
|
23
|
+
"ai",
|
|
24
|
+
"claude",
|
|
25
|
+
"codex",
|
|
26
|
+
"gemini",
|
|
27
|
+
"control-plane",
|
|
28
|
+
"mobile-first",
|
|
29
|
+
"android"
|
|
30
|
+
],
|
|
31
|
+
"author": "DioNanos",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"homepage": "https://github.com/DioNanos/nexuscli",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/DioNanos/nexuscli/issues"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/DioNanos/nexuscli.git"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public",
|
|
43
|
+
"registry": "https://registry.npmjs.org/"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"bin/",
|
|
47
|
+
"lib/",
|
|
48
|
+
"frontend/dist/",
|
|
49
|
+
"package.json",
|
|
50
|
+
"README.md",
|
|
51
|
+
"LICENSE"
|
|
52
|
+
],
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
},
|
|
56
|
+
"os": [
|
|
57
|
+
"linux",
|
|
58
|
+
"android"
|
|
59
|
+
],
|
|
60
|
+
"cpu": [
|
|
61
|
+
"arm64",
|
|
62
|
+
"x64"
|
|
63
|
+
],
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"bcryptjs": "^3.0.3",
|
|
66
|
+
"chalk": "^4.1.2",
|
|
67
|
+
"commander": "^12.1.0",
|
|
68
|
+
"cors": "^2.8.5",
|
|
69
|
+
"express": "^4.18.2",
|
|
70
|
+
"express-rate-limit": "^8.2.1",
|
|
71
|
+
"inquirer": "^8.2.6",
|
|
72
|
+
"jsonwebtoken": "^9.0.2",
|
|
73
|
+
"multer": "^2.0.2",
|
|
74
|
+
"node-cache": "^5.1.2",
|
|
75
|
+
"ora": "^5.4.1",
|
|
76
|
+
"sql.js": "^1.13.0",
|
|
77
|
+
"uuid": "^9.0.0"
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"jest": "^30.2.0"
|
|
81
|
+
}
|
|
82
|
+
}
|