botmux 2.21.3 → 2.21.5
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/tmux-backend.d.ts +5 -1
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +43 -22
- 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/adapters/cli/coco.d.ts.map +1 -1
- package/dist/adapters/cli/coco.js +182 -8
- package/dist/adapters/cli/coco.js.map +1 -1
- package/dist/cli.js +10 -8
- 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/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/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/worker.js +12 -1
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -28,7 +28,11 @@ export declare class TmuxBackend implements SessionBackend {
|
|
|
28
28
|
/** Target string to use for pane-scoped tmux commands. In adopt mode this
|
|
29
29
|
* is the real pane address ("0:2.0"); otherwise the bmx-* session name. */
|
|
30
30
|
private get cmdTarget();
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Check if tmux is usable — runs a functional probe (start + kill a
|
|
33
|
+
* disposable server), not just `tmux -V`. Same probe as config.ts so
|
|
34
|
+
* backend selection and runtime guard agree.
|
|
35
|
+
*/
|
|
32
36
|
static isAvailable(): boolean;
|
|
33
37
|
/** Derive tmux session name from a session UUID. */
|
|
34
38
|
static sessionName(sessionId: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tmux-backend.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/tmux-backend.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"tmux-backend.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/tmux-backend.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5D;;;;;;;;;;GAUG;AACH,qBAAa,WAAY,YAAW,cAAc;IAChD,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,WAAW,CAAS;IAC5B;;;;;2DAKuD;IACvD,OAAO,CAAC,iBAAiB,CAAuB;gBAEpC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE;IAKjE;gFAC4E;IAC5E,OAAO,KAAK,SAAS,GAEpB;IAID;;;;OAIG;IACH,MAAM,CAAC,WAAW,IAAI,OAAO;IAI7B,oDAAoD;IACpD,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAI7C,4CAA4C;IAC5C,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IASxC,6DAA6D;IAC7D,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMtC,oDAAoD;IACpD,MAAM,CAAC,kBAAkB,IAAI,MAAM,EAAE;IAcrC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAwEzD,wEAAwE;IACxE,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;sFACkF;IAClF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;sFACkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;yEACqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQ5B,qEAAqE;IACrE,eAAe,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAQxC;;;;OAIG;IACH,aAAa,IAAI,IAAI;IAQrB,kFAAkF;IAClF,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQ3C;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAc7B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIxC,yFAAyF;IACzF,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAIxC,yFAAyF;IACzF,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI;IAMtE,WAAW,IAAI,MAAM,GAAG,IAAI;IAuB5B,wEAAwE;IACxE,IAAI,IAAI,IAAI;IAqBZ,oEAAoE;IACpE,cAAc,IAAI,IAAI;IAOtB;;;;;;OAMG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IA8B3D,aAAa;;;;CAGd"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as pty from 'node-pty';
|
|
2
2
|
import { execSync, execFileSync } from 'node:child_process';
|
|
3
|
+
import { probeTmuxFunctional, tmuxEnv } from '../../setup/ensure-tmux.js';
|
|
3
4
|
/**
|
|
4
5
|
* TmuxBackend — session backend using tmux for process persistence.
|
|
5
6
|
*
|
|
@@ -33,15 +34,13 @@ export class TmuxBackend {
|
|
|
33
34
|
return this.adoptedPaneTarget ?? this.sessionName;
|
|
34
35
|
}
|
|
35
36
|
// ─── Static helpers ───────────────────────────────────────────────────────
|
|
36
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Check if tmux is usable — runs a functional probe (start + kill a
|
|
39
|
+
* disposable server), not just `tmux -V`. Same probe as config.ts so
|
|
40
|
+
* backend selection and runtime guard agree.
|
|
41
|
+
*/
|
|
37
42
|
static isAvailable() {
|
|
38
|
-
|
|
39
|
-
execSync('tmux -V', { stdio: 'ignore' });
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
43
|
+
return probeTmuxFunctional().ok;
|
|
45
44
|
}
|
|
46
45
|
/** Derive tmux session name from a session UUID. */
|
|
47
46
|
static sessionName(sessionId) {
|
|
@@ -50,7 +49,7 @@ export class TmuxBackend {
|
|
|
50
49
|
/** Check if a named tmux session exists. */
|
|
51
50
|
static hasSession(name) {
|
|
52
51
|
try {
|
|
53
|
-
execSync(`tmux has-session -t ${shellescape(name)}`, { stdio: 'ignore' });
|
|
52
|
+
execSync(`tmux has-session -t ${shellescape(name)}`, { stdio: 'ignore', env: tmuxEnv() });
|
|
54
53
|
return true;
|
|
55
54
|
}
|
|
56
55
|
catch {
|
|
@@ -60,7 +59,7 @@ export class TmuxBackend {
|
|
|
60
59
|
/** Kill a named tmux session (no-op if it doesn't exist). */
|
|
61
60
|
static killSession(name) {
|
|
62
61
|
try {
|
|
63
|
-
execSync(`tmux kill-session -t ${shellescape(name)}`, { stdio: 'ignore' });
|
|
62
|
+
execSync(`tmux kill-session -t ${shellescape(name)}`, { stdio: 'ignore', env: tmuxEnv() });
|
|
64
63
|
}
|
|
65
64
|
catch { /* session doesn't exist */ }
|
|
66
65
|
}
|
|
@@ -69,6 +68,7 @@ export class TmuxBackend {
|
|
|
69
68
|
try {
|
|
70
69
|
const out = execSync("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
71
70
|
encoding: 'utf-8',
|
|
71
|
+
env: tmuxEnv(),
|
|
72
72
|
});
|
|
73
73
|
return out.split('\n').filter(s => s.startsWith('bmx-'));
|
|
74
74
|
}
|
|
@@ -79,6 +79,13 @@ export class TmuxBackend {
|
|
|
79
79
|
// ─── SessionBackend implementation ────────────────────────────────────────
|
|
80
80
|
spawn(bin, args, opts) {
|
|
81
81
|
this.reattaching = TmuxBackend.hasSession(this.sessionName);
|
|
82
|
+
// Strip TMUX/TMUX_PANE from caller env before handing to pty.spawn — if
|
|
83
|
+
// the daemon was started inside a tmux session, leaving TMUX set would
|
|
84
|
+
// make this `tmux attach-session`/`new-session` target that parent
|
|
85
|
+
// session's socket. After the user's terminal tmux dies, every call
|
|
86
|
+
// here would print `error connecting to <stale-socket>` to the PTY and
|
|
87
|
+
// flood the daemon log via the leaked-stderr path.
|
|
88
|
+
const childEnv = tmuxEnv(opts.env);
|
|
82
89
|
if (this.reattaching) {
|
|
83
90
|
// Re-attach to surviving tmux session (CLI is still running)
|
|
84
91
|
this.process = pty.spawn('tmux', ['attach-session', '-t', this.sessionName], {
|
|
@@ -86,7 +93,7 @@ export class TmuxBackend {
|
|
|
86
93
|
cols: opts.cols,
|
|
87
94
|
rows: opts.rows,
|
|
88
95
|
cwd: opts.cwd,
|
|
89
|
-
env:
|
|
96
|
+
env: childEnv,
|
|
90
97
|
});
|
|
91
98
|
}
|
|
92
99
|
else {
|
|
@@ -114,7 +121,7 @@ export class TmuxBackend {
|
|
|
114
121
|
cols: opts.cols,
|
|
115
122
|
rows: opts.rows,
|
|
116
123
|
cwd: opts.cwd,
|
|
117
|
-
env:
|
|
124
|
+
env: childEnv,
|
|
118
125
|
});
|
|
119
126
|
}
|
|
120
127
|
// Configure tmux session options.
|
|
@@ -124,17 +131,18 @@ export class TmuxBackend {
|
|
|
124
131
|
setTimeout(() => {
|
|
125
132
|
try {
|
|
126
133
|
const t = shellescape(this.sessionName);
|
|
127
|
-
|
|
128
|
-
execSync(`tmux set-option -t ${t}
|
|
134
|
+
const env = tmuxEnv();
|
|
135
|
+
execSync(`tmux set-option -t ${t} status off`, { stdio: 'ignore', env });
|
|
136
|
+
execSync(`tmux set-option -t ${t} mouse on`, { stdio: 'ignore', env });
|
|
129
137
|
// set-clipboard is a server option — enable OSC 52 passthrough for web copy
|
|
130
|
-
execSync(`tmux set-option -s set-clipboard on`, { stdio: 'ignore' });
|
|
131
|
-
execSync(`tmux set-option -t ${t} history-limit 50000`, { stdio: 'ignore' });
|
|
138
|
+
execSync(`tmux set-option -s set-clipboard on`, { stdio: 'ignore', env });
|
|
139
|
+
execSync(`tmux set-option -t ${t} history-limit 50000`, { stdio: 'ignore', env });
|
|
132
140
|
// Prevent web terminal clients (smaller viewport) from shrinking the
|
|
133
141
|
// tmux window. If a web client at 80×24 causes tmux to resize the
|
|
134
142
|
// window down, reflowed content shifts buffer positions and the
|
|
135
143
|
// terminal renderer's baseline tracking breaks — historical output
|
|
136
144
|
// leaks into the streaming card.
|
|
137
|
-
execSync(`tmux set-option -t ${t} window-size largest`, { stdio: 'ignore' });
|
|
145
|
+
execSync(`tmux set-option -t ${t} window-size largest`, { stdio: 'ignore', env });
|
|
138
146
|
}
|
|
139
147
|
catch { /* session may not be ready yet — benign */ }
|
|
140
148
|
}, 500);
|
|
@@ -164,6 +172,7 @@ export class TmuxBackend {
|
|
|
164
172
|
execFileSync('tmux', ['send-keys', '-t', this.cmdTarget, '-l', '--', text], {
|
|
165
173
|
stdio: 'ignore',
|
|
166
174
|
timeout: 5000,
|
|
175
|
+
env: tmuxEnv(),
|
|
167
176
|
});
|
|
168
177
|
}
|
|
169
178
|
/** Send special keys (Enter, Escape, C-c, etc.) to the tmux pane. */
|
|
@@ -171,6 +180,7 @@ export class TmuxBackend {
|
|
|
171
180
|
execFileSync('tmux', ['send-keys', '-t', this.cmdTarget, ...keys], {
|
|
172
181
|
stdio: 'ignore',
|
|
173
182
|
timeout: 5000,
|
|
183
|
+
env: tmuxEnv(),
|
|
174
184
|
});
|
|
175
185
|
}
|
|
176
186
|
/**
|
|
@@ -182,6 +192,7 @@ export class TmuxBackend {
|
|
|
182
192
|
execFileSync('tmux', ['copy-mode', '-e', '-t', this.cmdTarget], {
|
|
183
193
|
stdio: 'ignore',
|
|
184
194
|
timeout: 5000,
|
|
195
|
+
env: tmuxEnv(),
|
|
185
196
|
});
|
|
186
197
|
}
|
|
187
198
|
/** Send a copy-mode X-command (e.g. 'halfpage-up', 'halfpage-down', 'cancel'). */
|
|
@@ -189,6 +200,7 @@ export class TmuxBackend {
|
|
|
189
200
|
execFileSync('tmux', ['send-keys', '-t', this.cmdTarget, '-X', xCommand], {
|
|
190
201
|
stdio: 'ignore',
|
|
191
202
|
timeout: 5000,
|
|
203
|
+
env: tmuxEnv(),
|
|
192
204
|
});
|
|
193
205
|
}
|
|
194
206
|
/**
|
|
@@ -201,10 +213,12 @@ export class TmuxBackend {
|
|
|
201
213
|
input: text,
|
|
202
214
|
stdio: ['pipe', 'ignore', 'ignore'],
|
|
203
215
|
timeout: 5000,
|
|
216
|
+
env: tmuxEnv(),
|
|
204
217
|
});
|
|
205
218
|
execFileSync('tmux', ['paste-buffer', '-t', this.cmdTarget, '-d'], {
|
|
206
219
|
stdio: 'ignore',
|
|
207
220
|
timeout: 5000,
|
|
221
|
+
env: tmuxEnv(),
|
|
208
222
|
});
|
|
209
223
|
}
|
|
210
224
|
resize(cols, rows) {
|
|
@@ -226,7 +240,14 @@ export class TmuxBackend {
|
|
|
226
240
|
// unlike list-panes which returns every pane in the target's window
|
|
227
241
|
// when cmdTarget is a pane address — taking the first line of that
|
|
228
242
|
// would silently bind to whichever pane tmux happens to list first.
|
|
229
|
-
const output = execSync(`tmux display-message -p -t ${shellescape(this.cmdTarget)} '#{pane_pid}'`,
|
|
243
|
+
const output = execSync(`tmux display-message -p -t ${shellescape(this.cmdTarget)} '#{pane_pid}'`,
|
|
244
|
+
// Explicit stdio: execSync's default leaks the child's stderr to the
|
|
245
|
+
// parent's stderr fd. When tmux server is unavailable (transient
|
|
246
|
+
// restart, killed by user, /tmp wiped), this command writes "error
|
|
247
|
+
// connecting to /tmp/tmux-UID/default" to stderr, which the daemon's
|
|
248
|
+
// worker.stderr handler then logs as a worker error every poll cycle.
|
|
249
|
+
// tmuxEnv() also strips $TMUX so we don't target a dead parent server.
|
|
250
|
+
{ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000, env: tmuxEnv() }).trim();
|
|
230
251
|
const pid = parseInt(output, 10);
|
|
231
252
|
return pid > 0 ? pid : null;
|
|
232
253
|
}
|
|
@@ -240,9 +261,9 @@ export class TmuxBackend {
|
|
|
240
261
|
if (this.adoptedPaneTarget) {
|
|
241
262
|
try {
|
|
242
263
|
// Only unzoom if the pane is currently zoomed
|
|
243
|
-
const zoomed = execSync(`tmux display -t ${shellescape(this.adoptedPaneTarget)} -p '#{window_zoomed_flag}'`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
264
|
+
const zoomed = execSync(`tmux display -t ${shellescape(this.adoptedPaneTarget)} -p '#{window_zoomed_flag}'`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], env: tmuxEnv() }).trim();
|
|
244
265
|
if (zoomed === '1') {
|
|
245
|
-
execSync(`tmux resize-pane -Z -t ${shellescape(this.adoptedPaneTarget)}`, { stdio: 'ignore' });
|
|
266
|
+
execSync(`tmux resize-pane -Z -t ${shellescape(this.adoptedPaneTarget)}`, { stdio: 'ignore', env: tmuxEnv() });
|
|
246
267
|
}
|
|
247
268
|
}
|
|
248
269
|
catch { /* pane may be gone — benign */ }
|
|
@@ -287,7 +308,7 @@ export class TmuxBackend {
|
|
|
287
308
|
// can be revisited later via `tmux pipe-pane` (out-of-band capture)
|
|
288
309
|
// without polluting the -CC client.
|
|
289
310
|
try {
|
|
290
|
-
execSync(`tmux resize-pane -Z -t ${shellescape(tmuxTarget)}`, { stdio: 'ignore' });
|
|
311
|
+
execSync(`tmux resize-pane -Z -t ${shellescape(tmuxTarget)}`, { stdio: 'ignore', env: tmuxEnv() });
|
|
291
312
|
}
|
|
292
313
|
catch { /* benign */ }
|
|
293
314
|
this.process = pty.spawn('tmux', ['attach-session', '-t', tmuxTarget], {
|
|
@@ -295,7 +316,7 @@ export class TmuxBackend {
|
|
|
295
316
|
cols: opts.cols,
|
|
296
317
|
rows: opts.rows,
|
|
297
318
|
cwd: opts.cwd,
|
|
298
|
-
env: opts.env,
|
|
319
|
+
env: tmuxEnv(opts.env),
|
|
299
320
|
});
|
|
300
321
|
}
|
|
301
322
|
getAttachInfo() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tmux-backend.js","sourceRoot":"","sources":["../../../src/adapters/backend/tmux-backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"tmux-backend.js","sourceRoot":"","sources":["../../../src/adapters/backend/tmux-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tmux-pipe-backend.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/tmux-pipe-backend.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"tmux-pipe-backend.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/tmux-pipe-backend.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAQ5D;;yEAEyE;AACzE,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,qBAAa,eAAgB,YAAW,cAAc;IACpD,6CAA6C;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmE;IAC3F,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,MAAM,CAAS;IACvB,+EAA+E;IAC/E,OAAO,CAAC,YAAY,CAAS;gBAEjB,UAAU,EAAE,MAAM;IAQ9B;;mEAE+D;IAC/D,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAoD3D,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAS5B,eAAe,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IASxC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAe7B,aAAa,IAAI,IAAI;IASrB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAS3C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQxC,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAIxC,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI;IAItE,IAAI,IAAI,IAAI;IAmBZ,cAAc,IAAI,IAAI;IAKtB,WAAW,IAAI,MAAM,GAAG,IAAI;IAkB5B,aAAa;IAMb;;;;;;;;;;;;0EAYsE;IACtE,oBAAoB,IAAI,MAAM;IAyB9B;;kEAE8D;IAC9D,OAAO,CAAC,iBAAiB;IAazB;iFAC6E;IAC7E,WAAW,IAAI,OAAO;IActB,OAAO,CAAC,QAAQ;CAKjB"}
|
|
@@ -28,6 +28,7 @@ import { execSync, execFileSync, spawnSync } from 'node:child_process';
|
|
|
28
28
|
import { tmpdir } from 'node:os';
|
|
29
29
|
import { join } from 'node:path';
|
|
30
30
|
import { randomBytes } from 'node:crypto';
|
|
31
|
+
import { tmuxEnv } from '../../setup/ensure-tmux.js';
|
|
31
32
|
function shellescape(s) {
|
|
32
33
|
// Single-quote-escape, replacing internal ' with '\''
|
|
33
34
|
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
@@ -99,7 +100,7 @@ export class TmuxPipeBackend {
|
|
|
99
100
|
// -O causes tmux to overwrite any prior pipe-pane subscription.
|
|
100
101
|
// The shell command must redirect to the fifo; tmux runs it via /bin/sh.
|
|
101
102
|
try {
|
|
102
|
-
execSync(`tmux pipe-pane -O -t ${shellescape(this.paneTarget)} 'cat > ${shellescape(this.fifoPath)}'`, { stdio: 'ignore', timeout: 5000 });
|
|
103
|
+
execSync(`tmux pipe-pane -O -t ${shellescape(this.paneTarget)} 'cat > ${shellescape(this.fifoPath)}'`, { stdio: 'ignore', timeout: 5000, env: tmuxEnv() });
|
|
103
104
|
this.pipeAttached = true;
|
|
104
105
|
}
|
|
105
106
|
catch (err) {
|
|
@@ -117,6 +118,7 @@ export class TmuxPipeBackend {
|
|
|
117
118
|
execFileSync('tmux', ['send-keys', '-t', this.paneTarget, '-l', '--', text], {
|
|
118
119
|
stdio: 'ignore',
|
|
119
120
|
timeout: 5000,
|
|
121
|
+
env: tmuxEnv(),
|
|
120
122
|
});
|
|
121
123
|
}
|
|
122
124
|
sendSpecialKeys(...keys) {
|
|
@@ -125,6 +127,7 @@ export class TmuxPipeBackend {
|
|
|
125
127
|
execFileSync('tmux', ['send-keys', '-t', this.paneTarget, ...keys], {
|
|
126
128
|
stdio: 'ignore',
|
|
127
129
|
timeout: 5000,
|
|
130
|
+
env: tmuxEnv(),
|
|
128
131
|
});
|
|
129
132
|
}
|
|
130
133
|
pasteText(text) {
|
|
@@ -134,10 +137,12 @@ export class TmuxPipeBackend {
|
|
|
134
137
|
input: text,
|
|
135
138
|
stdio: ['pipe', 'ignore', 'ignore'],
|
|
136
139
|
timeout: 5000,
|
|
140
|
+
env: tmuxEnv(),
|
|
137
141
|
});
|
|
138
142
|
execFileSync('tmux', ['paste-buffer', '-t', this.paneTarget, '-d'], {
|
|
139
143
|
stdio: 'ignore',
|
|
140
144
|
timeout: 5000,
|
|
145
|
+
env: tmuxEnv(),
|
|
141
146
|
});
|
|
142
147
|
}
|
|
143
148
|
enterCopyMode() {
|
|
@@ -146,6 +151,7 @@ export class TmuxPipeBackend {
|
|
|
146
151
|
execFileSync('tmux', ['copy-mode', '-e', '-t', this.paneTarget], {
|
|
147
152
|
stdio: 'ignore',
|
|
148
153
|
timeout: 5000,
|
|
154
|
+
env: tmuxEnv(),
|
|
149
155
|
});
|
|
150
156
|
}
|
|
151
157
|
sendCopyModeCommand(xCommand) {
|
|
@@ -154,6 +160,7 @@ export class TmuxPipeBackend {
|
|
|
154
160
|
execFileSync('tmux', ['send-keys', '-t', this.paneTarget, '-X', xCommand], {
|
|
155
161
|
stdio: 'ignore',
|
|
156
162
|
timeout: 5000,
|
|
163
|
+
env: tmuxEnv(),
|
|
157
164
|
});
|
|
158
165
|
}
|
|
159
166
|
resize(cols, rows) {
|
|
@@ -177,7 +184,7 @@ export class TmuxPipeBackend {
|
|
|
177
184
|
// turns it off for the target pane.
|
|
178
185
|
if (this.pipeAttached) {
|
|
179
186
|
try {
|
|
180
|
-
execSync(`tmux pipe-pane -t ${shellescape(this.paneTarget)}`, { stdio: 'ignore', timeout: 3000 });
|
|
187
|
+
execSync(`tmux pipe-pane -t ${shellescape(this.paneTarget)}`, { stdio: 'ignore', timeout: 3000, env: tmuxEnv() });
|
|
181
188
|
}
|
|
182
189
|
catch { /* pane may already be gone — benign */ }
|
|
183
190
|
this.pipeAttached = false;
|
|
@@ -201,7 +208,13 @@ export class TmuxPipeBackend {
|
|
|
201
208
|
}
|
|
202
209
|
getChildPid() {
|
|
203
210
|
try {
|
|
204
|
-
const out = execSync(`tmux display-message -p -t ${shellescape(this.paneTarget)} '#{pane_pid}'`,
|
|
211
|
+
const out = execSync(`tmux display-message -p -t ${shellescape(this.paneTarget)} '#{pane_pid}'`,
|
|
212
|
+
// Explicit stdio: execSync's default leaks the child's stderr to the
|
|
213
|
+
// parent — when tmux server is unavailable this would spam
|
|
214
|
+
// "error connecting to /tmp/tmux-UID/default" into daemon-error.log
|
|
215
|
+
// every poll. Capture stderr in the result instead.
|
|
216
|
+
// tmuxEnv() also strips $TMUX so we don't target a dead parent server.
|
|
217
|
+
{ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000, env: tmuxEnv() }).trim();
|
|
205
218
|
const pid = parseInt(out, 10);
|
|
206
219
|
return pid > 0 ? pid : null;
|
|
207
220
|
}
|
|
@@ -231,7 +244,9 @@ export class TmuxPipeBackend {
|
|
|
231
244
|
return '';
|
|
232
245
|
try {
|
|
233
246
|
const altOn = this.isPaneInAltBuffer();
|
|
234
|
-
const raw = execSync(`tmux capture-pane -e -p -t ${shellescape(this.paneTarget)} -S -`,
|
|
247
|
+
const raw = execSync(`tmux capture-pane -e -p -t ${shellescape(this.paneTarget)} -S -`,
|
|
248
|
+
// Explicit stdio — see getChildPid for why default leaks tmux stderr.
|
|
249
|
+
{ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 5000, maxBuffer: 16 * 1024 * 1024, env: tmuxEnv() });
|
|
235
250
|
const normalised = normaliseCaptureLineEndings(raw);
|
|
236
251
|
if (altOn) {
|
|
237
252
|
// The pane's CLI (e.g. Claude Code, vim) is in the alternate screen
|
|
@@ -253,7 +268,9 @@ export class TmuxPipeBackend {
|
|
|
253
268
|
* needs an alt-buffer-enter prefix for correct rendering. */
|
|
254
269
|
isPaneInAltBuffer() {
|
|
255
270
|
try {
|
|
256
|
-
const out = execSync(`tmux display-message -p -t ${shellescape(this.paneTarget)} '#{alternate_on}'`,
|
|
271
|
+
const out = execSync(`tmux display-message -p -t ${shellescape(this.paneTarget)} '#{alternate_on}'`,
|
|
272
|
+
// Explicit stdio — see getChildPid for why default leaks tmux stderr.
|
|
273
|
+
{ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 2000, env: tmuxEnv() }).trim();
|
|
257
274
|
return out === '1';
|
|
258
275
|
}
|
|
259
276
|
catch {
|
|
@@ -269,6 +286,7 @@ export class TmuxPipeBackend {
|
|
|
269
286
|
execSync(`tmux display-message -p -t ${shellescape(this.paneTarget)} ''`, {
|
|
270
287
|
stdio: 'ignore',
|
|
271
288
|
timeout: 2000,
|
|
289
|
+
env: tmuxEnv(),
|
|
272
290
|
});
|
|
273
291
|
return true;
|
|
274
292
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tmux-pipe-backend.js","sourceRoot":"","sources":["../../../src/adapters/backend/tmux-pipe-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"tmux-pipe-backend.js","sourceRoot":"","sources":["../../../src/adapters/backend/tmux-pipe-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coco.d.ts","sourceRoot":"","sources":["../../../src/adapters/cli/coco.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"coco.d.ts","sourceRoot":"","sources":["../../../src/adapters/cli/coco.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,YAAY,CAAC;AAyExD,wBAAgB,iBAAiB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,UAAU,CA0InE;AAED,eAAO,MAAM,MAAM,0BAAoB,CAAC"}
|
|
@@ -1,8 +1,88 @@
|
|
|
1
|
+
import { existsSync, statSync, openSync, readSync, closeSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
1
4
|
import { resolveCommand } from './registry.js';
|
|
2
5
|
import { BOTMUX_SHELL_HINTS } from './shared-hints.js';
|
|
6
|
+
/** Global submit log — CoCo appends one JSON line here on every successful
|
|
7
|
+
* user submit across all sessions (mode:"user"). Format observed:
|
|
8
|
+
* `{"content":"...","mode":"user","timestamp":"..."}`. Used the same way
|
|
9
|
+
* the Codex adapter uses ~/.codex/history.jsonl: write → poll for our
|
|
10
|
+
* marker → retry Enter if missing → return {submitted:false, recheck}
|
|
11
|
+
* on final failure so worker can surface a Lark warning. */
|
|
12
|
+
const HISTORY_PATH = join(homedir(), '.cache', 'coco', 'history.jsonl');
|
|
3
13
|
function delay(ms) {
|
|
4
14
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
5
15
|
}
|
|
16
|
+
function currentFileSize(path) {
|
|
17
|
+
if (!existsSync(path))
|
|
18
|
+
return 0;
|
|
19
|
+
try {
|
|
20
|
+
return statSync(path).size;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Scan `path` for a JSON line newer than `fromByte` that's a user-submit
|
|
27
|
+
* whose decoded `content` starts with `prefix`. Parses each candidate line
|
|
28
|
+
* with JSON.parse — substring match on the raw bytes is unreliable here
|
|
29
|
+
* because CoCo's Go marshaller HTML-escapes `<`, `>`, `&` into `<`,
|
|
30
|
+
* `>`, `&`, which our string-form prefix won't match. Decoding
|
|
31
|
+
* the field and comparing JS strings sidesteps all of that. */
|
|
32
|
+
function historyDeltaContains(path, fromByte, prefix) {
|
|
33
|
+
if (!existsSync(path))
|
|
34
|
+
return false;
|
|
35
|
+
let size;
|
|
36
|
+
try {
|
|
37
|
+
size = statSync(path).size;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (size <= fromByte)
|
|
43
|
+
return false;
|
|
44
|
+
const len = size - fromByte;
|
|
45
|
+
const buf = Buffer.alloc(len);
|
|
46
|
+
const fd = openSync(path, 'r');
|
|
47
|
+
try {
|
|
48
|
+
readSync(fd, buf, 0, len, fromByte);
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
closeSync(fd);
|
|
52
|
+
}
|
|
53
|
+
const delta = buf.toString('utf8');
|
|
54
|
+
for (const line of delta.split('\n')) {
|
|
55
|
+
if (!line || !line.includes('"mode":"user"'))
|
|
56
|
+
continue;
|
|
57
|
+
try {
|
|
58
|
+
const parsed = JSON.parse(line);
|
|
59
|
+
if (typeof parsed.content === 'string' && parsed.content.startsWith(prefix)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Truncated tail / non-JSON line — keep scanning the rest.
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
async function waitForHistoryAppend(path, fromByte, prefix, timeoutMs) {
|
|
70
|
+
const deadline = Date.now() + timeoutMs;
|
|
71
|
+
while (Date.now() < deadline) {
|
|
72
|
+
if (historyDeltaContains(path, fromByte, prefix))
|
|
73
|
+
return true;
|
|
74
|
+
await delay(100);
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
/** First 40 chars of the original content — used as a prefix match against
|
|
79
|
+
* the JSON-decoded `content` field of each user-mode line in history.jsonl.
|
|
80
|
+
* Compare against decoded strings, NOT against raw file bytes: CoCo's Go
|
|
81
|
+
* marshaller HTML-escapes `<`, `>`, `&` so a JSON-encoded marker wouldn't
|
|
82
|
+
* match the stored bytes. 40 chars is unique enough across concurrent bots. */
|
|
83
|
+
function submitPrefix(content) {
|
|
84
|
+
return content.slice(0, 40);
|
|
85
|
+
}
|
|
6
86
|
export function createCocoAdapter(pathOverride) {
|
|
7
87
|
const bin = resolveCommand(pathOverride ?? 'coco');
|
|
8
88
|
return {
|
|
@@ -24,16 +104,110 @@ export function createCocoAdapter(pathOverride) {
|
|
|
24
104
|
return `coco --resume ${sessionId}`;
|
|
25
105
|
},
|
|
26
106
|
async writeInput(pty, content) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
107
|
+
// CoCo is a Claude Code fork (Ink TUI) and inherits Claude Code's two
|
|
108
|
+
// failure modes when content has embedded newlines:
|
|
109
|
+
// 1. tmux `send-keys -l` treats each \n as Enter, so a raw sendText
|
|
110
|
+
// of multi-line content either submits fragments line-by-line or
|
|
111
|
+
// paste-burst-coalesces with the *trailing* Enter consumed as part
|
|
112
|
+
// of the paste — text just sits in the input box, never submitted.
|
|
113
|
+
// 2. The old adapter had no verification, so the worker never knew
|
|
114
|
+
// and the user stared at Lark waiting for a reply that never came.
|
|
115
|
+
//
|
|
116
|
+
// Fix mirrors claude-code.ts:
|
|
117
|
+
// - split content by \n, type each line via send-keys -l, insert
|
|
118
|
+
// soft-newline (`\` + Enter) between lines — Claude Code's
|
|
119
|
+
// documented idiom to add a newline without submitting.
|
|
120
|
+
// - throttle each tmux call (~30ms) to stay under the paste-burst
|
|
121
|
+
// threshold so soft-newlines aren't silently swallowed as a paste.
|
|
122
|
+
// - submitDelay then a single Enter to actually submit.
|
|
123
|
+
//
|
|
124
|
+
// Verification mirrors codex.ts using ~/.cache/coco/history.jsonl:
|
|
125
|
+
// write → poll for our content prefix in the delta → retry Enter up
|
|
126
|
+
// to 3 times → return {submitted:false, recheck} on final miss so the
|
|
127
|
+
// worker can defer-recheck and otherwise surface a Lark warning.
|
|
128
|
+
const hasImagePath = /\.(jpe?g|png|gif|webp|svg|bmp)\b/i.test(content);
|
|
129
|
+
const submitDelay = hasImagePath ? 800 : 500;
|
|
130
|
+
const TYPING_THROTTLE_MS = 30;
|
|
131
|
+
const tick = () => new Promise(r => setTimeout(r, TYPING_THROTTLE_MS));
|
|
132
|
+
const trySendEnter = () => {
|
|
133
|
+
try {
|
|
134
|
+
if (pty.sendSpecialKeys)
|
|
135
|
+
pty.sendSpecialKeys('Enter');
|
|
136
|
+
else
|
|
137
|
+
pty.write('\r');
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// tmux session is gone (CLI exited mid-write) — bail cleanly
|
|
142
|
+
// rather than crashing the worker on unhandled execFileSync.
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const baseByte = currentFileSize(HISTORY_PATH);
|
|
147
|
+
const prefix = submitPrefix(content);
|
|
148
|
+
try {
|
|
149
|
+
if (pty.sendText && pty.sendSpecialKeys) {
|
|
150
|
+
const lines = content.split('\n');
|
|
151
|
+
for (let i = 0; i < lines.length; i++) {
|
|
152
|
+
if (lines[i].length > 0) {
|
|
153
|
+
pty.sendText(lines[i]);
|
|
154
|
+
await tick();
|
|
155
|
+
}
|
|
156
|
+
if (i < lines.length - 1) {
|
|
157
|
+
// Soft-newline: `\` + Enter inserts a newline without submitting.
|
|
158
|
+
pty.sendText('\\');
|
|
159
|
+
await tick();
|
|
160
|
+
pty.sendSpecialKeys('Enter');
|
|
161
|
+
await tick();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
// Non-tmux fallback (raw PTY): bracketed paste is reliable here
|
|
167
|
+
// since we control the markers directly — no send-keys -l \n quirk.
|
|
168
|
+
pty.write('\x1b[200~' + content + '\x1b[201~');
|
|
169
|
+
}
|
|
31
170
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
171
|
+
catch {
|
|
172
|
+
return { submitted: false };
|
|
173
|
+
}
|
|
174
|
+
await delay(submitDelay);
|
|
175
|
+
if (!trySendEnter())
|
|
176
|
+
return { submitted: false };
|
|
177
|
+
// Fresh-install short-wait: when history.jsonl is absent at submit
|
|
178
|
+
// time, give CoCo up to 1.2s to create it. If our marker shows up →
|
|
179
|
+
// success. If the file is still absent → trust the Enter and return
|
|
180
|
+
// (this is the genuine "first run / coco doesn't write history"
|
|
181
|
+
// case). If the file appeared but our marker isn't there → fall
|
|
182
|
+
// through to the normal retry/failure loop — better to warn than to
|
|
183
|
+
// silently mask a real submit failure on a new install.
|
|
184
|
+
if (!existsSync(HISTORY_PATH) && baseByte === 0) {
|
|
185
|
+
if (await waitForHistoryAppend(HISTORY_PATH, baseByte, prefix, 1200)) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
if (!existsSync(HISTORY_PATH)) {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
// File appeared during the wait but our marker isn't in it — fall
|
|
192
|
+
// through to the retry loop. baseByte stays 0 so the loop scans
|
|
193
|
+
// the whole file.
|
|
194
|
+
}
|
|
195
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
196
|
+
if (await waitForHistoryAppend(HISTORY_PATH, baseByte, prefix, 800)) {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
if (!trySendEnter())
|
|
200
|
+
return { submitted: false };
|
|
201
|
+
}
|
|
202
|
+
if (await waitForHistoryAppend(HISTORY_PATH, baseByte, prefix, 800)) {
|
|
203
|
+
return undefined;
|
|
36
204
|
}
|
|
205
|
+
// In-band budget exhausted. Hand the worker a recheck closure: a slow
|
|
206
|
+
// CoCo (cold start, large initial prompt, heavy hooks) may still
|
|
207
|
+
// append our marker after retries gave up. Worker re-scans after a
|
|
208
|
+
// delay before deciding whether to warn the user.
|
|
209
|
+
const recheck = () => historyDeltaContains(HISTORY_PATH, baseByte, prefix);
|
|
210
|
+
return { submitted: false, recheck };
|
|
37
211
|
},
|
|
38
212
|
completionPattern: undefined,
|
|
39
213
|
// `⏵⏵` only shows when CoCo runs with --yolo (bypass permissions). Adopted
|