botmux 2.25.1 → 2.27.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.
Files changed (42) hide show
  1. package/README.en.md +48 -46
  2. package/README.md +47 -46
  3. package/dist/adapters/backend/session-backend-selector.d.ts +11 -0
  4. package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -0
  5. package/dist/adapters/backend/session-backend-selector.js +26 -0
  6. package/dist/adapters/backend/session-backend-selector.js.map +1 -0
  7. package/dist/adapters/backend/tmux-pipe-backend.d.ts +55 -15
  8. package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
  9. package/dist/adapters/backend/tmux-pipe-backend.js +163 -21
  10. package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
  11. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  12. package/dist/adapters/cli/claude-code.js +90 -14
  13. package/dist/adapters/cli/claude-code.js.map +1 -1
  14. package/dist/cli.js +259 -40
  15. package/dist/cli.js.map +1 -1
  16. package/dist/setup/bots-store.d.ts +3 -0
  17. package/dist/setup/bots-store.d.ts.map +1 -0
  18. package/dist/setup/bots-store.js +24 -0
  19. package/dist/setup/bots-store.js.map +1 -0
  20. package/dist/setup/lark-scopes.json +301 -0
  21. package/dist/setup/register-app.d.ts +46 -0
  22. package/dist/setup/register-app.d.ts.map +1 -0
  23. package/dist/setup/register-app.js +87 -0
  24. package/dist/setup/register-app.js.map +1 -0
  25. package/dist/setup/verify-permissions.d.ts +115 -0
  26. package/dist/setup/verify-permissions.d.ts.map +1 -0
  27. package/dist/setup/verify-permissions.js +207 -0
  28. package/dist/setup/verify-permissions.js.map +1 -0
  29. package/dist/utils/screenshot-renderer.d.ts.map +1 -1
  30. package/dist/utils/screenshot-renderer.js +63 -26
  31. package/dist/utils/screenshot-renderer.js.map +1 -1
  32. package/dist/utils/terminal-renderer.d.ts +16 -0
  33. package/dist/utils/terminal-renderer.d.ts.map +1 -1
  34. package/dist/utils/terminal-renderer.js +35 -21
  35. package/dist/utils/terminal-renderer.js.map +1 -1
  36. package/dist/utils/transient-snapshot.d.ts +28 -0
  37. package/dist/utils/transient-snapshot.d.ts.map +1 -0
  38. package/dist/utils/transient-snapshot.js +96 -0
  39. package/dist/utils/transient-snapshot.js.map +1 -0
  40. package/dist/worker.js +94 -31
  41. package/dist/worker.js.map +1 -1
  42. package/package.json +5 -3
package/README.en.md CHANGED
@@ -71,78 +71,71 @@ Compared to OpenClaw-style approaches built on Agent SDKs:
71
71
 
72
72
  ## 5-Minute Setup
73
73
 
74
+ > 💡 **TL;DR**: run `botmux setup` and pick "scan-to-create" to finish Steps 1+2 in one shot (the official `@larksuiteoapi/node-sdk` device flow gives you the AppID/AppSecret). PersonalAgent apps come with event subscriptions and bot capability pre-configured, so only Step 4 (permissions) + Step 5 (optional redirect URL) + Step 6 (publish) require browser clicks; the setup wizard writes a JSON file with a one-line clipboard copy command and prints deep-links to each remaining step.
75
+
74
76
  ### Step 1: Create a Lark App
75
77
 
76
- Go to the [Lark Open Platform](https://open.larkoffice.com/app) and click "Create Custom App".
78
+ **Recommended**: `botmux setup` → pick "1) Scan-to-create app". Scan with the Lark mobile app and the AppID/AppSecret are persisted automatically; no manual browser navigation. Falls back to manual paste on cancel/timeout/network error.
79
+
80
+ > ⚠️ **Currently only Feishu (feishu.cn) tenants are supported.** If scan detects a Lark international (larksuite.com) tenant, setup aborts — the daemon runtime (Lark Client/WSClient/event-dispatcher) hasn't been wired up for the `larksuite.com` domain yet, so accepting Lark credentials would land users in a half-working state. A follow-up PR will add full Lark support.
81
+
82
+ **Manual**: go to the [Lark Open Platform](https://open.larkoffice.com/app) and click "Create Custom App".
77
83
 
78
84
  ![Create App](docs/setup/create-app.png)
79
85
 
80
86
  ### Step 2: Get Credentials
81
87
 
88
+ > The scan-to-create path completes this step automatically; skip to Step 3.
89
+
82
90
  Open the app details page → "Credentials & Basic Info", and copy the **App ID** and **App Secret**.
83
91
 
84
92
  ![Get Credentials](docs/setup/credentials.png)
85
93
 
86
- ### Step 3: Add Permissions
87
-
88
- Go to "Permissions & Scopes" → "Batch Import/Export", and paste the following JSON to import all permissions at once:
89
-
90
- ![Permissions](docs/setup/permissions.png)
91
-
92
- <details>
93
- <summary>Click to expand batch import JSON</summary>
94
-
95
- ```json
96
- {
97
- "scopes": {
98
- "tenant": [
99
- "contact:user.base:readonly",
100
- "contact:user.id:readonly",
101
- "im:chat:read",
102
- "im:chat.members:bot_access",
103
- "im:chat.members:read",
104
- "im:message",
105
- "im:message:readonly",
106
- "im:message:send_as_bot",
107
- "im:message:update",
108
- "im:message.group_at_msg",
109
- "im:message.group_at_msg:readonly",
110
- "im:message.group_msg",
111
- "im:message.p2p_msg:readonly",
112
- "im:message.reactions:write_only",
113
- "im:resource"
114
- ]
115
- }
116
- }
117
- ```
118
- </details>
119
-
120
- ### Step 4: Install & Start botmux
94
+ ### Step 3: Install & Start botmux
121
95
 
122
96
  ```bash
123
97
  # Install
124
98
  npm install -g botmux
125
99
 
126
- # Interactive setup — enter the App ID and App Secret from Step 2
100
+ # Interactive setup — pick "1) Scan-to-create app" or "2) Paste AppID/Secret manually".
101
+ # Credentials are validated with a tenant_access_token call before bots.json is written.
102
+ # At the end of setup the wizard writes the full scope JSON to ~/.botmux/lark-scopes.json
103
+ # and prints a one-line clipboard copy command for your platform.
127
104
  botmux setup
128
105
 
129
- # Start (must be running before configuring WebSocket subscription Lark checks for an active connection)
106
+ # Start (if you ever need to verify the event subscription, Lark requires the daemon to be running so it can detect the WebSocket connection)
107
+ # Re-validates credentials before forking workers; missing scopes only WARN, do not block the daemon.
130
108
  botmux start
131
109
  ```
132
110
 
133
- ### Step 5: Configure Event Subscription
111
+ ### Step 4: Add Permissions
134
112
 
135
- Back in the Lark Open Platform, go to "Events & Callbacks":
113
+ Run the copy-to-clipboard command setup printed, then go to "Permissions & Scopes" → "Batch Import/Export" and paste. Submit for review — visibility "only me" auto-approves.
136
114
 
137
- 1. **Subscription mode**: Click the edit icon, select "Receive events via persistent connection" (WebSocket) — requires botmux to be running so Lark can detect the connection
115
+ ![Permissions](docs/setup/permissions.png)
138
116
 
139
- ![WebSocket subscription](docs/setup/event-websocket.png)
117
+ The full JSON lives at `~/.botmux/lark-scopes.json` (also tracked in-repo at [src/setup/lark-scopes.json](src/setup/lark-scopes.json), kept in sync with the internal wiki, covers ~290 tenant + user scopes).
140
118
 
141
- 2. **Add event**: Click "Add Event", search and add `im.message.receive_v1` (Receive messages v2.0)
119
+ ```bash
120
+ # macOS
121
+ cat ~/.botmux/lark-scopes.json | pbcopy
122
+ # Linux X
123
+ cat ~/.botmux/lark-scopes.json | xclip -selection clipboard
124
+ # Wayland
125
+ cat ~/.botmux/lark-scopes.json | wl-copy
126
+ ```
142
127
 
143
- ![Add event](docs/setup/event-receive-msg.png)
128
+ > Scan-created PersonalAgent apps have `im.message.receive_v1` + `card.action.trigger` subscribed and the bot capability enabled out of the box, per botmux maintainer testing. Lark hasn't documented this as stable behavior, so **if the bot receives no messages at all after setup**, see "Step 8: Troubleshoot — bot not receiving messages" below for a manual fallback.
144
129
 
145
- 3. **Enable callback**: Switch to the "Callback Configuration" tab, turn on "Card action callback" (`card.action.trigger`)
130
+ ### Step 5: Add Redirect URL (optional)
131
+
132
+ If you plan to use `/login` inside Lark to let botmux act on your behalf for docs / calendar / wiki / sheets, add a redirect URL under "Security Settings" → "Redirect URL":
133
+
134
+ ```
135
+ http://127.0.0.1:9768/callback
136
+ ```
137
+
138
+ Skip this step if you only need bot messaging.
146
139
 
147
140
  ### Step 6: Publish the App
148
141
 
@@ -158,7 +151,16 @@ Go to "Version Management & Release", click "Create Version" and publish. Set av
158
151
 
159
152
  ![Add bot to group](docs/setup/add-bot-to-group.png)
160
153
 
161
- ### Step 8: Enable Boot-time Autostart (recommended)
154
+ ### Step 8: Troubleshoot bot not receiving messages (fallback)
155
+
156
+ PersonalAgent apps come with event subscriptions and bot capability pre-configured; in normal cases you don't touch this. If the bot **receives no messages at all** after setup (not even DMs), verify these two settings:
157
+
158
+ - **Event subscription**: Open Platform → your app → Events & Callbacks → should be subscribed to `im.message.receive_v1` + `card.action.trigger`. If missing, add them manually. Subscription mode must be "Receive via persistent connection" (WebSocket), and the botmux daemon must be running.
159
+ - **Bot capability**: Open Platform → your app → Features → Bot should be enabled (it is by default). Adjust name/avatar if needed.
160
+
161
+ After verifying, restart: `botmux restart`.
162
+
163
+ ### Step 9: Enable Boot-time Autostart (recommended)
162
164
 
163
165
  Once the bot is sending and receiving messages cleanly, run:
164
166
 
package/README.md CHANGED
@@ -173,78 +173,70 @@ CLI 进入 botmux 会话时自动获得 `~/.botmux/bin` 在 PATH 中,以及一
173
173
 
174
174
  ## 5 分钟快速接入
175
175
 
176
+ > 💡 **TL;DR**:跑 `botmux setup` 选「扫码建应用」一步完成 Step 1+2(拿 AppID/AppSecret)。PersonalAgent 应用建出来时事件订阅和 bot 能力都已默认配好,只剩 Step 4 权限申请 + Step 5(按需)重定向 URL + Step 6 发版三步要在浏览器手动点;setup 完成后会自动写 JSON 文件 + 打印一键复制命令 + 各步骤的深链。
177
+
176
178
  ### Step 1: 创建飞书应用
177
179
 
178
- 打开 [飞书开放平台](https://open.larkoffice.com/app),点击「创建企业自建应用」。
180
+ **推荐路径**:`botmux setup` 选「1) 扫码建应用」,飞书扫码完成后自动落盘 AppID/AppSecret,无需手动浏览器创建。底层走 `@larksuiteoapi/node-sdk` 的官方 device flow。
181
+
182
+ > ⚠️ **目前仅支持飞书 (feishu.cn) 租户**。扫码检测到 Lark 国际版 (larksuite.com) 会中止 setup —— daemon runtime (Lark Client/WSClient/event-dispatcher 等) 需要一并接入 lark 域,会在单独 PR 跟进。
183
+
184
+ **手动路径**:打开 [飞书开放平台](https://open.larkoffice.com/app),点击「创建企业自建应用」。
179
185
 
180
186
  ![创建应用](docs/setup/create-app.png)
181
187
 
182
188
  ### Step 2: 获取凭证
183
189
 
190
+ > 扫码路径自动完成此步,可直接跳到 Step 3。
191
+
184
192
  进入应用详情 →「凭证与基础信息」,复制 **App ID** 和 **App Secret**。
185
193
 
186
194
  ![获取凭证](docs/setup/credentials.png)
187
195
 
188
- ### Step 3: 添加权限
189
-
190
- 进入「权限管理」→「批量导入/导出权限」,粘贴以下 JSON 一次性导入所有权限:
191
-
192
- ![权限管理](docs/setup/permissions.png)
193
-
194
- <details>
195
- <summary>点击展开批量导入 JSON</summary>
196
-
197
- ```json
198
- {
199
- "scopes": {
200
- "tenant": [
201
- "contact:user.base:readonly",
202
- "contact:user.id:readonly",
203
- "im:chat:read",
204
- "im:chat.members:bot_access",
205
- "im:chat.members:read",
206
- "im:message",
207
- "im:message:readonly",
208
- "im:message:send_as_bot",
209
- "im:message:update",
210
- "im:message.group_at_msg",
211
- "im:message.group_at_msg:readonly",
212
- "im:message.group_msg",
213
- "im:message.p2p_msg:readonly",
214
- "im:message.reactions:write_only",
215
- "im:resource"
216
- ]
217
- }
218
- }
219
- ```
220
- </details>
221
-
222
- ### Step 4: 安装 & 启动 botmux
196
+ ### Step 3: 安装 & 启动 botmux
223
197
 
224
198
  ```bash
225
199
  # 安装
226
200
  npm install -g botmux
227
201
 
228
- # 交互式配置 — 输入 Step 2 App ID 和 App Secret
202
+ # 交互式配置 — 选「1) 扫码建应用」或「2) 手动粘 AppID/Secret
203
+ # 凭证拿到后自动取一次 tenant_access_token 校验,通过才落盘 bots.json
204
+ # setup 末尾会把完整权限 JSON 写到 ~/.botmux/lark-scopes.json 并打印一键复制命令
229
205
  botmux setup
230
206
 
231
- # 启动(飞书后台配置长连接订阅前需要先启动,否则无法检测到连接)
207
+ # 启动(如果之后需要确认事件订阅,飞书后台会要求 daemon 已在跑才能识别长连接)
208
+ # start 前再校验一次凭证;权限未配齐不会阻塞 daemon,只 WARN
232
209
  botmux start
233
210
  ```
234
211
 
235
- ### Step 5: 配置事件订阅
212
+ ### Step 4: 添加权限
236
213
 
237
- 回到飞书开放平台,进入「事件与回调」:
214
+ setup 完成后,按 terminal 提示的一键复制命令把权限 JSON 复制到剪贴板,进入「权限管理」→「批量导入/导出权限」粘贴 → 提交审批。可用性范围选「仅自己可见」会自动通过:
238
215
 
239
- 1. **订阅方式**:点击编辑图标,选择「使用长连接接收事件」(需要 botmux 已启动,飞书会检测长连接是否建立)
216
+ ![权限管理](docs/setup/permissions.png)
240
217
 
241
- ![配置长连接](docs/setup/event-websocket.png)
218
+ 完整 JSON 已经写到 `~/.botmux/lark-scopes.json`,源仓库版本在 [src/setup/lark-scopes.json](src/setup/lark-scopes.json)(与本仓库内部 wiki 文档同步,覆盖 tenant + user 双套域 ≈ 290 项)。
242
219
 
243
- 2. **添加事件**:点击「添加事件」,搜索添加 `im.message.receive_v1`(接收消息 v2.0)
220
+ ```bash
221
+ # macOS
222
+ cat ~/.botmux/lark-scopes.json | pbcopy
223
+ # Linux X
224
+ cat ~/.botmux/lark-scopes.json | xclip -selection clipboard
225
+ # Wayland
226
+ cat ~/.botmux/lark-scopes.json | wl-copy
227
+ ```
244
228
 
245
- ![添加事件](docs/setup/event-receive-msg.png)
229
+ > 扫码建出来的 PersonalAgent 应用,botmux 维护者实测默认已订阅 `im.message.receive_v1` + `card.action.trigger` 并开通 bot 能力,所以主线流程不再要求手动配。但飞书没在公开文档里承诺这是稳定行为,**如果配好后机器人完全收不到消息**,参见下方「Step 8: 机器人收不到消息时的自查」。
246
230
 
247
- 3. **启用回调**:切换到「回调配置」tab,开启「卡片回传交互」(`card.action.trigger`)
231
+ ### Step 5: 添加重定向 URL(按需)
232
+
233
+ 如果之后要在飞书里 `/login` 让 botmux 以你的身份调云文档/日历/Wiki 等 API,进入「安全设置」→「重定向 URL」填入:
234
+
235
+ ```
236
+ http://127.0.0.1:9768/callback
237
+ ```
238
+
239
+ 只用 bot 收发消息的话这一步可以跳过。
248
240
 
249
241
  ### Step 6: 发版
250
242
 
@@ -260,7 +252,16 @@ botmux start
260
252
 
261
253
  ![添加机器人到群](docs/setup/add-bot-to-group.png)
262
254
 
263
- ### Step 8: 开机自启(推荐)
255
+ ### Step 8: 机器人收不到消息时的自查(fallback)
256
+
257
+ PersonalAgent 默认配好事件订阅 + bot 能力,正常情况下不用动。如果按上面步骤走完 bot **完全收不到任何消息**(连私聊都不回),分别确认这两项:
258
+
259
+ - **事件订阅**:开放平台 → 你的应用 → 事件与回调 → 应当订阅 `im.message.receive_v1` + `card.action.trigger`(默认已订阅,如缺失就手动添加)。订阅方式必须是「使用长连接接收事件」(WebSocket),且 botmux daemon 已经在跑。
260
+ - **机器人能力**:开放平台 → 你的应用 → 应用功能 → 机器人 应当已开通(默认开通),名字/头像可以改。
261
+
262
+ 确认后重启 daemon:`botmux restart`。
263
+
264
+ ### Step 9: 开机自启(推荐)
264
265
 
265
266
  确认机器人能正常收发消息之后,跑一次:
266
267
 
@@ -0,0 +1,11 @@
1
+ import type { SessionBackend } from './types.js';
2
+ export interface SelectedSessionBackend {
3
+ backend: SessionBackend;
4
+ isTmuxMode: boolean;
5
+ isPipeMode: boolean;
6
+ }
7
+ export declare function selectSessionBackend(opts: {
8
+ sessionId: string;
9
+ useTmux: boolean;
10
+ }): SelectedSessionBackend;
11
+ //# sourceMappingURL=session-backend-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-backend-selector.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/session-backend-selector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,sBAAsB,CAuB1G"}
@@ -0,0 +1,26 @@
1
+ import { PtyBackend } from './pty-backend.js';
2
+ import { TmuxBackend } from './tmux-backend.js';
3
+ import { TmuxPipeBackend } from './tmux-pipe-backend.js';
4
+ export function selectSessionBackend(opts) {
5
+ if (!opts.useTmux) {
6
+ return {
7
+ backend: new PtyBackend(),
8
+ isTmuxMode: false,
9
+ isPipeMode: false,
10
+ };
11
+ }
12
+ const sessionName = TmuxBackend.sessionName(opts.sessionId);
13
+ if (TmuxBackend.hasSession(sessionName)) {
14
+ return {
15
+ backend: new TmuxPipeBackend(sessionName, { ownsSession: true, isReattach: true }),
16
+ isTmuxMode: true,
17
+ isPipeMode: true,
18
+ };
19
+ }
20
+ return {
21
+ backend: new TmuxPipeBackend(sessionName, { createSession: true, ownsSession: true }),
22
+ isTmuxMode: true,
23
+ isPipeMode: true,
24
+ };
25
+ }
26
+ //# sourceMappingURL=session-backend-selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-backend-selector.js","sourceRoot":"","sources":["../../../src/adapters/backend/session-backend-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AASzD,MAAM,UAAU,oBAAoB,CAAC,IAA6C;IAChF,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAClB,OAAO;YACL,OAAO,EAAE,IAAI,UAAU,EAAE;YACzB,UAAU,EAAE,KAAK;YACjB,UAAU,EAAE,KAAK;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5D,IAAI,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QACxC,OAAO;YACL,OAAO,EAAE,IAAI,eAAe,CAAC,WAAW,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;YAClF,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,IAAI;SACjB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,IAAI,eAAe,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QACrF,UAAU,EAAE,IAAI;QAChB,UAAU,EAAE,IAAI;KACjB,CAAC;AACJ,CAAC"}
@@ -4,22 +4,42 @@ import type { SessionBackend, SpawnOpts } from './types.js';
4
4
  * to xterm.js (which treats bare LF as "down one row, keep column"). */
5
5
  export declare function normaliseCaptureLineEndings(s: string): string;
6
6
  export declare class TmuxPipeBackend implements SessionBackend {
7
- /** Real tmux pane address (e.g. "0:2.0"). */
7
+ /** Real tmux pane address (e.g. "0:2.0") or botmux session name (bmx-*). */
8
8
  private readonly paneTarget;
9
9
  private readonly fifoPath;
10
10
  private readStream;
11
11
  private readonly dataCbs;
12
12
  private readonly exitCbs;
13
+ private lifecycleTimer;
13
14
  private cols;
14
15
  private rows;
15
16
  private exited;
16
17
  /** Set after pipe-pane subscription is active so kill() knows to cancel it. */
17
18
  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;
19
+ private readonly createSession;
20
+ private readonly ownsSession;
21
+ private readonly _isReattach;
22
+ /** Claude Code session JSONL path set by worker for claude-code sessions so
23
+ * the claude-code adapter can verify paste+Enter submissions via file growth. */
24
+ claudeJsonlPath?: string;
25
+ /** PID of the spawned Claude Code child — used by the claude-code adapter to
26
+ * follow Claude's authoritative session id via ~/.claude/sessions/<pid>.json. */
27
+ cliPid?: number;
28
+ /** Working directory the CLI was spawned in — cross-checked against the pid
29
+ * file's cwd field so a recycled PID can't mislead the resolver. */
30
+ cliCwd?: string;
31
+ /** Whether this backend re-attached to an existing bmx-* tmux session
32
+ * (rather than creating a new detached one). Mirrors TmuxBackend.isReattach
33
+ * so the worker can branch on reattach behaviour without a private-cast. */
34
+ get isReattach(): boolean;
35
+ constructor(paneTarget: string, opts?: {
36
+ createSession?: boolean;
37
+ ownsSession?: boolean;
38
+ isReattach?: boolean;
39
+ });
40
+ /** spawn() sets up the pipe-pane subscription + fifo reader. In managed
41
+ * mode it first creates a detached bmx-* tmux session that runs the CLI. */
42
+ spawn(bin: string, args: string[], opts: SpawnOpts): void;
23
43
  write(data: string): void;
24
44
  sendText(text: string): void;
25
45
  sendSpecialKeys(...keys: string[]): void;
@@ -34,20 +54,40 @@ export declare class TmuxPipeBackend implements SessionBackend {
34
54
  destroySession(): void;
35
55
  getChildPid(): number | null;
36
56
  getAttachInfo(): null;
37
- /** Snapshot the current screen of the adopted pane WITH ANSI escapes,
38
- * including history (-S - = start of scrollback). New web-terminal
39
- * connections receive this string so xterm.js renders the existing
40
- * session state instead of a blank screen.
57
+ private startLifecycleWatcher;
58
+ private stopLifecycleWatcher;
59
+ private handlePaneExit;
60
+ private createDetachedSession;
61
+ private applySessionOptions;
62
+ /** Snapshot the full pane history WITH ANSI escapes (`-S - -E -`).
63
+ *
64
+ * Used by web reattach so a brand-new web client sees the whole prior
65
+ * conversation. For the screenshot / screen_update fast path use
66
+ * `captureViewport()` instead — that one only returns the visible pane
67
+ * and is safe to seed a transient xterm-headless with.
41
68
  *
42
69
  * IMPORTANT: tmux capture-pane separates rows with bare `\n`, no `\r`.
43
70
  * xterm.js (and any VT100-compliant emulator) treats a bare LF as
44
71
  * "move down one row, keep column" — every captured line lands further
45
- * to the right than the previous one, producing the staircase artefact
46
- * observed in early pipe-mode dogfooding. Normalising every `\n` to
47
- * `\r\n` makes the snapshot render correctly. The live pipe-pane stream
48
- * itself doesn't need this fix — applications write proper `\r\n` (and
49
- * Claude Code uses cursor-positioning instead of bare LF anyway). */
72
+ * to the right than the previous one. Normalising every `\n` to `\r\n`
73
+ * makes the snapshot render correctly. The live pipe-pane stream itself
74
+ * doesn't need this fix applications write proper `\r\n`. */
50
75
  captureCurrentScreen(): string;
76
+ /** Snapshot ONLY the currently visible pane (no scrollback). Equivalent to
77
+ * `tmux capture-pane` with no `-S`/`-E` flags, which defaults to the
78
+ * viewport. This is the right input for a transient xterm-headless seed:
79
+ * the snapshot row count matches the transient terminal's row count, so
80
+ * no normal-buffer scroll happens and the rendered screenshot lines up
81
+ * with what the user is seeing in the web terminal right now. */
82
+ captureViewport(): string;
83
+ private captureWithBounds;
84
+ /** Current real tmux pane dimensions. Drives transient-renderer sizing so
85
+ * the screenshot canvas matches whatever the web client resized the pane
86
+ * to. Returns null if tmux can't be queried (pane gone, server gone). */
87
+ getPaneSize(): {
88
+ cols: number;
89
+ rows: number;
90
+ } | null;
51
91
  /** Cheap probe: is the adopted pane currently in the alternate screen
52
92
  * buffer? Used by captureCurrentScreen to decide whether the snapshot
53
93
  * needs an alt-buffer-enter prefix for correct rendering. */
@@ -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;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;IAU5B,eAAe,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAUxC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAgB7B,OAAO,CAAC,oBAAoB;IAuB5B,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"}
1
+ {"version":3,"file":"tmux-pipe-backend.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/tmux-pipe-backend.ts"],"names":[],"mappings":"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"}