@stardustlc/dsh-docker 0.3.4 → 0.4.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/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # 更新记录
2
+
3
+ [返回简介](README.md) · [使用说明](docs/USAGE.md) · [验证记录](docs/VALIDATION.md)
4
+
5
+ ## 0.4.1 (2026-10-05)
6
+
7
+ - 修复旧版 DSH 生成 PTC SDK 时不支持的数组约束;仍在执行前严格限制 argv 的 128 项与总长度,保留精确参数和审批。
8
+
9
+ ## 0.4.0 (2026-10-05)
10
+
11
+ - docker_exec 增加 argv 精确参数数组,修复空参数、相邻引号与转义;拒绝未闭合命令,并在启动前处理取消。argv 同样显示完整审批内容。
12
+
13
+ ## 0.3.4 (2026-09-28)
14
+
15
+ - 更新官方 Harness 0.2.0-rc.1 的兼容声明和共同加载验证;运行时代码未变。验证范围见[验证记录](docs/VALIDATION.md)。
16
+
17
+ ## 更早的改动
18
+
19
+ 完整历史可查阅 [GitHub 提交记录](https://github.com/STARDUSTLC666/dsh-docker/commits/master)。
package/README.en.md CHANGED
@@ -1,90 +1,46 @@
1
- [中文](README.md)
2
-
3
1
  # dsh-docker
4
2
 
5
- > **Your agent can manage containers now**: seven tools covering containers, images, logs, inspection, in-container exec, lifecycle management, and health checks.
3
+ [中文](README.md)
6
4
 
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**.
5
+ ![dsh-docker whale girl plugin cover](https://raw.githubusercontent.com/STARDUSTLC666/dsh-docker/master/assets/cover-whale-girl.png)
8
6
 
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)
7
+ Inspect and manage local Docker containers and images from DSH.
10
8
 
11
- [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com)
9
+ [![npm](https://img.shields.io/npm/v/@stardustlc/dsh-docker)](https://www.npmjs.com/package/@stardustlc/dsh-docker) [![downloads](https://raw.githubusercontent.com/STARDUSTLC666/dsh-suite/npm-downloads/assets/dsh-docker-downloads.svg)](https://www.npmjs.com/package/@stardustlc/dsh-docker)
12
10
 
13
- ## Compatibility
11
+ ## What it does
14
12
 
15
- Validation host: Harness `0.2.0-rc.1` built from official sources (commit `407e65c8`) with Node `24.16.0` on 2026-09-28. All 36 plugin tests pass in an isolated environment; all 18 plugins mount together in one host registering 7 tools, with tool schemas and health-check contracts passing. No live ports or external services were exercised in this round.
13
+ - Inspect containers, images, logs and resource status.
14
+ - Run container commands and common management operations.
15
+ - Keep execution and destructive operations under host approval.
16
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.
17
+ ## Install
18
18
 
19
- ## Installation
19
+ In DSH Desktop, install `@stardustlc/dsh-docker` from the Plugins panel. If the bundled dsh command is available:
20
20
 
21
21
  ```bash
22
- dsh plugin --profile web add @stardustlc/dsh-docker
22
+ dsh plugin --profile desktop add @stardustlc/dsh-docker
23
23
  ```
24
24
 
25
- Requires Docker installed locally (`docker version` should work); use `dockerPath` when it is not on PATH.
25
+ For the web version, replace `desktop` with `web`. Restart DSH after installation.
26
26
 
27
- ## Uninstall
27
+ ## Start using it
28
28
 
29
- ```bash
30
- dsh plugin --profile web remove @stardustlc/dsh-docker
31
- ```
29
+ Ask: “Find out why this container exited. Start with its status and logs.”
32
30
 
33
- Then restart the web service. To clean up fully, also remove the plugin entry from your profile `cordis.patch.yml` if you overrode it.
31
+ ## Requirements and configuration
34
32
 
33
+ Requires an accessible Docker CLI and Docker daemon.
35
34
 
36
- ## Configuration
35
+ Detailed configuration, tool arguments and troubleshooting are in the [usage guide](docs/USAGE.en.md). For standalone development, follow the Node requirement in [package.json](package.json).
37
36
 
38
- ```yaml
39
- - id: docker
40
- name: '@stardustlc/dsh-docker'
41
- config:
42
- # dockerPath: C:\Program Files\Docker\Docker\resources\bin\docker.exe
43
- dockerPath: docker # optional; or use the DSH_DOCKER_PATH env var
44
- timeoutMs: 60000 # per-operation timeout (default 60s, 5s - 10min)
45
- # execApproval: false # disable the docker_exec approval gate (default true)
46
- # manageApproval: false # disable approval for stop/restart/rm (default true; not recommended)
47
- ```
37
+ ## Documentation
48
38
 
49
- ## Tools
50
-
51
- | Tool | Purpose | Safety |
52
- | :-- | :-- | :-- |
53
- | `docker_ps` | List containers (status/image/state, filterable) | — |
54
- | `docker_images` | List local images (repository/tag/size/created, dangling-only filter) | — |
55
- | `docker_logs` | Tail container logs (line clamp, short follow) | — |
56
- | `docker_inspect` | Container details (image/state/ports) | — |
57
- | `docker_exec` | Run a command inside a container | Approval gate + container-name validation |
58
- | `docker_manage` | start / stop / restart / rm | Approval gate for stop/restart/rm |
59
- | `docker_health` | Check Docker daemon and safety settings | — |
60
-
61
- ### Examples
62
-
63
- ```text
64
- docker_ps {}
65
- docker_ps { all: true, name: web }
66
- docker_images { dangling: true }
67
- docker_logs { container: web, tail: 200 }
68
- docker_inspect { container: web }
69
- docker_exec { container: web, command: 'df -h' }
70
- docker_manage { container: web, action: restart }
71
- ```
72
-
73
- ## Safety
74
-
75
- - **No shell**: every argument is its own argv element — command injection is impossible
76
- - **Approval gates**: docker_exec and docker_manage stop/restart/rm ask first; headless environments without an approval channel are denied
77
- - **Container-name validation**: only `[A-Za-z0-9][A-Za-z0-9_.:-]*` accepted — no argument injection
78
- - **Timeout clamps**: 5s - 10min per operation; follow mode capped at an extra 30s
79
- - **Log clamping**: tail 1-2000 lines
80
-
81
- ## Development
82
-
83
- ```bash
84
- pnpm install
85
- pnpm test # build + 35 tests
86
- ```
39
+ - [Usage and troubleshooting](docs/USAGE.en.md)
40
+ - [Changelog](CHANGELOG.md)
41
+ - [Validation scope and history](docs/VALIDATION.md)
42
+ - [Report a problem or suggest a feature](https://github.com/STARDUSTLC666/dsh-docker/issues)
87
43
 
88
44
  ## License
89
45
 
90
- MIT
46
+ [MIT](LICENSE)
package/README.md CHANGED
@@ -1,88 +1,46 @@
1
- [English](README.en.md)
2
-
3
1
  # dsh-docker
4
2
 
5
- > **你的 agent 会管容器了**:七个工具覆盖容器/镜像列表、日志、详情、容器内执行、生命周期管理与健康检查。
3
+ [English](README.en.md)
6
4
 
7
- DSH(DeepSeek Harness)容器管理插件:走官方 subprocess 服务跑 docker CLI,argv 数组无 shell 注入,`docker_exec` 与破坏性生命周期操作默认审批门,**零运行时依赖**。
5
+ ![dsh-docker 鲸鱼娘插件封面](https://raw.githubusercontent.com/STARDUSTLC666/dsh-docker/master/assets/cover-whale-girl.png)
8
6
 
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)
7
+ 在 DSH 中查看和管理本机 Docker 容器与镜像。
10
8
 
11
- [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com)
9
+ [![npm](https://img.shields.io/npm/v/@stardustlc/dsh-docker)](https://www.npmjs.com/package/@stardustlc/dsh-docker) [![downloads](https://raw.githubusercontent.com/STARDUSTLC666/dsh-suite/npm-downloads/assets/dsh-docker-downloads.svg)](https://www.npmjs.com/package/@stardustlc/dsh-docker)
12
10
 
13
- ## 兼容性
11
+ ## 功能
14
12
 
15
- 验证宿主:官方源码构建的 Harness `0.2.0-rc.1`(commit `407e65c8`)+ Node `24.16.0`(2026-09-28)。36 项插件测试在隔离环境全部通过;同一个宿主里 18 个插件共同加载,注册 7 个工具,工具 schema 与健康检查契约通过。本轮未启用真实端口与外部服务。
13
+ - 查看容器、镜像、日志和资源状态。
14
+ - 运行容器内命令,执行常用管理操作。
15
+ - 执行和破坏性操作遵循宿主审批。
16
16
 
17
17
  ## 安装
18
18
 
19
- ```bash
20
- dsh plugin --profile web add @stardustlc/dsh-docker
21
- ```
22
-
23
- 需要本机装有 Docker(`docker version` 能出结果即可);不在 PATH 上时用 `dockerPath` 指定。
24
-
25
- ## 卸载
19
+ 桌面版可在「插件」面板按包名 `@stardustlc/dsh-docker` 安装。已配置 dsh 命令时也可使用:
26
20
 
27
21
  ```bash
28
- dsh plugin --profile web remove @stardustlc/dsh-docker
22
+ dsh plugin --profile desktop add @stardustlc/dsh-docker
29
23
  ```
30
24
 
31
- 卸载后重启 Web 服务。如需彻底清理,可再手动删除自己 profile `cordis.patch.yml` 中覆盖的插件行。
32
-
25
+ 网页版把命令中的 `desktop` 改为 `web`。安装后重启 DSH。
33
26
 
34
- ## 配置
27
+ ## 开始使用
35
28
 
36
- ```yaml
37
- - id: docker
38
- name: '@stardustlc/dsh-docker'
39
- config:
40
- # dockerPath: C:\Program Files\Docker\Docker\resources\bin\docker.exe
41
- dockerPath: docker # 可选;也可用环境变量 DSH_DOCKER_PATH
42
- timeoutMs: 60000 # 单次操作超时(默认 60 秒,5 秒 - 10 分钟)
43
- # execApproval: false # 关闭 docker_exec 审批门(默认 true)
44
- # manageApproval: false # 关闭 stop/restart/rm 审批门(默认 true,不推荐)
45
- ```
29
+ 安装后可说:“检查这个容器为什么退出,先查看状态和日志。”
46
30
 
47
- ## 工具一览
48
-
49
- | 工具 | 作用 | 安全 |
50
- | :-- | :-- | :-- |
51
- | `docker_ps` | 列出容器(状态/镜像/运行态,可过滤)| — |
52
- | `docker_images` | 列出本地镜像(仓库/标签/大小/创建时间,可只看悬空镜像)| — |
53
- | `docker_logs` | 查看日志尾部(行数钳制,可短时 follow)| — |
54
- | `docker_inspect` | 容器详情(镜像/状态/端口)| — |
55
- | `docker_exec` | 容器内执行命令 | 审批门 + 容器名白名单校验 |
56
- | `docker_manage` | start / stop / restart / rm | stop/restart/rm 审批门 |
57
- | `docker_health` | Docker daemon 与安全配置自检 | — |
58
-
59
- ### 示例
60
-
61
- ```text
62
- docker_ps {}
63
- docker_ps { all: true, name: web }
64
- docker_images { dangling: true }
65
- docker_logs { container: web, tail: 200 }
66
- docker_inspect { container: web }
67
- docker_exec { container: web, command: 'df -h' }
68
- docker_manage { container: web, action: restart }
69
- ```
31
+ ## 依赖与配置
70
32
 
71
- ## 安全设计
33
+ 需要已安装且可访问的 Docker CLI 与 Docker 服务。
72
34
 
73
- - **无 shell**:全部参数独立 argv 数组,命令注入不可能
74
- - **审批门**:docker_exec 与 docker_manage 的 stop/restart/rm 默认弹审批;headless 无审批通道时拒绝
75
- - **容器名校验**:只允许 `[A-Za-z0-9][A-Za-z0-9_.:-]*`,杜绝参数注入
76
- - **超时钳制**:单次操作 5 秒 - 10 分钟;follow 模式额外限 30 秒
77
- - **日志钳制**:tail 1-2000 行
35
+ 详细配置、工具参数与排错见[使用说明](docs/USAGE.md)。从源码独立开发时,Node 要求以 [package.json](package.json) 为准。
78
36
 
79
- ## 开发
37
+ ## 文档
80
38
 
81
- ```bash
82
- pnpm install
83
- pnpm test # 构建 + 35 个测试
84
- ```
39
+ - [使用与排错](docs/USAGE.md)
40
+ - [更新记录](CHANGELOG.md)
41
+ - [验证范围与历史记录](docs/VALIDATION.md)
42
+ - [问题反馈与功能建议](https://github.com/STARDUSTLC666/dsh-docker/issues)
85
43
 
86
44
  ## License
87
45
 
88
- MIT
46
+ [MIT](LICENSE)
@@ -0,0 +1,79 @@
1
+ # dsh-docker usage guide
2
+
3
+ [Overview](../README.en.md) · [Changelog](../CHANGELOG.md) · [Validation](VALIDATION.md)
4
+
5
+ ## Current improvements
6
+
7
+ Prefer docker_exec with container="web" and argv=["printf","%s","a b"]. Supply exactly one of command or argv. The host performs no variable, pipe or command-substitution expansion. Explicit container shell calls such as ["sh","-c","..."] remain approval-gated.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ dsh plugin --profile web add @stardustlc/dsh-docker
13
+ ```
14
+
15
+ Requires Docker installed locally (`docker version` should work); use `dockerPath` when it is not on PATH.
16
+
17
+ ## Uninstall
18
+
19
+ ```bash
20
+ dsh plugin --profile web remove @stardustlc/dsh-docker
21
+ ```
22
+
23
+ Then restart the web service. To clean up fully, also remove the plugin entry from your profile `cordis.patch.yml` if you overrode it.
24
+
25
+ ## Configuration
26
+
27
+ ```yaml
28
+ - id: docker
29
+ name: '@stardustlc/dsh-docker'
30
+ config:
31
+ # dockerPath: C:\Program Files\Docker\Docker\resources\bin\docker.exe
32
+ dockerPath: docker # optional; or use the DSH_DOCKER_PATH env var
33
+ timeoutMs: 60000 # per-operation timeout (default 60s, 5s - 10min)
34
+ # execApproval: false # disable the docker_exec approval gate (default true)
35
+ # manageApproval: false # disable approval for stop/restart/rm (default true; not recommended)
36
+ ```
37
+
38
+ ## Tools
39
+
40
+ | Tool | Purpose | Safety |
41
+ | :-- | :-- | :-- |
42
+ | `docker_ps` | List containers (status/image/state, filterable) | — |
43
+ | `docker_images` | List local images (repository/tag/size/created, dangling-only filter) | — |
44
+ | `docker_logs` | Tail container logs (line clamp, short follow) | — |
45
+ | `docker_inspect` | Container details (image/state/ports) | — |
46
+ | `docker_exec` | Run a command inside a container | Approval gate + container-name validation |
47
+ | `docker_manage` | start / stop / restart / rm | Approval gate for stop/restart/rm |
48
+ | `docker_health` | Check Docker daemon and safety settings | — |
49
+
50
+ ### Examples
51
+
52
+ ```text
53
+ docker_ps {}
54
+ docker_ps { all: true, name: web }
55
+ docker_images { dangling: true }
56
+ docker_logs { container: web, tail: 200 }
57
+ docker_inspect { container: web }
58
+ docker_exec { container: web, command: 'df -h' }
59
+ docker_manage { container: web, action: restart }
60
+ ```
61
+
62
+ ## Safety
63
+
64
+ - **No shell concatenation**: arguments are passed separately to Docker CLI rather than combined into a shell command.
65
+ - **Approval gates**: docker_exec and docker_manage stop/restart/rm ask first; headless environments without an approval channel are denied
66
+ - **Container-name validation**: only `[A-Za-z0-9][A-Za-z0-9_.:-]*` accepted — no argument injection
67
+ - **Timeout clamps**: 5s - 10min per operation; follow mode capped at an extra 30s
68
+ - **Log clamping**: tail 1-2000 lines
69
+
70
+ ## Development
71
+
72
+ ```bash
73
+ pnpm install
74
+ pnpm test # build + 35 tests
75
+ ```
76
+
77
+ ## License
78
+
79
+ MIT
package/docs/USAGE.md ADDED
@@ -0,0 +1,79 @@
1
+ # dsh-docker 使用说明
2
+
3
+ [返回简介](../README.md) · [更新记录](../CHANGELOG.md) · [验证记录](VALIDATION.md)
4
+
5
+ ## 本次改进
6
+
7
+ 推荐 docker_exec { container: "web", argv: ["printf", "%s", "a b"] }。command 与 argv 二选一;宿主不执行变量、管道或命令替换。确需容器 Shell 时显式使用 ["sh", "-c", "..."],仍需审批。
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ dsh plugin --profile web add @stardustlc/dsh-docker
13
+ ```
14
+
15
+ 需要本机装有 Docker(`docker version` 能出结果即可);不在 PATH 上时用 `dockerPath` 指定。
16
+
17
+ ## 卸载
18
+
19
+ ```bash
20
+ dsh plugin --profile web remove @stardustlc/dsh-docker
21
+ ```
22
+
23
+ 卸载后重启 Web 服务。如需彻底清理,可再手动删除自己 profile `cordis.patch.yml` 中覆盖的插件行。
24
+
25
+ ## 配置
26
+
27
+ ```yaml
28
+ - id: docker
29
+ name: '@stardustlc/dsh-docker'
30
+ config:
31
+ # dockerPath: C:\Program Files\Docker\Docker\resources\bin\docker.exe
32
+ dockerPath: docker # 可选;也可用环境变量 DSH_DOCKER_PATH
33
+ timeoutMs: 60000 # 单次操作超时(默认 60 秒,5 秒 - 10 分钟)
34
+ # execApproval: false # 关闭 docker_exec 审批门(默认 true)
35
+ # manageApproval: false # 关闭 stop/restart/rm 审批门(默认 true,不推荐)
36
+ ```
37
+
38
+ ## 工具一览
39
+
40
+ | 工具 | 作用 | 安全 |
41
+ | :-- | :-- | :-- |
42
+ | `docker_ps` | 列出容器(状态/镜像/运行态,可过滤)| — |
43
+ | `docker_images` | 列出本地镜像(仓库/标签/大小/创建时间,可只看悬空镜像)| — |
44
+ | `docker_logs` | 查看日志尾部(行数钳制,可短时 follow)| — |
45
+ | `docker_inspect` | 容器详情(镜像/状态/端口)| — |
46
+ | `docker_exec` | 容器内执行命令 | 审批门 + 容器名白名单校验 |
47
+ | `docker_manage` | start / stop / restart / rm | stop/restart/rm 审批门 |
48
+ | `docker_health` | Docker daemon 与安全配置自检 | — |
49
+
50
+ ### 示例
51
+
52
+ ```text
53
+ docker_ps {}
54
+ docker_ps { all: true, name: web }
55
+ docker_images { dangling: true }
56
+ docker_logs { container: web, tail: 200 }
57
+ docker_inspect { container: web }
58
+ docker_exec { container: web, command: 'df -h' }
59
+ docker_manage { container: web, action: restart }
60
+ ```
61
+
62
+ ## 安全设计
63
+
64
+ - **无 shell 拼接**:参数通过独立 argv 传入 Docker CLI,避免由插件拼接 shell 命令。
65
+ - **审批门**:docker_exec 与 docker_manage 的 stop/restart/rm 默认弹审批;headless 无审批通道时拒绝
66
+ - **容器名校验**:只允许 `[A-Za-z0-9][A-Za-z0-9_.:-]*`,杜绝参数注入
67
+ - **超时钳制**:单次操作 5 秒 - 10 分钟;follow 模式额外限 30 秒
68
+ - **日志钳制**:tail 1-2000 行
69
+
70
+ ## 开发
71
+
72
+ ```bash
73
+ pnpm install
74
+ pnpm test # 构建并执行测试
75
+ ```
76
+
77
+ ## License
78
+
79
+ MIT
@@ -0,0 +1,13 @@
1
+ # dsh-docker 验证记录
2
+
3
+ 本页整理原 README 的历史验证说明,保留当时的版本、日期与范围。自动测试、启动检查、浏览器操作和真实服务验收分别记录,不能相互替代。更详细的版本验收文件仍保留在仓库中。
4
+
5
+ ## 原中文记录
6
+
7
+ 验证宿主:官方源码构建的 Harness `0.2.0-rc.1`(commit `407e65c8`)+ Node `24.16.0`(2026-09-28)。36 项插件测试在隔离环境全部通过;同一个宿主里 18 个插件共同加载,注册 7 个工具,工具 schema 与健康检查契约通过。本轮未启用真实端口与外部服务。
8
+
9
+ ## Original English record
10
+
11
+ Validation host: Harness `0.2.0-rc.1` built from official sources (commit `407e65c8`) with Node `24.16.0` on 2026-09-28. All 36 plugin tests pass in an isolated environment; all 18 plugins mount together in one host registering 7 tools, with tool schemas and health-check contracts passing. No live ports or external services were exercised in this round.
12
+
13
+ 2026-09-13 fix: retain the service receiver when calling `subprocess.spawn`, preventing failures caused by passing the method as an unbound callback. Verified against a real isolated host subprocess service. `docker_ps` passes through the real host subprocess service with a Node child producing fixed Docker output. A live Docker daemon was not used.
package/lib/args.js CHANGED
@@ -36,9 +36,60 @@ export function manageArgs(docker, action, container) {
36
36
  }
37
37
  /** 拆分 exec 的命令字符串(按空白,支持引号)。 */
38
38
  export function splitCommand(command) {
39
+ if (command.length > 32768 || command.includes('\0'))
40
+ throw new Error('命令超过 32768 字符或含 NUL;请检查输入。');
39
41
  const parts = [];
40
- const matches = command.match(/"[^"]*"|'[^']*'|\S+/g);
41
- if (matches === null)
42
- return [];
43
- return matches.map((part) => part.replace(/^["']|["']$/g, ''));
42
+ let token = '', started = false, quote = '';
43
+ for (let i = 0; i < command.length; i++) {
44
+ const ch = command[i];
45
+ if (quote === "'") {
46
+ if (ch === "'")
47
+ quote = '';
48
+ else
49
+ token += ch;
50
+ continue;
51
+ }
52
+ if (ch === '\\' && quote !== "'") {
53
+ const next = command[i + 1];
54
+ if (next === undefined)
55
+ throw new Error('命令末尾的转义未完成,请使用 argv 传递原始参数。');
56
+ if (quote === '"' && !['"', '\\', '$', '`', '\n'].includes(next))
57
+ token += ch;
58
+ else {
59
+ i++;
60
+ if (next !== '\n')
61
+ token += next;
62
+ }
63
+ started = true;
64
+ continue;
65
+ }
66
+ if (quote === '"') {
67
+ if (ch === '"')
68
+ quote = '';
69
+ else
70
+ token += ch;
71
+ continue;
72
+ }
73
+ if (ch === '"' || ch === "'") {
74
+ quote = ch;
75
+ started = true;
76
+ continue;
77
+ }
78
+ if (/\s/.test(ch)) {
79
+ if (started) {
80
+ parts.push(token);
81
+ token = '';
82
+ started = false;
83
+ }
84
+ ;
85
+ continue;
86
+ }
87
+ token += ch;
88
+ started = true;
89
+ }
90
+ if (quote)
91
+ throw new Error('命令引号未闭合,请修正或使用 argv 参数数组。');
92
+ if (started)
93
+ parts.push(token);
94
+ return parts;
44
95
  }
package/lib/exec.js CHANGED
@@ -17,6 +17,7 @@ export function createSubprocessRunner(spawn, graceMs, defaultTimeoutMs) {
17
17
  : AbortSignal.any([options.signal, controller.signal]);
18
18
  let handle;
19
19
  try {
20
+ signal.throwIfAborted();
20
21
  handle = spawn({
21
22
  argv,
22
23
  cwd: process.cwd(),
@@ -29,6 +30,7 @@ export function createSubprocessRunner(spawn, graceMs, defaultTimeoutMs) {
29
30
  signal,
30
31
  });
31
32
  const outcome = await handle.done;
33
+ signal.throwIfAborted();
32
34
  const stdout = handle.collected.stdout?.readFrom(0).text ?? '';
33
35
  const stderr = handle.collected.stderr?.readFrom(0).text ?? '';
34
36
  return { exitCode: outcome.exitCode, signal: outcome.signal, stdout, stderr };
package/lib/index.js CHANGED
@@ -24,7 +24,7 @@ export function apply(ctx, config) {
24
24
  ctx.on('tools/pre-execute', async (exec, next) => {
25
25
  const args = (typeof exec.arguments === 'object' && exec.arguments !== null ? exec.arguments : {});
26
26
  if (exec.name === 'docker_exec' && cfg.execApproval) {
27
- const command = typeof args.command === 'string' ? args.command : '';
27
+ const command = Array.isArray(args.argv) ? JSON.stringify(args.argv) : typeof args.command === 'string' ? args.command : '';
28
28
  const container = typeof args.container === 'string' ? args.container : '';
29
29
  return {
30
30
  kind: 'ask',
package/lib/tools.js CHANGED
@@ -18,6 +18,8 @@ function compileParameters(spec) {
18
18
  node.type = prop.type;
19
19
  if (typeof prop?.description === 'string')
20
20
  node.description = prop.description;
21
+ if (prop?.items)
22
+ node.items = prop.items;
21
23
  properties[key] = node;
22
24
  }
23
25
  return { type: 'object', properties, ...(required.length > 0 ? { required } : {}) };
@@ -42,7 +44,9 @@ function executionSignal(exec) {
42
44
  return signal instanceof AbortSignal ? signal : undefined;
43
45
  }
44
46
  async function runChecked(runner, argv, timeoutMs, label, signal) {
47
+ signal?.throwIfAborted();
45
48
  const result = await runner.run(argv, { timeoutMs, ...(signal === undefined ? {} : { signal }) });
49
+ signal?.throwIfAborted();
46
50
  if (result.exitCode !== 0) {
47
51
  const tail = result.stderr.trim().split(/\r?\n/).slice(-6).join(' | ');
48
52
  throw new Error(label + '失败(退出码 ' + String(result.exitCode ?? 'null') + '):' + (tail || '无错误输出') + '。请确认 docker 可用(docker version)。');
@@ -195,10 +199,11 @@ export function buildDockerTools(config, runner) {
195
199
  };
196
200
  const dockerExec = {
197
201
  name: 'docker_exec',
198
- description: '在容器内执行命令(默认走审批门)。command 为命令字符串(按空白拆分,支持引号);返回退出码与 stdout(stderr 并入错误提示)。',
202
+ description: '在容器内执行命令(默认走审批门)。command 或 argv 必须且只能提供一个。推荐 argv 参数数组,精确保留空格、引号和空参数;command 支持引号与转义。宿主不执行 Shell 展开。返回退出码与 stdout。',
199
203
  parameters: compileParameters({
200
204
  container: { type: 'string', required: true, description: '容器名或 ID(必填)。' },
201
- command: { type: 'string', required: true, description: '要执行的命令,如 ls -la 或 sh -c \'cat /etc/hosts\'(必填)。' },
205
+ command: { type: 'string', description: '命令字符串,如 ls -la;与 argv 二选一。' },
206
+ argv: { type: 'array', items: { type: 'string' }, description: '精确命令参数,最多 128 项,如 ["sh", "-c", "cat /etc/hosts"];与 command 二选一。' },
202
207
  }),
203
208
  output: {
204
209
  schema: execSchema,
@@ -210,9 +215,11 @@ export function buildDockerTools(config, runner) {
210
215
  async execute(rawArgs, exec) {
211
216
  const args = asRecord(rawArgs);
212
217
  const container = assertContainerRef(requiredString(args, 'container', '容器名'));
213
- const command = splitCommand(requiredString(args, 'command', '命令'));
214
- if (command.length === 0)
215
- throw new Error('command 不能为空。');
218
+ if ((args.command !== undefined) === (args.argv !== undefined))
219
+ throw new Error('command 与 argv 必须且只能提供一个。');
220
+ const command = args.argv !== undefined ? args.argv : splitCommand(requiredString(args, 'command', '命令'));
221
+ if (!Array.isArray(command) || command.length === 0 || command.length > 128 || command.some(part => typeof part !== 'string' || part.includes('\0')) || typeof command[0] !== 'string' || !command[0].trim() || command.reduce((size, part) => size + part.length, 0) > 32768)
222
+ throw new Error('命令参数无效:argv 需含 1–128 个字符串、首项非空、总长不超过 32768 字符且不含 NUL。');
216
223
  const result = await runChecked(runner, execArgs(cfg.dockerPath, container, command), timeout, 'docker exec', executionSignal(exec));
217
224
  return { container, exitCode: result.exitCode, stdout: result.stdout };
218
225
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stardustlc/dsh-docker",
3
- "version": "0.3.4",
4
- "description": "DSH 容器与镜像管理插件:走官方 subprocess 服务,argv 无 shell 注入,exec 与破坏性操作默认审批。",
3
+ "version": "0.4.1",
4
+ "description": "在 DSH 中查看和管理本机 Docker 容器与镜像。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -17,7 +17,9 @@
17
17
  "lib",
18
18
  "cordis.patch.yml",
19
19
  "README.md",
20
- "README.en.md"
20
+ "README.en.md",
21
+ "docs",
22
+ "CHANGELOG.md"
21
23
  ],
22
24
  "scripts": {
23
25
  "build": "tsc -p tsconfig.json",