botmux 3.18.2 → 3.18.4
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/dist/.runtime-build-id +1 -1
- package/dist/adapters/hook-installer.d.ts.map +1 -1
- package/dist/adapters/hook-installer.js +75 -2
- package/dist/adapters/hook-installer.js.map +1 -1
- package/dist/cli/current-actor.d.ts.map +1 -1
- package/dist/cli/current-actor.js +2 -1
- package/dist/cli/current-actor.js.map +1 -1
- package/dist/cli/dashboard-endpoint.d.ts.map +1 -1
- package/dist/cli/dashboard-endpoint.js +5 -2
- package/dist/cli/dashboard-endpoint.js.map +1 -1
- package/dist/cli/exact-chat-grant-client.d.ts.map +1 -1
- package/dist/cli/exact-chat-grant-client.js +2 -1
- package/dist/cli/exact-chat-grant-client.js.map +1 -1
- package/dist/cli/vc-agent.d.ts.map +1 -1
- package/dist/cli/vc-agent.js +2 -1
- package/dist/cli/vc-agent.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +35 -9
- package/dist/cli.js.map +1 -1
- package/dist/core/daemon-ipc-auth.d.ts +9 -1
- package/dist/core/daemon-ipc-auth.d.ts.map +1 -1
- package/dist/core/daemon-ipc-auth.js +14 -3
- package/dist/core/daemon-ipc-auth.js.map +1 -1
- package/dist/core/fleet-state-store.d.ts.map +1 -1
- package/dist/core/fleet-state-store.js +6 -0
- package/dist/core/fleet-state-store.js.map +1 -1
- package/dist/core/fleet-supervisor-policy.d.ts +38 -6
- package/dist/core/fleet-supervisor-policy.d.ts.map +1 -1
- package/dist/core/fleet-supervisor-policy.js +32 -9
- package/dist/core/fleet-supervisor-policy.js.map +1 -1
- package/dist/core/fleet-supervisor.d.ts +39 -0
- package/dist/core/fleet-supervisor.d.ts.map +1 -1
- package/dist/core/fleet-supervisor.js +61 -9
- package/dist/core/fleet-supervisor.js.map +1 -1
- package/dist/core/loopback-fetch.d.ts +59 -0
- package/dist/core/loopback-fetch.d.ts.map +1 -0
- package/dist/core/loopback-fetch.js +347 -0
- package/dist/core/loopback-fetch.js.map +1 -0
- package/dist/core/managed-origin-attestation.d.ts.map +1 -1
- package/dist/core/managed-origin-attestation.js +2 -1
- package/dist/core/managed-origin-attestation.js.map +1 -1
- package/dist/core/reply-target.d.ts +27 -0
- package/dist/core/reply-target.d.ts.map +1 -1
- package/dist/core/reply-target.js +58 -0
- package/dist/core/reply-target.js.map +1 -1
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +21 -6
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/aggregator.d.ts.map +1 -1
- package/dist/dashboard/aggregator.js +2 -1
- package/dist/dashboard/aggregator.js.map +1 -1
- package/dist/dashboard/daemon-internal-client.d.ts.map +1 -1
- package/dist/dashboard/daemon-internal-client.js +6 -1
- package/dist/dashboard/daemon-internal-client.js.map +1 -1
- package/dist/dashboard/groups-action-helpers.d.ts +0 -10
- package/dist/dashboard/groups-action-helpers.d.ts.map +1 -1
- package/dist/dashboard/groups-action-helpers.js +3 -2
- package/dist/dashboard/groups-action-helpers.js.map +1 -1
- package/dist/dashboard.js +2 -1
- package/dist/dashboard.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +13 -0
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +22 -1
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/platform/tunnel-client.d.ts +27 -0
- package/dist/platform/tunnel-client.d.ts.map +1 -1
- package/dist/platform/tunnel-client.js +205 -22
- package/dist/platform/tunnel-client.js.map +1 -1
- package/dist/services/hook-runner.d.ts.map +1 -1
- package/dist/services/hook-runner.js +2 -1
- package/dist/services/hook-runner.js.map +1 -1
- package/dist/services/send-policy.d.ts +38 -0
- package/dist/services/send-policy.d.ts.map +1 -1
- package/dist/services/send-policy.js +33 -0
- package/dist/services/send-policy.js.map +1 -1
- package/dist/services/voice/openai.d.ts.map +1 -1
- package/dist/services/voice/openai.js +10 -1
- package/dist/services/voice/openai.js.map +1 -1
- package/dist/services/worktree-slug-ai.d.ts.map +1 -1
- package/dist/services/worktree-slug-ai.js +8 -1
- package/dist/services/worktree-slug-ai.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/child-env.d.ts +41 -0
- package/dist/utils/child-env.d.ts.map +1 -1
- package/dist/utils/child-env.js +53 -0
- package/dist/utils/child-env.js.map +1 -1
- package/dist/workflows/v3/daemon-ipc-client.d.ts.map +1 -1
- package/dist/workflows/v3/daemon-ipc-client.js +2 -1
- package/dist/workflows/v3/daemon-ipc-client.js.map +1 -1
- package/dist/workflows/v3/session-relay-client.d.ts.map +1 -1
- package/dist/workflows/v3/session-relay-client.js +2 -1
- package/dist/workflows/v3/session-relay-client.js.map +1 -1
- package/package.json +8 -7
- package/scripts/install-path-entry.mjs +263 -0
- package/scripts/postinstall-bin.mjs +41 -7
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Put `~/.botmux/bin` on the user's PATH by writing their shell's startup file.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS
|
|
5
|
+
* `npm i -g botmux` has no `bin` field (removed with the Node fallback — see
|
|
6
|
+
* postinstall-bin.mjs), so the ONLY `botmux` command is the launcher written to
|
|
7
|
+
* `~/.botmux/bin/botmux`. If that directory is not on PATH, a successful install
|
|
8
|
+
* still leaves the user with `botmux: command not found`.
|
|
9
|
+
*
|
|
10
|
+
* Both installers used to just PRINT a hint, and the hint was:
|
|
11
|
+
*
|
|
12
|
+
* echo 'export PATH="…"' >> ~/.profile
|
|
13
|
+
*
|
|
14
|
+
* which is wrong for a large share of users, because **zsh never reads
|
|
15
|
+
* `~/.profile`** (measured: `zsh -lic` on a home dir containing `.profile` reads
|
|
16
|
+
* `.zshenv` + `.zprofile` + `.zshrc` and the `.profile` echo never fires). A zsh
|
|
17
|
+
* user who followed the hint verbatim got a file that is silently ignored — the
|
|
18
|
+
* command stayed missing with no indication why. That was the reported bug.
|
|
19
|
+
*
|
|
20
|
+
* ── WHICH FILE PER SHELL (all measured, not recalled) ─────────────────────────
|
|
21
|
+
* Startup files actually sourced, by shell and invocation mode:
|
|
22
|
+
*
|
|
23
|
+
* zsh -c → .zshenv
|
|
24
|
+
* -i → .zshenv .zshrc
|
|
25
|
+
* -li → .zshenv .zprofile .zshrc
|
|
26
|
+
* bash -c → (none)
|
|
27
|
+
* -i → .bashrc
|
|
28
|
+
* -li → the FIRST of .bash_profile → .bash_login → .profile
|
|
29
|
+
* fish → ~/.config/fish/conf.d/*.fish in all three modes
|
|
30
|
+
*
|
|
31
|
+
* So the target per shell is the file that covers the most modes:
|
|
32
|
+
* • zsh → ~/.zshenv (the only file read in all 3)
|
|
33
|
+
* • bash → ~/.bashrc AND its login file (login vs interactive are disjoint)
|
|
34
|
+
* • fish → ~/.config/fish/conf.d/botmux.fish
|
|
35
|
+
*
|
|
36
|
+
* bash genuinely needs both: writing only `.bashrc` misses login shells, and
|
|
37
|
+
* writing only the login file misses ordinary interactive ones.
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ bash's login file is "the first that EXISTS", not always `.bash_profile` —
|
|
40
|
+
* see bashLoginFile() below for why creating the wrong one destroys the user's
|
|
41
|
+
* existing login config.
|
|
42
|
+
*
|
|
43
|
+
* ── fish SYNTAX ──────────────────────────────────────────────────────────────
|
|
44
|
+
* fish is not POSIX; `export PATH="$INSTALL_DIR:$PATH"` is not its syntax. (fish
|
|
45
|
+
* 3.6 happens to tolerate `export` as a compatibility shim and even produces a
|
|
46
|
+
* correct PATH — measured — but `set -gx` is the real form and is what we write.)
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
import { existsSync, readFileSync, appendFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
50
|
+
import { join, dirname } from 'node:path';
|
|
51
|
+
import { homedir } from 'node:os';
|
|
52
|
+
import { basename } from 'node:path';
|
|
53
|
+
|
|
54
|
+
/** Marker so we can detect our own previous edit and never write twice. */
|
|
55
|
+
export const PATH_ENTRY_MARKER = '# added by botmux installer';
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Which shell is the user actually on? `$SHELL` is the login shell recorded in
|
|
59
|
+
* passwd, which is what future terminals will start — the right question here,
|
|
60
|
+
* and better than `process.ppid` guessing (the parent of a postinstall is npm,
|
|
61
|
+
* not the user's shell).
|
|
62
|
+
*/
|
|
63
|
+
export function detectShell(env = process.env) {
|
|
64
|
+
const shell = env.SHELL ?? '';
|
|
65
|
+
const name = basename(shell);
|
|
66
|
+
if (name.includes('zsh')) return 'zsh';
|
|
67
|
+
if (name.includes('fish')) return 'fish';
|
|
68
|
+
if (name.includes('bash')) return 'bash';
|
|
69
|
+
// Unknown or unset (containers, cron, exotic shells): POSIX `.profile` is the
|
|
70
|
+
// most portable thing we can offer, and sh/ksh/dash all read it at login.
|
|
71
|
+
return name ? 'other' : 'unknown';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* bash's LOGIN startup file, chosen without shadowing anything.
|
|
76
|
+
*
|
|
77
|
+
* ⚠️ bash reads only the FIRST of `.bash_profile` → `.bash_login` → `.profile`.
|
|
78
|
+
* So creating `.bash_profile` on a machine whose login config lives in
|
|
79
|
+
* `.profile` (or `.bash_login`) silently stops that file from ever being read.
|
|
80
|
+
* Measured — a sentinel exported from `.profile` is visible to `bash -lic`, and
|
|
81
|
+
* becomes MISSING the moment an unrelated `.bash_profile` appears; same for
|
|
82
|
+
* `.bash_login`. That is destroying the user's environment to fix a PATH entry,
|
|
83
|
+
* which is far worse than the bug being fixed.
|
|
84
|
+
*
|
|
85
|
+
* So: append to whichever of the three already exists (that is the file bash is
|
|
86
|
+
* actually reading), and only fall back to CREATING `.bash_profile` when none of
|
|
87
|
+
* them exists — in which case there is nothing to shadow.
|
|
88
|
+
*/
|
|
89
|
+
function bashLoginFile(home) {
|
|
90
|
+
for (const name of ['.bash_profile', '.bash_login', '.profile']) {
|
|
91
|
+
const file = join(home, name);
|
|
92
|
+
if (existsSync(file)) return file;
|
|
93
|
+
}
|
|
94
|
+
return join(home, '.bash_profile');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Quote a path for literal use inside a shell startup file.
|
|
99
|
+
*
|
|
100
|
+
* ⚠️ SECURITY, not cosmetics. The install dir is caller-controlled
|
|
101
|
+
* (`BOTMUX_INSTALL_DIR`, or just an unusual home), and the line we write is
|
|
102
|
+
* EXECUTED by the user's shell on every startup. Interpolating it into double
|
|
103
|
+
* quotes lets `$(…)`, backticks and `$VAR` run: measured — an install dir
|
|
104
|
+
* containing `$(touch /tmp/PWNED)` created that file the first time zsh started,
|
|
105
|
+
* and did so on every shell thereafter.
|
|
106
|
+
*
|
|
107
|
+
* Single quotes make the shell treat every byte literally; the only character
|
|
108
|
+
* needing care is `'` itself, closed and re-opened via `'\''`. This form is
|
|
109
|
+
* identical in POSIX shells and in fish.
|
|
110
|
+
*/
|
|
111
|
+
function shellQuote(value) {
|
|
112
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The startup files to write for a shell, plus the line to write into each.
|
|
117
|
+
* Returns [] when we have nothing safe to say.
|
|
118
|
+
*
|
|
119
|
+
* `env` is read for the two relocation variables below; both are honoured
|
|
120
|
+
* because writing the un-relocated path produces a file the shell never reads —
|
|
121
|
+
* the same class of bug as writing `.profile` for zsh.
|
|
122
|
+
*/
|
|
123
|
+
export function pathEntryTargets(shell, installDir, home = homedir(), env = process.env) {
|
|
124
|
+
const q = shellQuote(installDir);
|
|
125
|
+
// ⚠️ The STATEMENT has to be idempotent too, not just the file write. An
|
|
126
|
+
// unconditional `export PATH=<dir>:$PATH` re-prepends on every shell startup, so
|
|
127
|
+
// a nested or re-sourced shell keeps growing PATH — measured: the same directory
|
|
128
|
+
// appeared twice at nesting depth 2, and `.bash_profile` files that source
|
|
129
|
+
// `.bashrc` hit it within a single login. `case` is POSIX and quote-safe (no
|
|
130
|
+
// subshell, no `grep`), and the `:` padding makes it an exact ELEMENT test, so a
|
|
131
|
+
// sibling like `<dir>-old` never satisfies it.
|
|
132
|
+
const posix = `case ":$PATH:" in *:${q}:*) ;; *) export PATH=${q}":$PATH" ;; esac ${PATH_ENTRY_MARKER}`;
|
|
133
|
+
switch (shell) {
|
|
134
|
+
case 'zsh': {
|
|
135
|
+
// ⚠️ zsh reads its dotfiles from $ZDOTDIR when that is set, falling back to
|
|
136
|
+
// $HOME. Writing $HOME/.zshenv on a machine with ZDOTDIR set produces a file
|
|
137
|
+
// zsh never reads — measured: `zsh -c botmux` → "command not found".
|
|
138
|
+
const zdotdir = env.ZDOTDIR?.trim();
|
|
139
|
+
const base = zdotdir ? zdotdir : home;
|
|
140
|
+
return [{ file: join(base, '.zshenv'), line: posix }];
|
|
141
|
+
}
|
|
142
|
+
case 'bash': {
|
|
143
|
+
// Disjoint coverage — see header. Both, or one of the two common ways of
|
|
144
|
+
// opening a terminal is left broken. The login half must not shadow (above).
|
|
145
|
+
const login = bashLoginFile(home);
|
|
146
|
+
const targets = [{ file: join(home, '.bashrc'), line: posix }];
|
|
147
|
+
// When bash's login file IS .bashrc-adjacent there is nothing more to add;
|
|
148
|
+
// dedupe so we never append the same line to the same file twice.
|
|
149
|
+
if (login !== join(home, '.bashrc')) targets.push({ file: login, line: posix });
|
|
150
|
+
return targets;
|
|
151
|
+
}
|
|
152
|
+
case 'fish': {
|
|
153
|
+
// ⚠️ fish's config dir is $XDG_CONFIG_HOME/fish, defaulting to ~/.config/fish.
|
|
154
|
+
// Same failure as ZDOTDIR above — measured: with XDG_CONFIG_HOME set, a file
|
|
155
|
+
// under ~/.config/fish is ignored, while the same line under
|
|
156
|
+
// $XDG_CONFIG_HOME/fish/conf.d works (positive control).
|
|
157
|
+
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
158
|
+
const configHome = xdg ? xdg : join(home, '.config');
|
|
159
|
+
return [{
|
|
160
|
+
file: join(configHome, 'fish', 'conf.d', 'botmux.fish'),
|
|
161
|
+
// fish: `contains` is its own exact list-element test — PATH is a real list
|
|
162
|
+
// here, so no delimiter padding is needed.
|
|
163
|
+
line: `contains ${q} $PATH; or set -gx PATH ${q} $PATH ${PATH_ENTRY_MARKER}`,
|
|
164
|
+
}];
|
|
165
|
+
}
|
|
166
|
+
case 'other':
|
|
167
|
+
case 'unknown':
|
|
168
|
+
default:
|
|
169
|
+
return [{ file: join(home, '.profile'), line: posix }];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Is `installDir` already handled by this file (ours or the user's own line)? */
|
|
174
|
+
export function fileAlreadyHasEntry(file, installDir) {
|
|
175
|
+
if (!existsSync(file)) return false;
|
|
176
|
+
let text;
|
|
177
|
+
try { text = readFileSync(file, 'utf8'); } catch { return false; }
|
|
178
|
+
|
|
179
|
+
// ⚠️ TWO SEPARATE PREDICATES, deliberately not one clever one.
|
|
180
|
+
//
|
|
181
|
+
// Attempt #1 searched for the raw directory as a substring: `<installDir>-old`,
|
|
182
|
+
// `/backup<installDir>` and `<installDir>/other` all counted as configured, so
|
|
183
|
+
// the REAL directory was never written (measured, written=0 for each).
|
|
184
|
+
// Attempt #2 split the line into tokens and compared them — but the line we
|
|
185
|
+
// WRITE for a directory containing a quote is
|
|
186
|
+
// export PATH='/x/q'\''bin'":$PATH"
|
|
187
|
+
// and ANY tokenizer that treats `'` as a separator shreds that into `/x/q`,
|
|
188
|
+
// `\`, `bin`, so the quoted spelling can never match a token (measured: marker
|
|
189
|
+
// count 1 → 2 on every re-install). Shell quoting cannot be undone by splitting
|
|
190
|
+
// on characters.
|
|
191
|
+
//
|
|
192
|
+
// So: recognise OUR line by its own known syntax (marker + the exact quoted
|
|
193
|
+
// form we would generate), and use conservative whole-token matching only for a
|
|
194
|
+
// line the USER wrote by hand.
|
|
195
|
+
const ourQuoted = shellQuote(installDir);
|
|
196
|
+
const lines = text.split('\n').filter(l => !/^\s*#/.test(l));
|
|
197
|
+
|
|
198
|
+
// (a) Our own line, byte-for-byte on the part that identifies the directory.
|
|
199
|
+
// `PATH=` + the exact quoted dir is enough; the suffix may legitimately
|
|
200
|
+
// differ (`":$PATH"` vs fish's ` $PATH`).
|
|
201
|
+
if (lines.some(l => l.includes(PATH_ENTRY_MARKER) && l.includes(ourQuoted))) return true;
|
|
202
|
+
|
|
203
|
+
// (b) A hand-written line. Compare whole PATH elements, and do NOT treat quotes
|
|
204
|
+
// as element separators — strip the wrapping quotes first, then split on the
|
|
205
|
+
// characters that actually delimit PATH elements.
|
|
206
|
+
const PATH_ASSIGNMENT = new RegExp(
|
|
207
|
+
[
|
|
208
|
+
'export\\s+PATH\\s*=', // POSIX: export PATH="…"
|
|
209
|
+
'^\\s*PATH\\s*=', // POSIX: PATH=…
|
|
210
|
+
'set\\s+(-\\w+\\s+)*PATH', // fish: set -gx PATH …
|
|
211
|
+
'fish_add_path', // fish: fish_add_path …
|
|
212
|
+
'\\bpath\\+=', // zsh: path+=(…)
|
|
213
|
+
'\\bpath=\\(', // zsh: path=(…)
|
|
214
|
+
].join('|'),
|
|
215
|
+
'i',
|
|
216
|
+
);
|
|
217
|
+
const target = installDir.replace(/\/+$/, '');
|
|
218
|
+
return lines.some(l => {
|
|
219
|
+
if (!PATH_ASSIGNMENT.test(l)) return false;
|
|
220
|
+
// Quotes here are shell syntax around a plain path (the quote-containing case
|
|
221
|
+
// is handled by (a)); dropping them leaves the path bytes intact.
|
|
222
|
+
const bare = l.replace(/["']/g, '');
|
|
223
|
+
return bare
|
|
224
|
+
.split(/[:\s()=]+/)
|
|
225
|
+
.map(t => t.replace(/\/+$/, ''))
|
|
226
|
+
.some(t => t !== '' && t === target);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Append the PATH line to every startup file the user's shell reads.
|
|
232
|
+
*
|
|
233
|
+
* Returns `{ written: string[], skipped: string[], failed: [{file, error}] }`.
|
|
234
|
+
* Never throws: a PATH edit failing must not fail the install (the launcher is
|
|
235
|
+
* already in place and the caller prints a manual fallback).
|
|
236
|
+
*/
|
|
237
|
+
export function ensurePathEntry(opts) {
|
|
238
|
+
const installDir = opts.installDir;
|
|
239
|
+
const env = opts.env ?? process.env;
|
|
240
|
+
const home = opts.home ?? homedir();
|
|
241
|
+
const shell = opts.shell ?? detectShell(env);
|
|
242
|
+
const targets = pathEntryTargets(shell, installDir, home, env);
|
|
243
|
+
|
|
244
|
+
const written = [], skipped = [], failed = [];
|
|
245
|
+
for (const { file, line } of targets) {
|
|
246
|
+
if (fileAlreadyHasEntry(file, installDir)) { skipped.push(file); continue; }
|
|
247
|
+
try {
|
|
248
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
249
|
+
if (existsSync(file)) {
|
|
250
|
+
// Keep the user's file intact; only append, and only with a leading
|
|
251
|
+
// newline so we never glue onto an unterminated last line.
|
|
252
|
+
const prev = readFileSync(file, 'utf8');
|
|
253
|
+
appendFileSync(file, `${prev.endsWith('\n') || prev === '' ? '' : '\n'}${line}\n`);
|
|
254
|
+
} else {
|
|
255
|
+
writeFileSync(file, `${line}\n`);
|
|
256
|
+
}
|
|
257
|
+
written.push(file);
|
|
258
|
+
} catch (err) {
|
|
259
|
+
failed.push({ file, error: err && err.message ? err.message : String(err) });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return { shell, written, skipped, failed };
|
|
263
|
+
}
|
|
@@ -219,11 +219,45 @@ try {
|
|
|
219
219
|
);
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
// ── PATH
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
222
|
+
// ── PATH: write it, don't just suggest it ─────────────────────────────────────
|
|
223
|
+
// This used to only PRINT `echo 'export PATH=…' >> ~/.profile`. Two problems:
|
|
224
|
+
// the user had to act on it, and for zsh users the suggested file is WRONG —
|
|
225
|
+
// zsh never reads ~/.profile (measured), so following the hint verbatim left
|
|
226
|
+
// `botmux` still not found. There is no `bin` field any more, so PATH is the
|
|
227
|
+
// only way this launcher becomes a command; we now write the right startup file
|
|
228
|
+
// for the user's actual shell (bash/zsh/fish/other) and tell them what we did.
|
|
229
|
+
//
|
|
230
|
+
// ⚠️ FAIL-SOFT, and never `fail()`: the launcher is already installed and working
|
|
231
|
+
// at this point, so a PATH edit that cannot happen must degrade to the printed
|
|
232
|
+
// hint — not abort a successful install. That includes the sibling module simply
|
|
233
|
+
// not being there: it ships via package.json `files`, and if a future edit drops
|
|
234
|
+
// it (or a mirror repacks the tarball without it) the import throws. Guarding it
|
|
235
|
+
// keeps `npm i -g botmux` succeeding either way.
|
|
236
|
+
if (!(process.env.PATH ?? '').split(':').includes(binDir)) {
|
|
237
|
+
let ensurePathEntry = null;
|
|
238
|
+
try {
|
|
239
|
+
({ ensurePathEntry } = await import('./install-path-entry.mjs'));
|
|
240
|
+
} catch (err) {
|
|
241
|
+
console.error(`[botmux] PATH helper unavailable (${err && err.message ? err.message : String(err)})`);
|
|
242
|
+
}
|
|
243
|
+
let written = [], skipped = [];
|
|
244
|
+
if (ensurePathEntry) {
|
|
245
|
+
try {
|
|
246
|
+
const r = ensurePathEntry({ installDir: binDir });
|
|
247
|
+
({ written, skipped } = r);
|
|
248
|
+
for (const f of written) console.log(`[botmux] added ${binDir} to PATH in ${f} (${r.shell})`);
|
|
249
|
+
for (const f of skipped) console.log(`[botmux] ${f} already puts ${binDir} on PATH`);
|
|
250
|
+
for (const { file, error } of r.failed) console.error(`[botmux] could not update ${file}: ${error}`);
|
|
251
|
+
} catch (err) {
|
|
252
|
+
console.error(`[botmux] could not update your shell startup file: ${err && err.message ? err.message : String(err)}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (written.length > 0) {
|
|
256
|
+
console.log('[botmux] open a new terminal (or re-source that file) and `botmux` will be on PATH');
|
|
257
|
+
} else if (skipped.length === 0) {
|
|
258
|
+
// Nothing written and nothing already present — fall back to telling them.
|
|
259
|
+
// Deliberately not naming a specific file here: the correct one depends on
|
|
260
|
+
// the shell, and naming the wrong one is what caused the original bug.
|
|
261
|
+
console.log(`[botmux] add ${binDir} to your PATH so this launcher is the \`botmux\` your shell finds`);
|
|
262
|
+
}
|
|
229
263
|
}
|