@kevinhao/pi-terminal-mux 0.0.0-stage → 0.7.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/README.md CHANGED
@@ -1,3 +1,105 @@
1
- # Temporary Holding Version
1
+ # @kevinhao/pi-terminal-mux
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Terminal surface API for Pi extensions, supporting **tmux**, **Orca**, and **headless** background processes.
4
+
5
+ [中文文档](./README.zh-CN.md)
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @kevinhao/pi-terminal-mux
11
+ ```
12
+
13
+ ## Create and start a command
14
+
15
+ ```ts
16
+ import { createSurface, createSurfaceSplit, readScreen, closeSurface } from "@kevinhao/pi-terminal-mux";
17
+
18
+ const surface = createSurface("my-agent", {
19
+ command: "pi --session /tmp/child.jsonl",
20
+ });
21
+
22
+ const sibling = createSurfaceSplit("review", "right", surface, {
23
+ command: "pi --session /tmp/review.jsonl",
24
+ });
25
+
26
+ const tail = readScreen(surface, 50);
27
+ closeSurface(sibling);
28
+ closeSurface(surface);
29
+ ```
30
+
31
+ `command` is an optional, non-empty **shell command string**, including its arguments. Quote dynamic values with `shellEscape()` for POSIX shells. There is no separate `sendCommand()` call or library-side shell-ready sleep on this path:
32
+
33
+ - **tmux:** supplies the command to `tmux split-window` while creating the pane.
34
+ - **Orca:** supplies `--command` to `terminal create` or `terminal split`. Orca itself starts a shell and submits the command; this is not a direct argv process spawn.
35
+ - **Headless:** starts the command with `bash -c`, writing stdout/stderr to a log file.
36
+
37
+ Returning a handle confirms creation/submission, not application readiness or successful completion. Use your application's ready signal, logs, or exit protocol for that. Creation errors propagate to the caller. Orca input/read errors also propagate instead of appearing successful.
38
+
39
+ Existing calls without options keep their previous behavior:
40
+
41
+ ```ts
42
+ import { createSurface, sendLongCommand } from "@kevinhao/pi-terminal-mux";
43
+
44
+ const surface = createSurface("my-agent");
45
+ const scriptPath = sendLongCommand(surface, "pi --session /tmp/child.jsonl", {
46
+ scriptPreamble: "export MY_FLAG=1",
47
+ });
48
+ ```
49
+
50
+ `sendLongCommand` writes a script first and then submits `bash <script>` to the terminal. It remains available for existing callers and long commands. Do not also submit the startup command after passing it to `createSurface`, or it will run twice.
51
+
52
+ ## Detection
53
+
54
+ | Backend | Requirements |
55
+ |---------|--------------|
56
+ | tmux | `TMUX` environment variable and the `tmux` executable |
57
+ | Orca | `TERM_PROGRAM=Orca`, the `orca` executable, and a reachable runtime |
58
+ | Headless | Neither terminal backend is available |
59
+
60
+ Automatic detection prefers tmux, then Orca. Force a backend with `PI_TERMINAL_MUX=tmux|orca`; `PI_SUBAGENT_MUX` remains a compatibility alias. An unavailable forced backend does not select another terminal backend. Unknown or removed backend preferences throw an explicit error.
61
+
62
+ ## API
63
+
64
+ | Function | Behavior |
65
+ |----------|----------|
66
+ | `createSurface(name, options?)` | tmux and Orca both breadth-first split from the caller's pane into a grid; Orca opens a new tab without a caller handle; headless fallback. `options.command` starts work during creation |
67
+ | `createSurfaceSplit(name, direction, fromSurface?, options?)` | Explicit split; `direction` is left/right/up/down, `options.command` is optional. Requires tmux or Orca; headless has no pane layout |
68
+ | `sendCommand(surface, command)` | Send input followed by Enter to an existing terminal |
69
+ | `sendLongCommand(surface, command, options?)` | Write a script, then execute it. Supports `scriptPath`, `scriptPreamble`, and `interpreter: "bash" | "powershell"` |
70
+ | `sendEscape(surface)` | Send Escape; headless uses SIGINT |
71
+ | `readScreen(surface, lines?)` / `readScreenAsync` | Read terminal output or headless logs |
72
+ | `closeSurface(surface)` | Close a pane or headless process |
73
+ | `renameSurface(surface, name)` | Rename the backend's corresponding window/tab |
74
+ | `getLastSplitSource()` / `clearLastSplitSource()` | Track the most recent split source |
75
+ | `pollForExit(surface, signal, options)` | Wait for an `.exit` sidecar, sentinel, or headless process exit |
76
+
77
+ Types `CreateSurfaceOptions` and `CreateSurfaceSplitOptions` both expose `command?: string`. The existing first three split arguments are unchanged.
78
+
79
+ Detection helpers include `getMuxBackend`, `isMuxAvailable`, `isHeadlessMode`, `muxSetupHint`, `getAgentPaneId`, and `backendAgentPaneEnvVar`. Shell helpers include `shellEscape`, `isFishShell`, and `exitStatusVar`.
80
+
81
+ The shared API is also exported through `@kevinhao/pi-terminal-mux/mux`; Orca-native helpers are exported through `@kevinhao/pi-terminal-mux/orca`.
82
+
83
+ ## Naming ownership
84
+
85
+ `createSurfaceRenameContext(surface)` produces the `PI_TERMINAL_RENAME_CONTEXT` protocol for a child launcher. Replace inherited context on every launch and resume.
86
+
87
+ A child pane does not prove exclusive ownership of its tmux window or Orca tab. Child contexts therefore grant neither shared window/tab renaming nor workspace renaming. Invalid contexts never fall back to parent permissions.
88
+
89
+ For ordinary sessions, `resolveTerminalRenameTargets({ tab, workspace })` resolves explicit identities, and `renameTerminalTarget(reference, title)` operates on those captured targets, never on UI focus:
90
+
91
+ - tmux: window for `tab`, session for `workspace`.
92
+ - Orca: terminal tab for `tab`; `workspace` is unsupported.
93
+ - Headless: naming is unsupported.
94
+
95
+ Legacy `renameCurrentTab` / `renameWorkspace` return discriminated success, unsupported, disabled, or failed results. tmux's legacy operations require `PI_SUBAGENT_RENAME_TMUX_WINDOW=1` / `PI_SUBAGENT_RENAME_TMUX_SESSION=1`; explicit-ID resolution ignores those switches.
96
+
97
+ ## Migration
98
+
99
+ Only tmux and Orca remain in `MuxBackend`. Other backend implementations, native exports/subpaths, WezTerm's `activate` option, and Herdr's read-source options have been removed. Update old backend preferences and imports. This is a breaking reduction of the supported backend/API surface; tmux/Orca calls without startup options remain compatible.
100
+
101
+ No local executable paths are hardcoded. Orca's caller handle is captured at module load so later UI focus changes cannot redirect splits. Headless keeps the existing Bash default; `sendLongCommand({ interpreter: "powershell" })` explicitly selects PowerShell where available.
102
+
103
+ ## License
104
+
105
+ MIT
@@ -0,0 +1,105 @@
1
+ # @kevinhao/pi-terminal-mux
2
+
3
+ 供 Pi 扩展使用的终端 surface API,仅支持 **tmux**、**Orca** 和 **headless** 后台进程。
4
+
5
+ [English README](./README.md)
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install @kevinhao/pi-terminal-mux
11
+ ```
12
+
13
+ ## 创建时启动命令
14
+
15
+ ```ts
16
+ import { createSurface, createSurfaceSplit, readScreen, closeSurface } from "@kevinhao/pi-terminal-mux";
17
+
18
+ const surface = createSurface("my-agent", {
19
+ command: "pi --session /tmp/child.jsonl",
20
+ });
21
+
22
+ const sibling = createSurfaceSplit("review", "right", surface, {
23
+ command: "pi --session /tmp/review.jsonl",
24
+ });
25
+
26
+ const tail = readScreen(surface, 50);
27
+ closeSurface(sibling);
28
+ closeSurface(surface);
29
+ ```
30
+
31
+ `command` 是可选的、非空的 **shell 命令字符串**,包含启动参数。动态值在 POSIX shell 中使用 `shellEscape()` 转义。这条路径不再另外调用 `sendCommand()`,也没有库内固定的 shell-ready 等待:
32
+
33
+ - **tmux**:将命令直接传给 `tmux split-window`,在创建 pane 时启动。
34
+ - **Orca**:传给 `terminal create` / `terminal split` 的 `--command`。Orca 自己启动 shell 并提交命令,不等同于直接按 argv spawn 程序。
35
+ - **Headless**:用 `bash -c` 启动命令,stdout/stderr 写入日志文件。
36
+
37
+ 返回 handle 只证明创建/提交成功,不保证应用已就绪或任务成功。仍需使用应用自身的就绪信号、日志或退出协议。创建错误向上抛出;Orca 输入和读屏错误也会抛出,不再伪报成功。
38
+
39
+ 不传 options 的既有调用保持原来的行为:
40
+
41
+ ```ts
42
+ import { createSurface, sendLongCommand } from "@kevinhao/pi-terminal-mux";
43
+
44
+ const surface = createSurface("my-agent");
45
+ const scriptPath = sendLongCommand(surface, "pi --session /tmp/child.jsonl", {
46
+ scriptPreamble: "export MY_FLAG=1",
47
+ });
48
+ ```
49
+
50
+ `sendLongCommand` 先写脚本,再向终端提交 `bash <script>`,仍供旧调用和长命令使用。已经在创建时传入的启动命令不要再发送一次,否则会重复执行。
51
+
52
+ ## 后端探测
53
+
54
+ | 后端 | 条件 |
55
+ |------|------|
56
+ | tmux | 存在 `TMUX` 环境变量及 `tmux` 可执行文件 |
57
+ | Orca | `TERM_PROGRAM=Orca`、存在 `orca` 可执行文件,且 runtime 可达 |
58
+ | Headless | 上述终端后端均不可用 |
59
+
60
+ 自动探测优先 tmux,其次 Orca。可用 `PI_TERMINAL_MUX=tmux|orca` 强制指定;保留 `PI_SUBAGENT_MUX` 兼容别名。强制指定的后端不可用时不改选另一终端后端;未知或已移除的后端配置明确报错。
61
+
62
+ ## API
63
+
64
+ | 函数 | 行为 |
65
+ |------|------|
66
+ | `createSurface(name, options?)` | tmux 与 Orca 一致:从调用方 pane 出发做 BFS 网格分屏;Orca 没有调用方 handle 时新建 tab;无后端时 headless。`options.command` 在创建时启动任务 |
67
+ | `createSurfaceSplit(name, direction, fromSurface?, options?)` | 指定方向 left/right/up/down 分屏;可传 `options.command`。要求 tmux 或 Orca,headless 没有布局 |
68
+ | `sendCommand(surface, command)` | 向现有终端发送文本并回车 |
69
+ | `sendLongCommand(surface, command, options?)` | 先写脚本再执行;支持 `scriptPath`、`scriptPreamble` 和 `interpreter: "bash" | "powershell"` |
70
+ | `sendEscape(surface)` | 发送 Escape;headless 使用 SIGINT |
71
+ | `readScreen(surface, lines?)` / `readScreenAsync` | 读取终端输出或 headless 日志 |
72
+ | `closeSurface(surface)` | 关闭 pane 或 headless 进程 |
73
+ | `renameSurface(surface, name)` | 修改后端对应的 window/tab 名称 |
74
+ | `getLastSplitSource()` / `clearLastSplitSource()` | 最近一次分屏来源 |
75
+ | `pollForExit(surface, signal, options)` | 等待 `.exit` sidecar、sentinel 或 headless 进程退出 |
76
+
77
+ `CreateSurfaceOptions` 和 `CreateSurfaceSplitOptions` 均提供 `command?: string`。分屏的前三个位置参数保持不变。
78
+
79
+ 探测工具包括 `getMuxBackend`、`isMuxAvailable`、`isHeadlessMode`、`muxSetupHint`、`getAgentPaneId`、`backendAgentPaneEnvVar`;shell 工具包括 `shellEscape`、`isFishShell`、`exitStatusVar`。
80
+
81
+ 统一 API 同样从 `@kevinhao/pi-terminal-mux/mux` 导出;Orca 原生工具从 `@kevinhao/pi-terminal-mux/orca` 导出。
82
+
83
+ ## 改名归属
84
+
85
+ `createSurfaceRenameContext(surface)` 为子进程启动方生成 `PI_TERMINAL_RENAME_CONTEXT` 协议。每次启动和恢复都替换继承值。
86
+
87
+ 子 pane 不能证明独占 tmux window 或 Orca tab,因此子进程不获得共享 window/tab 或 workspace 改名权限。协议损坏时不退回父会话权限。
88
+
89
+ 普通会话使用 `resolveTerminalRenameTargets({ tab, workspace })` 解析明确身份,再通过 `renameTerminalTarget(reference, title)` 对捕获的目标执行,而不是使用 UI 焦点:
90
+
91
+ - tmux:`tab` 对应 window,`workspace` 对应 session。
92
+ - Orca:`tab` 对应终端 tab,不支持 `workspace`。
93
+ - Headless:不支持终端改名。
94
+
95
+ 旧 `renameCurrentTab` / `renameWorkspace` 返回可判别的成功、不支持、禁用或失败结果。tmux 的旧入口分别要求 `PI_SUBAGENT_RENAME_TMUX_WINDOW=1` / `PI_SUBAGENT_RENAME_TMUX_SESSION=1`;明确 ID 的解析不受这两个开关影响。
96
+
97
+ ## 迁移
98
+
99
+ `MuxBackend` 仅保留 tmux 和 Orca。其他后端实现、原生导出/子路径、WezTerm 的 `activate` 参数、Herdr 的读屏 source 参数已移除。请更新旧后端环境配置和导入路径。这是支持范围/API 的不兼容精简;既有 tmux/Orca 调用不传启动参数时保持兼容。
100
+
101
+ 代码不硬编码本地可执行文件路径;Orca 调用方 handle 在模块加载时捕获,后续焦点变化不会改变分屏来源。Headless 保留 Bash 默认运行时;需要 PowerShell 时显式使用 `sendLongCommand({ interpreter: "powershell" })`。
102
+
103
+ ## License
104
+
105
+ MIT
package/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index.ts";
package/package.json CHANGED
@@ -1,6 +1,61 @@
1
1
  {
2
2
  "name": "@kevinhao/pi-terminal-mux",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.7.0",
4
+ "description": "Terminal surface API for tmux and Orca, with native startup commands and headless fallback",
5
+ "type": "module",
6
+ "main": "./index.ts",
7
+ "exports": {
8
+ ".": "./index.ts",
9
+ "./package.json": "./package.json",
10
+ "./mux": "./src/mux.ts",
11
+ "./orca": "./src/orca.ts"
12
+ },
13
+ "files": [
14
+ "index.ts",
15
+ "src",
16
+ "README.md",
17
+ "README.zh-CN.md",
18
+ "tsconfig.json"
19
+ ],
20
+ "scripts": {
21
+ "test": "tsx --test tests/*.test.ts",
22
+ "typecheck": "tsc --noEmit --pretty false",
23
+ "build": "npm run typecheck",
24
+ "check": "npm run typecheck && npm test && npm pack --dry-run --json > /dev/null"
25
+ },
26
+ "engines": {
27
+ "node": ">=22"
28
+ },
29
+ "license": "MIT",
30
+ "author": "maplezzk",
31
+ "homepage": "https://github.com/haoyiqiang/pi-extensions/tree/main/packages/pi-terminal-mux",
32
+ "bugs": "https://github.com/haoyiqiang/pi-extensions/issues",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/haoyiqiang/pi-extensions.git",
36
+ "directory": "packages/pi-terminal-mux"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public",
40
+ "registry": "https://registry.npmjs.org"
41
+ },
42
+ "keywords": [
43
+ "pi-package",
44
+ "pi",
45
+ "pi-extension",
46
+ "terminal",
47
+ "multiplexer",
48
+ "tmux",
49
+ "orca",
50
+ "headless",
51
+ "coding-agent"
52
+ ],
53
+ "dependencies": {
54
+ "@kevinhao/pi-utils": "^0.1.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/node": "24.12.4",
58
+ "tsx": "4.23.1",
59
+ "typescript": "5.9.3"
60
+ }
61
+ }