@hyzyn/dsh-tty 0.26.0 → 0.27.1

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.en.md CHANGED
@@ -114,7 +114,7 @@ Both were reproduced on **Windows 11 ARM (24H2) + Node 22 ARM64**. The fix:
114
114
 
115
115
  ## Agent tools
116
116
 
117
- The plugin injects nineteen tools into the agent (with the same power as the bash tool; operations show up live in the user’s terminal):
117
+ The plugin injects nineteen tools into the agent (operations show up live in the user’s terminal; **every call is subject to the session permission tier**, see below):
118
118
 
119
119
  | Tool | Purpose |
120
120
  | --- | --- |
@@ -137,6 +137,26 @@ The plugin injects nineteen tools into the agent (with the same power as the bas
137
137
  | `tunnel_list` | List port-forwarding tunnels and their live state (active/connecting/error/stopped, rules, connection counts) |
138
138
  | `tunnel_start` / `tunnel_stop` | Start or stop one port-forwarding tunnel (0.25.0; `name` comes from `tunnel_list`). These **rewrite the configuration** — the same thing as ticking/unticking “Enable” in the settings card (settings → hot apply → `reconcile`), so the change is persistent and visible to the user. The returned snapshot usually still says `connecting` (dialling is async); call `tunnel_list` again for the final verdict |
139
139
 
140
+ ### Session permission tier gate (tier gate)
141
+
142
+ Tools are classified by risk; **every call** is judged against the session’s permission tier
143
+ (Read Only / Workspace Write / Full access / Auto review) and allowed, asked, or denied — the tier is
144
+ switched by the user in the session, and the very next call follows the new tier:
145
+
146
+ | Class | Tools | Behaviour under a confined tier (Read Only / Workspace Write) |
147
+ | --- | --- | --- |
148
+ | Read | `tty_list` `tty_capture` `tty_screen` `tty_expect` `tty_stats` `tunnel_list` `sftp_list` `sftp_read` `sftp_tree` `tty_close` | Allowed on every tier |
149
+ | Write (bounded mutation) | `sftp_write` `sftp_mkdir` `sftp_rename` `sftp_remove` `tunnel_stop` | Approval prompt (runs only if the user allows this one call); deterministically denied when unattended (approval policy `never`) |
150
+ | Execute (unbounded command surface) | `tty_open` `tty_run` `tty_send` `tunnel_start` | Same — these tools can run arbitrary commands on the local or a remote shell and open listening ports; they are the fence-sensitive surface |
151
+
152
+ **Full access never prompts** (that tier’s intent is fewer confirmation steps); under **Auto review the
153
+ plugin does not add its own prompt** (the host’s model pre-review already examines every call). When the
154
+ host has not composed the `sandboxPolicy` / `approval` services (older hosts, minimal deployments) the
155
+ gate is **off** (behaviour identical to before the gate), and the startup log says so:
156
+ `tier-gate: services absent (…), per-call gate disabled`. The mechanism (decision matrix / the two-layer
157
+ relationship with the static `allowProxyCommand` switch) lives in
158
+ [docs/permission-tier-plan.md](../../docs/permission-tier-plan.md).
159
+
140
160
  Typical agent flow (recommended): for a command that simply runs to completion, use `tty_run` to get its
141
161
  output + exit code in one call; for a **long-lived** session, `tty_open` opens one (pass `persistName` for tmux
142
162
  persistence on long-running work) → `tty_send` starts the command → `tty_expect` waits for the readiness marker →
@@ -852,6 +872,22 @@ ctx.inject(['ttyTerminal'], (c) => {
852
872
  - The service name `ttyTerminal` is likewise not declared on the `Context` type surface; inject it as an
853
873
  optional dependency; when tty is not installed or is older than 0.14.0 it never fires (dsh-docker degrades
854
874
  to “copy command”).
875
+ - **`ttyTerminal` / WS `spawn` is a command surface with no static capability gate** (added 2026-10-07;
876
+ consumers must not describe it the other way round): this service and the `{t:'spawn'}` frame both accept an
877
+ arbitrary `command` and are **not governed by any `DSH_*_ALLOW_*`** (this plugin’s own static gate covers only
878
+ ProxyCommand, the “a config field drives a local command” tier). That is deliberate — the terminal panel *is*
879
+ the product, and **its permission ceiling rests on the user acting directly**: a human clicks “+” to open a
880
+ tab and types commands inside it. **Note carefully that the tier gate does not reach here**: the tier gate
881
+ above hangs on the `tools/pre-execute` waterfall and filters by the `tty_` / `sftp_` / `tunnel_` **prefixes**
882
+ (`TTY_TIER_PREFIXES`) — i.e. it gates **agent tool calls only**. A browser `spawn` frame or a
883
+ `ttyTerminal.open` call is a **WS / client-service** path and **never passes through it**. So do not treat it
884
+ as this command surface’s gate: it does not block a single byte of it (only the `tty_open` agent path is
885
+ governed).
886
+ **The cost**: no consumer should advertise “the plugin’s own exec switch” as the boundary that keeps you out of
887
+ a container — once tty is installed, an in-page script can use `ttyTerminal.open` or a WS `spawn` frame to get
888
+ an arbitrary command surface strictly stronger than a one-shot exec. See
889
+ [architecture.md § 7](../../docs/architecture.md#7-一条请求经过什么) for the threat-model boundary; docker states
890
+ the same boundary in its own README under “Known limitations”.
855
891
 
856
892
  ### Embedded in-place terminal (`ttyTerminal.mount`, 0.15.0)
857
893
 
package/README.md CHANGED
@@ -105,7 +105,7 @@ dsh plugin --profile web add link:$(pwd)/packages/tty # 仓库开发调试
105
105
 
106
106
  ## agent 工具
107
107
 
108
- 插件向 agent 注入十九个工具(与 bash 工具同权,操作实时显示在用户终端里):
108
+ 插件向 agent 注入十九个工具(操作实时显示在用户终端里;**每次调用受会话权限档位约束**,见下节):
109
109
 
110
110
  | 工具 | 作用 |
111
111
  | --- | --- |
@@ -128,6 +128,23 @@ dsh plugin --profile web add link:$(pwd)/packages/tty # 仓库开发调试
128
128
  | `tunnel_list` | 列出端口转发隧道及其实时状态(活跃/连接中/错误/停止、规则、连接数);`fatal:true` = 人工介入级故障(本地监听失败 / 连接簿缺失),**不会自动重试**,修配置后重建 |
129
129
  | `tunnel_start` / `tunnel_stop` | 启停一条端口转发隧道(0.25.0,`name` 来自 `tunnel_list`)。**会改写配置**——与在设置卡片里勾选/取消那个「启用」是同一件事(写回 settings → 热应用 → `reconcile`),所以是持久的、用户在卡片上看得见。返回值是操作后的状态快照:`state` 通常还是 `connecting`(拨号异步),要最终结论稍后 `tunnel_list` |
130
130
 
131
+ ### 会话权限档位闸(tier gate)
132
+
133
+ 工具按风险分三类,**每次调用**按会话的权限档位(仅可查看 / 工作区内修改 / 完全权限 / Auto review)
134
+ 决定放行、询问还是拒绝——档位是用户在会话里切的,切完下一个调用立即按新档走:
135
+
136
+ | 类 | 工具 | 受限档(仅可查看 / 工作区内修改)下的行为 |
137
+ | --- | --- | --- |
138
+ | 读 | `tty_list` `tty_capture` `tty_screen` `tty_expect` `tty_stats` `tunnel_list` `sftp_list` `sftp_read` `sftp_tree` `tty_close` | 全档放行 |
139
+ | 写(有界变更) | `sftp_write` `sftp_mkdir` `sftp_rename` `sftp_remove` `tunnel_stop` | 弹授权询问(人允许这一次才执行);无人值守(审批策略 never)确定性拒绝 |
140
+ | 执行(无界命令面) | `tty_open` `tty_run` `tty_send` `tunnel_start` | 同上——这些工具能在本机或远程 shell 里跑任意命令、开监听端口,是围栏敏感面 |
141
+
142
+ **完全权限档零询问**(该档的 intent 就是减少确认步骤);**Auto review 档插件不叠加询问**
143
+ (宿主的模型预审已在逐调用审查)。宿主未组合 `sandboxPolicy` / `approval` 服务(老宿主、最小
144
+ 部署)时**不闸**(与引入前行为一致),启动日志会打一行
145
+ `tier-gate: services absent (…), per-call gate disabled` 说明这件事。机制(决策矩阵 / 与静态能力
146
+ 开关 `allowProxyCommand` 的双层关系)见 [docs/permission-tier-plan.md](../../docs/permission-tier-plan.md)。
147
+
131
148
  典型 agent 流程(推荐):跑完就算完的命令用 `tty_run` 一次调用拿回输出 + 退出码;
132
149
  需要**长驻**的会话用 `tty_open`(长驻进程用 `persistName` 要 tmux 持久化)→ `tty_send` 启动命令
133
150
  → `tty_expect` 等就绪标记 → `tty_capture{last:true}` 拿单条命令结果 → 用完 `tty_close`。
@@ -774,6 +791,19 @@ ctx.inject(['ttyTerminal'], (c) => {
774
791
  只受工具参数的字符串约束。两条通道的入参规则不同,别把这里当 `tty_open` 的限制。
775
792
  - 服务名 `ttyTerminal` 同样未声明在 `Context` 类型面上,按可选依赖注入;tty 未安装
776
793
  或版本 < 0.14.0 时不会触发(dsh-docker 会退化为「复制命令」)。
794
+ - **`ttyTerminal` / WS `spawn` 是「没有静态能力闸」的命令面**(2026-10-07 补,消费方文案别写反):
795
+ 本服务与 `{t:'spawn'}` 帧都直接接受任意 `command`,**不受任何 `DSH_*_ALLOW_*` 管辖**(本插件的
796
+ 那个静态闸只覆盖 ProxyCommand 这一档「配置字段驱动本机命令」)。这是刻意的——终端面板本身就是
797
+ 产品,**它的权限上界由用户的直接操作承担**:人点「+」开标签、人在里面敲命令。
798
+ **务必看清 tier gate 管不到这里**:上面的 tier gate 挂在 `tools/pre-execute` 瀑布上、只按
799
+ `tty_` / `sftp_` / `tunnel_` **前缀**过滤(`TTY_TIER_PREFIXES`),也就是只闸 **agent 工具调用**;
800
+ 浏览器发来的 `spawn` 帧与 `ttyTerminal.open` 是 **WS / 客户端服务**通道,**完全不经过它**。
801
+ 所以别把它当这条命令面的闸门——它一个字节都不拦(`tty_open` 那条 agent 路径才受它管)。
802
+ **代价**:任何消费方都不该把「配上 tty 之后本插件自己的 exec 开关」宣传成「进不了容器」的
803
+ 边界——装上 tty 后,页内脚本经 `ttyTerminal.open` 或 WS `spawn` 就能拿到比一次性 exec 更强的
804
+ 任意命令面。威胁模型边界见
805
+ [architecture.md § 7](../../docs/architecture.md#7-一条请求经过什么);docker 侧的同一句边界写在
806
+ 它的 README「已知限制」。
777
807
 
778
808
  ### 就地嵌入终端(`ttyTerminal.mount`,0.15.0)
779
809