botmux 2.21.4 → 2.21.6
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/adapters/backend/pty-backend.d.ts.map +1 -1
- package/dist/adapters/backend/pty-backend.js +4 -0
- package/dist/adapters/backend/pty-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts +19 -1
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +104 -58
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +23 -5
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/bot-registry.d.ts +2 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +70 -10
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli.js +31 -13
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -8
- package/dist/config.js.map +1 -1
- package/dist/core/dashboard-events.d.ts.map +1 -1
- package/dist/core/dashboard-events.js +2 -2
- package/dist/core/dashboard-events.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +6 -1
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +4 -3
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +7 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +10 -4
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +18 -10
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard.js +24 -4
- package/dist/dashboard.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +4 -0
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +20 -6
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +7 -3
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/services/chat-first-seen-store.d.ts +12 -0
- package/dist/services/chat-first-seen-store.d.ts.map +1 -0
- package/dist/services/chat-first-seen-store.js +108 -0
- package/dist/services/chat-first-seen-store.js.map +1 -0
- package/dist/setup/ensure-tmux.d.ts +49 -0
- package/dist/setup/ensure-tmux.d.ts.map +1 -1
- package/dist/setup/ensure-tmux.js +116 -31
- package/dist/setup/ensure-tmux.js.map +1 -1
- package/dist/setup/index.d.ts.map +1 -1
- package/dist/setup/index.js +14 -3
- package/dist/setup/index.js.map +1 -1
- package/dist/utils/bot-routing.d.ts +6 -0
- package/dist/utils/bot-routing.d.ts.map +1 -0
- package/dist/utils/bot-routing.js +50 -0
- package/dist/utils/bot-routing.js.map +1 -0
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +60 -7
- package/dist/utils/logger.js.map +1 -1
- package/dist/worker.js +12 -1
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,30 +2,105 @@
|
|
|
2
2
|
* Ensure tmux is installed before the daemon starts. Strategy (first one
|
|
3
3
|
* that fits wins):
|
|
4
4
|
*
|
|
5
|
-
* 1. Already installed → done.
|
|
5
|
+
* 1. Already installed AND functional → done.
|
|
6
6
|
* 2. brew available → `brew install tmux` (no sudo)
|
|
7
7
|
* 3. conda/mamba available → `conda install -y -c conda-forge tmux` (no sudo)
|
|
8
8
|
* 4. Linux + system pkg manager:
|
|
9
9
|
* a. NOPASSWD sudo or running as root → run non-interactively
|
|
10
10
|
* b. Has TTY → run interactively (sudo will prompt for password)
|
|
11
|
-
* c. No TTY (autostart / pm2 fork) → skip
|
|
12
|
-
* 5. Otherwise →
|
|
11
|
+
* c. No TTY (autostart / pm2 fork) → skip with manual command
|
|
12
|
+
* 5. Otherwise → return failure with manual command.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Tmux is a NICE-TO-HAVE (enables /adopt + multi-pane Web terminal), not
|
|
15
|
+
* load-bearing: PTY backend works without it. So this function never throws —
|
|
16
|
+
* the caller inspects `installed` and routes accordingly. Earlier versions
|
|
17
|
+
* threw on failure; that broke users whose tmux binary was present but
|
|
18
|
+
* couldn't actually start a server (corrupt install / restricted /tmp /
|
|
19
|
+
* broken ~/.tmux.conf — see daemon-error logs full of "error connecting to
|
|
20
|
+
* /tmp/tmux-UID/default"), because `tmux -V` would pass but every subsequent
|
|
21
|
+
* tmux command would fail. Functional probe + soft fallback fixes both.
|
|
17
22
|
*/
|
|
18
23
|
import { execSync, spawnSync } from 'node:child_process';
|
|
19
24
|
import { detectPlatform } from './detect-platform.js';
|
|
20
25
|
function probeTmuxVersion() {
|
|
21
26
|
try {
|
|
22
|
-
const out = execSync('tmux -V', {
|
|
27
|
+
const out = execSync('tmux -V', {
|
|
28
|
+
encoding: 'utf-8',
|
|
29
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
30
|
+
timeout: 3000,
|
|
31
|
+
env: tmuxEnv(),
|
|
32
|
+
});
|
|
23
33
|
return out.trim();
|
|
24
34
|
}
|
|
25
35
|
catch {
|
|
26
36
|
return undefined;
|
|
27
37
|
}
|
|
28
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Strip tmux-injected env vars when spawning a tmux child process.
|
|
41
|
+
*
|
|
42
|
+
* If the parent (daemon / worker / cli wrapper) was launched from inside a
|
|
43
|
+
* tmux session, tmux exports `TMUX=<socket-path>,<pid>,<session-id>` and
|
|
44
|
+
* `TMUX_PANE=...` to the environment. Any `tmux` subcommand we run without
|
|
45
|
+
* explicit `-L <socket>` then targets *that* parent server — when the user's
|
|
46
|
+
* terminal tmux is gone (logged out, server killed, /tmp wiped) every
|
|
47
|
+
* subsequent call fails with `error connecting to <stale-socket>`.
|
|
48
|
+
*
|
|
49
|
+
* This affects botmux even when the user's *new* `tmux -V` works fine on the
|
|
50
|
+
* shell, because that test starts from a fresh shell with no stale `TMUX`.
|
|
51
|
+
* The daemon, autostarted via pm2/systemd at login, inherited the original
|
|
52
|
+
* shell's tmux env — `/tmp/tmux-1001/default` in the wild we see — and keeps
|
|
53
|
+
* hammering at it forever.
|
|
54
|
+
*
|
|
55
|
+
* Caller pattern: every execSync / execFileSync / spawnSync / pty.spawn that
|
|
56
|
+
* invokes the `tmux` binary must pass `env: tmuxEnv()` (or `tmuxEnv(opts.env)`
|
|
57
|
+
* when forwarding caller-provided env). TMUX_TMPDIR is intentionally left
|
|
58
|
+
* alone — it just changes the socket directory and is the user's deliberate
|
|
59
|
+
* override if set.
|
|
60
|
+
*/
|
|
61
|
+
export function tmuxEnv(env = process.env) {
|
|
62
|
+
const { TMUX: _tmux, TMUX_PANE: _pane, ...rest } = env;
|
|
63
|
+
return rest;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Functional tmux probe — actually starts a tmux server and tears it down.
|
|
67
|
+
*
|
|
68
|
+
* `tmux -V` only checks the binary, not whether tmux can create a socket and
|
|
69
|
+
* fork a session. Real-world failures we've seen: (1) /tmp owned by another
|
|
70
|
+
* user, (2) broken ~/.tmux.conf, (3) the binary is a libc-mismatched dynamic
|
|
71
|
+
* link, (4) tmux 1.x on minimal images missing libevent. All of those make
|
|
72
|
+
* `tmux -V` succeed but every `new-session` / `attach-session` fail, which
|
|
73
|
+
* floods daemon-error.log and leaves the worker with no working backend.
|
|
74
|
+
*
|
|
75
|
+
* Uses a unique `-L <socket-name>` so the probe never clobbers an existing
|
|
76
|
+
* user tmux server. Stderr is captured (not inherited) so the failure
|
|
77
|
+
* reason can surface in the bootstrap warning without spilling onto the
|
|
78
|
+
* user's terminal.
|
|
79
|
+
*/
|
|
80
|
+
export function probeTmuxFunctional() {
|
|
81
|
+
const version = probeTmuxVersion();
|
|
82
|
+
if (!version)
|
|
83
|
+
return { ok: false, reason: 'tmux 二进制不在 PATH 上' };
|
|
84
|
+
const sockName = `bmx-probe-${process.pid}-${Date.now()}`;
|
|
85
|
+
// env: tmuxEnv() — without this, if the daemon inherited TMUX from a tmux
|
|
86
|
+
// session that has since died, this probe would target the dead server
|
|
87
|
+
// (despite the `-L` flag tmux still walks $TMUX in some 2.x paths during
|
|
88
|
+
// startup) and report "ok: false" even on a perfectly healthy install.
|
|
89
|
+
const run = spawnSync('tmux', ['-L', sockName, 'new-session', '-d', '-s', 'probe', 'true'], {
|
|
90
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
91
|
+
timeout: 5000,
|
|
92
|
+
env: tmuxEnv(),
|
|
93
|
+
});
|
|
94
|
+
if (run.status !== 0) {
|
|
95
|
+
const stderr = (run.stderr?.toString() ?? '').trim();
|
|
96
|
+
return { ok: false, reason: stderr || `tmux new-session 失败 (exit ${run.status})` };
|
|
97
|
+
}
|
|
98
|
+
// Tear down the probe server. Best-effort — if this leaks, the kernel
|
|
99
|
+
// will GC the abstract socket when the (now-empty) tmux server exits on
|
|
100
|
+
// its own once the only client (which we didn't attach) goes away.
|
|
101
|
+
spawnSync('tmux', ['-L', sockName, 'kill-server'], { stdio: 'ignore', timeout: 3000, env: tmuxEnv() });
|
|
102
|
+
return { ok: true, version };
|
|
103
|
+
}
|
|
29
104
|
/** Wrap a system command with the appropriate sudo prefix for the current
|
|
30
105
|
* platform context, or return undefined if we cannot escalate (no
|
|
31
106
|
* passwordless sudo and no TTY to prompt on). */
|
|
@@ -91,10 +166,25 @@ function runInstall(argv) {
|
|
|
91
166
|
}
|
|
92
167
|
export async function ensureTmux(info) {
|
|
93
168
|
const platform = info ?? detectPlatform();
|
|
94
|
-
// Step 1: already installed?
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
169
|
+
// Step 1: already installed AND functional?
|
|
170
|
+
// `tmux -V` alone is not enough — see probeTmuxFunctional jsdoc for the
|
|
171
|
+
// failure modes (broken /tmp perms / bad ~/.tmux.conf / mismatched libs)
|
|
172
|
+
// that pass -V but fail every actual tmux command.
|
|
173
|
+
const initialProbe = probeTmuxFunctional();
|
|
174
|
+
if (initialProbe.ok) {
|
|
175
|
+
return { installed: true, version: initialProbe.version, freshInstall: false };
|
|
176
|
+
}
|
|
177
|
+
// If the binary exists but the server can't start, no amount of
|
|
178
|
+
// `apt-get install tmux` will help — surface the underlying reason and
|
|
179
|
+
// let the caller fall back to PTY backend.
|
|
180
|
+
const versionPresent = probeTmuxVersion();
|
|
181
|
+
if (versionPresent) {
|
|
182
|
+
return {
|
|
183
|
+
installed: false,
|
|
184
|
+
freshInstall: false,
|
|
185
|
+
reason: `${versionPresent} 已安装但启动 server 失败:${initialProbe.reason}`,
|
|
186
|
+
manualCommand: '排查 ~/.tmux.conf / /tmp 权限 / libevent 依赖后再试',
|
|
187
|
+
};
|
|
98
188
|
}
|
|
99
189
|
console.log('⚠️ tmux 未检测到,正在安装...');
|
|
100
190
|
// Step 2..4: walk the package-manager preference list.
|
|
@@ -111,12 +201,12 @@ export async function ensureTmux(info) {
|
|
|
111
201
|
aptUpdateBeforeInstall(platform);
|
|
112
202
|
console.log(` 尝试 ${pm}: ${argv.join(' ')}`);
|
|
113
203
|
if (runInstall(argv)) {
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
console.log(`✅ tmux ${
|
|
117
|
-
return { installed: true, version:
|
|
204
|
+
const postInstall = probeTmuxFunctional();
|
|
205
|
+
if (postInstall.ok) {
|
|
206
|
+
console.log(`✅ tmux ${postInstall.version} 安装完成 (via ${pm})`);
|
|
207
|
+
return { installed: true, version: postInstall.version, freshInstall: true, strategy: pm };
|
|
118
208
|
}
|
|
119
|
-
tried.push(`${pm}
|
|
209
|
+
tried.push(`${pm}(装上了但 server 起不来:${postInstall.reason})`);
|
|
120
210
|
}
|
|
121
211
|
else {
|
|
122
212
|
tried.push(`${pm}(命令返回非零)`);
|
|
@@ -125,27 +215,22 @@ export async function ensureTmux(info) {
|
|
|
125
215
|
// Build a useful failure message with the most relevant manual command.
|
|
126
216
|
const preferred = platform.packageManagers.find(p => p !== 'unknown') ?? 'unknown';
|
|
127
217
|
const manual = suggestManualCommand(preferred, 'tmux');
|
|
128
|
-
const
|
|
129
|
-
'
|
|
130
|
-
'',
|
|
218
|
+
const reasonLines = [
|
|
219
|
+
'自动安装 tmux 失败',
|
|
131
220
|
'已尝试:',
|
|
132
221
|
...tried.map(t => ` - ${t}`),
|
|
133
|
-
'',
|
|
134
|
-
'请手动安装后重试:',
|
|
135
|
-
` ${manual}`,
|
|
136
222
|
];
|
|
137
|
-
// macOS without Homebrew → guide the user to install brew first.
|
|
138
223
|
if (platform.os === 'darwin' && !platform.packageManagers.includes('brew')) {
|
|
139
|
-
|
|
140
|
-
lines.push('macOS 推荐先安装 Homebrew:');
|
|
141
|
-
lines.push(' /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"');
|
|
142
|
-
lines.push('安装完成后重试 `botmux start`,会走 brew 自动装 tmux。');
|
|
224
|
+
reasonLines.push('macOS 推荐先安装 Homebrew:/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"');
|
|
143
225
|
}
|
|
144
226
|
if (!platform.hasTty && !platform.isRoot && !platform.passwordlessSudo && platform.os === 'linux') {
|
|
145
|
-
|
|
146
|
-
lines.push('提示:当前不是交互式 TTY 且 sudo 需要密码,systemd/pm2 自启场景下无法弹密码。');
|
|
147
|
-
lines.push('请在 shell 中手动跑一次 `botmux start`,或配置 NOPASSWD sudoers 后再启用 autostart。');
|
|
227
|
+
reasonLines.push('提示:当前不是交互式 TTY 且 sudo 需要密码,systemd/pm2 自启下无法弹密码 — 先在 shell 跑一次 `botmux start`,或配置 NOPASSWD sudoers。');
|
|
148
228
|
}
|
|
149
|
-
|
|
229
|
+
return {
|
|
230
|
+
installed: false,
|
|
231
|
+
freshInstall: false,
|
|
232
|
+
reason: reasonLines.join('\n'),
|
|
233
|
+
manualCommand: manual,
|
|
234
|
+
};
|
|
150
235
|
}
|
|
151
236
|
//# sourceMappingURL=ensure-tmux.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ensure-tmux.js","sourceRoot":"","sources":["../../src/setup/ensure-tmux.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ensure-tmux.js","sourceRoot":"","sources":["../../src/setup/ensure-tmux.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEjE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,kBAAkB,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEjE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CA8BtE"}
|
package/dist/setup/index.js
CHANGED
|
@@ -12,10 +12,21 @@ import { ensureFonts } from './ensure-fonts.js';
|
|
|
12
12
|
export { botmuxFontDir } from './ensure-fonts.js';
|
|
13
13
|
export async function ensureDependencies() {
|
|
14
14
|
const platform = detectPlatform();
|
|
15
|
-
// tmux
|
|
15
|
+
// tmux: nice-to-have (enables /adopt + multi-pane Web terminal). Daemon
|
|
16
|
+
// still works on PTY backend without it, so failure is a warning, not fatal.
|
|
16
17
|
const tmux = await ensureTmux(platform);
|
|
17
|
-
if (
|
|
18
|
-
|
|
18
|
+
if (tmux.installed) {
|
|
19
|
+
if (!tmux.freshInstall)
|
|
20
|
+
console.log(`✓ tmux ${tmux.version} (existing)`);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
console.warn('');
|
|
24
|
+
console.warn('⚠️ tmux 不可用,已退回到 PTY backend');
|
|
25
|
+
console.warn(` 原因:${tmux.reason ?? '未知'}`);
|
|
26
|
+
if (tmux.manualCommand)
|
|
27
|
+
console.warn(` 手动尝试:${tmux.manualCommand}`);
|
|
28
|
+
console.warn(' 影响:/adopt(接管已有 CLI 会话)和多人 Web 终端不可用;常规对话不受影响。');
|
|
29
|
+
console.warn('');
|
|
19
30
|
}
|
|
20
31
|
// Fonts second — best-effort.
|
|
21
32
|
const fonts = await ensureFonts(platform);
|
package/dist/setup/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAmB,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AAOjE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAElC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAmB,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AAOjE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAElC,wEAAwE;IACxE,6EAA6E;IAC7E,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC;IAC3E,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,OAAO,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QACvE,OAAO,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;QAClE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC;IAED,8BAA8B;IAC9B,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,IAAI,QAAQ,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,eAAe,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function loadOncallChatsByApp(botsJsonPath?: string): Map<string, Set<string>>;
|
|
2
|
+
export declare function pickBotEntryByName<T extends {
|
|
3
|
+
larkAppId: string;
|
|
4
|
+
botName: string | null;
|
|
5
|
+
}>(botEntries: T[], name: string, targetChatId: string | undefined, oncallChatsByApp: Map<string, Set<string>>): T | undefined;
|
|
6
|
+
//# sourceMappingURL=bot-routing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot-routing.d.ts","sourceRoot":"","sources":["../../src/utils/bot-routing.ts"],"names":[],"mappings":"AAiBA,wBAAgB,oBAAoB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAkBpF;AAED,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACxF,UAAU,EAAE,CAAC,EAAE,EACf,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,GACzC,CAAC,GAAG,SAAS,CAMf"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Same-name bot disambiguation for `botmux send` cross-ref reverse lookup.
|
|
3
|
+
*
|
|
4
|
+
* bots-info.json can hold multiple entries with the same `botName` when a
|
|
5
|
+
* deployment runs two apps under the same display name. Cross-ref files key
|
|
6
|
+
* on botName (`{ <name>: <sender-scoped open_id> }`), so the reverse path
|
|
7
|
+
* — botName → larkAppId — is ambiguous: `Array.find` silently routes to
|
|
8
|
+
* whichever entry sorts first, often the wrong one. Prefer the entry whose
|
|
9
|
+
* `oncallChats` includes the outbound chat — that's the deployment intent.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
12
|
+
import { resolve } from 'node:path';
|
|
13
|
+
import { homedir } from 'node:os';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
const DEFAULT_BOTS_JSON = join(homedir(), '.botmux', 'bots.json');
|
|
16
|
+
export function loadOncallChatsByApp(botsJsonPath) {
|
|
17
|
+
const map = new Map();
|
|
18
|
+
const path = botsJsonPath
|
|
19
|
+
?? (process.env.BOTS_CONFIG ? resolve(process.env.BOTS_CONFIG) : DEFAULT_BOTS_JSON);
|
|
20
|
+
try {
|
|
21
|
+
if (!existsSync(path))
|
|
22
|
+
return map;
|
|
23
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
24
|
+
if (!Array.isArray(parsed))
|
|
25
|
+
return map;
|
|
26
|
+
for (const cfg of parsed) {
|
|
27
|
+
if (!cfg?.larkAppId || !Array.isArray(cfg.oncallChats))
|
|
28
|
+
continue;
|
|
29
|
+
const chats = new Set();
|
|
30
|
+
for (const c of cfg.oncallChats) {
|
|
31
|
+
if (typeof c?.chatId === 'string')
|
|
32
|
+
chats.add(c.chatId);
|
|
33
|
+
}
|
|
34
|
+
if (chats.size > 0)
|
|
35
|
+
map.set(cfg.larkAppId, chats);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch { /* */ }
|
|
39
|
+
return map;
|
|
40
|
+
}
|
|
41
|
+
export function pickBotEntryByName(botEntries, name, targetChatId, oncallChatsByApp) {
|
|
42
|
+
const lower = name.toLowerCase();
|
|
43
|
+
const candidates = botEntries.filter(e => e.botName?.toLowerCase() === lower);
|
|
44
|
+
if (candidates.length === 0)
|
|
45
|
+
return undefined;
|
|
46
|
+
if (candidates.length === 1 || !targetChatId)
|
|
47
|
+
return candidates[0];
|
|
48
|
+
return candidates.find(e => oncallChatsByApp.get(e.larkAppId)?.has(targetChatId)) ?? candidates[0];
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=bot-routing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot-routing.js","sourceRoot":"","sources":["../../src/utils/bot-routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;AAElE,MAAM,UAAU,oBAAoB,CAAC,YAAqB;IACxD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC3C,MAAM,IAAI,GAAG,YAAY;WACpB,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACtF,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,GAAG,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,GAAG,CAAC;QACvC,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,EAAE,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;gBAAE,SAAS;YACjE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;YAChC,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;gBAChC,IAAI,OAAO,CAAC,EAAE,MAAM,KAAK,QAAQ;oBAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACzD,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;IACjB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,UAAe,EACf,IAAY,EACZ,YAAgC,EAChC,gBAA0C;IAE1C,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC;IAC9E,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,YAAY;QAAE,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;IACnE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;AACrG,CAAC"}
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -3,5 +3,15 @@ export declare const logger: {
|
|
|
3
3
|
warn(msg: string, ...args: unknown[]): void;
|
|
4
4
|
error(msg: string, ...args: unknown[]): void;
|
|
5
5
|
debug(msg: string, ...args: unknown[]): void;
|
|
6
|
+
/** Truthy if DEBUG=1 — callers can use this to skip expensive log-arg
|
|
7
|
+
* preparation (e.g. JSON.stringify of large objects) when debug is off. */
|
|
8
|
+
isDebug(): boolean;
|
|
9
|
+
/** Switch the logger into CLI mode:
|
|
10
|
+
* - info/debug become no-ops by default (silent stdout for JSON output).
|
|
11
|
+
* - DEBUG=1 re-enables info/debug *but routes them to stderr* so that
|
|
12
|
+
* `DEBUG=1 botmux thread messages` still emits clean JSON on stdout.
|
|
13
|
+
* warn/error always reach stderr.
|
|
14
|
+
* Called once by the CLI entrypoint; daemon never calls this. */
|
|
15
|
+
setSilent(s: boolean): void;
|
|
6
16
|
};
|
|
7
17
|
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAsCA,eAAO,MAAM,MAAM;cACP,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;cAIjC,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;eAGhC,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;eAGjC,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;IAI5C;gFAC4E;eACjE,OAAO;IAGlB;;;;;sEAKkE;iBACrD,OAAO,GAAG,IAAI;CAU5B,CAAC"}
|
package/dist/utils/logger.js
CHANGED
|
@@ -1,16 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon logger.
|
|
3
|
+
*
|
|
4
|
+
* Sink routing:
|
|
5
|
+
* info / debug → stdout (pm2 `out_file` → daemon.log)
|
|
6
|
+
* warn / error → stderr (pm2 `error_file` → error.log)
|
|
7
|
+
*
|
|
8
|
+
* Why split: when everything goes to stderr, pm2's error.log fills with normal
|
|
9
|
+
* operational events ("client ready", "session started", etc.) and real
|
|
10
|
+
* warnings/errors get lost in the noise. Routing info/debug to stdout keeps
|
|
11
|
+
* error.log focused on actionable signals.
|
|
12
|
+
*
|
|
13
|
+
* `debug` is gated behind the DEBUG env var (truthy = on). Use it for
|
|
14
|
+
* critical-node instrumentation that you only need while troubleshooting.
|
|
15
|
+
*/
|
|
1
16
|
function timestamp() {
|
|
2
17
|
return new Date().toISOString();
|
|
3
18
|
}
|
|
4
19
|
function fmt(msg, args) {
|
|
5
|
-
const extra = args.length ? ' ' + args.map(a =>
|
|
20
|
+
const extra = args.length ? ' ' + args.map(a => safeJson(a)).join(' ') : '';
|
|
6
21
|
return `[${timestamp()}] ${msg}${extra}\n`;
|
|
7
22
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
23
|
+
function safeJson(v) {
|
|
24
|
+
if (typeof v === 'string')
|
|
25
|
+
return v;
|
|
26
|
+
try {
|
|
27
|
+
return JSON.stringify(v);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return String(v);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const DEBUG_ON = !!process.env.DEBUG;
|
|
34
|
+
// Sinks for info/debug. Daemon mode: stdout (pm2 out_file → daemon.log).
|
|
35
|
+
// CLI mode: stderr (so stdout stays pristine for JSON output even under
|
|
36
|
+
// DEBUG=1). warn/error always go to process.stderr.
|
|
37
|
+
let infoSink = process.stdout;
|
|
38
|
+
let debugSink = process.stdout;
|
|
39
|
+
let silent = false;
|
|
11
40
|
export const logger = {
|
|
12
41
|
info(msg, ...args) {
|
|
13
|
-
|
|
42
|
+
if (silent && !DEBUG_ON)
|
|
43
|
+
return;
|
|
44
|
+
infoSink.write(fmt(`[INFO] ${msg}`, args));
|
|
14
45
|
},
|
|
15
46
|
warn(msg, ...args) {
|
|
16
47
|
process.stderr.write(fmt(`[WARN] ${msg}`, args));
|
|
@@ -19,8 +50,30 @@ export const logger = {
|
|
|
19
50
|
process.stderr.write(fmt(`[ERROR] ${msg}`, args));
|
|
20
51
|
},
|
|
21
52
|
debug(msg, ...args) {
|
|
22
|
-
if (
|
|
23
|
-
|
|
53
|
+
if (!DEBUG_ON)
|
|
54
|
+
return;
|
|
55
|
+
debugSink.write(fmt(`[DEBUG] ${msg}`, args));
|
|
56
|
+
},
|
|
57
|
+
/** Truthy if DEBUG=1 — callers can use this to skip expensive log-arg
|
|
58
|
+
* preparation (e.g. JSON.stringify of large objects) when debug is off. */
|
|
59
|
+
isDebug() {
|
|
60
|
+
return DEBUG_ON;
|
|
61
|
+
},
|
|
62
|
+
/** Switch the logger into CLI mode:
|
|
63
|
+
* - info/debug become no-ops by default (silent stdout for JSON output).
|
|
64
|
+
* - DEBUG=1 re-enables info/debug *but routes them to stderr* so that
|
|
65
|
+
* `DEBUG=1 botmux thread messages` still emits clean JSON on stdout.
|
|
66
|
+
* warn/error always reach stderr.
|
|
67
|
+
* Called once by the CLI entrypoint; daemon never calls this. */
|
|
68
|
+
setSilent(s) {
|
|
69
|
+
silent = s;
|
|
70
|
+
if (s) {
|
|
71
|
+
infoSink = process.stderr;
|
|
72
|
+
debugSink = process.stderr;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
infoSink = process.stdout;
|
|
76
|
+
debugSink = process.stdout;
|
|
24
77
|
}
|
|
25
78
|
},
|
|
26
79
|
};
|
package/dist/utils/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,SAAS,SAAS;IAChB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,SAAS,GAAG,CAAC,GAAW,EAAE,IAAe;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,SAAS,SAAS;IAChB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,SAAS,GAAG,CAAC,GAAW,EAAE,IAAe;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,OAAO,IAAI,SAAS,EAAE,KAAK,GAAG,GAAG,KAAK,IAAI,CAAC;AAC7C,CAAC;AAED,SAAS,QAAQ,CAAC,CAAU;IAC1B,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAAC,CAAC;AAC/D,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AAErC,yEAAyE;AACzE,wEAAwE;AACxE,oDAAoD;AACpD,IAAI,QAAQ,GAAuB,OAAO,CAAC,MAAM,CAAC;AAClD,IAAI,SAAS,GAAuB,OAAO,CAAC,MAAM,CAAC;AACnD,IAAI,MAAM,GAAG,KAAK,CAAC;AAEnB,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,CAAC,GAAW,EAAE,GAAG,IAAe;QAClC,IAAI,MAAM,IAAI,CAAC,QAAQ;YAAE,OAAO;QAChC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,CAAC,GAAW,EAAE,GAAG,IAAe;QAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,KAAK,CAAC,GAAW,EAAE,GAAG,IAAe;QACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,KAAK,CAAC,GAAW,EAAE,GAAG,IAAe;QACnC,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD;gFAC4E;IAC5E,OAAO;QACL,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD;;;;;sEAKkE;IAClE,SAAS,CAAC,CAAU;QAClB,MAAM,GAAG,CAAC,CAAC;QACX,IAAI,CAAC,EAAE,CAAC;YACN,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;YAC1B,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;YAC1B,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,CAAC;IACH,CAAC;CACF,CAAC"}
|
package/dist/worker.js
CHANGED
|
@@ -32,6 +32,7 @@ import { claudeJsonlPathForSession, resolveJsonlFromPid, findOpenClaudeSessionId
|
|
|
32
32
|
import { PtyBackend } from './adapters/backend/pty-backend.js';
|
|
33
33
|
import { TmuxBackend } from './adapters/backend/tmux-backend.js';
|
|
34
34
|
import { TmuxPipeBackend } from './adapters/backend/tmux-pipe-backend.js';
|
|
35
|
+
import { tmuxEnv } from './setup/ensure-tmux.js';
|
|
35
36
|
import { IdleDetector } from './utils/idle-detector.js';
|
|
36
37
|
import { ScreenAnalyzer } from './utils/screen-analyzer.js';
|
|
37
38
|
import { captureToPng } from './utils/screenshot-renderer.js';
|
|
@@ -2312,7 +2313,16 @@ function spawnCli(cfg) {
|
|
|
2312
2313
|
return;
|
|
2313
2314
|
}
|
|
2314
2315
|
cliAdapter = createCliAdapterSync(cfg.cliId, cfg.cliPathOverride);
|
|
2315
|
-
|
|
2316
|
+
// backendType=tmux trust-but-verify: an explicit per-bot config (or
|
|
2317
|
+
// BACKEND_TYPE=tmux env override) bypasses config.ts's auto-detect, so
|
|
2318
|
+
// the worker re-probes here. If tmux can't start a server we silently
|
|
2319
|
+
// fall back to PTY rather than letting attach-session / new-session spam
|
|
2320
|
+
// the daemon error log every poll cycle.
|
|
2321
|
+
let useTmux = cfg.backendType === 'tmux';
|
|
2322
|
+
if (useTmux && !TmuxBackend.isAvailable()) {
|
|
2323
|
+
log('tmux backend requested but functional probe failed — falling back to PTY backend');
|
|
2324
|
+
useTmux = false;
|
|
2325
|
+
}
|
|
2316
2326
|
isTmuxMode = useTmux;
|
|
2317
2327
|
const tmuxBe = useTmux ? new TmuxBackend(TmuxBackend.sessionName(cfg.sessionId)) : null;
|
|
2318
2328
|
backend = tmuxBe ?? new PtyBackend();
|
|
@@ -2545,6 +2555,7 @@ function startWebServer(host, preferredPort) {
|
|
|
2545
2555
|
name: 'xterm-256color',
|
|
2546
2556
|
cols,
|
|
2547
2557
|
rows,
|
|
2558
|
+
env: tmuxEnv(),
|
|
2548
2559
|
});
|
|
2549
2560
|
clientPtys.set(ws, cp);
|
|
2550
2561
|
cp.onData((d) => {
|