botmux 2.11.1 → 2.12.0
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 +6 -0
- package/dist/adapters/backend/pty-backend.d.ts.map +1 -1
- package/dist/adapters/backend/pty-backend.js +6 -0
- package/dist/adapters/backend/pty-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts +16 -2
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +40 -10
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts +59 -0
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -0
- package/dist/adapters/backend/tmux-pipe-backend.js +288 -0
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -0
- package/dist/adapters/cli/claude-code.d.ts +15 -0
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +205 -24
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +78 -16
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +10 -0
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/cli.js +63 -8
- package/dist/cli.js.map +1 -1
- package/dist/core/command-handler.d.ts +10 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +29 -1
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/scheduler.d.ts +3 -0
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +3 -0
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/session-manager.d.ts +17 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +51 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/types.d.ts +4 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +13 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +115 -4
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +59 -120
- package/dist/daemon.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +2 -1
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +23 -9
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +26 -93
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/merge-forward.d.ts +32 -0
- package/dist/im/lark/merge-forward.d.ts.map +1 -0
- package/dist/im/lark/merge-forward.js +99 -0
- package/dist/im/lark/merge-forward.js.map +1 -0
- package/dist/services/bridge-fallback-gate.d.ts +42 -0
- package/dist/services/bridge-fallback-gate.d.ts.map +1 -0
- package/dist/services/bridge-fallback-gate.js +12 -0
- package/dist/services/bridge-fallback-gate.js.map +1 -0
- package/dist/services/bridge-turn-queue.d.ts +111 -0
- package/dist/services/bridge-turn-queue.d.ts.map +1 -0
- package/dist/services/bridge-turn-queue.js +213 -0
- package/dist/services/bridge-turn-queue.js.map +1 -0
- package/dist/services/claude-transcript.d.ts +168 -0
- package/dist/services/claude-transcript.d.ts.map +1 -0
- package/dist/services/claude-transcript.js +524 -0
- package/dist/services/claude-transcript.js.map +1 -0
- package/dist/services/schedule-store.d.ts +3 -0
- package/dist/services/schedule-store.d.ts.map +1 -1
- package/dist/services/schedule-store.js +6 -0
- package/dist/services/schedule-store.js.map +1 -1
- package/dist/services/session-store.d.ts +10 -0
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +40 -0
- package/dist/services/session-store.js.map +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +2 -1
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/render-dimensions.d.ts +48 -0
- package/dist/utils/render-dimensions.d.ts.map +1 -0
- package/dist/utils/render-dimensions.js +55 -0
- package/dist/utils/render-dimensions.js.map +1 -0
- package/dist/utils/terminal-renderer.d.ts.map +1 -1
- package/dist/utils/terminal-renderer.js +5 -2
- package/dist/utils/terminal-renderer.js.map +1 -1
- package/dist/worker.js +1068 -37
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,6 +4,12 @@ export declare class PtyBackend implements SessionBackend {
|
|
|
4
4
|
/** Claude Code session JSONL path — set by worker for claude-code sessions so
|
|
5
5
|
* the claude-code adapter can verify paste+Enter submissions via file growth. */
|
|
6
6
|
claudeJsonlPath?: string;
|
|
7
|
+
/** PID of the spawned Claude Code child — used by the claude-code adapter to
|
|
8
|
+
* follow Claude's authoritative session id via ~/.claude/sessions/<pid>.json. */
|
|
9
|
+
cliPid?: number;
|
|
10
|
+
/** Working directory the CLI was spawned in — cross-checked against the pid
|
|
11
|
+
* file's cwd field so a recycled PID can't mislead the resolver. */
|
|
12
|
+
cliCwd?: string;
|
|
7
13
|
spawn(bin: string, args: string[], opts: SpawnOpts): void;
|
|
8
14
|
write(data: string): void;
|
|
9
15
|
resize(cols: number, rows: number): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pty-backend.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/pty-backend.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAW5D,qBAAa,UAAW,YAAW,cAAc;IAC/C,OAAO,CAAC,OAAO,CAAyB;IAExC;sFACkF;IAClF,eAAe,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"pty-backend.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/pty-backend.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAW5D,qBAAa,UAAW,YAAW,cAAc;IAC/C,OAAO,CAAC,OAAO,CAAyB;IAExC;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,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAUzD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB,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;IAI5B,IAAI,IAAI,IAAI;CAMb"}
|
|
@@ -16,6 +16,12 @@ export class PtyBackend {
|
|
|
16
16
|
/** Claude Code session JSONL path — set by worker for claude-code sessions so
|
|
17
17
|
* the claude-code adapter can verify paste+Enter submissions via file growth. */
|
|
18
18
|
claudeJsonlPath;
|
|
19
|
+
/** PID of the spawned Claude Code child — used by the claude-code adapter to
|
|
20
|
+
* follow Claude's authoritative session id via ~/.claude/sessions/<pid>.json. */
|
|
21
|
+
cliPid;
|
|
22
|
+
/** Working directory the CLI was spawned in — cross-checked against the pid
|
|
23
|
+
* file's cwd field so a recycled PID can't mislead the resolver. */
|
|
24
|
+
cliCwd;
|
|
19
25
|
spawn(bin, args, opts) {
|
|
20
26
|
this.process = pty.spawn(bin, args, {
|
|
21
27
|
name: 'xterm-256color',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pty-backend.js","sourceRoot":"","sources":["../../../src/adapters/backend/pty-backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,8EAA8E;AAC9E,IAAI,CAAC;IACH,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC,EAC/D,WAAW,EAAE,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,cAAc,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;IACnC,IAAI,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC;QAAE,SAAS,CAAC,MAAM,EAAE,IAAI,GAAG,KAAK,CAAC,CAAC;AACvD,CAAC;AAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;AAE7B,MAAM,OAAO,UAAU;IACb,OAAO,GAAoB,IAAI,CAAC;IAExC;sFACkF;IAClF,eAAe,CAAU;
|
|
1
|
+
{"version":3,"file":"pty-backend.js","sourceRoot":"","sources":["../../../src/adapters/backend/pty-backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,8EAA8E;AAC9E,IAAI,CAAC;IACH,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC,EAC/D,WAAW,EAAE,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,cAAc,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;IACnC,IAAI,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC;QAAE,SAAS,CAAC,MAAM,EAAE,IAAI,GAAG,KAAK,CAAC,CAAC;AACvD,CAAC;AAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;AAE7B,MAAM,OAAO,UAAU;IACb,OAAO,GAAoB,IAAI,CAAC;IAExC;sFACkF;IAClF,eAAe,CAAU;IACzB;sFACkF;IAClF,MAAM,CAAU;IAChB;yEACqE;IACrE,MAAM,CAAU;IAEhB,KAAK,CAAC,GAAW,EAAE,IAAc,EAAE,IAAe;QAChD,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;YAClC,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAY;QAChB,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,IAAY;QAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,yFAAyF;IACzF,MAAM,CAAC,EAA0B;QAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED,yFAAyF;IACzF,MAAM,CAAC,EAAwD;QAC7D,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;YAC5C,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC;IACnC,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC;gBAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;YACzD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -15,9 +15,19 @@ export declare class TmuxBackend implements SessionBackend {
|
|
|
15
15
|
private readonly sessionName;
|
|
16
16
|
private readonly ownsSession;
|
|
17
17
|
private reattaching;
|
|
18
|
+
/** Tmux pane target when in adopt mode (e.g. "0:2.0") — set by attachToExisting.
|
|
19
|
+
* When non-null, ALL pane-scoped tmux commands (send-keys / paste-buffer /
|
|
20
|
+
* copy-mode / list-panes) must address this pane explicitly; using
|
|
21
|
+
* `this.sessionName` would either resolve nothing (the name is synthetic
|
|
22
|
+
* in adopt mode) or fall through to whichever pane tmux happens to have
|
|
23
|
+
* active, which is exactly the bug we're avoiding. */
|
|
24
|
+
private adoptedPaneTarget;
|
|
18
25
|
constructor(sessionName: string, opts?: {
|
|
19
26
|
ownsSession?: boolean;
|
|
20
27
|
});
|
|
28
|
+
/** Target string to use for pane-scoped tmux commands. In adopt mode this
|
|
29
|
+
* is the real pane address ("0:2.0"); otherwise the bmx-* session name. */
|
|
30
|
+
private get cmdTarget();
|
|
21
31
|
/** Check if tmux binary is available on PATH. */
|
|
22
32
|
static isAvailable(): boolean;
|
|
23
33
|
/** Derive tmux session name from a session UUID. */
|
|
@@ -34,6 +44,12 @@ export declare class TmuxBackend implements SessionBackend {
|
|
|
34
44
|
/** Claude Code session JSONL path — set by worker for claude-code sessions so
|
|
35
45
|
* the claude-code adapter can verify paste+Enter submissions via file growth. */
|
|
36
46
|
claudeJsonlPath?: string;
|
|
47
|
+
/** PID of the spawned Claude Code child — used by the claude-code adapter to
|
|
48
|
+
* follow Claude's authoritative session id via ~/.claude/sessions/<pid>.json. */
|
|
49
|
+
cliPid?: number;
|
|
50
|
+
/** Working directory the CLI was spawned in — cross-checked against the pid
|
|
51
|
+
* file's cwd field so a recycled PID can't mislead the resolver. */
|
|
52
|
+
cliCwd?: string;
|
|
37
53
|
write(data: string): void;
|
|
38
54
|
/**
|
|
39
55
|
* Send text literally to the tmux pane via `tmux send-keys -l`.
|
|
@@ -75,8 +91,6 @@ export declare class TmuxBackend implements SessionBackend {
|
|
|
75
91
|
* The zoom is undone when the backend is killed (detach/disconnect).
|
|
76
92
|
*/
|
|
77
93
|
attachToExisting(tmuxTarget: string, opts: SpawnOpts): void;
|
|
78
|
-
/** Tmux pane target when in adopt mode (e.g. "0:2.0") — used for zoom cleanup. */
|
|
79
|
-
private adoptedPaneTarget;
|
|
80
94
|
getAttachInfo(): {
|
|
81
95
|
type: "tmux";
|
|
82
96
|
sessionName: 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;AAE5D;;;;;;;;;;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;
|
|
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;AAE5D;;;;;;;;;;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,iDAAiD;IACjD,MAAM,CAAC,WAAW,IAAI,OAAO;IAS7B,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;IAarC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAgEzD,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;IAO5B,qEAAqE;IACrE,eAAe,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAOxC;;;;OAIG;IACH,aAAa,IAAI,IAAI;IAOrB,kFAAkF;IAClF,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAO3C;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAY7B,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;IAiB5B,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"}
|
|
@@ -16,10 +16,22 @@ export class TmuxBackend {
|
|
|
16
16
|
sessionName;
|
|
17
17
|
ownsSession;
|
|
18
18
|
reattaching = false;
|
|
19
|
+
/** Tmux pane target when in adopt mode (e.g. "0:2.0") — set by attachToExisting.
|
|
20
|
+
* When non-null, ALL pane-scoped tmux commands (send-keys / paste-buffer /
|
|
21
|
+
* copy-mode / list-panes) must address this pane explicitly; using
|
|
22
|
+
* `this.sessionName` would either resolve nothing (the name is synthetic
|
|
23
|
+
* in adopt mode) or fall through to whichever pane tmux happens to have
|
|
24
|
+
* active, which is exactly the bug we're avoiding. */
|
|
25
|
+
adoptedPaneTarget = null;
|
|
19
26
|
constructor(sessionName, opts) {
|
|
20
27
|
this.sessionName = sessionName;
|
|
21
28
|
this.ownsSession = opts?.ownsSession ?? true;
|
|
22
29
|
}
|
|
30
|
+
/** Target string to use for pane-scoped tmux commands. In adopt mode this
|
|
31
|
+
* is the real pane address ("0:2.0"); otherwise the bmx-* session name. */
|
|
32
|
+
get cmdTarget() {
|
|
33
|
+
return this.adoptedPaneTarget ?? this.sessionName;
|
|
34
|
+
}
|
|
23
35
|
// ─── Static helpers ───────────────────────────────────────────────────────
|
|
24
36
|
/** Check if tmux binary is available on PATH. */
|
|
25
37
|
static isAvailable() {
|
|
@@ -134,6 +146,12 @@ export class TmuxBackend {
|
|
|
134
146
|
/** Claude Code session JSONL path — set by worker for claude-code sessions so
|
|
135
147
|
* the claude-code adapter can verify paste+Enter submissions via file growth. */
|
|
136
148
|
claudeJsonlPath;
|
|
149
|
+
/** PID of the spawned Claude Code child — used by the claude-code adapter to
|
|
150
|
+
* follow Claude's authoritative session id via ~/.claude/sessions/<pid>.json. */
|
|
151
|
+
cliPid;
|
|
152
|
+
/** Working directory the CLI was spawned in — cross-checked against the pid
|
|
153
|
+
* file's cwd field so a recycled PID can't mislead the resolver. */
|
|
154
|
+
cliCwd;
|
|
137
155
|
write(data) {
|
|
138
156
|
this.process?.write(data);
|
|
139
157
|
}
|
|
@@ -143,14 +161,14 @@ export class TmuxBackend {
|
|
|
143
161
|
* For multiline text, use pasteText() instead (send-keys -l sends \n as Enter).
|
|
144
162
|
*/
|
|
145
163
|
sendText(text) {
|
|
146
|
-
execFileSync('tmux', ['send-keys', '-t', this.
|
|
164
|
+
execFileSync('tmux', ['send-keys', '-t', this.cmdTarget, '-l', '--', text], {
|
|
147
165
|
stdio: 'ignore',
|
|
148
166
|
timeout: 5000,
|
|
149
167
|
});
|
|
150
168
|
}
|
|
151
169
|
/** Send special keys (Enter, Escape, C-c, etc.) to the tmux pane. */
|
|
152
170
|
sendSpecialKeys(...keys) {
|
|
153
|
-
execFileSync('tmux', ['send-keys', '-t', this.
|
|
171
|
+
execFileSync('tmux', ['send-keys', '-t', this.cmdTarget, ...keys], {
|
|
154
172
|
stdio: 'ignore',
|
|
155
173
|
timeout: 5000,
|
|
156
174
|
});
|
|
@@ -161,14 +179,14 @@ export class TmuxBackend {
|
|
|
161
179
|
* is in the alternate screen buffer (Claude Code, vim, etc.).
|
|
162
180
|
*/
|
|
163
181
|
enterCopyMode() {
|
|
164
|
-
execFileSync('tmux', ['copy-mode', '-e', '-t', this.
|
|
182
|
+
execFileSync('tmux', ['copy-mode', '-e', '-t', this.cmdTarget], {
|
|
165
183
|
stdio: 'ignore',
|
|
166
184
|
timeout: 5000,
|
|
167
185
|
});
|
|
168
186
|
}
|
|
169
187
|
/** Send a copy-mode X-command (e.g. 'halfpage-up', 'halfpage-down', 'cancel'). */
|
|
170
188
|
sendCopyModeCommand(xCommand) {
|
|
171
|
-
execFileSync('tmux', ['send-keys', '-t', this.
|
|
189
|
+
execFileSync('tmux', ['send-keys', '-t', this.cmdTarget, '-X', xCommand], {
|
|
172
190
|
stdio: 'ignore',
|
|
173
191
|
timeout: 5000,
|
|
174
192
|
});
|
|
@@ -184,7 +202,7 @@ export class TmuxBackend {
|
|
|
184
202
|
stdio: ['pipe', 'ignore', 'ignore'],
|
|
185
203
|
timeout: 5000,
|
|
186
204
|
});
|
|
187
|
-
execFileSync('tmux', ['paste-buffer', '-t', this.
|
|
205
|
+
execFileSync('tmux', ['paste-buffer', '-t', this.cmdTarget, '-d'], {
|
|
188
206
|
stdio: 'ignore',
|
|
189
207
|
timeout: 5000,
|
|
190
208
|
});
|
|
@@ -204,8 +222,12 @@ export class TmuxBackend {
|
|
|
204
222
|
}
|
|
205
223
|
getChildPid() {
|
|
206
224
|
try {
|
|
207
|
-
|
|
208
|
-
|
|
225
|
+
// display-message resolves the *exact* target pane (single line out),
|
|
226
|
+
// unlike list-panes which returns every pane in the target's window
|
|
227
|
+
// when cmdTarget is a pane address — taking the first line of that
|
|
228
|
+
// 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}'`, { encoding: 'utf-8', timeout: 3000 }).trim();
|
|
230
|
+
const pid = parseInt(output, 10);
|
|
209
231
|
return pid > 0 ? pid : null;
|
|
210
232
|
}
|
|
211
233
|
catch {
|
|
@@ -254,10 +276,20 @@ export class TmuxBackend {
|
|
|
254
276
|
// Zoom the target pane BEFORE attaching — this makes the pane fill the entire
|
|
255
277
|
// window, so the PTY output (and web terminal) only shows this one pane.
|
|
256
278
|
// If the pane is already the only one in the window, zoom is a no-op.
|
|
279
|
+
//
|
|
280
|
+
// We intentionally attach to the source session directly rather than
|
|
281
|
+
// creating a grouped viewer session: in tmux -CC + iTerm2 control mode
|
|
282
|
+
// the extra session disrupts the integration's window/pane bookkeeping
|
|
283
|
+
// and tearing it down on disconnect breaks the user's original layout
|
|
284
|
+
// (iTerm splits one source window's panes into separate native windows).
|
|
285
|
+
// The downside is the web terminal will follow whichever window the
|
|
286
|
+
// user's primary -CC client is currently focused on; that stickiness
|
|
287
|
+
// can be revisited later via `tmux pipe-pane` (out-of-band capture)
|
|
288
|
+
// without polluting the -CC client.
|
|
257
289
|
try {
|
|
258
290
|
execSync(`tmux resize-pane -Z -t ${shellescape(tmuxTarget)}`, { stdio: 'ignore' });
|
|
259
291
|
}
|
|
260
|
-
catch { /*
|
|
292
|
+
catch { /* benign */ }
|
|
261
293
|
this.process = pty.spawn('tmux', ['attach-session', '-t', tmuxTarget], {
|
|
262
294
|
name: 'xterm-256color',
|
|
263
295
|
cols: opts.cols,
|
|
@@ -266,8 +298,6 @@ export class TmuxBackend {
|
|
|
266
298
|
env: opts.env,
|
|
267
299
|
});
|
|
268
300
|
}
|
|
269
|
-
/** Tmux pane target when in adopt mode (e.g. "0:2.0") — used for zoom cleanup. */
|
|
270
|
-
adoptedPaneTarget = null;
|
|
271
301
|
getAttachInfo() {
|
|
272
302
|
return { type: 'tmux', sessionName: this.sessionName };
|
|
273
303
|
}
|
|
@@ -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;AAG5D;;;;;;;;;;GAUG;AACH,MAAM,OAAO,WAAW;IACd,OAAO,GAAoB,IAAI,CAAC;IACvB,WAAW,CAAS;IACpB,WAAW,CAAU;IAC9B,WAAW,GAAG,KAAK,CAAC;
|
|
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;AAG5D;;;;;;;;;;GAUG;AACH,MAAM,OAAO,WAAW;IACd,OAAO,GAAoB,IAAI,CAAC;IACvB,WAAW,CAAS;IACpB,WAAW,CAAU;IAC9B,WAAW,GAAG,KAAK,CAAC;IAC5B;;;;;2DAKuD;IAC/C,iBAAiB,GAAkB,IAAI,CAAC;IAEhD,YAAY,WAAmB,EAAE,IAAgC;QAC/D,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,IAAI,EAAE,WAAW,IAAI,IAAI,CAAC;IAC/C,CAAC;IAED;gFAC4E;IAC5E,IAAY,SAAS;QACnB,OAAO,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,WAAW,CAAC;IACpD,CAAC;IAED,6EAA6E;IAE7E,iDAAiD;IACjD,MAAM,CAAC,WAAW;QAChB,IAAI,CAAC;YACH,QAAQ,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YACzC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,oDAAoD;IACpD,MAAM,CAAC,WAAW,CAAC,SAAiB;QAClC,OAAO,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IACxC,CAAC;IAED,4CAA4C;IAC5C,MAAM,CAAC,UAAU,CAAC,IAAY;QAC5B,IAAI,CAAC;YACH,QAAQ,CAAC,uBAAuB,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC1E,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,MAAM,CAAC,WAAW,CAAC,IAAY;QAC7B,IAAI,CAAC;YACH,QAAQ,CAAC,wBAAwB,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7E,CAAC;QAAC,MAAM,CAAC,CAAC,2BAA2B,CAAC,CAAC;IACzC,CAAC;IAED,oDAAoD;IACpD,MAAM,CAAC,kBAAkB;QACvB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,QAAQ,CAAC,qDAAqD,EAAE;gBAC1E,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,6EAA6E;IAE7E,KAAK,CAAC,GAAW,EAAE,IAAc,EAAE,IAAe;QAChD,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAE5D,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,6DAA6D;YAC7D,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,gBAAgB,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE;gBAC3E,IAAI,EAAE,gBAAgB;gBACtB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,GAAG,EAAE,IAAI,CAAC,GAAG;aACd,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,mEAAmE;YACnE,sEAAsE;YACtE,yEAAyE;YACzE,MAAM,QAAQ,GAAa,EAAE,CAAC;YAC9B,KAAK,MAAM,GAAG,IAAI,qBAAqB,EAAE,CAAC;gBACxC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;gBAC5B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;oBACtB,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC;gBACvC,CAAC;YACH,CAAC;YAED,kDAAkD;YAClD,MAAM,QAAQ,GAAG;gBACf,aAAa;gBACb,IAAI,EAAE,IAAI,CAAC,WAAW;gBACtB,GAAG,QAAQ;gBACX,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;gBACvB,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;aACnB,CAAC;YACF,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE;gBACzC,IAAI,EAAE,gBAAgB;gBACtB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,GAAG,EAAE,IAAI,CAAC,GAAG;aACd,CAAC,CAAC;QACL,CAAC;QAED,kCAAkC;QAClC,mEAAmE;QACnE,mEAAmE;QACnE,mDAAmD;QACnD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBACxC,QAAQ,CAAC,sBAAsB,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACpE,QAAQ,CAAC,sBAAsB,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAClE,4EAA4E;gBAC5E,QAAQ,CAAC,qCAAqC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACrE,QAAQ,CAAC,sBAAsB,CAAC,sBAAsB,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAC7E,qEAAqE;gBACrE,mEAAmE;gBACnE,gEAAgE;gBAChE,mEAAmE;gBACnE,iCAAiC;gBACjC,QAAQ,CAAC,sBAAsB,CAAC,sBAAsB,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC/E,CAAC;YAAC,MAAM,CAAC,CAAC,2CAA2C,CAAC,CAAC;QACzD,CAAC,EAAE,GAAG,CAAC,CAAC;IACV,CAAC;IAED,wEAAwE;IACxE,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;sFACkF;IAClF,eAAe,CAAU;IACzB;sFACkF;IAClF,MAAM,CAAU;IAChB;yEACqE;IACrE,MAAM,CAAU;IAEhB,KAAK,CAAC,IAAY;QAChB,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,QAAQ,CAAC,IAAY;QACnB,YAAY,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE;YAC1E,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC;IAED,qEAAqE;IACrE,eAAe,CAAC,GAAG,IAAc;QAC/B,YAAY,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,EAAE;YACjE,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,aAAa;QACX,YAAY,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE;YAC9D,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC;IAED,kFAAkF;IAClF,mBAAmB,CAAC,QAAgB;QAClC,YAAY,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE;YACxE,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,IAAY;QACpB,YAAY,CAAC,MAAM,EAAE,CAAC,aAAa,EAAE,GAAG,CAAC,EAAE;YACzC,KAAK,EAAE,IAAI;YACX,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC;YACnC,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,YAAY,CAAC,MAAM,EAAE,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE;YACjE,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,IAAY;QAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,yFAAyF;IACzF,MAAM,CAAC,EAA0B;QAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED,yFAAyF;IACzF,MAAM,CAAC,EAAwD;QAC7D,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;YAC5C,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW;QACT,IAAI,CAAC;YACH,sEAAsE;YACtE,oEAAoE;YACpE,mEAAmE;YACnE,oEAAoE;YACpE,MAAM,MAAM,GAAG,QAAQ,CACrB,8BAA8B,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,EACzE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CACrC,CAAC,IAAI,EAAE,CAAC;YACT,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACjC,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,IAAI;QACF,wEAAwE;QACxE,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,8CAA8C;gBAC9C,MAAM,MAAM,GAAG,QAAQ,CACrB,mBAAmB,WAAW,CAAC,IAAI,CAAC,iBAAiB,CAAC,6BAA6B,EACnF,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CACvD,CAAC,IAAI,EAAE,CAAC;gBACT,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;oBACnB,QAAQ,CAAC,0BAA0B,WAAW,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACjG,CAAC;YACH,CAAC;YAAC,MAAM,CAAC,CAAC,+BAA+B,CAAC,CAAC;YAC3C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAChC,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC;gBAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;YACzD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IAED,oEAAoE;IACpE,cAAc;QACZ,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,gBAAgB,CAAC,UAAkB,EAAE,IAAe;QAClD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,iBAAiB,GAAG,UAAU,CAAC;QAEpC,8EAA8E;QAC9E,yEAAyE;QACzE,sEAAsE;QACtE,EAAE;QACF,qEAAqE;QACrE,uEAAuE;QACvE,uEAAuE;QACvE,sEAAsE;QACtE,yEAAyE;QACzE,oEAAoE;QACpE,qEAAqE;QACrE,oEAAoE;QACpE,oCAAoC;QACpC,IAAI,CAAC;YACH,QAAQ,CAAC,0BAA0B,WAAW,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACrF,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QAExB,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,gBAAgB,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE;YACrE,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC,CAAC;IACL,CAAC;IAED,aAAa;QACX,OAAO,EAAE,IAAI,EAAE,MAAe,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;IAClE,CAAC;CACF;AAED,iFAAiF;AAEjF;;;;;GAKG;AACH,MAAM,qBAAqB,GAAG;IAC5B,QAAQ;IACR,aAAa;IACb,iBAAiB;IACjB,iBAAiB;IACjB,kBAAkB;IAClB,oEAAoE;IACpE,0DAA0D;IAC1D,YAAY;CACb,CAAC;AAEF,yEAAyE;AACzE,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { SessionBackend, SpawnOpts } from './types.js';
|
|
2
|
+
/** Convert `\n` to `\r\n` while leaving existing `\r\n` alone. Exported for
|
|
3
|
+
* unit tests; used to normalise tmux capture-pane output before sending it
|
|
4
|
+
* to xterm.js (which treats bare LF as "down one row, keep column"). */
|
|
5
|
+
export declare function normaliseCaptureLineEndings(s: string): string;
|
|
6
|
+
export declare class TmuxPipeBackend implements SessionBackend {
|
|
7
|
+
/** Real tmux pane address (e.g. "0:2.0"). */
|
|
8
|
+
private readonly paneTarget;
|
|
9
|
+
private readonly fifoPath;
|
|
10
|
+
private readStream;
|
|
11
|
+
private readonly dataCbs;
|
|
12
|
+
private readonly exitCbs;
|
|
13
|
+
private cols;
|
|
14
|
+
private rows;
|
|
15
|
+
private exited;
|
|
16
|
+
/** Set after pipe-pane subscription is active so kill() knows to cancel it. */
|
|
17
|
+
private pipeAttached;
|
|
18
|
+
constructor(paneTarget: string);
|
|
19
|
+
/** spawn() in this backend doesn't actually spawn a process; it sets up
|
|
20
|
+
* the pipe-pane subscription + fifo reader. The bin/args params are
|
|
21
|
+
* ignored (the CLI is already running in the user's pane). */
|
|
22
|
+
spawn(_bin: string, _args: string[], opts: SpawnOpts): void;
|
|
23
|
+
write(data: string): void;
|
|
24
|
+
sendText(text: string): void;
|
|
25
|
+
sendSpecialKeys(...keys: string[]): void;
|
|
26
|
+
pasteText(text: string): void;
|
|
27
|
+
enterCopyMode(): void;
|
|
28
|
+
sendCopyModeCommand(xCommand: string): void;
|
|
29
|
+
resize(cols: number, rows: number): void;
|
|
30
|
+
onData(cb: (data: string) => void): void;
|
|
31
|
+
onExit(cb: (code: number | null, signal: string | null) => void): void;
|
|
32
|
+
kill(): void;
|
|
33
|
+
destroySession(): void;
|
|
34
|
+
getChildPid(): number | null;
|
|
35
|
+
getAttachInfo(): null;
|
|
36
|
+
/** Snapshot the current screen of the adopted pane WITH ANSI escapes,
|
|
37
|
+
* including history (-S - = start of scrollback). New web-terminal
|
|
38
|
+
* connections receive this string so xterm.js renders the existing
|
|
39
|
+
* session state instead of a blank screen.
|
|
40
|
+
*
|
|
41
|
+
* IMPORTANT: tmux capture-pane separates rows with bare `\n`, no `\r`.
|
|
42
|
+
* xterm.js (and any VT100-compliant emulator) treats a bare LF as
|
|
43
|
+
* "move down one row, keep column" — every captured line lands further
|
|
44
|
+
* to the right than the previous one, producing the staircase artefact
|
|
45
|
+
* observed in early pipe-mode dogfooding. Normalising every `\n` to
|
|
46
|
+
* `\r\n` makes the snapshot render correctly. The live pipe-pane stream
|
|
47
|
+
* itself doesn't need this fix — applications write proper `\r\n` (and
|
|
48
|
+
* Claude Code uses cursor-positioning instead of bare LF anyway). */
|
|
49
|
+
captureCurrentScreen(): string;
|
|
50
|
+
/** Cheap probe: is the adopted pane currently in the alternate screen
|
|
51
|
+
* buffer? Used by captureCurrentScreen to decide whether the snapshot
|
|
52
|
+
* needs an alt-buffer-enter prefix for correct rendering. */
|
|
53
|
+
private isPaneInAltBuffer;
|
|
54
|
+
/** True if the underlying pane is still addressable in tmux. Cheap check —
|
|
55
|
+
* used by callers to detect "user closed the pane while we were piping". */
|
|
56
|
+
isPaneAlive(): boolean;
|
|
57
|
+
private fireExit;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=tmux-pipe-backend.d.ts.map
|
|
@@ -0,0 +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;AAO5D;;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;IAQ5B,eAAe,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAQxC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAa7B,aAAa,IAAI,IAAI;IAQrB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQ3C,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;IAa5B,aAAa;IAMb;;;;;;;;;;;;0EAYsE;IACtE,oBAAoB,IAAI,MAAM;IAwB9B;;kEAE8D;IAC9D,OAAO,CAAC,iBAAiB;IAYzB;iFAC6E;IAC7E,WAAW,IAAI,OAAO;IAatB,OAAO,CAAC,QAAQ;CAKjB"}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TmuxPipeBackend — observe a user-owned tmux pane WITHOUT attaching to its
|
|
3
|
+
* session. Used by /adopt mode to avoid the renderer conflict that arises
|
|
4
|
+
* when a normal `tmux attach-session` client coexists with a tmux -CC
|
|
5
|
+
* (iTerm2 control mode) client on the same server (interleaved ANSI vs
|
|
6
|
+
* control-protocol writes corrupt cursor / status-bar / alt-screen state).
|
|
7
|
+
*
|
|
8
|
+
* Architecture (no PTY, no attach):
|
|
9
|
+
* - mkfifo a unique fifo under /tmp
|
|
10
|
+
* - `tmux pipe-pane -O -t <pane> 'cat > <fifo>'` — tmux replicates every
|
|
11
|
+
* byte the pane writes into the fifo (append-only, '-O' overwrites any
|
|
12
|
+
* existing pipe).
|
|
13
|
+
* - fs.createReadStream(<fifo>) — we read tmux's verbatim ANSI stream.
|
|
14
|
+
* - All writes (sendText / sendSpecialKeys / pasteText / copy-mode keys)
|
|
15
|
+
* go through `tmux send-keys / paste-buffer -t <pane>` — so the pane's
|
|
16
|
+
* real address ("0:2.0") is the addressing target, not a synthetic
|
|
17
|
+
* session name.
|
|
18
|
+
* - `tmux capture-pane -e -p -t <pane> -S -` returns the current screen
|
|
19
|
+
* with ANSI; the worker uses it to seed new web-terminal connections
|
|
20
|
+
* so they don't start from a blank screen.
|
|
21
|
+
*
|
|
22
|
+
* The user's source session is never attached, never zoomed, never
|
|
23
|
+
* grouped — fully zero-touch from tmux's perspective beyond the pipe-pane
|
|
24
|
+
* subscription, which is automatically detached when we kill the backend.
|
|
25
|
+
*/
|
|
26
|
+
import * as fs from 'node:fs';
|
|
27
|
+
import { execSync, execFileSync, spawnSync } from 'node:child_process';
|
|
28
|
+
import { tmpdir } from 'node:os';
|
|
29
|
+
import { join } from 'node:path';
|
|
30
|
+
import { randomBytes } from 'node:crypto';
|
|
31
|
+
function shellescape(s) {
|
|
32
|
+
// Single-quote-escape, replacing internal ' with '\''
|
|
33
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
34
|
+
}
|
|
35
|
+
/** Convert `\n` to `\r\n` while leaving existing `\r\n` alone. Exported for
|
|
36
|
+
* unit tests; used to normalise tmux capture-pane output before sending it
|
|
37
|
+
* to xterm.js (which treats bare LF as "down one row, keep column"). */
|
|
38
|
+
export function normaliseCaptureLineEndings(s) {
|
|
39
|
+
return s.replace(/\r?\n/g, '\r\n');
|
|
40
|
+
}
|
|
41
|
+
export class TmuxPipeBackend {
|
|
42
|
+
/** Real tmux pane address (e.g. "0:2.0"). */
|
|
43
|
+
paneTarget;
|
|
44
|
+
fifoPath;
|
|
45
|
+
readStream = null;
|
|
46
|
+
dataCbs = [];
|
|
47
|
+
exitCbs = [];
|
|
48
|
+
cols = 200;
|
|
49
|
+
rows = 50;
|
|
50
|
+
exited = false;
|
|
51
|
+
/** Set after pipe-pane subscription is active so kill() knows to cancel it. */
|
|
52
|
+
pipeAttached = false;
|
|
53
|
+
constructor(paneTarget) {
|
|
54
|
+
this.paneTarget = paneTarget;
|
|
55
|
+
// Per-instance fifo so concurrent adopt sessions don't collide.
|
|
56
|
+
this.fifoPath = join(tmpdir(), `botmux-pipe-${randomBytes(8).toString('hex')}.fifo`);
|
|
57
|
+
}
|
|
58
|
+
// ─── SessionBackend implementation ────────────────────────────────────────
|
|
59
|
+
/** spawn() in this backend doesn't actually spawn a process; it sets up
|
|
60
|
+
* the pipe-pane subscription + fifo reader. The bin/args params are
|
|
61
|
+
* ignored (the CLI is already running in the user's pane). */
|
|
62
|
+
spawn(_bin, _args, opts) {
|
|
63
|
+
this.cols = opts.cols;
|
|
64
|
+
this.rows = opts.rows;
|
|
65
|
+
// Step 1: create the fifo. mkfifo is POSIX; linux/darwin both have it.
|
|
66
|
+
spawnSync('mkfifo', [this.fifoPath], { stdio: 'ignore' });
|
|
67
|
+
// Step 2: open the read end with O_RDWR (no O_NONBLOCK).
|
|
68
|
+
//
|
|
69
|
+
// Why O_RDWR? Avoids the fifo open chicken-and-egg: opening O_RDONLY
|
|
70
|
+
// alone blocks until a writer arrives, opening O_WRONLY alone blocks
|
|
71
|
+
// until a reader arrives. Holding both ends ourselves makes either
|
|
72
|
+
// peer's open() return immediately.
|
|
73
|
+
//
|
|
74
|
+
// Why NOT O_NONBLOCK? libuv's ReadStream issues blocking read() calls
|
|
75
|
+
// from its threadpool — that's how it stays responsive without
|
|
76
|
+
// burning CPU. With O_NONBLOCK the very first read() on an empty
|
|
77
|
+
// fifo returns EAGAIN, which libuv surfaces as an error, and the
|
|
78
|
+
// stream dies before tmux ever gets a chance to write a byte. (This
|
|
79
|
+
// exact bug ate the bridge final_output pipeline on first ship: an
|
|
80
|
+
// strace showed worker only read its IPC fd, never the fifo.)
|
|
81
|
+
const fd = fs.openSync(this.fifoPath, fs.constants.O_RDWR);
|
|
82
|
+
this.readStream = fs.createReadStream('', { fd, autoClose: false, highWaterMark: 64 * 1024 });
|
|
83
|
+
this.readStream.on('data', (chunk) => {
|
|
84
|
+
const data = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
85
|
+
for (const cb of this.dataCbs) {
|
|
86
|
+
try {
|
|
87
|
+
cb(data);
|
|
88
|
+
}
|
|
89
|
+
catch { /* listener crash shouldn't kill the stream */ }
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
this.readStream.on('error', (err) => {
|
|
93
|
+
// Errors are best-effort logged via the worker's stderr (we can't
|
|
94
|
+
// pull a logger in a backend without circular imports). Don't fire
|
|
95
|
+
// exit — the user's CLI is still alive, we just lost realtime view.
|
|
96
|
+
process.stderr.write(`[tmux-pipe-backend] read error: ${err?.message ?? err}\n`);
|
|
97
|
+
});
|
|
98
|
+
// Step 3: ask tmux to replicate the pane's bytes into our fifo.
|
|
99
|
+
// -O causes tmux to overwrite any prior pipe-pane subscription.
|
|
100
|
+
// The shell command must redirect to the fifo; tmux runs it via /bin/sh.
|
|
101
|
+
try {
|
|
102
|
+
execSync(`tmux pipe-pane -O -t ${shellescape(this.paneTarget)} 'cat > ${shellescape(this.fifoPath)}'`, { stdio: 'ignore', timeout: 5000 });
|
|
103
|
+
this.pipeAttached = true;
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
this.fireExit(1, null);
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
write(data) {
|
|
111
|
+
// No PTY to write to — interpret as a literal send-keys.
|
|
112
|
+
this.sendText(data);
|
|
113
|
+
}
|
|
114
|
+
sendText(text) {
|
|
115
|
+
if (this.exited)
|
|
116
|
+
return;
|
|
117
|
+
execFileSync('tmux', ['send-keys', '-t', this.paneTarget, '-l', '--', text], {
|
|
118
|
+
stdio: 'ignore',
|
|
119
|
+
timeout: 5000,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
sendSpecialKeys(...keys) {
|
|
123
|
+
if (this.exited)
|
|
124
|
+
return;
|
|
125
|
+
execFileSync('tmux', ['send-keys', '-t', this.paneTarget, ...keys], {
|
|
126
|
+
stdio: 'ignore',
|
|
127
|
+
timeout: 5000,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
pasteText(text) {
|
|
131
|
+
if (this.exited)
|
|
132
|
+
return;
|
|
133
|
+
execFileSync('tmux', ['load-buffer', '-'], {
|
|
134
|
+
input: text,
|
|
135
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
136
|
+
timeout: 5000,
|
|
137
|
+
});
|
|
138
|
+
execFileSync('tmux', ['paste-buffer', '-t', this.paneTarget, '-d'], {
|
|
139
|
+
stdio: 'ignore',
|
|
140
|
+
timeout: 5000,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
enterCopyMode() {
|
|
144
|
+
if (this.exited)
|
|
145
|
+
return;
|
|
146
|
+
execFileSync('tmux', ['copy-mode', '-e', '-t', this.paneTarget], {
|
|
147
|
+
stdio: 'ignore',
|
|
148
|
+
timeout: 5000,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
sendCopyModeCommand(xCommand) {
|
|
152
|
+
if (this.exited)
|
|
153
|
+
return;
|
|
154
|
+
execFileSync('tmux', ['send-keys', '-t', this.paneTarget, '-X', xCommand], {
|
|
155
|
+
stdio: 'ignore',
|
|
156
|
+
timeout: 5000,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
resize(cols, rows) {
|
|
160
|
+
// Don't resize the user's tmux pane on every web client resize — that
|
|
161
|
+
// would visibly snap the user's own client around. We just remember the
|
|
162
|
+
// requested size for getAttachInfo / capture sizing.
|
|
163
|
+
this.cols = cols;
|
|
164
|
+
this.rows = rows;
|
|
165
|
+
}
|
|
166
|
+
onData(cb) {
|
|
167
|
+
this.dataCbs.push(cb);
|
|
168
|
+
}
|
|
169
|
+
onExit(cb) {
|
|
170
|
+
this.exitCbs.push(cb);
|
|
171
|
+
}
|
|
172
|
+
kill() {
|
|
173
|
+
if (this.exited)
|
|
174
|
+
return;
|
|
175
|
+
this.exited = true;
|
|
176
|
+
// Cancel tmux's pipe subscription. Calling pipe-pane without a command
|
|
177
|
+
// turns it off for the target pane.
|
|
178
|
+
if (this.pipeAttached) {
|
|
179
|
+
try {
|
|
180
|
+
execSync(`tmux pipe-pane -t ${shellescape(this.paneTarget)}`, { stdio: 'ignore', timeout: 3000 });
|
|
181
|
+
}
|
|
182
|
+
catch { /* pane may already be gone — benign */ }
|
|
183
|
+
this.pipeAttached = false;
|
|
184
|
+
}
|
|
185
|
+
if (this.readStream) {
|
|
186
|
+
try {
|
|
187
|
+
this.readStream.destroy();
|
|
188
|
+
}
|
|
189
|
+
catch { /* already closed */ }
|
|
190
|
+
this.readStream = null;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
fs.unlinkSync(this.fifoPath);
|
|
194
|
+
}
|
|
195
|
+
catch { /* already gone */ }
|
|
196
|
+
this.fireExit(0, null);
|
|
197
|
+
}
|
|
198
|
+
destroySession() {
|
|
199
|
+
// Adopt mode never owns the source session — kill() is enough.
|
|
200
|
+
this.kill();
|
|
201
|
+
}
|
|
202
|
+
getChildPid() {
|
|
203
|
+
try {
|
|
204
|
+
const out = execSync(`tmux display-message -p -t ${shellescape(this.paneTarget)} '#{pane_pid}'`, { encoding: 'utf-8', timeout: 3000 }).trim();
|
|
205
|
+
const pid = parseInt(out, 10);
|
|
206
|
+
return pid > 0 ? pid : null;
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
getAttachInfo() {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
// ─── Pipe-specific helpers ────────────────────────────────────────────────
|
|
216
|
+
/** Snapshot the current screen of the adopted pane WITH ANSI escapes,
|
|
217
|
+
* including history (-S - = start of scrollback). New web-terminal
|
|
218
|
+
* connections receive this string so xterm.js renders the existing
|
|
219
|
+
* session state instead of a blank screen.
|
|
220
|
+
*
|
|
221
|
+
* IMPORTANT: tmux capture-pane separates rows with bare `\n`, no `\r`.
|
|
222
|
+
* xterm.js (and any VT100-compliant emulator) treats a bare LF as
|
|
223
|
+
* "move down one row, keep column" — every captured line lands further
|
|
224
|
+
* to the right than the previous one, producing the staircase artefact
|
|
225
|
+
* observed in early pipe-mode dogfooding. Normalising every `\n` to
|
|
226
|
+
* `\r\n` makes the snapshot render correctly. The live pipe-pane stream
|
|
227
|
+
* itself doesn't need this fix — applications write proper `\r\n` (and
|
|
228
|
+
* Claude Code uses cursor-positioning instead of bare LF anyway). */
|
|
229
|
+
captureCurrentScreen() {
|
|
230
|
+
if (this.exited)
|
|
231
|
+
return '';
|
|
232
|
+
try {
|
|
233
|
+
const altOn = this.isPaneInAltBuffer();
|
|
234
|
+
const raw = execSync(`tmux capture-pane -e -p -t ${shellescape(this.paneTarget)} -S -`, { encoding: 'utf-8', timeout: 5000, maxBuffer: 16 * 1024 * 1024 });
|
|
235
|
+
const normalised = normaliseCaptureLineEndings(raw);
|
|
236
|
+
if (altOn) {
|
|
237
|
+
// The pane's CLI (e.g. Claude Code, vim) is in the alternate screen
|
|
238
|
+
// buffer. capture-pane returns the alt-buffer's content but no
|
|
239
|
+
// mode-switch escape sequence — we have to bracket the snapshot
|
|
240
|
+
// with `enter alt screen + home + clear` so xterm.js renders it in
|
|
241
|
+
// the alt buffer instead of leaking it into the main buffer where
|
|
242
|
+
// it would persist after the application exits.
|
|
243
|
+
return `\x1b[?1049h\x1b[H\x1b[2J${normalised}`;
|
|
244
|
+
}
|
|
245
|
+
return normalised;
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
return '';
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/** Cheap probe: is the adopted pane currently in the alternate screen
|
|
252
|
+
* buffer? Used by captureCurrentScreen to decide whether the snapshot
|
|
253
|
+
* needs an alt-buffer-enter prefix for correct rendering. */
|
|
254
|
+
isPaneInAltBuffer() {
|
|
255
|
+
try {
|
|
256
|
+
const out = execSync(`tmux display-message -p -t ${shellescape(this.paneTarget)} '#{alternate_on}'`, { encoding: 'utf-8', timeout: 2000 }).trim();
|
|
257
|
+
return out === '1';
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/** True if the underlying pane is still addressable in tmux. Cheap check —
|
|
264
|
+
* used by callers to detect "user closed the pane while we were piping". */
|
|
265
|
+
isPaneAlive() {
|
|
266
|
+
if (this.exited)
|
|
267
|
+
return false;
|
|
268
|
+
try {
|
|
269
|
+
execSync(`tmux display-message -p -t ${shellescape(this.paneTarget)} ''`, {
|
|
270
|
+
stdio: 'ignore',
|
|
271
|
+
timeout: 2000,
|
|
272
|
+
});
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
fireExit(code, signal) {
|
|
280
|
+
for (const cb of this.exitCbs) {
|
|
281
|
+
try {
|
|
282
|
+
cb(code, signal);
|
|
283
|
+
}
|
|
284
|
+
catch { /* listener crash is benign */ }
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=tmux-pipe-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux-pipe-backend.js","sourceRoot":"","sources":["../../../src/adapters/backend/tmux-pipe-backend.ts"],"names":[],"mappings":"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"}
|