@mmmbuto/nexuscli 0.9.9 → 0.9.10

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
@@ -244,3 +244,19 @@ npm run dev
244
244
 
245
245
  MIT License.
246
246
  See `LICENSE` for details.
247
+
248
+ ## PTY Support (Shared Library)
249
+
250
+ NexusCLI uses `@mmmbuto/pty-termux-utils` as a shared library for PTY
251
+ management across all Termux CLI projects (Gemini, Qwen, Nexus).
252
+
253
+ - **Native PTY:** Uses `@mmmbuto/node-pty-android-arm64@~1.1.0` when available
254
+ - **Linux ARM64:** Uses `@lydell/node-pty-linux-arm64@~1.2.0-beta.2` when available
255
+ - **Fallback:** Gracefully degrades to `child_process` adapter
256
+ - **Debug Logging:** Enable with `PTY_DEBUG=1` environment variable
257
+ - **Architecture:** See `@mmmbuto/pty-termux-utils` documentation
258
+
259
+ ```bash
260
+ # Enable PTY debug logging
261
+ PTY_DEBUG=1 nexuscli start
262
+ ```
@@ -0,0 +1,48 @@
1
+ /**
2
+ * PTY Loader for NexusCLI (legacy compatibility wrapper)
3
+ * Delegates to @mmmbuto/pty-termux-utils multi-provider strategy.
4
+ *
5
+ * Provider priority:
6
+ * 1. Termux: @mmmbuto/node-pty-android-arm64
7
+ * 2. Linux ARM64: @lydell/node-pty-linux-arm64
8
+ * 3. Fallback: child_process adapter (pty-adapter.js)
9
+ *
10
+ * @version 1.1.0
11
+ */
12
+
13
+ const { getPty: getSharedPty, logDebug } = require('@mmmbuto/pty-termux-utils');
14
+ const tryRequire = (moduleName) => {
15
+ try {
16
+ require(moduleName);
17
+ return true;
18
+ } catch (_) {
19
+ return false;
20
+ }
21
+ };
22
+
23
+ module.exports = {
24
+ /**
25
+ * Get PTY implementation with fallback
26
+ * @returns {Promise<{module: any, name: string}|null>}
27
+ */
28
+ async getPty() {
29
+ const pty = await getSharedPty();
30
+ if (pty) {
31
+ logDebug(`Using native PTY provider: ${pty.name}`);
32
+ return pty;
33
+ }
34
+ logDebug('Native PTY not available, using fallback adapter');
35
+ return null;
36
+ },
37
+
38
+ /**
39
+ * Synchronous check if PTY is available
40
+ * @returns {boolean}
41
+ */
42
+ isPtyAvailable() {
43
+ return (
44
+ tryRequire('@mmmbuto/node-pty-android-arm64') ||
45
+ tryRequire('@lydell/node-pty-linux-arm64')
46
+ );
47
+ }
48
+ };
@@ -1,81 +1,124 @@
1
1
  /**
2
- * PTY Adapter for NexusCLI (Termux)
3
- * Provides node-pty-like interface using child_process.spawn
4
- * Termux-only: no native node-pty compilation needed
2
+ * PTY Adapter for NexusCLI (Termux + Linux ARM64)
3
+ * Wrapper around @mmmbuto/pty-termux-utils shared library
4
+ * Provides node-pty-like interface with graceful fallback
5
5
  *
6
- * @version 0.5.0 - Added stdin support for interrupt (ESC key)
6
+ * @version 1.0.0
7
7
  */
8
8
 
9
- const { spawn: cpSpawn } = require('child_process');
9
+ const { createFallbackAdapter } = require('@mmmbuto/pty-termux-utils');
10
+ const tryRequire = (moduleName) => {
11
+ try {
12
+ require(moduleName);
13
+ return true;
14
+ } catch (_) {
15
+ return false;
16
+ }
17
+ };
18
+
19
+ // Create shared fallback adapter (always uses child_process)
20
+ const fallbackAdapter = createFallbackAdapter();
21
+
22
+ // Cache for native PTY (sync access)
23
+ let cachedNativePty = null;
10
24
 
11
25
  /**
12
- * Spawn a process with node-pty-like interface
26
+ * Initialize native PTY (called at startup)
27
+ */
28
+ async function initNativePty() {
29
+ if (cachedNativePty !== null) {
30
+ return cachedNativePty;
31
+ }
32
+
33
+ try {
34
+ const { getPty } = require('@mmmbuto/pty-termux-utils');
35
+ cachedNativePty = await getPty();
36
+ return cachedNativePty;
37
+ } catch (e) {
38
+ cachedNativePty = false; // Error loading
39
+ return null;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Synchronous spawn (for compatibility with existing wrapper code)
45
+ * Uses fallback adapter for simplicity (no native PTY in sync mode)
13
46
  * @param {string} command - Command to spawn
14
47
  * @param {string[]} args - Arguments
15
48
  * @param {Object} options - Spawn options (cwd, env)
16
- * @returns {Object} PTY-like interface with onData, onExit, kill
49
+ * @returns {Object} PTY-like interface
17
50
  */
18
51
  function spawn(command, args, options = {}) {
19
- const proc = cpSpawn(command, args, {
20
- cwd: options.cwd,
21
- env: options.env,
22
- shell: false,
23
- stdio: ['pipe', 'pipe', 'pipe'], // stdin enabled for interrupt support
24
- });
25
-
26
- const dataHandlers = [];
27
- const exitHandlers = [];
28
- const errorHandlers = [];
52
+ return fallbackAdapter.spawn(command, args, options);
53
+ }
29
54
 
30
- proc.stdout.on('data', (buf) => {
31
- const data = buf.toString();
32
- console.log('[PTY-Adapter] stdout:', data.substring(0, 200));
33
- dataHandlers.forEach((fn) => fn(data));
34
- });
55
+ /**
56
+ * Async spawn with native PTY support
57
+ * @param {string} command - Command to spawn
58
+ * @param {string[]} args - Arguments
59
+ * @param {Object} options - Spawn options (cwd, env)
60
+ * @returns {Promise<Object>} PTY-like interface
61
+ */
62
+ async function spawnAsync(command, args, options = {}) {
63
+ const pty = await initNativePty();
64
+
65
+ if (pty) {
66
+ // Use native PTY
67
+ const proc = pty.module.spawn(command, args, {
68
+ cols: options.cols || 80,
69
+ rows: options.rows || 24,
70
+ cwd: options.cwd,
71
+ env: options.env,
72
+ });
35
73
 
36
- proc.stderr.on('data', (buf) => {
37
- const data = buf.toString();
38
- console.log('[PTY-Adapter] stderr:', data.substring(0, 200));
39
- dataHandlers.forEach((fn) => fn(data));
40
- });
74
+ const dataHandlers = [];
75
+ const exitHandlers = [];
41
76
 
42
- proc.on('close', (code) => {
43
- exitHandlers.forEach((fn) => fn({ exitCode: code ?? 0 }));
44
- });
77
+ proc.on('data', (data) => {
78
+ dataHandlers.forEach((fn) => fn(data));
79
+ });
45
80
 
46
- proc.on('error', (err) => {
47
- console.error('[PTY-Adapter] Error:', err.message);
48
- errorHandlers.forEach((fn) => fn(err));
49
- });
81
+ proc.on('exit', (code, signal) => {
82
+ exitHandlers.forEach((fn) => fn({ exitCode: code, signal }));
83
+ });
50
84
 
51
- return {
52
- onData: (fn) => dataHandlers.push(fn),
53
- onExit: (fn) => exitHandlers.push(fn),
54
- onError: (fn) => errorHandlers.push(fn),
55
- write: (data) => proc.stdin?.writable && proc.stdin.write(data),
56
- /**
57
- * Send ESC key (0x1B) to interrupt CLI
58
- * Used to gracefully stop Claude/Gemini CLI execution
59
- * @returns {boolean} true if ESC was sent successfully
60
- */
61
- sendEsc: () => {
62
- if (proc.stdin?.writable) {
63
- proc.stdin.write('\x1B');
85
+ return {
86
+ onData: (fn) => dataHandlers.push(fn),
87
+ onExit: (fn) => exitHandlers.push(fn),
88
+ onError: (fn) => {}, // No error event in native PTY
89
+ write: (data) => proc.write(data),
90
+ sendEsc: () => {
91
+ proc.write('\x1B');
64
92
  return true;
65
- }
66
- return false;
67
- },
68
- kill: (signal = 'SIGTERM') => proc.kill(signal),
69
- pid: proc.pid,
70
- };
93
+ },
94
+ resize: (cols, rows) => proc.resize(cols, rows),
95
+ kill: (signal = 'SIGTERM') => proc.kill(signal),
96
+ pid: proc.pid,
97
+ };
98
+ } else {
99
+ // Use fallback adapter
100
+ return fallbackAdapter.spawn(command, args, options);
101
+ }
71
102
  }
72
103
 
73
104
  /**
74
- * Check if native PTY is available
75
- * Always returns false on Termux (we use spawn adapter)
105
+ * Synchronous check if PTY is available
106
+ * @returns {boolean}
76
107
  */
77
108
  function isPtyAvailable() {
78
- return false;
109
+ return (
110
+ tryRequire('@mmmbuto/node-pty-android-arm64') ||
111
+ tryRequire('@lydell/node-pty-linux-arm64')
112
+ );
79
113
  }
80
114
 
81
- module.exports = { spawn, isPtyAvailable };
115
+ // Initialize native PTY in background (non-blocking)
116
+ initNativePty().catch(() => {
117
+ // Silently ignore initialization errors
118
+ });
119
+
120
+ module.exports = {
121
+ spawn, // Sync spawn (uses fallback)
122
+ spawnAsync, // Async spawn (uses native PTY if available)
123
+ isPtyAvailable,
124
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * PTY Provider for NexusCLI (Termux + Linux ARM64)
3
+ * Provides PTY detection with fallback to child_process adapter
4
+ * Uses @mmmbuto/pty-termux-utils shared library
5
+ *
6
+ * @version 1.0.0
7
+ */
8
+
9
+ const { getPty: getSharedPty, createFallbackAdapter } = require('@mmmbuto/pty-termux-utils');
10
+ const tryRequire = (moduleName) => {
11
+ try {
12
+ require(moduleName);
13
+ return true;
14
+ } catch (_) {
15
+ return false;
16
+ }
17
+ };
18
+
19
+ /**
20
+ * Get PTY implementation with fallback
21
+ * @returns {Promise<{module: any, name: string}|null>}
22
+ */
23
+ async function getPty() {
24
+ const pty = await getSharedPty();
25
+ if (pty) {
26
+ return pty;
27
+ }
28
+ return null;
29
+ }
30
+
31
+ /**
32
+ * Get fallback adapter (child_process)
33
+ * @returns {Object} Adapter with spawn method
34
+ */
35
+ function getFallbackAdapter() {
36
+ return createFallbackAdapter();
37
+ }
38
+
39
+ /**
40
+ * Synchronous check if PTY is available
41
+ * @returns {boolean}
42
+ */
43
+ function isPtyAvailable() {
44
+ return (
45
+ tryRequire('@mmmbuto/node-pty-android-arm64') ||
46
+ tryRequire('@lydell/node-pty-linux-arm64')
47
+ );
48
+ }
49
+
50
+ module.exports = {
51
+ getPty,
52
+ getFallbackAdapter,
53
+ isPtyAvailable,
54
+ // Re-export from shared library
55
+ spawnPty: require('@mmmbuto/pty-termux-utils').spawnPty,
56
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmmbuto/nexuscli",
3
- "version": "0.9.9",
3
+ "version": "0.9.10",
4
4
  "description": "NexusCLI - TRI CLI Control Plane (Claude/Codex/Gemini/Qwen)",
5
5
  "main": "lib/server/server.js",
6
6
  "bin": {
@@ -58,6 +58,7 @@
58
58
  "x64"
59
59
  ],
60
60
  "dependencies": {
61
+ "@mmmbuto/pty-termux-utils": "^1.1.0",
61
62
  "bcryptjs": "^3.0.3",
62
63
  "chalk": "^4.1.2",
63
64
  "commander": "^12.1.0",
@@ -75,5 +76,8 @@
75
76
  },
76
77
  "devDependencies": {
77
78
  "jest": "^30.2.0"
79
+ },
80
+ "optionalDependencies": {
81
+ "@mmmbuto/node-pty-android-arm64": "~1.1.0"
78
82
  }
79
83
  }