cli-setu 0.3.0 → 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/bin/setu.js
CHANGED
|
@@ -29,7 +29,27 @@ const args = process.argv.slice(2);
|
|
|
29
29
|
const firstArg = args[0];
|
|
30
30
|
const isSubcommand = firstArg && !firstArg.startsWith('-');
|
|
31
31
|
|
|
32
|
+
// Real bug found by testing: a genuinely unrecognized flag like `-rfesfd`
|
|
33
|
+
// or `-g` (a typo, not meant for supervisor.js at all) also starts with
|
|
34
|
+
// "-", so it fell through the isSubcommand check above straight into the
|
|
35
|
+
// bare-`cli-setu` session-watching path below - silently pairing/starting
|
|
36
|
+
// a real session and generating a QR code instead of telling the user
|
|
37
|
+
// their command doesn't exist. supervisor.js only ever actually reads
|
|
38
|
+
// `--cli <name>` out of argv (confirmed by reading it directly - the
|
|
39
|
+
// `[workingDir]` mentioned in its own old comment is dead, WORKING_DIR is
|
|
40
|
+
// just process.cwd()) - every other `-`-prefixed flag is unrecognized by
|
|
41
|
+
// definition, same as an unrecognized subcommand already is below.
|
|
42
|
+
const KNOWN_FLAGS = new Set(['--version', '-v', '--help', '-h', '--update', '--cli']);
|
|
43
|
+
const hasUnknownFlag = args.some((a) => a.startsWith('-') && !KNOWN_FLAGS.has(a));
|
|
44
|
+
|
|
32
45
|
async function main() {
|
|
46
|
+
if (hasUnknownFlag) {
|
|
47
|
+
const bad = args.find((a) => a.startsWith('-') && !KNOWN_FLAGS.has(a));
|
|
48
|
+
console.error(`[cli-setu] Unknown option: ${bad}`);
|
|
49
|
+
console.error('[cli-setu] For more info, run: cli-setu --help');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
33
53
|
if (args.includes('--version') || args.includes('-v')) {
|
|
34
54
|
console.log(require('../package.json').version);
|
|
35
55
|
return;
|
package/package.json
CHANGED
|
@@ -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
|
-
//
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
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);
|