pi-terminal-mux0.6.7
Terminal multiplexer abstraction for pi extensions — unified surface API across muxy, cmux, tmux, zellij, wezterm, herdr, otty and orca, with headless fallback
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-terminal-mux
终端多路复用器统一抽象层,供 pi 扩展复用。任何涉及终端交互(分屏、发命令、读屏、关屏、等待退出)的插件都应依赖本包,而不是各自重新实现 backend 探测与命令拼装。
一套统一的 surface API 跨 muxy、cmux、tmux、zellij、wezterm、herdr、otty、orca 八个后端,探测不到任何后端时自动降级为 headless(后台子进程 + 日志文件)。
安装
npm install pi-terminal-mux
快速上手
import {
isMuxAvailable,
muxSetupHint,
createSurface,
createSurfaceSplit,
sendCommand,
sendLongCommand,
sendEscape,
readScreen,
closeSurface,
pollForExit,
} from "pi-terminal-mux";
if (!isMuxAvailable()) {
console.warn(muxSetupHint()); // 中英文安装提示,由 pi-extensions-i18n 决定语言
}
// 智能放置:按后端策略分屏 / 堆叠 / 开 tab(headless 时返回 headless surface)
const surface = createSurface("my-agent");
// 长命令自动落脚本文件,避免终端宽度截断
// (默认 Bash;Windows 上显式传 interpreter: "powershell" 切到 PowerShell)
const scriptPath = sendLongCommand(surface, "pi --session abc", {
scriptPreamble: "export MY_FLAG=1",
});
const tail = readScreen(surface, 50);
sendEscape(surface);
closeSurface(surface);
后端探测
| 后端 | 探测条件 |
|---|---|
| muxy | MUXY_SOCKET_PATH + muxy 命令 |
| cmux | CMUX_SOCKET_PATH + cmux 命令 |
| tmux | TMUX + tmux 命令 |
| zellij | ZELLIJ / ZELLIJ_SESSION_NAME + zellij 命令 |
| wezterm | WEZTERM_UNIX_SOCKET + wezterm 命令 |
| herdr | HERDR_ENV=1 + HERDR_PANE_ID + herdr 命令(tab 模式还需要 HERDR_WORKSPACE_ID) |
| otty | TERM_PROGRAM=otty + otty 命令 |
| orca | TERM_PROGRAM=Orca + orca 命令 + Orca runtime 可达 |
默认优先级即上表顺序(muxy 优先)。可用环境变量强制指定后端:
PI_TERMINAL_MUX(推荐):muxy | cmux | tmux | zellij | wezterm | herdr | otty | orcaPI_SUBAGENT_MUX:同上的向后兼容别名
指定的后端运行环境不满足时 getMuxBackend() 返回 null,不会悄悄降级到其他后端。
Herdr surface 模式
Herdr 默认保持向后兼容的广度优先分屏模式。设置 PI_SUBAGENT_HERDR_MODE=tab 后,每个 subagent 会创建独立后台 Tab;设置为 split 可显式选择原有分屏布局。createSurfaceSplit() 始终保留显式 pane 分屏语义。
export PI_SUBAGENT_HERDR_MODE=tab
API 概览
统一 surface API(跨后端语义一致)
| 函数 | 说明 |
|---|---|
createSurface(name) |
智能放置新 surface(herdr 默认广度优先分屏,设置 PI_SUBAGENT_HERDR_MODE=tab 后每个 surface 创建独立后台 Tab;cmux 首次右分屏后续开 tab、zellij tab 感知平铺/堆叠、muxy/otty/orca 广度优先分屏;orca 缺少 agent handle 时新建 tab),返回 surface 标识 |
createSurfaceSplit(name, direction, fromSurface?, options?) |
指定方向(left/right/up/down)分屏;options.activate(仅 wezterm,默认 false)分屏后聚焦新 pane |
sendCommand(surface, command) |
发送命令并回车执行 |
sendLongCommand(surface, command, opts?) |
长命令先写脚本文件再执行;opts.scriptPreamble 可注入前置片段;opts.interpreter(默认 "bash",Windows 可显式 "powershell")选择脚本运行时;返回脚本路径 |
sendEscape(surface) |
发送一次 ESC |
readScreen(surface, lines?, options?) / readScreenAsync |
读取屏幕尾部 N 行;options.source(仅 herdr)透传 herdr 读屏来源(如 "recent_unwrapped"),其他后端忽略 |
closeSurface(surface) |
关闭 surface |
renameSurface(surface, name) / renameAgent(surface, name) |
重命名已知 surface 或 agent 标签 |
getRenameCapability(operation, backend?, env?) |
不执行命令,返回实际重命名目标,或明确的 unsupported / disabled 能力结果 |
renameCurrentTab(title) / renameWorkspace(title) |
执行重命名并返回可判别的 renamed / unsupported / disabled / failed 结果 |
pollForExit(surface, signal, opts) |
等待 surface 内进程退出:优先 .exit sidecar 文件,其次屏幕 sentinel(__SUBAGENT_DONE_<code>__),headless 走子进程 exit |
getLastSplitSource() / clearLastSplitSource() |
最近一次分屏的来源 pane(用于 UI 展示) |
各后端的实际目标不同:muxy/zellij 的 tab 重命名作用于 pane;tmux 作用于 window/session;WezTerm 的 workspace 重命名作用于 window;cmux 和 Herdr 提供原生 workspace 重命名;Otty、Orca 没有 workspace 重命名。resolveTerminalRenameTargets 是明确 ID 路径,忽略旧环境开关;getRenameCapability、renameCurrentTab、renameWorkspace 保持旧开关行为。Headless 会明确返回 unsupported,不再静默成功。
探测与工具
getMuxBackend()、isMuxAvailable()、isHeadlessMode()、muxSetupHint()、getAgentPaneId(backend?)、backendAgentPaneEnvVar(backend)、shellEscape()、isFishShell()、exitStatusVar(),以及 zellij 放置规划(selectZellijPlacement 等)与 cmux/otty JSON 解析等纯函数,均可直接引用做单元测试。
后端原生 API
各后端原生函数也从包入口透出(如 createHerdrSurface、splitHerdrPane、readHerdrScreen、sendOttyCommand、renameOttyTab、createOrcaSurface、sendOrcaCommand……),子路径导入亦可:pi-terminal-mux/mux、pi-terminal-mux/herdr、pi-terminal-mux/otty、pi-terminal-mux/orca。
Headless 模式
探测不到任何后端时,createSurface 返回 headless: 前缀的 surface,sendLongCommand 直接 spawn 后台子进程并把输出写入日志文件,readScreen/pollForExit/closeSurface 语义保持不变,调用方无需特判。
Windows PowerShell 支持
所有平台默认保持 Bash 作为脚本运行时,以不变更现有调用方语义。Windows 11 PowerShell/WezTerm/herdr 组合下显式开启:
- 命令提交(wezterm):Enter 终止符在
win32用\r、其他平台用\n,PowerShell 输入只会提交一次,不会停在续行提示。 - 长命令(
sendLongCommand):传interpreter: "powershell"会生成.ps1,mux 通过powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File <path>执行、headless 走-Command "& <path>"。显式scriptPath原样保留;自动路径按解释器选.ps1/.sh。未传 interpreter 时保持既有 Bash command、.sh路径与$?数值哨兵不变。 - 读屏(
readScreen/readScreenAsync):传{ source: "recent_unwrapped" }(仅 herdr)选择 herdr 的软换行合并捕获;不传 options 时 herdr 保持recent,其他后端保持各自读屏语义。
这些都是可选项——既有 Bash 调用方与 pi-interactive-subagents 在所有平台继续走默认 Bash 运行时。
环境变量
| 变量 | 说明 |
|---|---|
PI_TERMINAL_MUX / PI_SUBAGENT_MUX |
强制指定后端 |
PI_SUBAGENT_ZELLIJ_MIN_COLUMNS / PI_SUBAGENT_ZELLIJ_MIN_ROWS |
zellij 分屏最小可用尺寸(默认 50×10,不满足时改堆叠) |
PI_SUBAGENT_RENAME_TMUX_WINDOW / PI_SUBAGENT_RENAME_TMUX_SESSION |
tmux 下旧 getRenameCapability / renameCurrentTab / renameWorkspace 的兼容开关;明确目标解析忽略 |
PI_SUBAGENT_HERDR_MODE |
herdr surface 放置模式:split(默认)或 tab |
PI_SUBAGENT_RENAME_HERDR_WORKSPACE |
herdr 下旧 getRenameCapability / renameWorkspace 的兼容开关;明确目标解析忽略 |
PI_EXTENSIONS_LOCALE |
提示文案语言(zh-CN / en-US / auto),由 pi-extensions-i18n 提供 |
设计约束
- 不绑定具体机器:全部后端通过运行时探测(环境变量 + 命令存在性)选择,零硬编码本机路径;外部 CLI 缺失时按后端逐个降级,最终落到 headless。
- 用户文案国际化:面向用户的提示走 pi-extensions-i18n catalog,中英文齐全。
- agent pane 锚定:muxy/herdr/otty/orca 的 agent 自身 pane ID 在模块加载时捕获(
AGENT_MUXY_PANE_ID、AGENT_ORCA_TERMINAL_HANDLE等),不受用户后续焦点切换影响。
License
MIT
按归属改名
createSurfaceRenameContext(surface) 描述启动方可授予子进程的终端改名目标。把返回值序列化为 JSON 放入 PI_TERMINAL_RENAME_CONTEXT,每次启动和恢复都替换继承值。协议属于 terminal-mux,不属于命名或子代理插件。
resolveTerminalRenameTargets({ tab, workspace }) 返回明确的目标 ID、surface/shared 范围,或逐目标跳过/失败结果。renameTerminalTarget(reference, title) 对捕获的身份执行改名。调用方决定何时改、改哪些目标;库不生成标题,也不修改 Pi session。
受限子进程不能改 workspace。cmux surface、muxy/zellij pane、Herdr pane 或明确新建的 Herdr tab 可以授予。tmux/WezTerm/Otty/Orca 的分屏不能证明独占 window/tab,改名会跳过,不扩大到共享父目标。普通会话必须能确定自身目标 ID;缺失时不退回当前焦点或第一个 tab。明确目标解析直接使用后端能力,独立于旧改名开关。
协议 JSON 损坏或版本未知会报错,不能退回无限制范围。只有旧子代理身份标志、没有归属协议时,终端改名受限,直到启动方提供归属。此协议用于可信本地进程协作,不是安全沙箱。WezTerm/Otty 创建分屏时不会重命名共享 tab。