@stardustlc/dsh-docker 0.3.1 → 0.3.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.en.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  # dsh-docker
4
4
 
5
- > **Your agent can manage containers now**: five tools covering container listing, logs, inspection, in-container exec, and lifecycle management.
5
+ > **Your agent can manage containers now**: seven tools covering containers, images, logs, inspection, in-container exec, lifecycle management, and health checks.
6
6
 
7
- DSH (DeepSeek Harness) container-management plugin: runs the docker CLI through the official subprocess service with shell-free argv arrays, six tools including image listing, an approval gate on `docker_exec`, and **zero runtime dependencies**.
7
+ DSH (DeepSeek Harness) container-management plugin: runs the docker CLI through the official subprocess service with shell-free argv arrays, approval gates on `docker_exec` and destructive lifecycle actions, and **zero runtime dependencies**.
8
8
 
9
9
  ![npm version](https://img.shields.io/npm/v/@stardustlc/dsh-docker?label=npm&color=blue) ![npm downloads](https://img.shields.io/npm/dm/@stardustlc/dsh-docker) ![license](https://img.shields.io/npm/l/@stardustlc/dsh-docker) ![stars](https://img.shields.io/github/stars/STARDUSTLC666/dsh-docker?style=social)
10
10
 
@@ -12,7 +12,9 @@ DSH (DeepSeek Harness) container-management plugin: runs the docker CLI through
12
12
 
13
13
  ## Compatibility
14
14
 
15
- Verified against source-run `@deepseek-ai/dsh@0.1.2-alpha.4` on 2026-09-02. Built for the cordis patch-bundle plugin model (`cordis.patch.yml` + `dsh.bundle.patch`). No runtime imports of `@deepseek-ai/*` internals.
15
+ Verified with official `@deepseek-ai/dsh@0.1.5-rc.1` and Node `24.16.0` on 2026-09-11: all 18 components load alongside Modlens, with passing tool-schema, skill-registration and offline read-only invocation checks. Uses the `cordis.patch.yml` + `dsh.bundle.patch` bundle model. Node requirements match this Harness release: 22.19 or later within 22.x, or 24 or later. Live external-service workflows require separate configuration and validation.
16
+
17
+ 2026-09-13 fix: retain the service receiver when calling `subprocess.spawn`, preventing failures caused by passing the method as an unbound callback. Verified with official `0.1.5-rc.1` and `0.1.5-rc.2` on Node `24.16.0`. `docker_ps` passes through the real host subprocess service with a Node child producing fixed Docker output. A live Docker daemon was not used.
16
18
 
17
19
  ## Installation
18
20
 
@@ -41,6 +43,7 @@ Then restart the web service. To clean up fully, also remove the plugin entry fr
41
43
  dockerPath: docker # optional; or use the DSH_DOCKER_PATH env var
42
44
  timeoutMs: 60000 # per-operation timeout (default 60s, 5s - 10min)
43
45
  # execApproval: false # disable the docker_exec approval gate (default true)
46
+ # manageApproval: false # disable approval for stop/restart/rm (default true; not recommended)
44
47
  ```
45
48
 
46
49
  ## Tools
@@ -52,7 +55,8 @@ Then restart the web service. To clean up fully, also remove the plugin entry fr
52
55
  | `docker_logs` | Tail container logs (line clamp, short follow) | — |
53
56
  | `docker_inspect` | Container details (image/state/ports) | — |
54
57
  | `docker_exec` | Run a command inside a container | Approval gate + container-name validation |
55
- | `docker_manage` | start / stop / restart / rm | Destructive actions clearly labeled |
58
+ | `docker_manage` | start / stop / restart / rm | Approval gate for stop/restart/rm |
59
+ | `docker_health` | Check Docker daemon and safety settings | — |
56
60
 
57
61
  ### Examples
58
62
 
@@ -69,7 +73,7 @@ docker_manage { container: web, action: restart }
69
73
  ## Safety
70
74
 
71
75
  - **No shell**: every argument is its own argv element — command injection is impossible
72
- - **Approval gate**: docker_exec asks first (mirroring dsh-email / dsh-sql); headless environments without an approval channel are denied
76
+ - **Approval gates**: docker_exec and docker_manage stop/restart/rm ask first; headless environments without an approval channel are denied
73
77
  - **Container-name validation**: only `[A-Za-z0-9][A-Za-z0-9_.:-]*` accepted — no argument injection
74
78
  - **Timeout clamps**: 5s - 10min per operation; follow mode capped at an extra 30s
75
79
  - **Log clamping**: tail 1-2000 lines
@@ -78,7 +82,7 @@ docker_manage { container: web, action: restart }
78
82
 
79
83
  ```bash
80
84
  pnpm install
81
- pnpm test # build + 24 tests
85
+ pnpm test # build + 35 tests
82
86
  ```
83
87
 
84
88
  ## License
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  # dsh-docker
4
4
 
5
- > **你的 agent 会管容器了**:六个工具覆盖容器/镜像列表、日志、详情、容器内执行与生命周期管理。
5
+ > **你的 agent 会管容器了**:七个工具覆盖容器/镜像列表、日志、详情、容器内执行、生命周期管理与健康检查。
6
6
 
7
- DSH(DeepSeek Harness)容器管理插件:走官方 subprocess 服务跑 docker CLI,argv 数组无 shell 注入,`docker_exec` 默认审批门,**零运行时依赖**。
7
+ DSH(DeepSeek Harness)容器管理插件:走官方 subprocess 服务跑 docker CLI,argv 数组无 shell 注入,`docker_exec` 与破坏性生命周期操作默认审批门,**零运行时依赖**。
8
8
 
9
9
  ![npm version](https://img.shields.io/npm/v/@stardustlc/dsh-docker?label=npm&color=blue) ![npm downloads](https://img.shields.io/npm/dm/@stardustlc/dsh-docker) ![license](https://img.shields.io/npm/l/@stardustlc/dsh-docker) ![stars](https://img.shields.io/github/stars/STARDUSTLC666/dsh-docker?style=social)
10
10
 
@@ -12,7 +12,9 @@ DSH(DeepSeek Harness)容器管理插件:走官方 subprocess 服务跑 doc
12
12
 
13
13
  ## 兼容性
14
14
 
15
- 在 `@deepseek-ai/dsh@0.1.2-alpha.4` 源码模式下验证(2026-09-02)。遵循 cordis 组合包补丁模型(`cordis.patch.yml` + `dsh.bundle.patch`),运行时不 import 任何 `@deepseek-ai/*` 内部模块。
15
+ 已在官方 `@deepseek-ai/dsh@0.1.5-rc.1`、Node `24.16.0` 上验证(2026-09-11):18 个组件与 Modlens 同载,工具 schema、技能注册及离线只读调用检查通过。采用 `cordis.patch.yml` + `dsh.bundle.patch` 组合包模型。Node 要求与该版本 Harness 一致:22.19 及以上的 22.x,或 24 及以上。外部服务的实际业务操作需按各组件配置单独验证。
16
+
17
+ 2026-09-13 修复:保留宿主 `subprocess.spawn` 的服务对象,避免方法独立传递后因 `this` 丢失而报错。已在官方 `0.1.5-rc.1` 和 `0.1.5-rc.2`、Node `24.16.0` 上验证。`docker_ps` 已通过真实宿主进程服务验证;Docker 输出由 Node 子进程生成的固定测试数据提供,未连接真实 Docker daemon。
16
18
 
17
19
  ## 安装
18
20
 
@@ -41,6 +43,7 @@ dsh plugin --profile web remove @stardustlc/dsh-docker
41
43
  dockerPath: docker # 可选;也可用环境变量 DSH_DOCKER_PATH
42
44
  timeoutMs: 60000 # 单次操作超时(默认 60 秒,5 秒 - 10 分钟)
43
45
  # execApproval: false # 关闭 docker_exec 审批门(默认 true)
46
+ # manageApproval: false # 关闭 stop/restart/rm 审批门(默认 true,不推荐)
44
47
  ```
45
48
 
46
49
  ## 工具一览
@@ -52,7 +55,8 @@ dsh plugin --profile web remove @stardustlc/dsh-docker
52
55
  | `docker_logs` | 查看日志尾部(行数钳制,可短时 follow)| — |
53
56
  | `docker_inspect` | 容器详情(镜像/状态/端口)| — |
54
57
  | `docker_exec` | 容器内执行命令 | 审批门 + 容器名白名单校验 |
55
- | `docker_manage` | start / stop / restart / rm | 明确提示破坏性 |
58
+ | `docker_manage` | start / stop / restart / rm | stop/restart/rm 审批门 |
59
+ | `docker_health` | Docker daemon 与安全配置自检 | — |
56
60
 
57
61
  ### 示例
58
62
 
@@ -69,7 +73,7 @@ docker_manage { container: web, action: restart }
69
73
  ## 安全设计
70
74
 
71
75
  - **无 shell**:全部参数独立 argv 数组,命令注入不可能
72
- - **审批门**:docker_exec 默认弹审批(对齐 dsh-email / dsh-sql),headless 无审批通道时拒绝
76
+ - **审批门**:docker_exec 与 docker_manage 的 stop/restart/rm 默认弹审批;headless 无审批通道时拒绝
73
77
  - **容器名校验**:只允许 `[A-Za-z0-9][A-Za-z0-9_.:-]*`,杜绝参数注入
74
78
  - **超时钳制**:单次操作 5 秒 - 10 分钟;follow 模式额外限 30 秒
75
79
  - **日志钳制**:tail 1-2000 行
@@ -78,7 +82,7 @@ docker_manage { container: web, action: restart }
78
82
 
79
83
  ```bash
80
84
  pnpm install
81
- pnpm test # 构建 + 24 个测试
85
+ pnpm test # 构建 + 35 个测试
82
86
  ```
83
87
 
84
88
  ## License
package/cordis.patch.yml CHANGED
@@ -9,6 +9,7 @@
9
9
  # # dockerPath: C:\Program Files\Docker\Docker\resources\bin\docker.exe
10
10
  # timeoutMs: 60000 # 单次操作超时(默认 60 秒,5 秒 - 10 分钟)
11
11
  # # execApproval: false # 关闭 docker_exec 的审批门(默认 true)
12
+ # # manageApproval: false # 关闭 stop/restart/rm 审批门(默认 true,不推荐)
12
13
  #
13
14
  - insert:
14
15
  - id: docker
package/lib/config.d.ts CHANGED
@@ -9,6 +9,7 @@ export interface DockerConfig {
9
9
  timeoutMs?: number;
10
10
  graceMs?: number;
11
11
  execApproval?: boolean;
12
+ manageApproval?: boolean;
12
13
  }
13
14
  /** 解析后的配置。 */
14
15
  export interface ResolvedDockerConfig {
@@ -16,6 +17,7 @@ export interface ResolvedDockerConfig {
16
17
  timeoutMs: number;
17
18
  graceMs: number;
18
19
  execApproval: boolean;
20
+ manageApproval: boolean;
19
21
  }
20
22
  /**
21
23
  * 解析并校验配置。
package/lib/config.js CHANGED
@@ -22,7 +22,8 @@ export function resolveConfig(config, env = process.env) {
22
22
  graceMs = Math.min(120000, Math.max(1000, Math.round(cfg.graceMs)));
23
23
  }
24
24
  const execApproval = cfg.execApproval !== false;
25
- return { dockerPath, timeoutMs, graceMs, execApproval };
25
+ const manageApproval = cfg.manageApproval !== false;
26
+ return { dockerPath, timeoutMs, graceMs, execApproval, manageApproval };
26
27
  }
27
28
  /** 校验容器名/ID,防参数注入(只允许字母数字与 _-.:/ 之外的拒绝)。 */
28
29
  export function assertContainerRef(value) {
package/lib/exec.d.ts CHANGED
@@ -13,6 +13,7 @@ export interface RunResult {
13
13
  export interface ProcessRunner {
14
14
  run(argv: readonly string[], options?: {
15
15
  timeoutMs?: number;
16
+ signal?: AbortSignal;
16
17
  }): Promise<RunResult>;
17
18
  }
18
19
  export interface SubprocessHandleLike {
package/lib/exec.js CHANGED
@@ -12,6 +12,9 @@ export function createSubprocessRunner(spawn, graceMs, defaultTimeoutMs) {
12
12
  const timeoutMs = options?.timeoutMs ?? defaultTimeoutMs;
13
13
  const controller = new AbortController();
14
14
  const timer = setTimeout(() => controller.abort(new Error('docker operation timed out')), timeoutMs);
15
+ const signal = options?.signal === undefined
16
+ ? controller.signal
17
+ : AbortSignal.any([options.signal, controller.signal]);
15
18
  let handle;
16
19
  try {
17
20
  handle = spawn({
@@ -23,7 +26,7 @@ export function createSubprocessRunner(spawn, graceMs, defaultTimeoutMs) {
23
26
  stderr: { maxBytes: COLLECT_BYTES },
24
27
  },
25
28
  graceMs,
26
- signal: controller.signal,
29
+ signal,
27
30
  });
28
31
  const outcome = await handle.done;
29
32
  const stdout = handle.collected.stdout?.readFrom(0).text ?? '';
package/lib/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * dsh-docker —— 容器管理工具插件(node 半身,配置走 cordis.patch.yml)。
3
3
  *
4
- * 插件导出 apply(ctx, config):注册六个面向模型的工具(docker_ps / docker_logs /
5
- * docker_images / docker_inspect / docker_exec / docker_manage)。进程执行走 DSH 官方 subprocess 服务
6
- * (argv 数组、无 shell),docker_exec 默认走宿主审批门。零运行时依赖。
4
+ * 插件导出 apply(ctx, config):注册七个面向模型的工具(docker_ps / docker_logs /
5
+ * docker_images / docker_inspect / docker_exec / docker_manage / docker_health)。进程执行走 DSH 官方 subprocess 服务
6
+ * (argv 数组、无 shell),docker_exec 与破坏性 docker_manage 默认走宿主审批门。零运行时依赖。
7
7
  *
8
8
  * @module dsh-docker
9
9
  */
@@ -23,7 +23,7 @@ type DockerPreToolDecision = {
23
23
  kind: 'ask';
24
24
  reason?: string;
25
25
  };
26
- /** 审批策略需要读取的 alpha.4 工具执行字段。 */
26
+ /** 审批策略需要读取的工具执行字段。 */
27
27
  interface DockerToolExecution {
28
28
  readonly name: string;
29
29
  readonly arguments: unknown;
@@ -42,7 +42,7 @@ export interface DockerPluginContext {
42
42
  on(event: 'dispose', listener: () => void): () => void;
43
43
  }
44
44
  /**
45
- * 插件入口:解析配置、封装执行器、注册六工具;docker_exec 注入审批门。
45
+ * 插件入口:解析配置、封装执行器、注册七工具;为 docker_exec 与破坏性 docker_manage 注入审批门。
46
46
  */
47
47
  export declare function apply(ctx: DockerPluginContext, config?: DockerConfig | null): void;
48
48
  export * from './args.js';
package/lib/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * dsh-docker —— 容器管理工具插件(node 半身,配置走 cordis.patch.yml)。
3
3
  *
4
- * 插件导出 apply(ctx, config):注册六个面向模型的工具(docker_ps / docker_logs /
5
- * docker_images / docker_inspect / docker_exec / docker_manage)。进程执行走 DSH 官方 subprocess 服务
6
- * (argv 数组、无 shell),docker_exec 默认走宿主审批门。零运行时依赖。
4
+ * 插件导出 apply(ctx, config):注册七个面向模型的工具(docker_ps / docker_logs /
5
+ * docker_images / docker_inspect / docker_exec / docker_manage / docker_health)。进程执行走 DSH 官方 subprocess 服务
6
+ * (argv 数组、无 shell),docker_exec 与破坏性 docker_manage 默认走宿主审批门。零运行时依赖。
7
7
  *
8
8
  * @module dsh-docker
9
9
  */
@@ -14,30 +14,34 @@ import { buildDockerTools } from './tools.js';
14
14
  export const name = 'docker';
15
15
  export const inject = ['subprocess', 'tools'];
16
16
  /**
17
- * 插件入口:解析配置、封装执行器、注册六工具;docker_exec 注入审批门。
17
+ * 插件入口:解析配置、封装执行器、注册七工具;为 docker_exec 与破坏性 docker_manage 注入审批门。
18
18
  */
19
19
  export function apply(ctx, config) {
20
- let cfg;
21
- try {
22
- cfg = resolveConfig(config);
23
- }
24
- catch (error) {
25
- console.warn('[dsh-docker] ' + (error instanceof Error ? error.message : String(error)));
26
- cfg = resolveConfig(null);
27
- }
28
- const runner = createSubprocessRunner(ctx.subprocess.spawn, cfg.graceMs, cfg.timeoutMs);
20
+ const cfg = resolveConfig(config);
21
+ const runner = createSubprocessRunner((spec) => ctx.subprocess.spawn(spec), cfg.graceMs, cfg.timeoutMs);
29
22
  const tools = buildDockerTools(cfg, runner);
30
- if (cfg.execApproval) {
23
+ if (cfg.execApproval || cfg.manageApproval) {
31
24
  ctx.on('tools/pre-execute', async (exec, next) => {
32
- if (exec.name !== 'docker_exec')
33
- return next();
34
25
  const args = (typeof exec.arguments === 'object' && exec.arguments !== null ? exec.arguments : {});
35
- const command = typeof args.command === 'string' ? args.command : '';
36
- const container = typeof args.container === 'string' ? args.container : '';
37
- return {
38
- kind: 'ask',
39
- reason: 'docker_exec 需要确认:在容器 ' + (container || '(未指定)') + ' 内执行:' + command.slice(0, 200) + (command.length > 200 ? '…' : ''),
40
- };
26
+ if (exec.name === 'docker_exec' && cfg.execApproval) {
27
+ const command = typeof args.command === 'string' ? args.command : '';
28
+ const container = typeof args.container === 'string' ? args.container : '';
29
+ return {
30
+ kind: 'ask',
31
+ reason: 'docker_exec 需要确认:在容器 ' + (container || '(未指定)') + ' 内执行:' + command.slice(0, 200) + (command.length > 200 ? '…' : ''),
32
+ };
33
+ }
34
+ if (exec.name === 'docker_manage' && cfg.manageApproval) {
35
+ const action = typeof args.action === 'string' ? args.action : '';
36
+ if (action === 'stop' || action === 'restart' || action === 'rm') {
37
+ const container = typeof args.container === 'string' ? args.container : '';
38
+ return {
39
+ kind: 'ask',
40
+ reason: 'docker_manage 需要确认:对容器 ' + (container || '(未指定)') + ' 执行破坏性操作 ' + action + '。',
41
+ };
42
+ }
43
+ }
44
+ return next();
41
45
  });
42
46
  }
43
47
  const disposers = [];
package/lib/tools.d.ts CHANGED
@@ -19,5 +19,5 @@ export interface DockerToolDefinition {
19
19
  execute(args: unknown, exec: unknown): Promise<unknown>;
20
20
  timeoutMs?: number;
21
21
  }
22
- /** 构建五个工具定义。 */
22
+ /** 构建七个工具定义。 */
23
23
  export declare function buildDockerTools(config: ResolvedDockerConfig, runner: ProcessRunner): DockerToolDefinition[];
package/lib/tools.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /**
2
- * 五个面向模型的容器工具:docker_ps / docker_logs / docker_inspect / docker_exec / docker_manage。
2
+ * 七个面向模型的容器工具:docker_ps / docker_logs / docker_images / docker_inspect /
3
+ * docker_exec / docker_manage / docker_health。
3
4
  *
4
5
  * @module dsh-docker/tools
5
6
  */
@@ -34,8 +35,14 @@ function requiredString(args, key, label) {
34
35
  throw new Error(label + '(参数 ' + key + ')为必填,请提供非空字符串。');
35
36
  return value;
36
37
  }
37
- async function runChecked(runner, argv, timeoutMs, label) {
38
- const result = await runner.run(argv, { timeoutMs });
38
+ function executionSignal(exec) {
39
+ if (typeof exec !== 'object' || exec === null)
40
+ return undefined;
41
+ const signal = exec.signal;
42
+ return signal instanceof AbortSignal ? signal : undefined;
43
+ }
44
+ async function runChecked(runner, argv, timeoutMs, label, signal) {
45
+ const result = await runner.run(argv, { timeoutMs, ...(signal === undefined ? {} : { signal }) });
39
46
  if (result.exitCode !== 0) {
40
47
  const tail = result.stderr.trim().split(/\r?\n/).slice(-6).join(' | ');
41
48
  throw new Error(label + '失败(退出码 ' + String(result.exitCode ?? 'null') + '):' + (tail || '无错误输出') + '。请确认 docker 可用(docker version)。');
@@ -77,7 +84,7 @@ const manageSchema = {
77
84
  properties: { container: { type: 'string' }, action: { type: 'string' }, output: { type: 'string' } },
78
85
  additionalProperties: true,
79
86
  };
80
- /** 构建五个工具定义。 */
87
+ /** 构建七个工具定义。 */
81
88
  export function buildDockerTools(config, runner) {
82
89
  const cfg = config;
83
90
  const timeout = cfg.timeoutMs;
@@ -101,10 +108,10 @@ export function buildDockerTools(config, runner) {
101
108
  return [{ type: 'text', text: lines.join('\n') }];
102
109
  },
103
110
  },
104
- async execute(rawArgs) {
111
+ async execute(rawArgs, exec) {
105
112
  const args = asRecord(rawArgs);
106
113
  const all = args.all === true;
107
- const result = await runChecked(runner, psArgs(cfg.dockerPath, all, optionalString(args, 'name')), timeout, 'docker ps');
114
+ const result = await runChecked(runner, psArgs(cfg.dockerPath, all, optionalString(args, 'name')), timeout, 'docker ps', executionSignal(exec));
108
115
  const containers = parsePsJson(result.stdout);
109
116
  return { count: containers.length, all, containers };
110
117
  },
@@ -125,13 +132,13 @@ export function buildDockerTools(config, runner) {
125
132
  return [{ type: 'text', text: '容器 ' + rec.container + ' 最近日志:\n' + rec.text }];
126
133
  },
127
134
  },
128
- async execute(rawArgs) {
135
+ async execute(rawArgs, exec) {
129
136
  const args = asRecord(rawArgs);
130
137
  const container = assertContainerRef(requiredString(args, 'container', '容器名'));
131
138
  const tailRaw = args.tail;
132
139
  const tail = typeof tailRaw === 'number' && Number.isInteger(tailRaw) ? Math.min(2000, Math.max(1, tailRaw)) : 100;
133
140
  const follow = args.follow === true;
134
- const result = await runChecked(runner, logsArgs(cfg.dockerPath, container, tail, follow), follow ? Math.min(timeout, 30000) : timeout, 'docker logs');
141
+ const result = await runChecked(runner, logsArgs(cfg.dockerPath, container, tail, follow), follow ? Math.min(timeout, 30000) : timeout, 'docker logs', executionSignal(exec));
135
142
  return { container, tail, text: result.stdout };
136
143
  },
137
144
  timeoutMs: timeout,
@@ -155,10 +162,10 @@ export function buildDockerTools(config, runner) {
155
162
  return [{ type: 'text', text: lines.join('\n') }];
156
163
  },
157
164
  },
158
- async execute(rawArgs) {
165
+ async execute(rawArgs, exec) {
159
166
  const args = asRecord(rawArgs);
160
167
  const dangling = args.dangling === true;
161
- const result = await runChecked(runner, imagesArgs(cfg.dockerPath, dangling), timeout, 'docker images');
168
+ const result = await runChecked(runner, imagesArgs(cfg.dockerPath, dangling), timeout, 'docker images', executionSignal(exec));
162
169
  const images = parseImagesJson(result.stdout);
163
170
  return { count: images.length, dangling, images };
164
171
  },
@@ -178,10 +185,10 @@ export function buildDockerTools(config, runner) {
178
185
  return [{ type: 'text', text: '容器 ' + info.name + '(' + info.image + '):' + (info.running === true ? '运行中' : '已停止') + ',端口:' + (Array.isArray(info.ports) ? info.ports.join(', ') : '无') }];
179
186
  },
180
187
  },
181
- async execute(rawArgs) {
188
+ async execute(rawArgs, exec) {
182
189
  const args = asRecord(rawArgs);
183
190
  const container = assertContainerRef(requiredString(args, 'container', '容器名'));
184
- const result = await runChecked(runner, inspectArgs(cfg.dockerPath, container), timeout, 'docker inspect');
191
+ const result = await runChecked(runner, inspectArgs(cfg.dockerPath, container), timeout, 'docker inspect', executionSignal(exec));
185
192
  return { container, info: parseInspectJson(result.stdout) };
186
193
  },
187
194
  timeoutMs: timeout,
@@ -200,20 +207,20 @@ export function buildDockerTools(config, runner) {
200
207
  return [{ type: 'text', text: '容器 ' + rec.container + ' 执行完成(退出码 ' + rec.exitCode + '):\n' + rec.stdout }];
201
208
  },
202
209
  },
203
- async execute(rawArgs) {
210
+ async execute(rawArgs, exec) {
204
211
  const args = asRecord(rawArgs);
205
212
  const container = assertContainerRef(requiredString(args, 'container', '容器名'));
206
213
  const command = splitCommand(requiredString(args, 'command', '命令'));
207
214
  if (command.length === 0)
208
215
  throw new Error('command 不能为空。');
209
- const result = await runChecked(runner, execArgs(cfg.dockerPath, container, command), timeout, 'docker exec');
216
+ const result = await runChecked(runner, execArgs(cfg.dockerPath, container, command), timeout, 'docker exec', executionSignal(exec));
210
217
  return { container, exitCode: result.exitCode, stdout: result.stdout };
211
218
  },
212
219
  timeoutMs: timeout,
213
220
  };
214
221
  const dockerManage = {
215
222
  name: 'docker_manage',
216
- description: '容器生命周期管理:start / stop / restart / rm(rm 强制删除)。注意 stop/restart/rm 会中断容器,谨慎使用。',
223
+ description: '容器生命周期管理:start / stop / restart / rm(rm 强制删除)。stop/restart/rm 默认进入审批门,批准后才执行。',
217
224
  parameters: compileParameters({
218
225
  container: { type: 'string', required: true, description: '容器名或 ID(必填)。' },
219
226
  action: { type: 'string', required: true, description: '操作:start / stop / restart / rm(必填)。' },
@@ -225,20 +232,20 @@ export function buildDockerTools(config, runner) {
225
232
  return [{ type: 'text', text: '容器 ' + rec.container + ' 已执行 ' + rec.action + '。' }];
226
233
  },
227
234
  },
228
- async execute(rawArgs) {
235
+ async execute(rawArgs, exec) {
229
236
  const args = asRecord(rawArgs);
230
237
  const container = assertContainerRef(requiredString(args, 'container', '容器名'));
231
238
  const action = requiredString(args, 'action', '操作');
232
239
  if (!['start', 'stop', 'restart', 'rm'].includes(action))
233
240
  throw new Error('action 必须是 start / stop / restart / rm 之一(当前:' + action + ')。');
234
- const result = await runChecked(runner, manageArgs(cfg.dockerPath, action, container), timeout, 'docker ' + action);
241
+ const result = await runChecked(runner, manageArgs(cfg.dockerPath, action, container), timeout, 'docker ' + action, executionSignal(exec));
235
242
  return { container, action, output: result.stdout.trim() };
236
243
  },
237
244
  timeoutMs: timeout,
238
245
  };
239
246
  const dockerHealth = {
240
247
  name: 'docker_health',
241
- description: 'dsh-docker 自检:验证 docker CLI 与守护进程是否可用(执行 docker version),并汇总安全配置(exec 审批门、超时)。遇到问题时先运行本工具定位。',
248
+ description: 'dsh-docker 自检:验证 docker CLI 与守护进程是否可用(执行 docker version),并汇总安全配置(exec/破坏性 manage 审批门、超时)。遇到问题时先运行本工具定位。',
242
249
  parameters: compileParameters({}),
243
250
  output: {
244
251
  schema: { type: 'object', additionalProperties: true },
@@ -253,18 +260,21 @@ export function buildDockerTools(config, runner) {
253
260
  return [{ type: 'text', text: lines.join('\n') }];
254
261
  },
255
262
  },
256
- async execute() {
263
+ async execute(_rawArgs, exec) {
257
264
  const checks = [];
258
265
  let ok = true;
259
266
  try {
260
- const result = await runChecked(runner, [cfg.dockerPath, 'version', '--format', '{{.Server.Version}}'], 15000, 'docker version');
267
+ const result = await runChecked(runner, [cfg.dockerPath, 'version', '--format', '{{.Server.Version}}'], 15000, 'docker version', executionSignal(exec));
261
268
  checks.push({ name: 'docker daemon', ok: true, detail: 'server ' + result.stdout.trim() });
262
269
  }
263
270
  catch (error) {
271
+ if (executionSignal(exec)?.aborted === true)
272
+ throw error;
264
273
  ok = false;
265
274
  checks.push({ name: 'docker daemon', ok: false, detail: error instanceof Error ? error.message : String(error) });
266
275
  }
267
276
  checks.push({ name: 'exec 审批门', ok: true, detail: cfg.execApproval === true ? '开启' : '关闭' });
277
+ checks.push({ name: 'manage 审批门', ok: true, detail: cfg.manageApproval === true ? '开启' : '关闭' });
268
278
  checks.push({ name: '超时配置', ok: true, detail: 'timeoutMs=' + cfg.timeoutMs + ', graceMs=' + cfg.graceMs });
269
279
  return { ok, plugin: 'dsh-docker', checks };
270
280
  },
package/package.json CHANGED
@@ -1,59 +1,59 @@
1
- {
2
- "name": "@stardustlc/dsh-docker",
3
- "version": "0.3.1",
4
- "description": "DSH 容器管理插件:docker_ps/logs/images/inspect/exec/manage 六工具,走官方 subprocess 服务跑 docker CLI、argv 无 shell 注入、exec 审批门、零运行时依赖。",
5
- "type": "module",
6
- "main": "lib/index.js",
7
- "types": "lib/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./lib/index.d.ts",
11
- "default": "./lib/index.js"
12
- },
13
- "./cordis.patch.yml": "./cordis.patch.yml",
14
- "./package.json": "./package.json"
15
- },
16
- "files": [
17
- "lib",
18
- "cordis.patch.yml",
19
- "README.md",
20
- "README.en.md"
21
- ],
22
- "scripts": {
23
- "build": "tsc -p tsconfig.json",
24
- "typecheck": "tsc -p tsconfig.json --noEmit",
25
- "test": "pnpm run build && node --test \"test/*.test.mjs\"",
26
- "prepublishOnly": "pnpm run build"
27
- },
28
- "dsh": {
29
- "bundle": {
30
- "patch": "./cordis.patch.yml"
31
- }
32
- },
33
- "keywords": [
34
- "dsh",
35
- "deepseek-harness",
36
- "plugin",
37
- "docker",
38
- "container",
39
- "devops"
40
- ],
41
- "license": "MIT",
42
- "engines": {
43
- "node": ">=22"
44
- },
45
- "devDependencies": {
46
- "@types/node": "^24.0.0",
47
- "typescript": "^5.6.0"
48
- },
49
- "repository": {
50
- "type": "git",
51
- "url": "git+https://github.com/STARDUSTLC666/dsh-docker.git"
52
- },
53
- "bugs": {
54
- "url": "https://github.com/STARDUSTLC666/dsh-docker/issues"
55
- },
56
- "homepage": "https://github.com/STARDUSTLC666/dsh-docker#readme",
57
- "author": "stardustlc",
58
- "packageManager": "pnpm@11.7.0"
59
- }
1
+ {
2
+ "name": "@stardustlc/dsh-docker",
3
+ "version": "0.3.3",
4
+ "description": "DSH 容器管理插件:七工具覆盖容器与镜像管理,走官方 subprocess 服务、argv 无 shell 注入,exec 与破坏性 manage 默认审批。",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./cordis.patch.yml": "./cordis.patch.yml",
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "lib",
18
+ "cordis.patch.yml",
19
+ "README.md",
20
+ "README.en.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.json",
24
+ "typecheck": "tsc -p tsconfig.json --noEmit",
25
+ "test": "pnpm run build && node --test \"test/*.test.mjs\"",
26
+ "prepublishOnly": "pnpm run build"
27
+ },
28
+ "dsh": {
29
+ "bundle": {
30
+ "patch": "./cordis.patch.yml"
31
+ }
32
+ },
33
+ "keywords": [
34
+ "dsh",
35
+ "deepseek-harness",
36
+ "plugin",
37
+ "docker",
38
+ "container",
39
+ "devops"
40
+ ],
41
+ "license": "MIT",
42
+ "engines": {
43
+ "node": "^22.19.0 || >=24.0.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^24.0.0",
47
+ "typescript": "^5.6.0"
48
+ },
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/STARDUSTLC666/dsh-docker.git"
52
+ },
53
+ "bugs": {
54
+ "url": "https://github.com/STARDUSTLC666/dsh-docker/issues"
55
+ },
56
+ "homepage": "https://github.com/STARDUSTLC666/dsh-docker#readme",
57
+ "author": "stardustlc",
58
+ "packageManager": "pnpm@11.7.0"
59
+ }