@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.
Files changed (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/bin/nexuscli.js +117 -0
  4. package/frontend/dist/apple-touch-icon.png +0 -0
  5. package/frontend/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  6. package/frontend/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  7. package/frontend/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  8. package/frontend/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  9. package/frontend/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  10. package/frontend/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  11. package/frontend/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  12. package/frontend/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  13. package/frontend/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  14. package/frontend/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  15. package/frontend/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  16. package/frontend/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  17. package/frontend/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  18. package/frontend/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  19. package/frontend/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  20. package/frontend/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  21. package/frontend/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  22. package/frontend/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  23. package/frontend/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  24. package/frontend/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  25. package/frontend/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  26. package/frontend/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  27. package/frontend/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  28. package/frontend/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  29. package/frontend/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  30. package/frontend/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  31. package/frontend/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  32. package/frontend/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  33. package/frontend/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  34. package/frontend/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  35. package/frontend/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  36. package/frontend/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  37. package/frontend/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  38. package/frontend/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  39. package/frontend/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  40. package/frontend/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  41. package/frontend/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  42. package/frontend/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  43. package/frontend/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  44. package/frontend/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  45. package/frontend/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  46. package/frontend/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  47. package/frontend/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  48. package/frontend/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  49. package/frontend/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  50. package/frontend/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  51. package/frontend/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  52. package/frontend/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  53. package/frontend/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  54. package/frontend/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  55. package/frontend/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  56. package/frontend/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  57. package/frontend/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  58. package/frontend/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  59. package/frontend/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  60. package/frontend/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  61. package/frontend/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  62. package/frontend/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  63. package/frontend/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  64. package/frontend/dist/assets/index-Bn_l1e6e.css +1 -0
  65. package/frontend/dist/assets/index-CikJbUR5.js +8617 -0
  66. package/frontend/dist/browserconfig.xml +12 -0
  67. package/frontend/dist/favicon-16x16.png +0 -0
  68. package/frontend/dist/favicon-32x32.png +0 -0
  69. package/frontend/dist/favicon-48x48.png +0 -0
  70. package/frontend/dist/favicon.ico +0 -0
  71. package/frontend/dist/icon-192.png +0 -0
  72. package/frontend/dist/icon-512.png +0 -0
  73. package/frontend/dist/icon-maskable-192.png +0 -0
  74. package/frontend/dist/icon-maskable-512.png +0 -0
  75. package/frontend/dist/index.html +79 -0
  76. package/frontend/dist/manifest.json +75 -0
  77. package/frontend/dist/sw.js +122 -0
  78. package/frontend/package.json +28 -0
  79. package/lib/cli/api.js +156 -0
  80. package/lib/cli/boot.js +172 -0
  81. package/lib/cli/config.js +185 -0
  82. package/lib/cli/engines.js +257 -0
  83. package/lib/cli/init.js +660 -0
  84. package/lib/cli/logs.js +72 -0
  85. package/lib/cli/start.js +220 -0
  86. package/lib/cli/status.js +187 -0
  87. package/lib/cli/stop.js +64 -0
  88. package/lib/cli/uninstall.js +194 -0
  89. package/lib/cli/users.js +295 -0
  90. package/lib/cli/workspaces.js +337 -0
  91. package/lib/config/manager.js +233 -0
  92. package/lib/server/.env.example +20 -0
  93. package/lib/server/db/adapter.js +314 -0
  94. package/lib/server/db/drivers/better-sqlite3.js +38 -0
  95. package/lib/server/db/drivers/sql-js.js +75 -0
  96. package/lib/server/db/migrate.js +174 -0
  97. package/lib/server/db/migrations/001_ultra_light_schema.sql +96 -0
  98. package/lib/server/db/migrations/002_session_conversation_mapping.sql +19 -0
  99. package/lib/server/db/migrations/003_message_engine_tracking.sql +18 -0
  100. package/lib/server/db/migrations/004_performance_indexes.sql +16 -0
  101. package/lib/server/db.js +2 -0
  102. package/lib/server/lib/cli-wrapper.js +164 -0
  103. package/lib/server/lib/output-parser.js +132 -0
  104. package/lib/server/lib/pty-adapter.js +57 -0
  105. package/lib/server/middleware/auth.js +103 -0
  106. package/lib/server/models/Conversation.js +259 -0
  107. package/lib/server/models/Message.js +228 -0
  108. package/lib/server/models/User.js +115 -0
  109. package/lib/server/package-lock.json +5895 -0
  110. package/lib/server/routes/auth.js +168 -0
  111. package/lib/server/routes/chat.js +206 -0
  112. package/lib/server/routes/codex.js +205 -0
  113. package/lib/server/routes/conversations.js +224 -0
  114. package/lib/server/routes/gemini.js +228 -0
  115. package/lib/server/routes/jobs.js +317 -0
  116. package/lib/server/routes/messages.js +60 -0
  117. package/lib/server/routes/models.js +198 -0
  118. package/lib/server/routes/sessions.js +285 -0
  119. package/lib/server/routes/upload.js +134 -0
  120. package/lib/server/routes/wake-lock.js +95 -0
  121. package/lib/server/routes/workspace.js +80 -0
  122. package/lib/server/routes/workspaces.js +142 -0
  123. package/lib/server/scripts/cleanup-ghost-sessions.js +71 -0
  124. package/lib/server/scripts/seed-users.js +37 -0
  125. package/lib/server/scripts/test-history-access.js +50 -0
  126. package/lib/server/server.js +227 -0
  127. package/lib/server/services/cache.js +85 -0
  128. package/lib/server/services/claude-wrapper.js +312 -0
  129. package/lib/server/services/cli-loader.js +384 -0
  130. package/lib/server/services/codex-output-parser.js +277 -0
  131. package/lib/server/services/codex-wrapper.js +224 -0
  132. package/lib/server/services/context-bridge.js +289 -0
  133. package/lib/server/services/gemini-output-parser.js +398 -0
  134. package/lib/server/services/gemini-wrapper.js +249 -0
  135. package/lib/server/services/history-sync.js +407 -0
  136. package/lib/server/services/output-parser.js +415 -0
  137. package/lib/server/services/session-manager.js +465 -0
  138. package/lib/server/services/summary-generator.js +259 -0
  139. package/lib/server/services/workspace-manager.js +516 -0
  140. package/lib/server/tests/history-sync.test.js +90 -0
  141. package/lib/server/tests/integration-session-sync.test.js +151 -0
  142. package/lib/server/tests/integration.test.js +76 -0
  143. package/lib/server/tests/performance.test.js +118 -0
  144. package/lib/server/tests/services.test.js +160 -0
  145. package/lib/setup/postinstall.js +216 -0
  146. package/lib/utils/paths.js +107 -0
  147. package/lib/utils/termux.js +145 -0
  148. 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
+ }