pi-terminal-mux 0.5.1 → 0.6.2
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 +4 -4
- package/README.zh-CN.md +4 -4
- package/package.json +2 -2
- package/src/rename.ts +24 -6
package/README.md
CHANGED
|
@@ -94,7 +94,7 @@ export PI_SUBAGENT_HERDR_MODE=tab
|
|
|
94
94
|
| `pollForExit(surface, signal, opts)` | Wait for the process in a surface to exit: `.exit` sidecar file first, then a screen sentinel (`__SUBAGENT_DONE_<code>__`); headless uses child process exit |
|
|
95
95
|
| `getLastSplitSource()` / `clearLastSplitSource()` | Source pane of the most recent split (for UI display) |
|
|
96
96
|
|
|
97
|
-
Rename targets differ by backend: muxy/zellij tab rename targets a pane; tmux targets a window/session
|
|
97
|
+
Rename targets differ by backend: muxy/zellij tab rename targets a pane; tmux targets a window/session; WezTerm workspace rename targets the window; cmux and Herdr provide native workspace rename; Otty and Orca have no workspace rename. `resolveTerminalRenameTargets` is the explicit-ID path and ignores legacy opt-in variables; `getRenameCapability`, `renameCurrentTab` and `renameWorkspace` retain their legacy opt-in behavior. Headless reports `unsupported` instead of silently succeeding.
|
|
98
98
|
|
|
99
99
|
### Detection and utilities
|
|
100
100
|
|
|
@@ -124,9 +124,9 @@ These are opt-in capabilities — existing Bash callers and `pi-interactive-suba
|
|
|
124
124
|
|----------|-------------|
|
|
125
125
|
| `PI_TERMINAL_MUX` / `PI_SUBAGENT_MUX` | Force a backend |
|
|
126
126
|
| `PI_SUBAGENT_ZELLIJ_MIN_COLUMNS` / `PI_SUBAGENT_ZELLIJ_MIN_ROWS` | Minimum usable size for zellij splits (default 50x10; stacks instead when smaller) |
|
|
127
|
-
| `PI_SUBAGENT_RENAME_TMUX_WINDOW` / `PI_SUBAGENT_RENAME_TMUX_SESSION` |
|
|
127
|
+
| `PI_SUBAGENT_RENAME_TMUX_WINDOW` / `PI_SUBAGENT_RENAME_TMUX_SESSION` | Compatibility switches for legacy `getRenameCapability` / `renameCurrentTab` / `renameWorkspace` on tmux; ignored by explicit target resolution |
|
|
128
128
|
| `PI_SUBAGENT_HERDR_MODE` | Herdr surface placement: `split` (default) or `tab` |
|
|
129
|
-
| `PI_SUBAGENT_RENAME_HERDR_WORKSPACE` |
|
|
129
|
+
| `PI_SUBAGENT_RENAME_HERDR_WORKSPACE` | Compatibility switch for legacy `getRenameCapability` / `renameWorkspace` on herdr; ignored by explicit target resolution |
|
|
130
130
|
| `PI_EXTENSIONS_LOCALE` | Hint language (`zh-CN` / `en-US` / `auto`), provided by pi-extensions-i18n |
|
|
131
131
|
|
|
132
132
|
## Design constraints
|
|
@@ -146,6 +146,6 @@ MIT
|
|
|
146
146
|
|
|
147
147
|
`resolveTerminalRenameTargets({ tab, workspace })` returns explicit target IDs and `surface`/`shared` scope, or individual skipped/failed results. `renameTerminalTarget(reference, title)` executes against that captured identity. Callers decide when to rename and which targets to request; the library does not generate titles or change Pi sessions.
|
|
148
148
|
|
|
149
|
-
A restricted child never renames a workspace. cmux surfaces, muxy/zellij panes, and Herdr panes or explicitly created Herdr tabs can be granted. tmux/WezTerm/Otty/Orca split surfaces do not prove exclusive ownership of their window/tab: naming is skipped rather than expanded to the shared parent. Ordinary sessions require their own target IDs; missing IDs never fall back to focus or the first tab.
|
|
149
|
+
A restricted child never renames a workspace. cmux surfaces, muxy/zellij panes, and Herdr panes or explicitly created Herdr tabs can be granted. tmux/WezTerm/Otty/Orca split surfaces do not prove exclusive ownership of their window/tab: naming is skipped rather than expanded to the shared parent. Ordinary sessions require their own target IDs; missing IDs never fall back to focus or the first tab. Explicit target resolution uses backend capability directly, independent of legacy rename opt-ins.
|
|
150
150
|
|
|
151
151
|
Invalid JSON or an unknown protocol version is an error, not unrestricted access. Legacy child identity variables without this protocol restrict terminal naming until the launcher supplies ownership. This is a cooperation contract for trusted local processes, not a security sandbox. WezTerm/Otty split creation does not rename a shared tab.
|
package/README.zh-CN.md
CHANGED
|
@@ -93,7 +93,7 @@ export PI_SUBAGENT_HERDR_MODE=tab
|
|
|
93
93
|
| `pollForExit(surface, signal, opts)` | 等待 surface 内进程退出:优先 `.exit` sidecar 文件,其次屏幕 sentinel(`__SUBAGENT_DONE_<code>__`),headless 走子进程 exit |
|
|
94
94
|
| `getLastSplitSource()` / `clearLastSplitSource()` | 最近一次分屏的来源 pane(用于 UI 展示) |
|
|
95
95
|
|
|
96
|
-
各后端的实际目标不同:muxy/zellij 的 tab 重命名作用于 pane;tmux
|
|
96
|
+
各后端的实际目标不同:muxy/zellij 的 tab 重命名作用于 pane;tmux 作用于 window/session;WezTerm 的 workspace 重命名作用于 window;cmux 和 Herdr 提供原生 workspace 重命名;Otty、Orca 没有 workspace 重命名。`resolveTerminalRenameTargets` 是明确 ID 路径,忽略旧环境开关;`getRenameCapability`、`renameCurrentTab`、`renameWorkspace` 保持旧开关行为。Headless 会明确返回 `unsupported`,不再静默成功。
|
|
97
97
|
|
|
98
98
|
### 探测与工具
|
|
99
99
|
|
|
@@ -123,9 +123,9 @@ export PI_SUBAGENT_HERDR_MODE=tab
|
|
|
123
123
|
|------|------|
|
|
124
124
|
| `PI_TERMINAL_MUX` / `PI_SUBAGENT_MUX` | 强制指定后端 |
|
|
125
125
|
| `PI_SUBAGENT_ZELLIJ_MIN_COLUMNS` / `PI_SUBAGENT_ZELLIJ_MIN_ROWS` | zellij 分屏最小可用尺寸(默认 50×10,不满足时改堆叠) |
|
|
126
|
-
| `PI_SUBAGENT_RENAME_TMUX_WINDOW` / `PI_SUBAGENT_RENAME_TMUX_SESSION` | tmux
|
|
126
|
+
| `PI_SUBAGENT_RENAME_TMUX_WINDOW` / `PI_SUBAGENT_RENAME_TMUX_SESSION` | tmux 下旧 `getRenameCapability` / `renameCurrentTab` / `renameWorkspace` 的兼容开关;明确目标解析忽略 |
|
|
127
127
|
| `PI_SUBAGENT_HERDR_MODE` | herdr surface 放置模式:`split`(默认)或 `tab` |
|
|
128
|
-
| `PI_SUBAGENT_RENAME_HERDR_WORKSPACE` | herdr
|
|
128
|
+
| `PI_SUBAGENT_RENAME_HERDR_WORKSPACE` | herdr 下旧 `getRenameCapability` / `renameWorkspace` 的兼容开关;明确目标解析忽略 |
|
|
129
129
|
| `PI_EXTENSIONS_LOCALE` | 提示文案语言(`zh-CN` / `en-US` / `auto`),由 pi-extensions-i18n 提供 |
|
|
130
130
|
|
|
131
131
|
## 设计约束
|
|
@@ -145,6 +145,6 @@ MIT
|
|
|
145
145
|
|
|
146
146
|
`resolveTerminalRenameTargets({ tab, workspace })` 返回明确的目标 ID、`surface`/`shared` 范围,或逐目标跳过/失败结果。`renameTerminalTarget(reference, title)` 对捕获的身份执行改名。调用方决定何时改、改哪些目标;库不生成标题,也不修改 Pi session。
|
|
147
147
|
|
|
148
|
-
受限子进程不能改 workspace。cmux surface、muxy/zellij pane、Herdr pane 或明确新建的 Herdr tab 可以授予。tmux/WezTerm/Otty/Orca 的分屏不能证明独占 window/tab,改名会跳过,不扩大到共享父目标。普通会话必须能确定自身目标 ID;缺失时不退回当前焦点或第一个 tab
|
|
148
|
+
受限子进程不能改 workspace。cmux surface、muxy/zellij pane、Herdr pane 或明确新建的 Herdr tab 可以授予。tmux/WezTerm/Otty/Orca 的分屏不能证明独占 window/tab,改名会跳过,不扩大到共享父目标。普通会话必须能确定自身目标 ID;缺失时不退回当前焦点或第一个 tab。明确目标解析直接使用后端能力,独立于旧改名开关。
|
|
149
149
|
|
|
150
150
|
协议 JSON 损坏或版本未知会报错,不能退回无限制范围。只有旧子代理身份标志、没有归属协议时,终端改名受限,直到启动方提供归属。此协议用于可信本地进程协作,不是安全沙箱。WezTerm/Otty 创建分屏时不会重命名共享 tab。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-terminal-mux",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Terminal multiplexer abstraction for pi extensions — unified surface API across muxy, cmux, tmux, zellij, wezterm, herdr, otty and orca, with headless fallback",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"coding-agent"
|
|
55
55
|
],
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"pi-extensions-i18n": "^0.
|
|
57
|
+
"pi-extensions-i18n": "^0.5.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/node": "24.12.4",
|
package/src/rename.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { getMuxBackend, type MuxBackend } from "./detection.ts";
|
|
3
|
-
import {
|
|
3
|
+
import { type RenameOperation, type RenameTarget } from "./surface.ts";
|
|
4
4
|
import { getCreatedHerdrTabId } from "./backends/herdr.ts";
|
|
5
5
|
import { AGENT_OTTY_PANE_ID, getTabIdForPane } from "./backends/otty.ts";
|
|
6
6
|
import { renameOrcaTerminal } from "./backends/orca.ts";
|
|
@@ -108,6 +108,25 @@ export interface ResolveRenameOptions {
|
|
|
108
108
|
env?: NodeJS.ProcessEnv;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/** 新的明确目标路径只判断后端能力;旧公开 API 的环境开关不参与这里。 */
|
|
112
|
+
function explicitRenameCapability(
|
|
113
|
+
operation: RenameOperation,
|
|
114
|
+
backend: MuxBackend | null,
|
|
115
|
+
): { backend: MuxBackend; target: RenameTarget } | undefined {
|
|
116
|
+
if (!backend) return undefined;
|
|
117
|
+
if (operation === "tab") {
|
|
118
|
+
const target: Record<MuxBackend, RenameTarget> = {
|
|
119
|
+
muxy: "pane", cmux: "tab", tmux: "window", zellij: "pane", wezterm: "tab",
|
|
120
|
+
herdr: "tab", otty: "tab", orca: "terminal",
|
|
121
|
+
};
|
|
122
|
+
return { backend, target: target[backend] };
|
|
123
|
+
}
|
|
124
|
+
const target: Partial<Record<MuxBackend, RenameTarget>> = {
|
|
125
|
+
cmux: "workspace", tmux: "session", wezterm: "window", herdr: "workspace",
|
|
126
|
+
};
|
|
127
|
+
return target[backend] ? { backend, target: target[backend] } : undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
111
130
|
/** 返回当前进程的明确目标 ID,不以当前焦点或第一个 tab 代替未知身份。 */
|
|
112
131
|
function currentTargetId(reference: { backend: MuxBackend; operation: RenameOperation; env: NodeJS.ProcessEnv }, query: RenameIdQuery): string | undefined {
|
|
113
132
|
const { backend, operation, env } = reference;
|
|
@@ -154,16 +173,15 @@ export function resolveTerminalRenameTargets(options: ResolveRenameOptions, quer
|
|
|
154
173
|
}
|
|
155
174
|
continue;
|
|
156
175
|
}
|
|
157
|
-
const capability =
|
|
158
|
-
if (capability
|
|
159
|
-
outcomes.push({ status: "skipped", operation, reason:
|
|
160
|
-
...(capability.status === "disabled" ? { setting: capability.setting } : {}) });
|
|
176
|
+
const capability = explicitRenameCapability(operation, backend);
|
|
177
|
+
if (!capability) {
|
|
178
|
+
outcomes.push({ status: "skipped", operation, reason: "unsupported" });
|
|
161
179
|
continue;
|
|
162
180
|
}
|
|
163
181
|
try {
|
|
164
182
|
const id = currentTargetId({ backend: capability.backend, operation, env }, query);
|
|
165
183
|
outcomes.push(id?.trim() ? { status: "ready", reference: {
|
|
166
|
-
backend: capability.backend, operation, target: capability.
|
|
184
|
+
backend: capability.backend, operation, target: capability.target, id,
|
|
167
185
|
scope: operation === "workspace" || ["tmux", "wezterm", "herdr", "otty", "orca"].includes(capability.backend) ? "shared" : "surface",
|
|
168
186
|
} } : { status: "skipped", operation, reason: "missing-id" });
|
|
169
187
|
} catch (error) {
|