@stardustlc/dsh-docker 0.1.1 → 0.2.0
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 +4 -1
- package/README.md +4 -1
- package/lib/args.d.ts +2 -0
- package/lib/args.js +7 -0
- package/lib/config.d.ts +1 -1
- package/lib/config.js +2 -2
- package/lib/exec.js +4 -4
- package/lib/index.d.ts +3 -3
- package/lib/index.js +3 -3
- package/lib/parse.d.ts +10 -0
- package/lib/parse.js +21 -0
- package/lib/tools.js +36 -3
- package/package.json +2 -2
package/README.en.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
> **Your agent can manage containers now**: five tools covering container listing, logs, inspection, in-container exec, and lifecycle management.
|
|
6
6
|
|
|
7
|
-
DSH (DeepSeek Harness) container-management plugin: runs the docker CLI through the official subprocess service with shell-free argv arrays, 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, six tools including image listing, an approval gate on `docker_exec`, and **zero runtime dependencies**.
|
|
8
8
|
|
|
9
9
|
   
|
|
10
10
|
|
|
@@ -25,6 +25,7 @@ Requires Docker installed locally (`docker version` should work); use `dockerPat
|
|
|
25
25
|
name: '@stardustlc/dsh-docker'
|
|
26
26
|
config:
|
|
27
27
|
# dockerPath: C:\Program Files\Docker\Docker\resources\bin\docker.exe
|
|
28
|
+
dockerPath: docker # optional; or use the DSH_DOCKER_PATH env var
|
|
28
29
|
timeoutMs: 60000 # per-operation timeout (default 60s, 5s - 10min)
|
|
29
30
|
# execApproval: false # disable the docker_exec approval gate (default true)
|
|
30
31
|
```
|
|
@@ -34,6 +35,7 @@ Requires Docker installed locally (`docker version` should work); use `dockerPat
|
|
|
34
35
|
| Tool | Purpose | Safety |
|
|
35
36
|
| :-- | :-- | :-- |
|
|
36
37
|
| `docker_ps` | List containers (status/image/state, filterable) | — |
|
|
38
|
+
| `docker_images` | List local images (repository/tag/size/created, dangling-only filter) | — |
|
|
37
39
|
| `docker_logs` | Tail container logs (line clamp, short follow) | — |
|
|
38
40
|
| `docker_inspect` | Container details (image/state/ports) | — |
|
|
39
41
|
| `docker_exec` | Run a command inside a container | Approval gate + container-name validation |
|
|
@@ -44,6 +46,7 @@ Requires Docker installed locally (`docker version` should work); use `dockerPat
|
|
|
44
46
|
```text
|
|
45
47
|
docker_ps {}
|
|
46
48
|
docker_ps { all: true, name: web }
|
|
49
|
+
docker_images { dangling: true }
|
|
47
50
|
docker_logs { container: web, tail: 200 }
|
|
48
51
|
docker_inspect { container: web }
|
|
49
52
|
docker_exec { container: web, command: 'df -h' }
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# dsh-docker
|
|
4
4
|
|
|
5
|
-
> **你的 agent
|
|
5
|
+
> **你的 agent 会管容器了**:六个工具覆盖容器/镜像列表、日志、详情、容器内执行与生命周期管理。
|
|
6
6
|
|
|
7
7
|
DSH(DeepSeek Harness)容器管理插件:走官方 subprocess 服务跑 docker CLI,argv 数组无 shell 注入,`docker_exec` 默认审批门,**零运行时依赖**。
|
|
8
8
|
|
|
@@ -25,6 +25,7 @@ dsh plugin --profile web add @stardustlc/dsh-docker
|
|
|
25
25
|
name: '@stardustlc/dsh-docker'
|
|
26
26
|
config:
|
|
27
27
|
# dockerPath: C:\Program Files\Docker\Docker\resources\bin\docker.exe
|
|
28
|
+
dockerPath: docker # 可选;也可用环境变量 DSH_DOCKER_PATH
|
|
28
29
|
timeoutMs: 60000 # 单次操作超时(默认 60 秒,5 秒 - 10 分钟)
|
|
29
30
|
# execApproval: false # 关闭 docker_exec 审批门(默认 true)
|
|
30
31
|
```
|
|
@@ -34,6 +35,7 @@ dsh plugin --profile web add @stardustlc/dsh-docker
|
|
|
34
35
|
| 工具 | 作用 | 安全 |
|
|
35
36
|
| :-- | :-- | :-- |
|
|
36
37
|
| `docker_ps` | 列出容器(状态/镜像/运行态,可过滤)| — |
|
|
38
|
+
| `docker_images` | 列出本地镜像(仓库/标签/大小/创建时间,可只看悬空镜像)| — |
|
|
37
39
|
| `docker_logs` | 查看日志尾部(行数钳制,可短时 follow)| — |
|
|
38
40
|
| `docker_inspect` | 容器详情(镜像/状态/端口)| — |
|
|
39
41
|
| `docker_exec` | 容器内执行命令 | 审批门 + 容器名白名单校验 |
|
|
@@ -44,6 +46,7 @@ dsh plugin --profile web add @stardustlc/dsh-docker
|
|
|
44
46
|
```text
|
|
45
47
|
docker_ps {}
|
|
46
48
|
docker_ps { all: true, name: web }
|
|
49
|
+
docker_images { dangling: true }
|
|
47
50
|
docker_logs { container: web, tail: 200 }
|
|
48
51
|
docker_inspect { container: web }
|
|
49
52
|
docker_exec { container: web, command: 'df -h' }
|
package/lib/args.d.ts
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
export declare function psArgs(docker: string, all: boolean, filter?: string): string[];
|
|
8
8
|
/** 查看日志(tail 钳制)。 */
|
|
9
9
|
export declare function logsArgs(docker: string, container: string, tail: number, follow: boolean): string[];
|
|
10
|
+
/** 列出镜像(JSON 格式;dangling=true 只列悬空镜像)。 */
|
|
11
|
+
export declare function imagesArgs(docker: string, dangling?: boolean): string[];
|
|
10
12
|
/** 查看详情(docker inspect 原生 JSON)。 */
|
|
11
13
|
export declare function inspectArgs(docker: string, container: string): string[];
|
|
12
14
|
/** 容器内执行命令。 */
|
package/lib/args.js
CHANGED
|
@@ -14,6 +14,13 @@ export function psArgs(docker, all, filter) {
|
|
|
14
14
|
export function logsArgs(docker, container, tail, follow) {
|
|
15
15
|
return [docker, 'logs', follow ? '--follow' : '', '--tail', String(tail), container].filter((item) => item !== '');
|
|
16
16
|
}
|
|
17
|
+
/** 列出镜像(JSON 格式;dangling=true 只列悬空镜像)。 */
|
|
18
|
+
export function imagesArgs(docker, dangling = false) {
|
|
19
|
+
const argv = [docker, 'images', '--format', 'json'];
|
|
20
|
+
if (dangling)
|
|
21
|
+
argv.push('--filter', 'dangling=true');
|
|
22
|
+
return argv;
|
|
23
|
+
}
|
|
17
24
|
/** 查看详情(docker inspect 原生 JSON)。 */
|
|
18
25
|
export function inspectArgs(docker, container) {
|
|
19
26
|
return [docker, 'inspect', container];
|
package/lib/config.d.ts
CHANGED
|
@@ -20,6 +20,6 @@ export interface ResolvedDockerConfig {
|
|
|
20
20
|
/**
|
|
21
21
|
* 解析并校验配置。
|
|
22
22
|
*/
|
|
23
|
-
export declare function resolveConfig(config: DockerConfig | undefined | null): ResolvedDockerConfig;
|
|
23
|
+
export declare function resolveConfig(config: DockerConfig | undefined | null, env?: NodeJS.ProcessEnv): ResolvedDockerConfig;
|
|
24
24
|
/** 校验容器名/ID,防参数注入(只允许字母数字与 _-.:/ 之外的拒绝)。 */
|
|
25
25
|
export declare function assertContainerRef(value: string): string;
|
package/lib/config.js
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* 解析并校验配置。
|
|
8
8
|
*/
|
|
9
|
-
export function resolveConfig(config) {
|
|
9
|
+
export function resolveConfig(config, env = process.env) {
|
|
10
10
|
const cfg = config ?? {};
|
|
11
|
-
const dockerPath = typeof cfg.dockerPath === 'string' && cfg.dockerPath.trim() !== '' ? cfg.dockerPath.trim() : 'docker';
|
|
11
|
+
const dockerPath = typeof cfg.dockerPath === 'string' && cfg.dockerPath.trim() !== '' ? cfg.dockerPath.trim() : (env.DSH_DOCKER_PATH?.trim() || 'docker');
|
|
12
12
|
let timeoutMs = 60000;
|
|
13
13
|
if (cfg.timeoutMs !== undefined) {
|
|
14
14
|
if (typeof cfg.timeoutMs !== 'number' || !Number.isFinite(cfg.timeoutMs) || cfg.timeoutMs <= 0)
|
package/lib/exec.js
CHANGED
|
@@ -25,14 +25,14 @@ export function createSubprocessRunner(spawn, graceMs, defaultTimeoutMs) {
|
|
|
25
25
|
graceMs,
|
|
26
26
|
signal: controller.signal,
|
|
27
27
|
});
|
|
28
|
+
const outcome = await handle.done;
|
|
29
|
+
const stdout = handle.collected.stdout?.readFrom(0).text ?? '';
|
|
30
|
+
const stderr = handle.collected.stderr?.readFrom(0).text ?? '';
|
|
31
|
+
return { exitCode: outcome.exitCode, signal: outcome.signal, stdout, stderr };
|
|
28
32
|
}
|
|
29
33
|
finally {
|
|
30
34
|
clearTimeout(timer);
|
|
31
35
|
}
|
|
32
|
-
const outcome = await handle.done;
|
|
33
|
-
const stdout = handle.collected.stdout?.readFrom(0).text ?? '';
|
|
34
|
-
const stderr = handle.collected.stderr?.readFrom(0).text ?? '';
|
|
35
|
-
return { exitCode: outcome.exitCode, signal: outcome.signal, stdout, stderr };
|
|
36
36
|
},
|
|
37
37
|
};
|
|
38
38
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-docker —— 容器管理工具插件(node 半身,配置走 cordis.patch.yml)。
|
|
3
3
|
*
|
|
4
|
-
* 插件导出 apply(ctx, config)
|
|
5
|
-
* docker_inspect / docker_exec / docker_manage)。进程执行走 DSH 官方 subprocess 服务
|
|
4
|
+
* 插件导出 apply(ctx, config):注册六个面向模型的工具(docker_ps / docker_logs /
|
|
5
|
+
* docker_images / docker_inspect / docker_exec / docker_manage)。进程执行走 DSH 官方 subprocess 服务
|
|
6
6
|
* (argv 数组、无 shell),docker_exec 默认走宿主审批门。零运行时依赖。
|
|
7
7
|
*
|
|
8
8
|
* @module dsh-docker
|
|
@@ -36,7 +36,7 @@ export interface DockerPluginContext {
|
|
|
36
36
|
on?(event: string, listener: () => void): () => void;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* 插件入口:解析配置、封装执行器、注册六工具;docker_exec 注入审批门。
|
|
40
40
|
*/
|
|
41
41
|
export declare function apply(ctx: DockerPluginContext, config?: DockerConfig | null): void;
|
|
42
42
|
export * from './args.js';
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-docker —— 容器管理工具插件(node 半身,配置走 cordis.patch.yml)。
|
|
3
3
|
*
|
|
4
|
-
* 插件导出 apply(ctx, config)
|
|
5
|
-
* docker_inspect / docker_exec / docker_manage)。进程执行走 DSH 官方 subprocess 服务
|
|
4
|
+
* 插件导出 apply(ctx, config):注册六个面向模型的工具(docker_ps / docker_logs /
|
|
5
|
+
* docker_images / docker_inspect / docker_exec / docker_manage)。进程执行走 DSH 官方 subprocess 服务
|
|
6
6
|
* (argv 数组、无 shell),docker_exec 默认走宿主审批门。零运行时依赖。
|
|
7
7
|
*
|
|
8
8
|
* @module dsh-docker
|
|
@@ -13,7 +13,7 @@ import { buildDockerTools } from './tools.js';
|
|
|
13
13
|
/** cordis 服务注入:apply 里要用 ctx.subprocess 与 ctx.tools。 */
|
|
14
14
|
export const inject = ['subprocess', 'tools'];
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* 插件入口:解析配置、封装执行器、注册六工具;docker_exec 注入审批门。
|
|
17
17
|
*/
|
|
18
18
|
export function apply(ctx, config) {
|
|
19
19
|
let cfg;
|
package/lib/parse.d.ts
CHANGED
|
@@ -13,5 +13,15 @@ export interface ContainerInfo {
|
|
|
13
13
|
}
|
|
14
14
|
/** docker ps --format json 的每一行解析。 */
|
|
15
15
|
export declare function parsePsJson(text: string): ContainerInfo[];
|
|
16
|
+
/** 镜像摘要。 */
|
|
17
|
+
export interface ImageInfo {
|
|
18
|
+
id: string;
|
|
19
|
+
repository: string;
|
|
20
|
+
tag: string;
|
|
21
|
+
size: string;
|
|
22
|
+
createdSince: string;
|
|
23
|
+
}
|
|
24
|
+
/** docker images --format json 的每一行解析。 */
|
|
25
|
+
export declare function parseImagesJson(text: string): ImageInfo[];
|
|
16
26
|
/** docker inspect 的 JSON 摘要。 */
|
|
17
27
|
export declare function parseInspectJson(text: string): Record<string, unknown>;
|
package/lib/parse.js
CHANGED
|
@@ -24,6 +24,27 @@ export function parsePsJson(text) {
|
|
|
24
24
|
}
|
|
25
25
|
return rows;
|
|
26
26
|
}
|
|
27
|
+
/** docker images --format json 的每一行解析。 */
|
|
28
|
+
export function parseImagesJson(text) {
|
|
29
|
+
const rows = [];
|
|
30
|
+
for (const line of text.split(/\r?\n/)) {
|
|
31
|
+
const trimmed = line.trim();
|
|
32
|
+
if (trimmed === '')
|
|
33
|
+
continue;
|
|
34
|
+
try {
|
|
35
|
+
const raw = JSON.parse(trimmed);
|
|
36
|
+
rows.push({
|
|
37
|
+
id: typeof raw.ID === 'string' ? raw.ID : String(raw.ID ?? ''),
|
|
38
|
+
repository: typeof raw.Repository === 'string' ? raw.Repository : String(raw.Repository ?? ''),
|
|
39
|
+
tag: typeof raw.Tag === 'string' ? raw.Tag : String(raw.Tag ?? ''),
|
|
40
|
+
size: typeof raw.Size === 'string' ? raw.Size : String(raw.Size ?? ''),
|
|
41
|
+
createdSince: typeof raw.CreatedSince === 'string' ? raw.CreatedSince : String(raw.CreatedSince ?? ''),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch { /* 跳过坏行 */ }
|
|
45
|
+
}
|
|
46
|
+
return rows;
|
|
47
|
+
}
|
|
27
48
|
/** docker inspect 的 JSON 摘要。 */
|
|
28
49
|
export function parseInspectJson(text) {
|
|
29
50
|
const raw = JSON.parse(text);
|
package/lib/tools.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module dsh-docker/tools
|
|
5
5
|
*/
|
|
6
|
-
import { inspectArgs, logsArgs, manageArgs, execArgs, psArgs, splitCommand } from './args.js';
|
|
6
|
+
import { imagesArgs, inspectArgs, logsArgs, manageArgs, execArgs, psArgs, splitCommand } from './args.js';
|
|
7
7
|
import { assertContainerRef } from './config.js';
|
|
8
|
-
import { parseInspectJson, parsePsJson } from './parse.js';
|
|
8
|
+
import { parseImagesJson, parseInspectJson, parsePsJson } from './parse.js';
|
|
9
9
|
function compileParameters(spec) {
|
|
10
10
|
const properties = {};
|
|
11
11
|
const required = [];
|
|
@@ -67,6 +67,11 @@ const execSchema = {
|
|
|
67
67
|
properties: { container: { type: 'string' }, exitCode: { type: 'integer' }, stdout: { type: 'string' } },
|
|
68
68
|
additionalProperties: true,
|
|
69
69
|
};
|
|
70
|
+
const imagesSchema = {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: { count: { type: 'integer' }, dangling: { type: 'boolean' }, images: { type: 'array', items: { type: 'object', additionalProperties: true } } },
|
|
73
|
+
additionalProperties: true,
|
|
74
|
+
};
|
|
70
75
|
const manageSchema = {
|
|
71
76
|
type: 'object',
|
|
72
77
|
properties: { container: { type: 'string' }, action: { type: 'string' }, output: { type: 'string' } },
|
|
@@ -131,6 +136,34 @@ export function buildDockerTools(config, runner) {
|
|
|
131
136
|
},
|
|
132
137
|
timeoutMs: timeout,
|
|
133
138
|
};
|
|
139
|
+
const dockerImages = {
|
|
140
|
+
name: 'docker_images',
|
|
141
|
+
description: '列出本地 Docker 镜像(仓库/标签/大小/创建时间)。dangling=true 时只列未被任何标签引用的悬空镜像。',
|
|
142
|
+
parameters: compileParameters({
|
|
143
|
+
dangling: { type: 'boolean', description: '是否只列出悬空镜像(默认 false)。' },
|
|
144
|
+
}),
|
|
145
|
+
output: {
|
|
146
|
+
schema: imagesSchema,
|
|
147
|
+
render: (_args, value) => {
|
|
148
|
+
const rec = asRecord(value);
|
|
149
|
+
const images = Array.isArray(rec.images) ? rec.images : [];
|
|
150
|
+
const lines = ['共 ' + images.length + ' 个镜像:'];
|
|
151
|
+
for (const item of images) {
|
|
152
|
+
const image = asRecord(item);
|
|
153
|
+
lines.push('- ' + image.repository + ':' + image.tag + '(' + image.size + ',' + image.createdSince + ')');
|
|
154
|
+
}
|
|
155
|
+
return [{ type: 'text', text: lines.join('\n') }];
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
async execute(rawArgs) {
|
|
159
|
+
const args = asRecord(rawArgs);
|
|
160
|
+
const dangling = args.dangling === true;
|
|
161
|
+
const result = await runChecked(runner, imagesArgs(cfg.dockerPath, dangling), timeout, 'docker images');
|
|
162
|
+
const images = parseImagesJson(result.stdout);
|
|
163
|
+
return { count: images.length, dangling, images };
|
|
164
|
+
},
|
|
165
|
+
timeoutMs: timeout,
|
|
166
|
+
};
|
|
134
167
|
const dockerInspect = {
|
|
135
168
|
name: 'docker_inspect',
|
|
136
169
|
description: '查看容器详情:镜像、运行状态、启动时间、端口映射。',
|
|
@@ -206,5 +239,5 @@ export function buildDockerTools(config, runner) {
|
|
|
206
239
|
},
|
|
207
240
|
timeoutMs: timeout,
|
|
208
241
|
};
|
|
209
|
-
return [dockerPs, dockerLogs, dockerInspect, dockerExec, dockerManage];
|
|
242
|
+
return [dockerPs, dockerLogs, dockerImages, dockerInspect, dockerExec, dockerManage];
|
|
210
243
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stardustlc/dsh-docker",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "DSH 容器管理插件:docker_ps/logs/inspect/exec/manage
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "DSH 容器管理插件:docker_ps/logs/images/inspect/exec/manage 六工具,走官方 subprocess 服务跑 docker CLI、argv 无 shell 注入、exec 审批门、零运行时依赖。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/index.d.ts",
|