@deepseek-ai/dsh-bash-local 0.1.1-rc.2 → 0.1.2-alpha.3

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.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/shell/bash-local/README.md
5
- README.md: 5e62ea24676f3bedf32b1b48f8618d703d5fb462
6
- README.zh.md: 92f842a777aebec2e2cb6a8c54966e46202531f1
5
+ README.md: 1523430aa27c5b07d6dd1db614a697a1c1738cc2
6
+ README.zh.md: dc5200e764623f8482b9e07f21bed300f0895c2e
package/README.md CHANGED
@@ -1,34 +1,124 @@
1
+ ---
2
+ description: "The default POSIX Bash executor for deployments and maintainers choosing, configuring, or debugging unconfined command execution over the shell seam."
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-bash-local
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- Local Service Provider for the `@deepseek-ai/dsh-shell` executor seam over the [`@deepseek-ai/dsh-subprocess`](../../subprocess/subprocess/README.md) service: `LocalBashExecutor` spawns `bash -c <command>` per call as a managed process group through `ctx.subprocess`, and owns everything bash-shaped — command defaulting and caps, timeout/cancel classification, the model-friendly terminal environment, and the model-facing stdout/stderr merge for background reads. Group mechanics (bounded spill-backed output, credential scrub, kill escalation, disposal) are the subprocess service's.
10
+ ## Summary
11
+
12
+ `dsh-bash-local` is the default Bash executor for POSIX: every command runs as a fresh, non-login `bash -c` process with no rc files, so no shell state survives between calls. It applies configured budgets — working directory, timeout, output caps — to each command, classifies timeouts and cancellations, and returns bounded output with spill-file recovery when a stream overflows. Commands run with the harness process's own authority: this executor confines nothing, so compose `dsh-bash-sandbox` when commands need the sandbox capability. The model-facing `bash` tool talks to it once it is mounted.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
6
24
 
7
- The package root exports the default and named `LocalBashExecutor` plugin plus its `Config`.
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
8
27
 
9
- ## Config
28
+ Mount this executor when a composition needs Bash command execution on POSIX without confinement. It registers as `ctx.shell`, and the model-facing `bash` tool works over it immediately: an agent calls the tool, and the command runs as a fresh `bash -c` process with the budgets below.
29
+
30
+ ### Minimal configuration
31
+
32
+ Load the executor with the budgets you want; every field has a default, so the smallest composition is the plugin entry alone. The settings provider (when composed) layers a user section over this entry, so budgets can change at runtime without a reload (see [Adjusting budgets at runtime](#adjusting-budgets-at-runtime)).
10
33
 
11
34
  ```yaml
12
35
  - id: bash
13
36
  name: '@deepseek-ai/dsh-bash-local'
14
37
  config:
15
- cwd: /path/to/workspace # default: process.cwd()
16
- timeoutMs: 120000 # default foreground timeout
17
- maxTimeoutMs: 600000 # cap for per-call overrides
18
- maxOutputBytes: 64000 # per-stream in-memory cap; overflow spills to disk
19
- maxSpillBytes: 67108864 # per-stream full-output spill cap
20
- graceMs: 3000 # kill escalation and post-exit pipe-drain grace
38
+ cwd: /path/to/workspace
39
+ timeoutMs: 120000
40
+ ```
41
+
42
+ | Field | Default | Meaning |
43
+ |---|---|---|
44
+ | `cwd` | `process.cwd()` | Default working directory for commands |
45
+ | `timeoutMs` | `120,000` | Default foreground timeout, in milliseconds |
46
+ | `maxTimeoutMs` | `600,000` | Cap for per-call timeout overrides |
47
+ | `maxOutputBytes` | `64,000` | Per-stream in-memory output cap; overflow spills to a temp file |
48
+ | `maxSpillBytes` | `67,108,864` | Per-stream full-output spill cap |
49
+ | `graceMs` | `3,000` | Grace period for kill escalation and post-exit pipe draining |
50
+
51
+ The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-bash-local) is the exhaustive source for every accepted field and its JSDoc.
52
+
53
+ ### Running commands
54
+
55
+ Run a command with `run` and read its output from the result. A nonzero exit, a timeout, or a cancellation resolves with a descriptive result — only infrastructure failures reject. Per-call `timeoutMs` overrides are capped by the configuration, while `workdir` falls back to the configured default when unset; a trusted foreground caller can also raise the stdout capture budget for one call, while stderr and background runs keep `maxOutputBytes`. The environment is model-friendly by default: `NO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat` keep pagers and ANSI colors from garbling output, and an explicit caller-provided entry still wins.
56
+
57
+ ```text
58
+ const result = await ctx.shell.run(ctx.shell.resolve({ command: 'ls -la' }))
59
+ if (result.timedOut) console.log('timed out after', result.timeoutMs)
21
60
  ```
22
61
 
23
- ## Behavior
62
+ ### Background processes
63
+
64
+ Call `start` to run a command in the background; it returns a handle immediately and no timeout applies. `readOutput()` merges the stream deltas into one consuming read, marking stderr under a `[stderr]` section; `kill()` stops the process group; `done` settles when the process closes and never rejects. Job ids, ownership, polling, and notices belong to the generic `ctx.jobs` runtime, which the tool layer registers the handle with.
65
+
66
+ <a id="adjusting-budgets-at-runtime"></a>
67
+ ### Adjusting budgets at runtime
68
+
69
+ When a settings provider is composed, this executor registers the capability's shared `shell` settings namespace with the composition entry as its base, so a user section in `settings.yaml` layers over it and the next command runs with the new budgets. Values the schema cannot judge — positive and finite numbers, and the `graceMs` timer bound — are refused at the write, leaving the running executor on its last good section; without a provider, the composition entry is what runs.
70
+
71
+ -----
72
+
73
+ <a id="understand-the-implementation"></a>
74
+ ## Understand the implementation
75
+
76
+ <details>
77
+ <summary>Implementation internals — click to expand</summary>
78
+
79
+ This section explains the design of the executor and points at the code that realizes it; the observable behavior is fully covered in [Use this package](#use-this-package).
24
80
 
25
- - **Spawn per call, no shell state** — every call is a fresh non-login `bash -c` with no rc files.
26
- - **The composition entry is a layer, not the last word** — when a settings provider is composed, this executor registers the capability's [`bash` namespace](../shell/README.md) with the entry above as its base, so a user section in `settings.yaml` layers over it and the next command runs with the new budgets. Values the schema cannot judge (positive and finite, the `graceMs` timer bound) are refused at the write, leaving the running executor on its last good section; without a provider, or after one detaches, the composition entry is what runs.
27
- - **Configured budgets over managed groups** — `resolve()` fills `workdir`/`timeoutMs`/`stdoutMaxBytes` from config, and every spawn hands the service explicit byte caps, spill cap, and `graceMs`. The grace must be positive, finite, and no greater than [`MAX_TIMER_DELAY_MS`](../../util/timeout/README.md), so Node can represent it with one timer. Process-group kills, post-exit pipe draining, tail retention, and bounded spill files are [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.md) mechanics. A foreground `ShellExecRequest.stdoutMaxBytes` can raise stdout's capture budget for one trusted caller; stderr and background runs still use `maxOutputBytes`.
28
- - **Timeout and cancel classification** — `run()` fuses its config-clamped timeout with the caller's signal through one deadline; only the executor's own timeout reports `timedOut`, an upstream cancel reports `aborted`, and a self-signaled command reports neither ([timeout-library Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md)).
29
- - **Model-friendly terminal env** — `NO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat` prevents pagers and ANSI color from garbling results. These values merge as ordinary env under the service's credential scrub and `DSH_*` channel rules; an explicit caller entry still wins. See the [stdin/env Agent Note](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-api.md) and [managed environment Agent Note](../../../.agents/notes/implemented/feature/2026-07-10-agent-session-identity-and-log-location.md).
30
- - **Background processes** — `start()` returns a live `ShellProcess` handle immediately with no timeout, and `readOutput()` merges offset-based stdout/stderr reads into one consuming delta, placing stderr under a `[stderr]` marker when present. A running process belongs to the subprocess service, survives executor reloads, and is killed and joined on service disposal. Job ids, ownership, polling, and notices belong to the generic [`ctx.jobs` runtime](../../jobs/jobs/README.md), which the tool layer registers the handle with.
81
+ ### Design concept
31
82
 
83
+ The executor is a Service Provider for the `ctx.shell` seam built on the subprocess capability: it owns everything bash-shaped — command defaulting and caps, deadline fusion and cause classification, the model-friendly terminal environment, and the background read merge — while process-group mechanics (bounded spill-backed output, credential scrub, kill escalation, disposal) belong to the subprocess service. Every call spawns a fresh non-login `bash -c` with no rc files, so commands are deterministic and shell state never leaks between calls.
84
+
85
+ ### Source map
86
+
87
+ | File | Role |
88
+ |---|---|
89
+ | [`src/index.ts`](src/index.ts) | Plugin entry: `LocalBashExecutor`, `Config`, settings-section wiring |
90
+ | [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; contracts are enforced at the owning seam) |
91
+ | `tests/executor.spec.ts` | Exercised behavior: budgets, classification, background handles, ownership |
92
+ | `tests/settings.spec.ts` | Settings layering over the composition entry |
93
+
94
+ ### Main flow
95
+
96
+ A call runs through three steps: `resolve()` fills `workdir`/`timeoutMs`/`stdoutMaxBytes` from config (capping per-call overrides); `run` fuses the config-clamped timeout with the caller's abort signal into one deadline and spawns `['bash', '-c', command]` through `ctx.subprocess` with explicit byte caps and the `graceMs`; the settled subprocess outcome is classified — only the executor's own timeout reports `timedOut`, an upstream cancel reports `aborted`, a self-signaled command reports neither — and projected into a `ShellRunResult` with collected output.
97
+
98
+ ### Invariants and ownership
99
+
100
+ - The `graceMs` budget must be positive, finite, and no greater than `MAX_TIMER_DELAY_MS` so Node can represent it with one timer; invalid values are refused where they are written.
101
+ - Environment layering is fixed: terminal overrides first, then the caller's `env`, then the trusted `dshEnv` snapshot last; the subprocess service scrubs ambient credentials and inherited `DSH_*` names independently.
102
+ - A background process belongs to the subprocess service: it survives an executor-only reload and is killed and joined when the service disposes.
103
+
104
+ </details>
105
+
106
+ -----
107
+
108
+ <a id="further-exploration"></a>
109
+ ## Further Exploration
110
+
111
+ Read these pages when the executor contract is not enough. They move from the seam to the confining sibling and the mechanics underneath.
112
+
113
+ - [shell seam](../shell/README.md) — the executor contract this provider implements, including the request/spec split.
114
+ - [bash-sandbox](../bash-sandbox/README.md) — the confining executor to compose instead when commands need the sandbox capability.
115
+ - [tool-bash](../tool-bash/README.md) — the model-facing `bash` tool over this executor.
116
+ - [Bash executor subsystem](../../../docs/subsystems/shell.md) — request/spec vocabulary, results, and the service contract in full.
117
+ - [subprocess-local](../../subprocess/subprocess-local/README.md) — the process-group mechanics behind this executor.
118
+
119
+ -----
120
+
121
+ <a id="model-experience"></a>
32
122
  ## Model Experience
33
123
 
34
124
  Indirectly, through `dsh-tool-bash`, which renders this executor's bounded stdout/stderr tails, background-process deltas, spill-file paths, and infrastructure failures.
@@ -39,9 +129,22 @@ No direct invalidation; the named consumer owns any request-prefix changes.
39
129
 
40
130
  ## Known Limitations and Deferred Work
41
131
 
42
- - **Unconfined by itself** — this executor always runs commands with the harness process's authority; deployments needing confinement compose [`dsh-bash-sandbox`](../bash-sandbox/README.md), while per-call allow/deny/ask policy belongs on `tools/pre-execute`.
132
+ <a id="known-limitations-and-deferred-work"></a>
133
+
134
+
135
+ These limits define when this executor is a poor fit. They are current package constraints, not a roadmap.
136
+
137
+ - **Unconfined by itself** — commands run with the harness process's authority; deployments needing confinement compose `dsh-bash-sandbox`, while per-call allow/deny/ask policy belongs on the tools' `pre-execute` waterfall.
43
138
  - **No persistent shell or PTY** — every call starts a fresh non-login `bash -c`; cwd-only persistence and interactive terminal sessions remain deferred until a real workflow requires them.
44
- - **POSIX-only** — the `bash` binary is hardcoded, and the underlying service's group semantics are POSIX; Windows is unsupported.
139
+ - **POSIX-only** — the `bash` binary is hardcoded and the underlying service's group semantics are POSIX; Windows is unsupported.
45
140
  - **A background spawn-failure note is single-delivery** — the subprocess service buffers no output for a process that never ran, so the executor injects `spawn failed: …` into exactly one `readOutput()` delta; a reader that discards that delta cannot recover it.
46
141
 
47
- Scrub-heuristic and spill-retention caveats live with [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.md), which owns those mechanics.
142
+ <a id="dev-note"></a>
143
+ ### Dev Note
144
+
145
+ <details>
146
+ <summary>Working context for maintainers — click to expand</summary>
147
+
148
+ None.
149
+
150
+ </details>
package/README.zh.md CHANGED
@@ -1,47 +1,150 @@
1
+ ---
2
+ description: "面向部署方与维护者的默认 POSIX Bash 执行器说明,用于选择、配置或排查基于 shell seam 的非隔离命令执行。"
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-bash-local
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- `@deepseek-ai/dsh-shell` 执行器 seam 的本地 Service Provider,构建在 [`@deepseek-ai/dsh-subprocess`](../../subprocess/subprocess/README.zh.md) 服务之上:`LocalBashExecutor` 每次调用都通过 `ctx.subprocess` 把 `bash -c <command>` 作为受管进程组 spawn,并负责所有 Bash 层职责(命令默认值补全与上限、超时与取消分类、适合模型的终端环境,以及后台读取时面向模型的 stdout/stderr 合并)。以 spill 文件兜底的有界输出、凭据清除、kill 升级和 dispose(资源释放)等进程组机制则由 subprocess 服务负责。
10
+ ## 概述
11
+
12
+ `dsh-bash-local` 是 POSIX 上的默认 Bash 执行器:每条命令都以全新的非登录 `bash -c` 进程运行,不读取 rc 文件,因此调用之间不会残留任何 shell 状态。它会为每条命令应用已配置的预算——工作目录、超时、输出上限——对超时与取消进行分类,并在流溢出时返回有界输出与 spill 文件恢复。命令以 harness 进程自身的权限运行:本执行器不做任何隔离,需要沙箱能力时请组合 `dsh-bash-sandbox`。挂载后,面向模型的 `bash` 工具会与它对接。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
6
24
 
7
- 包根目录导出默认与具名的 `LocalBashExecutor` 插件及其 `Config`。
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
8
27
 
9
- ## 配置
28
+ 当组合需要在 POSIX 上执行 Bash 命令且不需要隔离时,挂载此执行器。它注册为 `ctx.shell`,面向模型的 `bash` 工具会立即基于它工作:agent 调用工具,命令即以全新 `bash -c` 进程按下面的预算运行。
29
+
30
+ ### 最小配置
31
+
32
+ 按你需要的预算加载执行器;每个字段都有默认值,因此最小的组合就是单独一个插件条目。当组合了设置提供方时,用户段会叠加在该条目之上,预算无需重载即可在运行时变更(见[运行时调整预算](#adjusting-budgets-at-runtime))。
10
33
 
11
34
  ```yaml
12
35
  - id: bash
13
36
  name: '@deepseek-ai/dsh-bash-local'
14
37
  config:
15
- cwd: /path/to/workspace # default: process.cwd()
16
- timeoutMs: 120000 # default foreground timeout
17
- maxTimeoutMs: 600000 # cap for per-call overrides
18
- maxOutputBytes: 64000 # per-stream in-memory cap; overflow spills to disk
19
- maxSpillBytes: 67108864 # per-stream full-output spill cap
20
- graceMs: 3000 # kill escalation and post-exit pipe-drain grace
38
+ cwd: /path/to/workspace
39
+ timeoutMs: 120000
40
+ ```
41
+
42
+ | 字段 | 默认值 | 含义 |
43
+ |---|---|---|
44
+ | `cwd` | `process.cwd()` | 命令的默认工作目录 |
45
+ | `timeoutMs` | `120,000` | 默认前台超时,单位为毫秒 |
46
+ | `maxTimeoutMs` | `600,000` | 每次调用超时覆盖值的上限 |
47
+ | `maxOutputBytes` | `64,000` | 每流内存输出上限;溢出后 spill 到临时文件 |
48
+ | `maxSpillBytes` | `67,108,864` | 每流完整输出的 spill 上限 |
49
+ | `graceMs` | `3,000` | 终止升级与退出后管道排空的宽限时间 |
50
+
51
+ 生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-bash-local)是每个受支持字段及其 JSDoc 的穷尽式真源。
52
+
53
+ ### 运行命令
54
+
55
+ 用 `run` 运行命令并从结果读取输出。非零退出、超时或取消都会 resolve 为描述性结果——只有基础设施失败才 reject。每次调用的 `timeoutMs` 覆盖值受配置上限约束,`workdir` 未设置时则回退到配置的默认值;受信任的前台调用方还可以为单次调用提高 stdout 捕获预算,而 stderr 与后台运行仍使用 `maxOutputBytes`。环境默认面向模型:`NO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat` 可防止分页器与 ANSI 颜色破坏输出,调用方显式提供的条目仍然优先。
56
+
57
+ ```text
58
+ const result = await ctx.shell.run(ctx.shell.resolve({ command: 'ls -la' }))
59
+ if (result.timedOut) console.log('timed out after', result.timeoutMs)
21
60
  ```
22
61
 
23
- ## 行为
62
+ ### 后台进程
63
+
64
+ 调用 `start` 即可在后台运行命令;它立即返回句柄,且不应用任何超时。`readOutput()` 把流增量合并为一次消费式读取,并在 `[stderr]` 分段下标记 stderr;`kill()` 停止进程组;`done` 在进程关闭时结算且绝不 reject。job id、所有权、轮询与通知属于通用 `ctx.jobs` 运行时,工具层会把句柄注册进去。
65
+
66
+ <a id="adjusting-budgets-at-runtime"></a>
67
+ ### 运行时调整预算
68
+
69
+ 当组合了设置提供方时,本执行器以组合条目为 base 注册该能力共享的 `shell` 设置命名空间,因此 `settings.yaml` 中的用户段会叠加其上,下一条命令即按新预算运行。schema 无法判定的值——正有限数字与 `graceMs` 的定时器上界——会在写入时被拒绝,运行中的执行器保持它最后一份可用的段;没有提供方时,运行的就是组合条目。
70
+
71
+ -----
72
+
73
+ <a id="understand-the-implementation"></a>
74
+ ## 理解实现
75
+
76
+ <details>
77
+ <summary>实现细节——点击展开</summary>
78
+
79
+ 本节解释执行器的设计并指出实现它们的代码位置;可观察行为已在[使用本包](#use-this-package)中完整说明。
24
80
 
25
- - **每次调用都 spawn,不保留 shell 状态**:每次调用都启动新的非登录 `bash -c`,且不读取 rc 文件。
26
- - **组装条目是一层,而不是最终值**:当组装中存在 settings 提供方时,本执行器以上面的条目为 base 注册该能力的 [`bash` 命名空间](../shell/README.zh.md),因此 `settings.yaml` 中的用户段会叠加其上,下一条命令即按新预算运行。schema 无法判定的值(正有限、`graceMs` 的定时器上界)会在写入时被拒绝,运行中的执行器保持它最后一份可用的段;没有提供方、或提供方脱离之后,运行的就是组装条目。
27
- - **在受管进程组之上应用配置预算**:`resolve()` 从配置补全 `workdir`/`timeoutMs`/`stdoutMaxBytes`,每次 spawn 都向服务传入显式的字节上限、spill 上限与 `graceMs`。该宽限期须为正有限值,且不得大于 [`MAX_TIMER_DELAY_MS`](../../util/timeout/README.zh.md),这样 Node 就能用一个定时器表示它。进程组终止、退出后管道排空、尾部保留与有界 spill 文件是 [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.zh.md) 的机制。前台 `ShellExecRequest.stdoutMaxBytes` 可为某个受信任调用方提高单次 stdout 捕获预算;stderr 和后台运行仍使用 `maxOutputBytes`。
28
- - **超时与取消分类**:`run()` 通过同一个 deadline 把经配置钳位的超时与调用方的信号融合;只有执行器自身的超时报告 `timedOut`,上游取消报告 `aborted`,自身因信号终止的命令两者皆不报告(见[超时库 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.zh.md))。
29
- - **适合模型的终端环境**:`NO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat` 防止分页器与 ANSI 颜色破坏结果。这些值作为普通 env 合并,遵循服务的凭据清除与 `DSH_*` 通道规则;调用方的显式条目依旧优先。详见 [stdin/env Agent Note](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-api.zh.md) 与 [受管环境 Agent Note](../../../.agents/notes/implemented/feature/2026-07-10-agent-session-identity-and-log-location.zh.md)。
30
- - **后台进程**:`start()` 会立即返回活动的 `ShellProcess` 句柄且不应用超时;`readOutput()` 把基于偏移量的 stdout/stderr 读取合并为一条消费式增量,并在存在 stderr 时将其置于 `[stderr]` 标记下。运行中的进程属于 subprocess 服务,可在执行器重载后存活,并在服务 dispose 时被终止且等待退出。job id、所有权、轮询和通知属于通用 [`ctx.jobs` 运行时](../../jobs/jobs/README.zh.md),工具层会在其中注册该句柄。
81
+ ### 设计概念
31
82
 
83
+ 本执行器是基于 subprocess 能力的 `ctx.shell` seam 的 Service Provider:它负责所有 bash 层职责——命令默认化与上限、deadline 融合与原因分类、面向模型的终端环境,以及后台读取合并——而进程组机制(有界 spill 输出、凭据清除、终止升级、dispose(资源释放))属于 subprocess 服务。每次调用都 spawn 全新的非登录 `bash -c`,不读取 rc 文件,因此命令是确定性的,shell 状态绝不会在调用之间泄漏。
84
+
85
+ ### 源码地图
86
+
87
+ | 文件 | 职责 |
88
+ |---|---|
89
+ | [`src/index.ts`](src/index.ts) | 插件入口:`LocalBashExecutor`、`Config`、设置段接线 |
90
+ | [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;约定在所属 seam 处执行) |
91
+ | `tests/executor.spec.ts` | 已演练的行为:预算、分类、后台句柄、归属 |
92
+ | `tests/settings.spec.ts` | 设置段叠加在组合条目之上 |
93
+
94
+ ### 主要流程
95
+
96
+ 一次调用分三步:`resolve()` 从配置填充 `workdir`/`timeoutMs`/`stdoutMaxBytes`(并限制每次调用的覆盖值);`run` 把按配置钳位的超时与调用方的中止信号融合为一个 deadline,再以显式字节上限与 `graceMs` 通过 `ctx.subprocess` spawn `['bash', '-c', command]`;结算的 subprocess 结果被分类——只有执行器自身的超时报告 `timedOut`,上游取消报告 `aborted`,自身因信号终止的命令两者皆不报告——并投影为带收集输出的 `ShellRunResult`。
97
+
98
+ ### 不变式与归属
99
+
100
+ - `graceMs` 预算必须为正有限值且不大于 `MAX_TIMER_DELAY_MS`,这样 Node 就能用一个定时器表示它;无效值在写入处被拒绝。
101
+ - 环境分层固定:先是终端覆盖值,然后是调用方的 `env`,最后才是受信任的 `dshEnv` 快照;subprocess 服务独立清除环境中的凭据与继承的 `DSH_*` 名称。
102
+ - 后台进程属于 subprocess 服务:它能在仅重载执行器后存活,并在服务 dispose 时被终止并 join。
103
+
104
+ </details>
105
+
106
+ -----
107
+
108
+ <a id="further-exploration"></a>
109
+ ## 进一步探索
110
+
111
+ 当执行器约定不够用时阅读以下页面。它们从 seam 进入受限的兄弟包及其底层机制。
112
+
113
+ - [shell seam](../shell/README.zh.md) —— 本提供方实现的执行器约定,包括请求/spec 拆分。
114
+ - [bash-sandbox](../bash-sandbox/README.zh.md) —— 需要沙箱能力时替换组合的受限执行器。
115
+ - [tool-bash](../tool-bash/README.zh.md) —— 基于本执行器的面向模型 `bash` 工具。
116
+ - [Bash 执行器子系统](../../../docs/subsystems/shell.zh.md) —— 请求/spec 词汇、结果与完整的服务约定。
117
+ - [subprocess-local](../../subprocess/subprocess-local/README.zh.md) —— 本执行器背后的进程组机制。
118
+
119
+ -----
120
+
121
+ <a id="model-experience"></a>
32
122
  ## 模型体验
33
123
 
34
- 通过 `dsh-tool-bash` 间接影响;该工具会渲染此执行器有界的 stdout/stderr 尾部、后台进程增量、spill 文件路径与基础设施失败。
124
+ 通过 `dsh-tool-bash` 间接影响;该工具会渲染本执行器有界的 stdout/stderr 尾部、后台进程增量、spill 文件路径与基础设施失败。
35
125
 
36
126
  #### KV Cache 影响
37
127
 
38
- 不会直接导致 KV Cache 失效;请求前缀变更由具名消费方负责。
128
+ 不会直接导致 KV Cache 失效;请求前缀的任何变更由具名消费方负责。
129
+
130
+ ## 已知限制与延期工作
131
+
132
+ <a id="known-limitations-and-deferred-work"></a>
133
+
134
+
135
+ 这些限制说明本执行器何时不合适。它们是当前包约束,不是路线图。
136
+
137
+ - **自身不提供隔离**——命令以 harness 进程的权限运行;需要隔离的部署组合 `dsh-bash-sandbox`,每次调用的 allow/deny/ask 策略则属于工具的 `pre-execute` waterfall。
138
+ - **没有持久 shell 或 PTY**——每次调用都启动全新的非登录 `bash -c`;仅持久化 cwd 与交互式终端会话均继续延期,直到真实工作流需要它们。
139
+ - **仅支持 POSIX**——`bash` 二进制已硬编码,底层服务的进程组语义也是 POSIX 的;不支持 Windows。
140
+ - **后台 spawn 失败提示只交付一次**——subprocess 服务不会为从未真正运行的进程缓冲任何输出,因此执行器把 `spawn failed: …` 注入恰好一个 `readOutput()` 增量;丢弃了该增量的读取方无法再恢复它。
141
+
142
+ <a id="dev-note"></a>
143
+ ### 开发备注
39
144
 
40
- ## 已知限制与暂缓事项
145
+ <details>
146
+ <summary>维护者的工作上下文——点击展开</summary>
41
147
 
42
- - **自身不提供隔离**:此执行器始终以 harness 进程的权限运行命令;需要隔离的部署可以组合 [`dsh-bash-sandbox`](../bash-sandbox/README.zh.md),每次调用的 allow/deny/ask 策略则属于 `tools/pre-execute`。
43
- - **没有持久 shell 或 PTY**:每次调用都启动新的非登录 `bash -c`;仅持久化 cwd 与交互式终端会话均继续暂缓,直到真实工作流需要它们。
44
- - **仅支持 POSIX**:`bash` 二进制已硬编码,底层服务的进程组语义也是 POSIX 的;不支持 Windows。
45
- - **后台 spawn 失败提示只交付一次**:subprocess 服务不会为从未真正运行的进程缓冲任何输出,因此执行器把 `spawn failed: …` 注入恰好一个 `readOutput()` 增量;丢弃了该增量的读取方无法再恢复它。
148
+ None.
46
149
 
47
- 凭据清除启发式规则与 spill 保留的注意事项随 [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.zh.md) 记录;这些机制归它所有。
150
+ </details>
package/lib/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
2
  import { SHELL_SETTINGS_NAMESPACE, ShellExecutor } from "@deepseek-ai/dsh-shell";
3
- import { installSettingsSection } from "@deepseek-ai/dsh-settings";
4
3
  import { MAX_TIMER_DELAY_MS, clampTimeout, deadline, timeoutOf } from "@deepseek-ai/dsh-timeout";
5
4
  //#region lib/types/index.js
6
5
  /**
@@ -145,12 +144,14 @@ var LocalBashExecutor = class LocalBashExecutor extends ShellExecutor {
145
144
  const entry = config;
146
145
  assertServiceableBashConfig(entry);
147
146
  this.source = () => entry;
148
- installSettingsSection(ctx, SHELL_SETTINGS_NAMESPACE, LocalBashExecutor.Config, entry, {
149
- validate: assertServiceableBashConfig,
150
- setSource: (current) => {
151
- this.source = current;
152
- },
153
- onChange: () => {}
147
+ ctx.inject(["settings"], (settingsCtx) => {
148
+ settingsCtx.settings.installSection(ctx, SHELL_SETTINGS_NAMESPACE, LocalBashExecutor.Config, entry, {
149
+ validate: assertServiceableBashConfig,
150
+ setSource: (current) => {
151
+ this.source = current;
152
+ },
153
+ onChange: () => {}
154
+ });
154
155
  });
155
156
  }
156
157
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-bash-local",
3
3
  "description": "Local-subprocess implementation of the DeepSeek Harness bash executor seam",
4
- "version": "0.1.1-rc.2",
4
+ "version": "0.1.2-alpha.3",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,23 +32,23 @@
32
32
  ],
33
33
  "license": "MIT",
34
34
  "peerDependencies": {
35
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
36
- "@deepseek-ai/dsh-shell": "^0.1.1-rc.2",
37
- "@deepseek-ai/dsh-subprocess": "^0.1.1-rc.2",
38
- "@deepseek-ai/dsh-timeout": "^0.1.1-rc.2",
39
- "@deepseek-ai/cordis": "^4.0.1",
40
- "@deepseek-ai/dsh-settings": "^0.1.1-rc.2"
35
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.3",
36
+ "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.3",
37
+ "@deepseek-ai/dsh-shell": "^0.1.2-alpha.3",
38
+ "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.3",
39
+ "@deepseek-ai/dsh-settings": "^0.1.2-alpha.3",
40
+ "@deepseek-ai/cordis": "^4.0.2"
41
41
  },
42
42
  "dependencies": {
43
- "@deepseek-ai/schemastery": "^3.18.1"
43
+ "@deepseek-ai/schemastery": "^3.18.2"
44
44
  },
45
45
  "devDependencies": {
46
- "@deepseek-ai/dsh-shell": "^0.1.1-rc.2",
47
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
48
- "@deepseek-ai/dsh-subprocess": "^0.1.1-rc.2",
49
- "@deepseek-ai/dsh-subprocess-local": "^0.1.1-rc.2",
50
- "@deepseek-ai/dsh-timeout": "^0.1.1-rc.2",
51
- "@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
52
- "@deepseek-ai/cordis": "^4.0.1"
46
+ "@deepseek-ai/dsh-shell": "^0.1.2-alpha.3",
47
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.3",
48
+ "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.3",
49
+ "@deepseek-ai/dsh-subprocess-local": "^0.1.2-alpha.3",
50
+ "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.3",
51
+ "@deepseek-ai/cordis": "^4.0.2",
52
+ "@deepseek-ai/dsh-settings": "^0.1.2-alpha.3"
53
53
  }
54
54
  }