@hyzyn/dsh-docker 0.3.2 → 0.4.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.md +390 -73
- package/client.js +592 -7
- package/lib/docker.d.ts +282 -1
- package/lib/docker.js +607 -11
- package/lib/docker.js.map +1 -1
- package/lib/index.d.ts +16 -3
- package/lib/index.js +921 -3
- package/lib/index.js.map +1 -1
- package/lib/ssh-exec.d.ts +36 -0
- package/lib/ssh-exec.js +200 -2
- package/lib/ssh-exec.js.map +1 -1
- package/package.json +2 -2
package/lib/docker.d.ts
CHANGED
|
@@ -9,12 +9,17 @@
|
|
|
9
9
|
* 需要权威数据时用 `docker inspect`。
|
|
10
10
|
* - 纯函数(parse*)单独导出,供 scripts/smoke.mjs 用固定输出做回归。
|
|
11
11
|
*/
|
|
12
|
-
import type { ExecResult, HostKeyStore, SshSpec, ExecLogger } from './ssh-exec.js';
|
|
12
|
+
import type { ExecResult, HostKeyStore, SshSpec, ExecLogger, StreamHandlers, StreamResult } from './ssh-exec.js';
|
|
13
13
|
import { RemoteExec } from './ssh-exec.js';
|
|
14
|
+
export type { StreamHandlers, StreamResult } from './ssh-exec.js';
|
|
14
15
|
/** 校验一个 docker 引用(容器名 / ID / 镜像)。不合法直接抛错,绝不拼接进命令。 */
|
|
15
16
|
export declare function assertRef(value: unknown, field: string): string;
|
|
16
17
|
/** docker CLI 可执行文件白名单(argv[0],不设默认值以免误用其他程序)。 */
|
|
17
18
|
export declare function assertBin(value: unknown): string;
|
|
19
|
+
/** 校验一个镜像引用(tag / digest / ID)。不合法直接抛错,绝不拼接进命令。 */
|
|
20
|
+
export declare function assertImageRef(value: unknown, field: string): string;
|
|
21
|
+
/** 校验一个 docker 网络 / 卷名(也是 inspect / rm 的引用)。 */
|
|
22
|
+
export declare function assertName(value: unknown, field: string): string;
|
|
18
23
|
/** 逐行 JSON 解析:兼容 `{{json .}}`(每行一个对象)与整体 JSON 数组。 */
|
|
19
24
|
export declare function parseJsonLines(text: string): Record<string, unknown>[];
|
|
20
25
|
/** `12.3MiB` / `1.2kB` / `0B` → 字节数(解析失败返回 null)。 */
|
|
@@ -78,6 +83,29 @@ export interface ContainerStats {
|
|
|
78
83
|
}
|
|
79
84
|
/** `docker stats --no-stream --format '{{json .}}'` → ContainerStats[]。 */
|
|
80
85
|
export declare function parseStatsJson(text: string): ContainerStats[];
|
|
86
|
+
/** 一条容器事件(SSE 帧协议与 docker_events 工具共用同一形状)。 */
|
|
87
|
+
export interface ContainerEvent {
|
|
88
|
+
/** 完整动作串;health_status 带状态后缀(如 'health_status: healthy')。 */
|
|
89
|
+
action: string;
|
|
90
|
+
name: string;
|
|
91
|
+
image: string;
|
|
92
|
+
composeProject: string | null;
|
|
93
|
+
/** 事件时间(Unix 秒;缺失为 null,客户端按本地时区格式化)。 */
|
|
94
|
+
time: number | null;
|
|
95
|
+
/** 仅 die 事件有:容器退出码。 */
|
|
96
|
+
exitCode: number | null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 单行 docker events --format '{{json .}}' → ContainerEvent;坏行 / 非白名单动作
|
|
100
|
+
* 返回 null,由调用方丢弃:事件流里混进一条解析不了的行(daemon 版本差异、
|
|
101
|
+
* 被截断的 chunk)不该把整条流掐掉,也不该变成 error 帧。
|
|
102
|
+
*
|
|
103
|
+
* Action 在老版本里叫 status;health_status 的两种写法都要吃:
|
|
104
|
+
* Action: 'health_status: healthy'(新)与 Action: 'health_status'(老)。
|
|
105
|
+
*/
|
|
106
|
+
export declare function parseContainerEvent(line: string): ContainerEvent | null;
|
|
107
|
+
/** 多行事件输出 → ContainerEvent[](逐行解析,坏行直接丢)。 */
|
|
108
|
+
export declare function parseEventsJson(text: string): ContainerEvent[];
|
|
81
109
|
export interface ImageSummary {
|
|
82
110
|
id: string;
|
|
83
111
|
shortId: string;
|
|
@@ -93,6 +121,129 @@ export interface ImageSummary {
|
|
|
93
121
|
}
|
|
94
122
|
/** `docker images --format '{{json .}}'` → ImageSummary[]。 */
|
|
95
123
|
export declare function parseImagesJson(text: string): ImageSummary[];
|
|
124
|
+
/** 镜像详情(`docker image inspect <ref>` 的权威数据)。 */
|
|
125
|
+
export interface ImageDetail {
|
|
126
|
+
id: string;
|
|
127
|
+
shortId: string;
|
|
128
|
+
/** 标签列表(dangling 镜像为空数组)。 */
|
|
129
|
+
repoTags: string[];
|
|
130
|
+
repoDigests: string[];
|
|
131
|
+
/** 压缩后大小(字节;缺字段为 null)。 */
|
|
132
|
+
size: number | null;
|
|
133
|
+
/** 含父层的虚拟大小(老版本无此字段)。 */
|
|
134
|
+
virtualSize: number | null;
|
|
135
|
+
created: string;
|
|
136
|
+
architecture: string;
|
|
137
|
+
os: string;
|
|
138
|
+
entrypoint: string;
|
|
139
|
+
command: string;
|
|
140
|
+
workingDir: string;
|
|
141
|
+
user: string;
|
|
142
|
+
exposedPorts: string[];
|
|
143
|
+
volumes: string[];
|
|
144
|
+
/** 层(RootFS.Layers 的 diff id,底层 → 顶层)。 */
|
|
145
|
+
layers: string[];
|
|
146
|
+
/** 层数(= layers.length,单独给出便于直接展示)。 */
|
|
147
|
+
layerCount: number;
|
|
148
|
+
/** 镜像标签(截断展示用;值可能很长,仅回传前 50 条)。 */
|
|
149
|
+
labels: Record<string, string>;
|
|
150
|
+
}
|
|
151
|
+
/** `docker image inspect <ref>` 的 JSON 数组 → ImageDetail[]。 */
|
|
152
|
+
export declare function parseImageInspectJson(text: string): ImageDetail[];
|
|
153
|
+
/** 构建历史的一条(`docker history`)。 */
|
|
154
|
+
export interface ImageHistoryEntry {
|
|
155
|
+
id: string;
|
|
156
|
+
shortId: string;
|
|
157
|
+
created: string;
|
|
158
|
+
createdSince: string;
|
|
159
|
+
createdBy: string;
|
|
160
|
+
size: number | null;
|
|
161
|
+
sizeText: string;
|
|
162
|
+
comment: string;
|
|
163
|
+
tags: string[];
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* `docker history --no-trunc --format '{{json .}}'` → ImageHistoryEntry[]。
|
|
167
|
+
* `--format` 只有 Docker ≥ 26 才支持;老版本输出的是纯文本表格,
|
|
168
|
+
* 由 parseImageHistoryText 兜底(调用方先试 JSON)。
|
|
169
|
+
*/
|
|
170
|
+
export declare function parseImageHistoryJson(text: string): ImageHistoryEntry[];
|
|
171
|
+
/**
|
|
172
|
+
* `docker history --no-trunc` 的纯文本表格兜底解析(老版本 docker 没有 --format)。
|
|
173
|
+
*
|
|
174
|
+
* 表格列以 2 个以上空格对齐,但 **CREATED BY 内部也常出现连续双空格**
|
|
175
|
+
* (`#(nop) CMD`、`#(nop) ADD`),所以不按 `\s{2,}` 盲切:先从右往左定位 SIZE
|
|
176
|
+
* (行内最后一个「数字 + 单位」),再用第一个连续双空格把 CREATED 与 CREATED BY 分开。
|
|
177
|
+
* 这是 best-effort:列宽截断(尾部 `…`)与极端构建命令可能让个别字段不完整,
|
|
178
|
+
* 但不会抛错,也不会把整行吞掉。
|
|
179
|
+
*/
|
|
180
|
+
export declare function parseImageHistoryText(text: string): ImageHistoryEntry[];
|
|
181
|
+
export interface NetworkSummary {
|
|
182
|
+
id: string;
|
|
183
|
+
shortId: string;
|
|
184
|
+
name: string;
|
|
185
|
+
driver: string;
|
|
186
|
+
scope: string;
|
|
187
|
+
internal: boolean;
|
|
188
|
+
ipv6: boolean;
|
|
189
|
+
}
|
|
190
|
+
/** `docker network ls --format '{{json .}}'` → NetworkSummary[]。 */
|
|
191
|
+
export declare function parseNetworksJson(text: string): NetworkSummary[];
|
|
192
|
+
/** 网络详情(`docker network inspect <name>` 的权威数据)。 */
|
|
193
|
+
export interface NetworkDetail {
|
|
194
|
+
id: string;
|
|
195
|
+
shortId: string;
|
|
196
|
+
name: string;
|
|
197
|
+
driver: string;
|
|
198
|
+
scope: string;
|
|
199
|
+
created: string;
|
|
200
|
+
internal: boolean;
|
|
201
|
+
attachable: boolean;
|
|
202
|
+
ingress: boolean;
|
|
203
|
+
enableIpv6: boolean;
|
|
204
|
+
/** IPAM.Config 的每一项(子网 / 网关;老 daemon 可能没有)。 */
|
|
205
|
+
subnets: {
|
|
206
|
+
subnet: string;
|
|
207
|
+
gateway: string;
|
|
208
|
+
}[];
|
|
209
|
+
options: Record<string, string>;
|
|
210
|
+
labels: Record<string, string>;
|
|
211
|
+
/** 接入这个网络的容器(Endpoint 视角的数据)。 */
|
|
212
|
+
containers: {
|
|
213
|
+
id: string;
|
|
214
|
+
shortId: string;
|
|
215
|
+
name: string;
|
|
216
|
+
ipv4: string;
|
|
217
|
+
ipv6: string;
|
|
218
|
+
mac: string;
|
|
219
|
+
}[];
|
|
220
|
+
}
|
|
221
|
+
/** `docker network inspect <name>` 的 JSON 数组 → NetworkDetail[]。 */
|
|
222
|
+
export declare function parseNetworkInspectJson(text: string): NetworkDetail[];
|
|
223
|
+
export interface VolumeSummary {
|
|
224
|
+
name: string;
|
|
225
|
+
driver: string;
|
|
226
|
+
scope: string;
|
|
227
|
+
/**
|
|
228
|
+
* 挂载点。`volume ls` 的模板里**没有** CreatedAt(实测 27.5.1),所以列表不带时间;
|
|
229
|
+
* Mountpoint 也不是所有版本都提供,缺字段就退化成空串,列表显示 '—'。
|
|
230
|
+
*/
|
|
231
|
+
mountpoint: string;
|
|
232
|
+
}
|
|
233
|
+
/** `docker volume ls --format '{{json .}}'` → VolumeSummary[]。 */
|
|
234
|
+
export declare function parseVolumesJson(text: string): VolumeSummary[];
|
|
235
|
+
/** 卷详情(`docker volume inspect <name>` 的权威数据)。 */
|
|
236
|
+
export interface VolumeDetail {
|
|
237
|
+
name: string;
|
|
238
|
+
driver: string;
|
|
239
|
+
scope: string;
|
|
240
|
+
mountpoint: string;
|
|
241
|
+
created: string;
|
|
242
|
+
options: Record<string, string>;
|
|
243
|
+
labels: Record<string, string>;
|
|
244
|
+
}
|
|
245
|
+
/** `docker volume inspect <name>` 的 JSON 数组 → VolumeDetail[]。 */
|
|
246
|
+
export declare function parseVolumeInspectJson(text: string): VolumeDetail[];
|
|
96
247
|
export interface MountInfo {
|
|
97
248
|
type: string;
|
|
98
249
|
source: string;
|
|
@@ -146,6 +297,8 @@ export interface Runner {
|
|
|
146
297
|
timeoutMs?: number;
|
|
147
298
|
maxBytes?: number;
|
|
148
299
|
}): Promise<ExecResult>;
|
|
300
|
+
/** 长流(logs --follow):逐块回调,signal 中止;无总超时与输出上限。 */
|
|
301
|
+
stream(argv: readonly string[], handlers: StreamHandlers, signal?: AbortSignal): Promise<StreamResult>;
|
|
149
302
|
}
|
|
150
303
|
export interface DockerAction {
|
|
151
304
|
action: 'start' | 'stop' | 'restart' | 'remove';
|
|
@@ -155,6 +308,8 @@ export interface LogsOptions {
|
|
|
155
308
|
tail?: number;
|
|
156
309
|
timestamps?: boolean;
|
|
157
310
|
since?: string;
|
|
311
|
+
/** 实时跟随(`docker logs --follow`):仅 logsStream() 使用,logs() 忽略。 */
|
|
312
|
+
follow?: boolean;
|
|
158
313
|
}
|
|
159
314
|
export interface ProbeResult {
|
|
160
315
|
ok: boolean;
|
|
@@ -179,13 +334,139 @@ export declare class DockerApi {
|
|
|
179
334
|
listContainers(all: boolean): Promise<ContainerSummary[]>;
|
|
180
335
|
inspect(ids: readonly string[]): Promise<ContainerDetail[]>;
|
|
181
336
|
stats(ids: readonly string[]): Promise<ContainerStats[]>;
|
|
337
|
+
/**
|
|
338
|
+
* 实时统计流:`docker stats`(**不带 --no-stream**)每秒为每个容器输出一行
|
|
339
|
+
* `{{json .}}`。与快照共用 statsArgv 的构造,只差 --no-stream。
|
|
340
|
+
*
|
|
341
|
+
* 与日志流的语义差异:这条流**不会自然结束**——容器一直跑,docker stats 就
|
|
342
|
+
* 一直输出;只有全部被统计的容器退出(或 id 无效)时 docker 才自己退出。
|
|
343
|
+
* 因此「关闭」由浏览器主动断(EventSource.close → res close → abort),
|
|
344
|
+
* 服务端在这条路径上静默中止,不写任何帧。
|
|
345
|
+
*/
|
|
346
|
+
statsStream(ids: readonly string[], handlers: StreamHandlers, signal?: AbortSignal): Promise<StreamResult>;
|
|
347
|
+
/** 统计 argv 的唯一构造点:快照与流式只在 --no-stream 上有差异。 */
|
|
348
|
+
private statsArgv;
|
|
349
|
+
/**
|
|
350
|
+
* 事件流:docker events 持续输出 JSON 行,**不会自然结束**,关闭由浏览器主动断。
|
|
351
|
+
* 不带 --since:默认只从「现在」开始推,活动条要的是新动静而不是历史回放。
|
|
352
|
+
* 带 --filter type=container 挡掉 network / volume / image 事件。
|
|
353
|
+
*/
|
|
354
|
+
eventsStream(handlers: StreamHandlers, signal?: AbortSignal): Promise<StreamResult>;
|
|
355
|
+
/**
|
|
356
|
+
* 事件快照(agent 工具用):必须先有 --until 才能让它退出——docker events
|
|
357
|
+
* 只给 --since 时会一直 follow 下去,run() 会挂到超时。这里把 until 取成**请求
|
|
358
|
+
* 时刻的 RFC3339**(不是字符串 'now':docker 的 --until 只认时间戳或时长)。
|
|
359
|
+
*/
|
|
360
|
+
events(since: string): Promise<ContainerEvent[]>;
|
|
361
|
+
/** 事件 argv 的唯一构造点:流式与快照只差 --since / --until。 */
|
|
362
|
+
private eventsArgv;
|
|
182
363
|
images(): Promise<ImageSummary[]>;
|
|
364
|
+
/**
|
|
365
|
+
* 镜像详情:`docker image inspect`(权威元数据 + 层列表)加上
|
|
366
|
+
* `docker history`(构建历史)。
|
|
367
|
+
*
|
|
368
|
+
* history 走 **两段降级**:先试 `--format '{{json .}}'`(Docker ≥ 26),
|
|
369
|
+
* 老版本会因 unknown flag 失败,再退回纯文本表格;两段都失败只是
|
|
370
|
+
* `historyError` 非空、detail 照常返回——详情页不该因为构建历史取不到就整页报错。
|
|
371
|
+
*/
|
|
372
|
+
imageInspect(ref: string): Promise<{
|
|
373
|
+
ref: string;
|
|
374
|
+
detail: ImageDetail;
|
|
375
|
+
history: ImageHistoryEntry[];
|
|
376
|
+
historyError: string | null;
|
|
377
|
+
}>;
|
|
378
|
+
/** 删除镜像(`docker image rm`,不带 -f)。调用方负责 allowMutations 门禁。 */
|
|
379
|
+
imageRemove(ref: string): Promise<{
|
|
380
|
+
ref: string;
|
|
381
|
+
message: string;
|
|
382
|
+
}>;
|
|
383
|
+
/**
|
|
384
|
+
* 清理 dangling(无标签)镜像:`docker image prune -f`。
|
|
385
|
+
*
|
|
386
|
+
* 刻意**不加 --all**:`--all` 会删掉所有未被容器使用的镜像(含普通 tag 的
|
|
387
|
+
* 基础镜像),破坏性远超「清 dangling」的直觉。要删有标签的镜像请走单个删除
|
|
388
|
+
* (imageRemove)并二次确认。
|
|
389
|
+
*/
|
|
390
|
+
imagePrune(): Promise<{
|
|
391
|
+
message: string;
|
|
392
|
+
}>;
|
|
393
|
+
networks(): Promise<NetworkSummary[]>;
|
|
394
|
+
/**
|
|
395
|
+
* 网络详情。inspect 顺带返回接入的容器,所以列表行**不**逐行 inspect 算容器数
|
|
396
|
+
* (N 条网络就是 N 次 docker 调用),改成点进详情才取一次。
|
|
397
|
+
*/
|
|
398
|
+
networkInspect(name: string): Promise<{
|
|
399
|
+
name: string;
|
|
400
|
+
detail: NetworkDetail;
|
|
401
|
+
}>;
|
|
402
|
+
/** 删除网络(`docker network rm`)。调用方负责 allowMutations 门禁。 */
|
|
403
|
+
networkRemove(name: string): Promise<{
|
|
404
|
+
name: string;
|
|
405
|
+
message: string;
|
|
406
|
+
}>;
|
|
407
|
+
/**
|
|
408
|
+
* 清理未被使用的网络:`docker network prune -f`。
|
|
409
|
+
* `-f` 是必须的(否则 docker 会等交互确认,我们是非交互调用),
|
|
410
|
+
* 且 prune 只动「没有容器接入」的网络——但 compose 的自定义网络也会被清掉
|
|
411
|
+
* (下次 up 会重建),所以调用方仍然要二次确认。
|
|
412
|
+
*/
|
|
413
|
+
networkPrune(): Promise<{
|
|
414
|
+
message: string;
|
|
415
|
+
}>;
|
|
416
|
+
volumes(): Promise<VolumeSummary[]>;
|
|
417
|
+
/** 卷详情(`docker volume inspect <name>`)。 */
|
|
418
|
+
volumeInspect(name: string): Promise<{
|
|
419
|
+
name: string;
|
|
420
|
+
detail: VolumeDetail;
|
|
421
|
+
}>;
|
|
422
|
+
/** 删除卷(`docker volume rm`)。数据随卷一起没,调用方负责 allowMutations 门禁。 */
|
|
423
|
+
volumeRemove(name: string): Promise<{
|
|
424
|
+
name: string;
|
|
425
|
+
message: string;
|
|
426
|
+
}>;
|
|
427
|
+
/**
|
|
428
|
+
* 清理未被容器使用的卷:`docker volume prune -f`。
|
|
429
|
+
*
|
|
430
|
+
* **破坏性最高的一个 prune**:卷里装的是数据。刻意不带 `--all`——实测 docker 27
|
|
431
|
+
* 的 `volume prune` 有 `-a/--all` 开关、不带时只删**匿名**卷;但 docker < 23 没有这个
|
|
432
|
+
* 开关,plain prune 会把命名卷一起删。所以调用方必须二次确认,并且确认文案要写明
|
|
433
|
+
* 这个版本差异(见 README「已知限制」)。
|
|
434
|
+
*/
|
|
435
|
+
volumePrune(): Promise<{
|
|
436
|
+
message: string;
|
|
437
|
+
}>;
|
|
438
|
+
/**
|
|
439
|
+
* 拉取镜像(`docker pull`)。逐层进度天然是流:非 TTY 下 docker 按状态行输出
|
|
440
|
+
* (Pulling fs layer / Downloading / Extracting / Pull complete),直接复用
|
|
441
|
+
* ssh-exec 的长流通道(runLocalStream / RemoteExec.stream),无总超时,
|
|
442
|
+
* 由连接生命周期收尾。调用方负责 allowMutations 门禁。
|
|
443
|
+
*/
|
|
444
|
+
pullStream(ref: string, handlers: StreamHandlers, signal?: AbortSignal): Promise<StreamResult>;
|
|
445
|
+
/** 拉取的快照形态(agent 工具用):一次性跑完,输出有上限。 */
|
|
446
|
+
pull(ref: string, timeoutMs?: number): Promise<{
|
|
447
|
+
ref: string;
|
|
448
|
+
code: number | null;
|
|
449
|
+
text: string;
|
|
450
|
+
truncated: boolean;
|
|
451
|
+
durationMs: number;
|
|
452
|
+
}>;
|
|
183
453
|
/** 日志:stdout / stderr 分别收,再按到达顺序合并(docker logs 两者都有内容)。 */
|
|
184
454
|
logs(id: string, options?: LogsOptions): Promise<{
|
|
185
455
|
id: string;
|
|
186
456
|
text: string;
|
|
187
457
|
truncated: boolean;
|
|
188
458
|
}>;
|
|
459
|
+
/**
|
|
460
|
+
* 实时日志流:`docker logs --follow`,stdout/stderr 逐块回调,直到容器退出 /
|
|
461
|
+
* 远端关闭 / signal 中止。argv 与快照 logs() 共用同一构造(tail 夹紧
|
|
462
|
+
* 1..5000、timestamps / since 语义完全一致),只多一个 --follow。
|
|
463
|
+
*/
|
|
464
|
+
logsStream(id: string, options: LogsOptions | undefined, handlers: StreamHandlers, signal?: AbortSignal): Promise<StreamResult>;
|
|
465
|
+
/**
|
|
466
|
+
* 日志 argv 的唯一构造点:快照与流式只在 `--follow` 上有差异,
|
|
467
|
+
* 校验与夹紧必须逐字一致(否则同一 id 在两条路径上行为漂移)。
|
|
468
|
+
*/
|
|
469
|
+
private logsArgv;
|
|
189
470
|
/** 生命周期操作;调用方负责 readOnly / allowMutations 门禁。 */
|
|
190
471
|
action(request: DockerAction): Promise<{
|
|
191
472
|
id: string;
|