@deepseek-ai/dsh-bash-local 0.1.5-rc.1 → 0.1.6-alpha.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.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: 462054d435433067355a91103aa0d606cea6521b
6
- README.zh.md: 6127c6265079d522b5a58d8b1fd019c1bd909262
5
+ README.md: 2cd69993b454a462bcbfa43175ff7dc7dba72e01
6
+ README.zh.md: 81436511992d93a2bb81c4f3e242dd2046ec11e8
package/README.md CHANGED
@@ -61,7 +61,7 @@ if (result.timedOut) console.log('timed out after', result.timeoutMs)
61
61
 
62
62
  ### Background processes
63
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()` terminates the provider-managed range; `done` settles when the direct command 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.
64
+ Await `start` to run a command in the background; it resolves with the prepared process handle and no execution timeout applies. Cancellation or preparation failure rejects before a handle is published. `readOutput()` merges the stream deltas into one consuming read, marking stderr under a `[stderr]` section; `kill()` terminates the provider-managed range; `done` settles when the direct command 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
65
 
66
66
  <a id="adjusting-budgets-at-runtime"></a>
67
67
  ### Adjusting budgets at runtime
@@ -95,6 +95,8 @@ The executor is a Service Provider for the `ctx.shell` seam built on the subproc
95
95
 
96
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
97
 
98
+ The foreground deadline starts before argv preparation and retains the same signal and remaining budget through execution. Preparation timeout returns empty output, `timedOut: true`, and null `exitCode` and `signal`; caller cancellation before process publication still rejects. Late preparation success or failure cannot trigger a spawn.
99
+
98
100
  ### Invariants and ownership
99
101
 
100
102
  - 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.
package/README.zh.md CHANGED
@@ -25,7 +25,7 @@ kind: "package-reference"
25
25
  <a id="use-this-package"></a>
26
26
  ## 使用本包
27
27
 
28
- 当组合需要在 POSIX 上执行 Bash 命令且不需要隔离时,挂载此执行器。它注册为 `ctx.shell`,面向模型的 `bash` 工具会立即基于它工作:agent 调用工具,命令即以全新 `bash -c` 进程按下面的预算运行。
28
+ 当组合需要在 POSIX 上执行 Bash 命令且不需要隔离时,挂载此执行器。它注册为 `ctx.shell`,面向模型的 `bash` 工具会立即基于它工作:agent(智能体)调用工具,命令即以全新 `bash -c` 进程按下面的预算运行。
29
29
 
30
30
  ### 最小配置
31
31
 
@@ -61,7 +61,7 @@ if (result.timedOut) console.log('timed out after', result.timeoutMs)
61
61
 
62
62
  ### 后台进程
63
63
 
64
- 调用 `start` 即可在后台运行命令;它立即返回句柄,且不应用任何超时。`readOutput()` 把流增量合并为一次消费式读取,并在 `[stderr]` 分段下标记 stderr;`kill()` 终止提供方管理的 range;`done` 在直接命令关闭时结算且绝不 reject。job id、所有权、轮询与通知属于通用 `ctx.jobs` 运行时,工具层会把句柄注册进去。
64
+ 等待 `start` 即可在后台运行命令;它完成准备后返回进程句柄,且不应用执行超时。取消或准备失败会在发布句柄前拒绝调用。`readOutput()` 把流增量合并为一次消费式读取,并在 `[stderr]` 分段下标记 stderr;`kill()` 终止提供方管理的 range;`done` 在直接命令关闭时结算且绝不 reject。job id、所有权、轮询与通知属于通用 `ctx.jobs` 运行时,工具层会把句柄注册进去。
65
65
 
66
66
  <a id="adjusting-budgets-at-runtime"></a>
67
67
  ### 运行时调整预算
@@ -87,7 +87,7 @@ if (result.timedOut) console.log('timed out after', result.timeoutMs)
87
87
  | 文件 | 职责 |
88
88
  |---|---|
89
89
  | [`src/index.ts`](src/index.ts) | 插件入口:`LocalBashExecutor`、`Config`、设置段接线 |
90
- | — | 不发布运行时不变式伴生入口;约定在所属 seam 处执行。 |
90
+ | — | 不发布运行时不变式伴生入口;除由所属 seam 强制执行的约定外,本包不公开独立的事件序列或可变数据关系。 |
91
91
  | `tests/executor.spec.ts` | 已演练的行为:预算、分类、后台句柄、归属 |
92
92
  | `tests/settings.spec.ts` | 设置段叠加在组合条目之上 |
93
93
 
@@ -95,11 +95,13 @@ if (result.timedOut) console.log('timed out after', result.timeoutMs)
95
95
 
96
96
  一次调用分三步:`resolve()` 从配置填充 `workdir`/`timeoutMs`/`stdoutMaxBytes`(并限制每次调用的覆盖值);`run` 把按配置钳位的超时与调用方的中止信号融合为一个 deadline,再以显式字节上限与 `graceMs` 通过 `ctx.subprocess` spawn `['bash', '-c', command]`;结算的 subprocess 结果被分类——只有执行器自身的超时报告 `timedOut`,上游取消报告 `aborted`,自身因信号终止的命令两者皆不报告——并投影为带收集输出的 `ShellRunResult`。
97
97
 
98
+ 前台 deadline 从 argv 准备开始,并在准备与执行之间保持同一信号和剩余预算。准备阶段超时返回空输出、`timedOut: true`,且 `exitCode` 和 `signal` 均为 `null`;调用方在发布进程前取消仍会拒绝调用。准备晚到的成功或失败不会触发 spawn。
99
+
98
100
  ### 不变式与归属
99
101
 
100
102
  - `graceMs` 预算必须为正有限值且不大于 `MAX_TIMER_DELAY_MS`,这样 Node 就能用一个定时器表示它;无效值在写入处被拒绝。
101
103
  - 环境分层固定:先是终端覆盖值,然后是调用方的 `env`,最后才是受信任的 `dshEnv` 快照;subprocess 服务独立清除环境中的凭据与继承的 `DSH_*` 名称。
102
- - 后台进程属于 subprocess 服务:它能在仅重载执行器后存活,并在服务 dispose 时被终止并 join。
104
+ - 后台进程属于 subprocess 服务:它能在仅重载执行器后存活,并在服务 dispose 时被终止且等待退出。
103
105
 
104
106
  </details>
105
107
 
@@ -108,10 +110,10 @@ if (result.timedOut) console.log('timed out after', result.timeoutMs)
108
110
  <a id="further-exploration"></a>
109
111
  ## 进一步探索
110
112
 
111
- 当执行器约定不够用时阅读以下页面。它们从 seam 进入受限的兄弟包及其底层机制。
113
+ 当执行器约定不够用时阅读以下页面。这些页面从 seam 讲到提供隔离的同级包及其底层机制。
112
114
 
113
115
  - [shell seam](../shell/README.zh.md) —— 本提供方实现的执行器约定,包括请求/spec 拆分。
114
- - [bash-sandbox](../bash-sandbox/README.zh.md) —— 需要沙箱能力时替换组合的受限执行器。
116
+ - [bash-sandbox](../bash-sandbox/README.zh.md) —— 需要沙箱能力时,应改为组合此隔离执行器。
115
117
  - [tool-bash](../tool-bash/README.zh.md) —— 基于本执行器的面向模型 `bash` 工具。
116
118
  - [Bash 执行器子系统](../../../docs/subsystems/shell.zh.md) —— 请求/spec 词汇、结果与完整的服务约定。
117
119
  - [subprocess-local](../../subprocess/subprocess-local/README.zh.md) —— 本执行器背后的 managed-range 机制。
@@ -134,10 +136,10 @@ if (result.timedOut) console.log('timed out after', result.timeoutMs)
134
136
 
135
137
  这些限制说明本执行器何时不合适。它们是当前包约束,不是路线图。
136
138
 
137
- - **自身不提供隔离**——命令以 harness 进程的权限运行;需要隔离的部署组合 `dsh-bash-sandbox`,每次调用的 allow/deny/ask 策略则属于工具的 `pre-execute` waterfall。
139
+ - **自身不提供隔离**——命令以 harness 进程的权限运行;需要隔离的部署组合 `dsh-bash-sandbox`,每次调用的 allow/deny/ask 策略则属于工具的 `pre-execute` waterfall(瀑布式事件)。
138
140
  - **没有持久 shell 或 PTY**——每次调用都启动全新的非登录 `bash -c`;仅持久化 cwd 与交互式终端会话均继续延期,直到真实工作流需要它们。
139
141
  - **仅支持 POSIX**——`bash` 二进制已硬编码,底层服务的进程组语义也是 POSIX 的;不支持 Windows。
140
- - **后台 provider failure 提示只交付一次**——`SubprocessHandle.done` 可能在 target 开始执行前或后 reject,因此执行器把不声明失败阶段的 `subprocess failed before reporting an outcome: …` 注入恰好一个 `readOutput()` 增量;丢弃了该增量的读取方无法再恢复它。
142
+ - **后台提供方失败提示只交付一次**——`SubprocessHandle.done` 可能在目标命令开始执行前或后被拒绝,因此执行器把不声明失败阶段的 `subprocess failed before reporting an outcome: …` 注入恰好一个 `readOutput()` 增量;丢弃了该增量的读取方无法再恢复它。
141
143
 
142
144
  <a id="dev-note"></a>
143
145
  ### 开发备注
@@ -145,6 +147,6 @@ if (result.timedOut) console.log('timed out after', result.timeoutMs)
145
147
  <details>
146
148
  <summary>维护者的工作上下文——点击展开</summary>
147
149
 
148
- None.
150
+ 无。
149
151
 
150
152
  </details>
package/lib/index.js CHANGED
@@ -212,21 +212,21 @@ var LocalBashExecutor = class LocalBashExecutor extends ShellExecutor {
212
212
  };
213
213
  }
214
214
  async run(spec) {
215
- return this.runArgv(spec, [
215
+ return (await this.runArgv(spec, [
216
216
  "bash",
217
217
  "-c",
218
218
  spec.command
219
- ]);
219
+ ])).result;
220
220
  }
221
221
  /**
222
222
  * Run an explicit argv with the foreground lifecycle, environment, output,
223
223
  * timeout, and cancellation semantics of this executor. Subclasses use this
224
224
  * after replacing the public command's shell argv at an execution boundary.
225
225
  * @param spec - resolved execution settings and caller-owned command metadata.
226
- * @param argv - exact executable and arguments to hand to `ctx.subprocess`.
227
- * @returns the settled foreground result with collected output and cause facts.
226
+ * @param argvOrPrepare - exact argv, or preparation cancelled by the same deadline as execution.
227
+ * @returns the foreground result and whether argv reached the subprocess provider.
228
228
  */
229
- async runArgv(spec, argv) {
229
+ async runArgv(spec, argvOrPrepare) {
230
230
  const env_1 = {
231
231
  stack: [],
232
232
  error: void 0,
@@ -234,18 +234,58 @@ var LocalBashExecutor = class LocalBashExecutor extends ShellExecutor {
234
234
  };
235
235
  try {
236
236
  const d = __addDisposableResource(env_1, deadline(spec.signal, spec.timeoutMs, "BASH_TIMEOUT"), false);
237
+ let argv;
238
+ if (typeof argvOrPrepare === "function") {
239
+ const cancelled = Promise.withResolvers();
240
+ const abort = () => {
241
+ cancelled.reject(d.signal.reason);
242
+ };
243
+ d.signal.addEventListener("abort", abort, { once: true });
244
+ try {
245
+ argv = await Promise.race([Promise.resolve().then(() => {
246
+ d.signal.throwIfAborted();
247
+ return argvOrPrepare(d.signal);
248
+ }), cancelled.promise]);
249
+ d.signal.throwIfAborted();
250
+ } catch (error) {
251
+ if (timeoutOf(d.signal, "BASH_TIMEOUT") === void 0) throw error;
252
+ return {
253
+ spawnRequested: false,
254
+ result: {
255
+ exitCode: null,
256
+ signal: null,
257
+ timedOut: true,
258
+ aborted: false,
259
+ timeoutMs: spec.timeoutMs,
260
+ stdout: {
261
+ text: "",
262
+ truncated: false
263
+ },
264
+ stderr: {
265
+ text: "",
266
+ truncated: false
267
+ }
268
+ }
269
+ };
270
+ } finally {
271
+ d.signal.removeEventListener("abort", abort);
272
+ }
273
+ } else argv = argvOrPrepare;
237
274
  const handle = this.ctx.subprocess.spawn(this.spawnSpec(spec, argv, spec.stdoutMaxBytes, d.signal));
238
275
  const outcome = await handle.done;
239
276
  const collected = LocalBashExecutor.collected(handle);
240
277
  const timedOut = timeoutOf(d.signal, "BASH_TIMEOUT") !== void 0;
241
278
  const aborted = d.signal.aborted && !timedOut;
242
279
  return {
243
- ...outcome,
244
- timedOut,
245
- aborted,
246
- timeoutMs: spec.timeoutMs,
247
- stdout: finalOutput(collected.stdout),
248
- stderr: finalOutput(collected.stderr)
280
+ spawnRequested: true,
281
+ result: {
282
+ ...outcome,
283
+ timedOut,
284
+ aborted,
285
+ timeoutMs: spec.timeoutMs,
286
+ stdout: finalOutput(collected.stdout),
287
+ stderr: finalOutput(collected.stderr)
288
+ }
249
289
  };
250
290
  } catch (e_1) {
251
291
  env_1.error = e_1;
@@ -254,12 +294,12 @@ var LocalBashExecutor = class LocalBashExecutor extends ShellExecutor {
254
294
  __disposeResources(env_1);
255
295
  }
256
296
  }
257
- start(spec) {
258
- return this.startArgv(spec, [
297
+ async start(spec) {
298
+ return Promise.resolve(this.startArgv(spec, [
259
299
  "bash",
260
300
  "-c",
261
301
  spec.command
262
- ]);
302
+ ]));
263
303
  }
264
304
  /**
265
305
  * Start an explicit argv with the background lifecycle, environment, output,
@@ -271,6 +311,7 @@ var LocalBashExecutor = class LocalBashExecutor extends ShellExecutor {
271
311
  * @returns the live background handle; provider rejection settles it as killed.
272
312
  */
273
313
  startArgv(spec, argv) {
314
+ spec.signal?.throwIfAborted();
274
315
  const running = this.ctx.subprocess.spawn(this.spawnSpec(spec, argv, this.config.maxOutputBytes, spec.signal));
275
316
  const collected = LocalBashExecutor.collected(running);
276
317
  let providerFailureNote;
@@ -84,11 +84,14 @@ export declare class LocalBashExecutor extends ShellExecutor {
84
84
  * timeout, and cancellation semantics of this executor. Subclasses use this
85
85
  * after replacing the public command's shell argv at an execution boundary.
86
86
  * @param spec - resolved execution settings and caller-owned command metadata.
87
- * @param argv - exact executable and arguments to hand to `ctx.subprocess`.
88
- * @returns the settled foreground result with collected output and cause facts.
87
+ * @param argvOrPrepare - exact argv, or preparation cancelled by the same deadline as execution.
88
+ * @returns the foreground result and whether argv reached the subprocess provider.
89
89
  */
90
- protected runArgv(spec: ShellExecSpec, argv: readonly string[]): Promise<ShellRunResult>;
91
- start(spec: ShellExecSpec): ShellProcess;
90
+ protected runArgv(spec: ShellExecSpec, argvOrPrepare: readonly string[] | ((signal: AbortSignal) => Promise<readonly string[]>)): Promise<{
91
+ result: ShellRunResult;
92
+ spawnRequested: boolean;
93
+ }>;
94
+ start(spec: ShellExecSpec): Promise<ShellProcess>;
92
95
  /**
93
96
  * Start an explicit argv with the background lifecycle, environment, output,
94
97
  * cancellation, and managed-range ownership semantics of this executor.
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.5-rc.1",
4
+ "version": "0.1.6-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,21 +27,21 @@
27
27
  ],
28
28
  "license": "MIT",
29
29
  "peerDependencies": {
30
- "@deepseek-ai/dsh-shell": "^0.1.5-rc.1",
31
- "@deepseek-ai/dsh-subprocess": "^0.1.5-rc.1",
32
- "@deepseek-ai/dsh-timeout": "^0.1.5-rc.1",
30
+ "@deepseek-ai/dsh-shell": "^0.1.6-alpha.1",
31
+ "@deepseek-ai/dsh-timeout": "^0.1.6-alpha.1",
32
+ "@deepseek-ai/dsh-subprocess": "^0.1.6-alpha.1",
33
33
  "@deepseek-ai/cordis": "^4.0.2",
34
- "@deepseek-ai/dsh-settings": "^0.1.5-rc.1"
34
+ "@deepseek-ai/dsh-settings": "^0.1.6-alpha.1"
35
35
  },
36
36
  "dependencies": {
37
37
  "@deepseek-ai/schemastery": "^3.18.2"
38
38
  },
39
39
  "devDependencies": {
40
- "@deepseek-ai/dsh-shell": "^0.1.5-rc.1",
41
- "@deepseek-ai/dsh-subprocess": "^0.1.5-rc.1",
42
- "@deepseek-ai/dsh-subprocess-local": "^0.1.5-rc.1",
43
- "@deepseek-ai/dsh-timeout": "^0.1.5-rc.1",
40
+ "@deepseek-ai/dsh-shell": "^0.1.6-alpha.1",
41
+ "@deepseek-ai/dsh-subprocess": "^0.1.6-alpha.1",
42
+ "@deepseek-ai/dsh-subprocess-local": "^0.1.6-alpha.1",
43
+ "@deepseek-ai/dsh-timeout": "^0.1.6-alpha.1",
44
44
  "@deepseek-ai/cordis": "^4.0.2",
45
- "@deepseek-ai/dsh-settings": "^0.1.5-rc.1"
45
+ "@deepseek-ai/dsh-settings": "^0.1.6-alpha.1"
46
46
  }
47
47
  }