cli-setu 0.3.1 → 0.3.2

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": "cli-setu",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Bridge your terminal to your phone — run and approve Claude Code, Codex CLI and other AI coding agents from anywhere.",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -19,6 +19,7 @@
19
19
  // regex for exactly what shape it expects today.
20
20
  const fs = require('fs');
21
21
  const path = require('path');
22
+ const { resolveWindowsBin } = require('./resolveWindowsBin');
22
23
 
23
24
  function queueFileFor(baseDir, cwd) {
24
25
  const safeName = cwd.replace(/[\\/:]/g, '_');
@@ -249,10 +250,7 @@ module.exports = {
249
250
  // icon + color stands in until a real logo image is supplied.
250
251
  icon: { name: 'sparkles', color: '#D97757' }, // Anthropic's own public brand accent is a terracotta/orange - approximated here, not an official value
251
252
  spawn: {
252
- command:
253
- process.platform === 'win32'
254
- ? 'C:\\Users\\hp\\AppData\\Roaming\\npm\\claude.cmd'
255
- : 'claude',
253
+ command: process.platform === 'win32' ? resolveWindowsBin('claude') : 'claude',
256
254
  args: () => ['--permission-mode', 'default'],
257
255
  },
258
256
  approvals: {
@@ -48,6 +48,8 @@
48
48
  // laptop/folder doesn't stall waiting on a phone tap for a purely local
49
49
  // one-time step - see supervisor-qr.js's onData handler for where this
50
50
  // gets called before real-approval detection even runs.
51
+ const { resolveWindowsBin } = require('./resolveWindowsBin');
52
+
51
53
  const ONBOARDING_TRUST_RE = /do you trust the contents of this directory\?/i;
52
54
  const ONBOARDING_SANDBOX_RE = /set up the codex agent sandbox/i;
53
55
 
@@ -193,14 +195,12 @@ module.exports = {
193
195
  // actual logo (no official asset bundled/fabricated here).
194
196
  icon: { name: 'terminal', color: '#10A37F' }, // OpenAI's own public brand accent is a teal/green - approximated here, not an official value
195
197
  spawn: {
196
- // Real, verified path (see `where codex` / `Get-Command codex` output
197
- // in the project chat history) - node-pty on Windows needs a real
198
- // resolved executable, the same requirement claude.js's spawn.command
199
- // already works around for Claude Code.
200
- command:
201
- process.platform === 'win32'
202
- ? 'C:\\Users\\hp\\AppData\\Roaming\\npm\\codex.cmd'
203
- : 'codex',
198
+ // node-pty on Windows needs a real resolved executable (a bare name
199
+ // doesn't reliably spawn the way it does via a shell's own PATH
200
+ // lookup) - resolveWindowsBin finds it under the CURRENT user's own
201
+ // npm global bin, not one hardcoded developer machine's path (see
202
+ // its own comment for the real bug this fixes).
203
+ command: process.platform === 'win32' ? resolveWindowsBin('codex') : 'codex',
204
204
  args: () => [],
205
205
  },
206
206
  approvals: {
@@ -0,0 +1,40 @@
1
+ // Real bug found by testing (PM report - broke for every real Windows
2
+ // tester except the one machine this was originally written on):
3
+ // claude.js and codex.js both had their Windows spawn command HARDCODED
4
+ // to this project's own developer machine's npm global bin path
5
+ // (C:\Users\hp\AppData\Roaming\npm\<name>.cmd) - node-pty on Windows
6
+ // needs a real resolved executable (a bare name like 'claude' doesn't
7
+ // reliably spawn via node-pty the way it does via a shell's own PATH
8
+ // lookup), but a path baked in for one specific Windows username can
9
+ // only ever work on that one machine. Every other real user's own
10
+ // global npm installs live under THEIR OWN username's AppData, which
11
+ // this never accounted for - spawn() then failed with an opaque native
12
+ // "posix_spawnp failed", with no indication the real cause was a
13
+ // hardcoded path from a different computer entirely.
14
+ //
15
+ // Resolves via the current process's own environment instead: APPDATA
16
+ // always points at the CURRENT user's roaming profile on any real
17
+ // Windows installation, regardless of username - this is the same
18
+ // directory `npm config get prefix`'s default resolves to for a global
19
+ // npm install on Windows, so it is exactly where `npm install -g
20
+ // <name>` actually places <name>.cmd.
21
+ const path = require('path');
22
+ const fs = require('fs');
23
+
24
+ function resolveWindowsBin(name) {
25
+ const appData = process.env.APPDATA;
26
+ if (appData) {
27
+ const candidate = path.join(appData, 'npm', `${name}.cmd`);
28
+ if (fs.existsSync(candidate)) return candidate;
29
+ }
30
+ // APPDATA missing or the .cmd isn't where expected (a non-default npm
31
+ // prefix, a different Node version manager) - fall back to the bare
32
+ // name and let the OS's own PATH search handle it, same as every
33
+ // other platform already does. Better than throwing here: a wrong
34
+ // guess that still gets passed to node-pty will fail with its own
35
+ // real error, surfaced by supervisor.js's own spawn try/catch, rather
36
+ // than this file itself deciding no path exists.
37
+ return name;
38
+ }
39
+
40
+ module.exports = { resolveWindowsBin };
@@ -11,6 +11,30 @@
11
11
  // - detection is about what's actually running inside the PTY moment to
12
12
  // moment, not about which adapter spawned the shell around it.
13
13
  const os = require('os');
14
+ const fs = require('fs');
15
+
16
+ // Real bug found by testing (PM report, multiple real macOS users hit
17
+ // this): node-pty's spawn() throws synchronously with an opaque native
18
+ // "posix_spawnp failed" error - and nothing ever caught it - whenever
19
+ // $SHELL points at a path that doesn't actually exist or isn't
20
+ // executable anymore (a shell installed via Homebrew/nvm/asdf that was
21
+ // later upgraded, moved, or uninstalled; a stale value left over from a
22
+ // deleted user profile; a corporate-managed Mac with $SHELL pointing at
23
+ // something sandboxed). This crashed the ENTIRE supervisor process on
24
+ // startup with no actionable message - "not able to start any session"
25
+ // was the only thing the user could see. Validating the path exists AND
26
+ // is actually a regular file before trusting it, falling back through a
27
+ // short list of real, almost-always-present POSIX shells, means a
28
+ // broken $SHELL no longer takes down the whole bridge - it just quietly
29
+ // uses a working shell instead, same as if $SHELL had never been set at
30
+ // all.
31
+ function isExecutableFile(path) {
32
+ try {
33
+ return fs.statSync(path).isFile();
34
+ } catch {
35
+ return false;
36
+ }
37
+ }
14
38
 
15
39
  function resolveShellCommand() {
16
40
  if (process.platform === 'win32') {
@@ -27,7 +51,19 @@ function resolveShellCommand() {
27
51
  // configured shell" (bash, zsh, fish, ...) - respecting it here means
28
52
  // this bridges whatever shell the user already actually uses day to
29
53
  // day, not a hardcoded assumption that might not even be installed.
30
- return process.env.SHELL || '/bin/bash';
54
+ // Only trusted if it actually resolves to a real file on disk right
55
+ // now - see isExecutableFile's own comment for why this check exists.
56
+ if (process.env.SHELL && isExecutableFile(process.env.SHELL)) {
57
+ return process.env.SHELL;
58
+ }
59
+ for (const candidate of ['/bin/zsh', '/bin/bash', '/bin/sh']) {
60
+ if (isExecutableFile(candidate)) return candidate;
61
+ }
62
+ // Every one of those is a standard part of macOS/Linux and should
63
+ // never actually be missing - this is a last-resort value, not an
64
+ // expected path, kept only so spawn() still gets SOME string instead
65
+ // of undefined.
66
+ return '/bin/sh';
31
67
  }
32
68
 
33
69
  module.exports = {
package/src/supervisor.js CHANGED
@@ -1112,13 +1112,28 @@ async function main() {
1112
1112
 
1113
1113
  console.log(`\n[supervisor] Spawning ${adapter.displayName} in a PTY (device=${DEVICE_NAME}, session=${SESSION_ID}, cwd=${WORKING_DIR})...`);
1114
1114
 
1115
- ptyProcess = pty.spawn(adapter.spawn.command, adapter.spawn.args(WORKING_DIR), {
1116
- name: 'xterm-color',
1117
- cols: PTY_COLS,
1118
- rows: PTY_ROWS,
1119
- cwd: WORKING_DIR,
1120
- env: process.env,
1121
- });
1115
+ try {
1116
+ ptyProcess = pty.spawn(adapter.spawn.command, adapter.spawn.args(WORKING_DIR), {
1117
+ name: 'xterm-color',
1118
+ cols: PTY_COLS,
1119
+ rows: PTY_ROWS,
1120
+ cwd: WORKING_DIR,
1121
+ env: process.env,
1122
+ });
1123
+ } catch (err) {
1124
+ // Real bug found by testing (PM report, multiple real macOS users):
1125
+ // node-pty's spawn() can still throw an opaque native error (e.g.
1126
+ // "posix_spawnp failed") for reasons shell.js's own path validation
1127
+ // doesn't cover - a CLI adapter (claude/codex) genuinely not
1128
+ // installed/on PATH, a cwd the OS won't let this process enter, a
1129
+ // sandboxed/restricted environment. Previously this propagated all
1130
+ // the way up to main().catch's generic "[supervisor] fatal: <msg>"
1131
+ // with zero context about WHAT was being spawned or WHERE - telling
1132
+ // the user only that something failed, never why. Re-throwing with
1133
+ // the actual command and cwd attached turns that into an actionable
1134
+ // message instead of a dead end.
1135
+ throw new Error(`Could not start "${adapter.spawn.command}" in ${WORKING_DIR}: ${err.message}`);
1136
+ }
1122
1137
 
1123
1138
  ptyProcess.onData((data) => {
1124
1139
  process.stdout.write(data);