dex-termux-cli 0.2.0 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dex-termux-cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Termux CLI for Android with guided search, readable tree views, command help, and safe project install flows for Python, Node, and PHP.",
6
6
  "keywords": [
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import { spawn } from 'node:child_process';
3
3
  import { getUserConfigPath, loadUserConfig } from '../core/config.js';
4
+ import { resolveInteractiveShell } from '../utils/shell.js';
4
5
 
5
6
  const ANDROID_ROOT = '/sdcard';
6
7
 
@@ -19,14 +20,15 @@ export async function runAndroidShellCommand() {
19
20
  throw new Error(`No se puede acceder a la ruta Android: ${ANDROID_ROOT}`);
20
21
  }
21
22
 
22
- const shell = process.env.SHELL || '/data/data/com.termux/files/usr/bin/sh';
23
+ const interactiveShell = await resolveInteractiveShell();
23
24
 
24
25
  console.log(`Abriendo shell en ${ANDROID_ROOT}`);
26
+ console.log(`Shell : ${interactiveShell.shellName} interactiva`);
25
27
  console.log('Escribe exit para volver.');
26
28
  console.log('');
27
29
 
28
30
  await new Promise((resolve, reject) => {
29
- const child = spawn(shell, {
31
+ const child = spawn(interactiveShell.shellPath, interactiveShell.shellArgs, {
30
32
  cwd: ANDROID_ROOT,
31
33
  stdio: 'inherit',
32
34
  env: {
@@ -4,6 +4,7 @@ import { createHash } from 'node:crypto';
4
4
  import { spawn } from 'node:child_process';
5
5
  import { loadUserConfig } from '../core/config.js';
6
6
  import { detectProjectContext } from '../utils/project-context.js';
7
+ import { resolveInteractiveShell } from '../utils/shell.js';
7
8
 
8
9
  export async function runSafeShellCommand() {
9
10
  const config = await loadUserConfig();
@@ -25,7 +26,7 @@ export async function runSafeShellCommand() {
25
26
 
26
27
  const projectKey = getProjectKey(projectRoot);
27
28
  const projectState = config.projects && config.projects[projectKey] ? config.projects[projectKey] : null;
28
- const shell = process.env.SHELL || '/data/data/com.termux/files/usr/bin/sh';
29
+ const interactiveShell = await resolveInteractiveShell();
29
30
  const reason = projectState && projectState.preferSafeInstall
30
31
  ? 'Este proyecto ya quedo marcado para modo seguro.'
31
32
  : 'Entrando al entorno seguro manual de Dex.';
@@ -47,13 +48,14 @@ export async function runSafeShellCommand() {
47
48
  console.log('Proyecto : ' + projectRoot);
48
49
  console.log('Entorno : ' + venvDir);
49
50
  console.log('Python : ' + rescuePython);
51
+ console.log('Shell : ' + interactiveShell.shellName + ' interactiva');
50
52
  console.log('Nota : ' + reason);
51
53
  console.log('Usa python, pip o python bot.py desde aqui.');
52
54
  console.log('Si sales de esta shell, el modo seguro deja de estar activo.');
53
55
  console.log('Escribe exit para volver a tu shell normal.');
54
56
  console.log('');
55
57
 
56
- await openSafeShell(shell, projectRoot, {
58
+ await openSafeShell(interactiveShell, projectRoot, {
57
59
  PATH: nextPath,
58
60
  VIRTUAL_ENV: venvDir,
59
61
  DEX_CONTEXT: 'safe-shell',
@@ -82,13 +84,14 @@ export async function runSafeShellCommand() {
82
84
  console.log('Proyecto : ' + projectRoot);
83
85
  console.log('Seguro : ' + safeProjectDir);
84
86
  console.log('Modulos : ' + nodeModulesDir);
87
+ console.log('Shell : ' + interactiveShell.shellName + ' interactiva');
85
88
  console.log('Nota : ' + reason);
86
89
  console.log('Usa node, npm o npm run desde aqui.');
87
90
  console.log('Esta shell abre la copia segura del proyecto dentro de HOME.');
88
91
  console.log('Escribe exit para volver a tu shell normal.');
89
92
  console.log('');
90
93
 
91
- await openSafeShell(shell, safeProjectDir, {
94
+ await openSafeShell(interactiveShell, safeProjectDir, {
92
95
  PATH: nextPath,
93
96
  DEX_CONTEXT: 'safe-shell',
94
97
  DEX_SAFE_PROJECT: projectRoot,
@@ -118,6 +121,7 @@ export async function runSafeShellCommand() {
118
121
  console.log('Proyecto : ' + projectRoot);
119
122
  console.log('Seguro : ' + safeProjectDir);
120
123
  console.log('Vendor : ' + vendorDir);
124
+ console.log('Shell : ' + interactiveShell.shellName + ' interactiva');
121
125
  console.log('Nota : ' + reason);
122
126
  console.log('Usa php, composer o binarios de vendor/bin desde aqui.');
123
127
  console.log('Esta shell abre la copia segura del proyecto dentro de HOME.');
@@ -126,7 +130,7 @@ export async function runSafeShellCommand() {
126
130
 
127
131
  await fs.mkdir(composerHome, { recursive: true });
128
132
 
129
- await openSafeShell(shell, safeProjectDir, {
133
+ await openSafeShell(interactiveShell, safeProjectDir, {
130
134
  PATH: nextPath,
131
135
  COMPOSER_HOME: composerHome,
132
136
  DEX_CONTEXT: 'safe-shell',
@@ -171,9 +175,9 @@ function supportsSafeMode(projectType) {
171
175
  return projectType === 'python' || projectType === 'node' || projectType === 'php';
172
176
  }
173
177
 
174
- function openSafeShell(shell, cwd, envPatch) {
178
+ function openSafeShell(interactiveShell, cwd, envPatch) {
175
179
  return new Promise((resolve, reject) => {
176
- const child = spawn(shell, ['-i'], {
180
+ const child = spawn(interactiveShell.shellPath, interactiveShell.shellArgs, {
177
181
  cwd,
178
182
  stdio: 'inherit',
179
183
  env: {
@@ -0,0 +1,54 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ const TERMUX_ZSH = '/data/data/com.termux/files/usr/bin/zsh';
5
+ const TERMUX_BASH = '/data/data/com.termux/files/usr/bin/bash';
6
+ const TERMUX_SH = '/data/data/com.termux/files/usr/bin/sh';
7
+
8
+ export async function resolveInteractiveShell() {
9
+ const preferredShell = process.env.DEX_PREFERRED_SHELL || process.env.SHELL || '';
10
+ const candidates = buildShellCandidates(preferredShell);
11
+
12
+ for (const shellPath of candidates) {
13
+ if (!shellPath) {
14
+ continue;
15
+ }
16
+
17
+ if (await shellExists(shellPath)) {
18
+ return {
19
+ shellPath,
20
+ shellArgs: ['-i'],
21
+ shellName: path.basename(shellPath),
22
+ };
23
+ }
24
+ }
25
+
26
+ return {
27
+ shellPath: TERMUX_SH,
28
+ shellArgs: ['-i'],
29
+ shellName: 'sh',
30
+ };
31
+ }
32
+
33
+ function buildShellCandidates(preferredShell) {
34
+ const shellName = path.basename(preferredShell || '');
35
+
36
+ if (preferredShell && shellName && shellName !== 'sh') {
37
+ return dedupe([preferredShell, TERMUX_ZSH, TERMUX_BASH, TERMUX_SH]);
38
+ }
39
+
40
+ return dedupe([TERMUX_ZSH, TERMUX_BASH, preferredShell, TERMUX_SH]);
41
+ }
42
+
43
+ async function shellExists(shellPath) {
44
+ try {
45
+ await fs.access(shellPath);
46
+ return true;
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+
52
+ function dedupe(values) {
53
+ return [...new Set(values.filter(Boolean))];
54
+ }