@paretools/docker 0.8.5 → 0.10.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 +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/docker-runner.d.ts +1 -1
- package/dist/lib/docker-runner.d.ts.map +1 -1
- package/dist/lib/docker-runner.js +2 -2
- package/dist/lib/docker-runner.js.map +1 -1
- package/dist/lib/formatters.d.ts +62 -21
- package/dist/lib/formatters.d.ts.map +1 -1
- package/dist/lib/formatters.js +369 -52
- package/dist/lib/formatters.js.map +1 -1
- package/dist/lib/parsers.d.ts +50 -23
- package/dist/lib/parsers.d.ts.map +1 -1
- package/dist/lib/parsers.js +852 -129
- package/dist/lib/parsers.js.map +1 -1
- package/dist/schemas/index.d.ts +267 -9
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +186 -7
- package/dist/schemas/index.js.map +1 -1
- package/dist/tools/build.d.ts.map +1 -1
- package/dist/tools/build.js +122 -21
- package/dist/tools/build.js.map +1 -1
- package/dist/tools/compose-build.d.ts.map +1 -1
- package/dist/tools/compose-build.js +67 -8
- package/dist/tools/compose-build.js.map +1 -1
- package/dist/tools/compose-down.d.ts.map +1 -1
- package/dist/tools/compose-down.js +39 -7
- package/dist/tools/compose-down.js.map +1 -1
- package/dist/tools/compose-logs.d.ts.map +1 -1
- package/dist/tools/compose-logs.js +52 -7
- package/dist/tools/compose-logs.js.map +1 -1
- package/dist/tools/compose-ps.d.ts.map +1 -1
- package/dist/tools/compose-ps.js +65 -8
- package/dist/tools/compose-ps.js.map +1 -1
- package/dist/tools/compose-up.d.ts.map +1 -1
- package/dist/tools/compose-up.js +83 -6
- package/dist/tools/compose-up.js.map +1 -1
- package/dist/tools/exec.d.ts.map +1 -1
- package/dist/tools/exec.js +66 -17
- package/dist/tools/exec.js.map +1 -1
- package/dist/tools/images.d.ts.map +1 -1
- package/dist/tools/images.js +33 -11
- package/dist/tools/images.js.map +1 -1
- package/dist/tools/inspect.d.ts.map +1 -1
- package/dist/tools/inspect.js +43 -16
- package/dist/tools/inspect.js.map +1 -1
- package/dist/tools/logs.d.ts.map +1 -1
- package/dist/tools/logs.js +34 -11
- package/dist/tools/logs.js.map +1 -1
- package/dist/tools/network-ls.d.ts.map +1 -1
- package/dist/tools/network-ls.js +23 -10
- package/dist/tools/network-ls.js.map +1 -1
- package/dist/tools/ps.d.ts.map +1 -1
- package/dist/tools/ps.js +21 -6
- package/dist/tools/ps.js.map +1 -1
- package/dist/tools/pull.d.ts.map +1 -1
- package/dist/tools/pull.js +16 -14
- package/dist/tools/pull.js.map +1 -1
- package/dist/tools/run.d.ts.map +1 -1
- package/dist/tools/run.js +107 -10
- package/dist/tools/run.js.map +1 -1
- package/dist/tools/stats.d.ts.map +1 -1
- package/dist/tools/stats.js +21 -6
- package/dist/tools/stats.js.map +1 -1
- package/dist/tools/volume-ls.d.ts.map +1 -1
- package/dist/tools/volume-ls.js +23 -10
- package/dist/tools/volume-ls.js.map +1 -1
- package/package.json +2 -2
package/dist/lib/formatters.js
CHANGED
|
@@ -6,11 +6,15 @@ export function formatPs(data) {
|
|
|
6
6
|
const ports = portsArr.length
|
|
7
7
|
? ` [${portsArr.map((p) => (p.host ? `${p.host}->${p.container}/${p.protocol}` : `${p.container}/${p.protocol}`)).join(", ")}]`
|
|
8
8
|
: "";
|
|
9
|
-
|
|
9
|
+
// #117/#118: Show labels count and networks
|
|
10
|
+
const labelsInfo = c.labels && Object.keys(c.labels).length > 0 ? ` labels=${Object.keys(c.labels).length}` : "";
|
|
11
|
+
const networksInfo = c.networks && c.networks.length > 0 ? ` nets=${c.networks.join(",")}` : "";
|
|
12
|
+
lines.push(` ${(c.state ?? "unknown").padEnd(10)} ${c.name} (${c.image})${ports}${labelsInfo}${networksInfo}`);
|
|
10
13
|
}
|
|
11
14
|
return lines.join("\n");
|
|
12
15
|
}
|
|
13
|
-
/** Formats structured Docker build results into a human-readable success/failure summary.
|
|
16
|
+
/** Formats structured Docker build results into a human-readable success/failure summary.
|
|
17
|
+
* #97: Includes structured error details with line numbers. */
|
|
14
18
|
export function formatBuild(data) {
|
|
15
19
|
if (data.success) {
|
|
16
20
|
const parts = [`Build succeeded in ${data.duration}s`];
|
|
@@ -18,68 +22,158 @@ export function formatBuild(data) {
|
|
|
18
22
|
parts[0] += ` → ${data.imageId}`;
|
|
19
23
|
if (data.steps)
|
|
20
24
|
parts.push(`${data.steps} steps`);
|
|
25
|
+
if (data.cacheHits != null || data.cacheMisses != null) {
|
|
26
|
+
parts.push(`cache hits=${data.cacheHits ?? 0}, misses=${data.cacheMisses ?? 0}`);
|
|
27
|
+
}
|
|
21
28
|
return parts.join(", ");
|
|
22
29
|
}
|
|
23
30
|
const lines = [`Build failed (${data.duration}s)`];
|
|
31
|
+
if (data.cacheHits != null || data.cacheMisses != null) {
|
|
32
|
+
lines.push(` cache hits=${data.cacheHits ?? 0}, misses=${data.cacheMisses ?? 0}`);
|
|
33
|
+
}
|
|
24
34
|
for (const err of data.errors ?? []) {
|
|
25
|
-
|
|
35
|
+
if (typeof err === "string") {
|
|
36
|
+
lines.push(` ${err}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const lineInfo = err.line != null ? `:${err.line}` : "";
|
|
40
|
+
const fileInfo = err.dockerfile
|
|
41
|
+
? `${err.dockerfile}${lineInfo}`
|
|
42
|
+
: lineInfo
|
|
43
|
+
? `line${lineInfo}`
|
|
44
|
+
: "";
|
|
45
|
+
const prefix = fileInfo ? `${fileInfo}: ` : "";
|
|
46
|
+
lines.push(` ${prefix}${err.message}`);
|
|
47
|
+
}
|
|
26
48
|
}
|
|
27
49
|
return lines.join("\n");
|
|
28
50
|
}
|
|
29
|
-
/** Formats structured Docker logs data into a human-readable output with container name and line count.
|
|
51
|
+
/** Formats structured Docker logs data into a human-readable output with container name and line count.
|
|
52
|
+
* #113: Notes when separate stdout/stderr streams are available. */
|
|
30
53
|
export function formatLogs(data) {
|
|
31
54
|
const header = data.isTruncated
|
|
32
55
|
? `${data.container} (${data.total} of ${data.totalLines} lines, truncated)`
|
|
33
56
|
: `${data.container} (${data.total} lines)`;
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
57
|
+
const streamInfo = data.stdoutLines || data.stderrLines
|
|
58
|
+
? ` [stdout=${(data.stdoutLines ?? []).length}, stderr=${(data.stderrLines ?? []).length}]`
|
|
59
|
+
: "";
|
|
60
|
+
const body = data.entries
|
|
61
|
+
? data.entries.map((e) => `${e.timestamp ? `${e.timestamp} ` : ""}${e.message}`).join("\n")
|
|
62
|
+
: (data.lines ?? []).join("\n");
|
|
63
|
+
return `${header}${streamInfo}\n${body}`;
|
|
64
|
+
}
|
|
65
|
+
/** Formats structured Docker image data into a human-readable listing with repository, tag, and size.
|
|
66
|
+
* #110: Shows ISO creation timestamp when available. */
|
|
37
67
|
export function formatImages(data) {
|
|
38
68
|
if (data.total === 0)
|
|
39
69
|
return "No images found.";
|
|
40
70
|
const lines = [`${data.total} images:`];
|
|
41
71
|
for (const img of data.images) {
|
|
42
72
|
const tag = img.tag && img.tag !== "<none>" ? `:${img.tag}` : "";
|
|
43
|
-
|
|
73
|
+
const digest = img.digest ? ` [${img.digest.slice(0, 19)}...]` : "";
|
|
74
|
+
const createdAt = img.createdAt ? ` (${img.createdAt})` : ` (${img.created})`;
|
|
75
|
+
const bytes = img.sizeBytes != null ? ` ~${img.sizeBytes}B` : "";
|
|
76
|
+
const labelCount = img.labels && Object.keys(img.labels).length > 0
|
|
77
|
+
? ` labels=${Object.keys(img.labels).length}`
|
|
78
|
+
: "";
|
|
79
|
+
lines.push(` ${img.repository}${tag} (${img.size}${bytes})${createdAt}${digest}${labelCount}`);
|
|
44
80
|
}
|
|
45
81
|
return lines.join("\n");
|
|
46
82
|
}
|
|
47
|
-
/** Formats structured Docker run output into a human-readable summary.
|
|
83
|
+
/** Formats structured Docker run output into a human-readable summary.
|
|
84
|
+
* #121/#122: Shows exitCode, errorCategory, and stdout/stderr for non-detached runs. */
|
|
48
85
|
export function formatRun(data) {
|
|
49
86
|
const name = data.name ? ` (${data.name})` : "";
|
|
50
87
|
const mode = data.detached ? "detached" : "attached";
|
|
51
|
-
|
|
88
|
+
const lines = [`Container ${data.containerId}${name} started from ${data.image} [${mode}]`];
|
|
89
|
+
if (data.exitCode != null && data.exitCode !== 0) {
|
|
90
|
+
lines.push(` Exit code: ${data.exitCode}`);
|
|
91
|
+
if (data.errorCategory)
|
|
92
|
+
lines.push(` Error: ${data.errorCategory}`);
|
|
93
|
+
if (data.stderr)
|
|
94
|
+
lines.push(` stderr: ${data.stderr.slice(0, 200)}`);
|
|
95
|
+
}
|
|
96
|
+
else if (!data.detached) {
|
|
97
|
+
if (data.stdout)
|
|
98
|
+
lines.push(data.stdout);
|
|
99
|
+
if (data.stderr)
|
|
100
|
+
lines.push(`stderr: ${data.stderr}`);
|
|
101
|
+
}
|
|
102
|
+
return lines.join("\n");
|
|
52
103
|
}
|
|
53
|
-
/** Formats structured Docker exec output into a human-readable summary.
|
|
104
|
+
/** Formats structured Docker exec output into a human-readable summary.
|
|
105
|
+
* #108: Shows truncation indicator. */
|
|
54
106
|
export function formatExec(data) {
|
|
55
|
-
const status = data.
|
|
56
|
-
|
|
107
|
+
const status = data.timedOut
|
|
108
|
+
? "timed out"
|
|
109
|
+
: data.success
|
|
110
|
+
? "succeeded"
|
|
111
|
+
: `failed (exit code ${data.exitCode})`;
|
|
112
|
+
const dur = data.duration != null ? ` in ${data.duration}s` : "";
|
|
113
|
+
const trunc = data.isTruncated ? " [truncated]" : "";
|
|
114
|
+
const lines = [`Exec ${status}${dur}${trunc}`];
|
|
57
115
|
if (data.stdout)
|
|
58
116
|
lines.push(data.stdout);
|
|
59
117
|
if (data.stderr)
|
|
60
118
|
lines.push(`stderr: ${data.stderr}`);
|
|
119
|
+
if (data.json !== undefined)
|
|
120
|
+
lines.push(`json: ${JSON.stringify(data.json)}`);
|
|
121
|
+
if (data.parseJsonError)
|
|
122
|
+
lines.push(`json-parse-error: ${data.parseJsonError}`);
|
|
61
123
|
return lines.join("\n");
|
|
62
124
|
}
|
|
63
|
-
/** Formats structured Docker Compose up output into a human-readable summary.
|
|
125
|
+
/** Formats structured Docker Compose up output into a human-readable summary.
|
|
126
|
+
* #107: Shows per-service state details when available. */
|
|
64
127
|
export function formatComposeUp(data) {
|
|
65
128
|
if (!data.success)
|
|
66
129
|
return "Compose up failed";
|
|
67
130
|
if (data.started === 0)
|
|
68
131
|
return "Compose up succeeded (no new services started)";
|
|
69
|
-
|
|
132
|
+
const lines = [
|
|
133
|
+
`Compose up: ${data.started} services started (${(data.services ?? []).join(", ")})`,
|
|
134
|
+
];
|
|
135
|
+
if (data.serviceStates && data.serviceStates.length > 0) {
|
|
136
|
+
for (const ss of data.serviceStates) {
|
|
137
|
+
lines.push(` ${ss.name}: ${ss.action}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (data.networksCreated != null)
|
|
141
|
+
lines.push(` networks created: ${data.networksCreated}`);
|
|
142
|
+
if (data.volumesCreated != null)
|
|
143
|
+
lines.push(` volumes created: ${data.volumesCreated}`);
|
|
144
|
+
return lines.join("\n");
|
|
70
145
|
}
|
|
71
|
-
/** Formats structured Docker Compose down output into a human-readable summary.
|
|
146
|
+
/** Formats structured Docker Compose down output into a human-readable summary.
|
|
147
|
+
* #100: Shows per-container details.
|
|
148
|
+
* #101: Shows separate volume and network counts. */
|
|
72
149
|
export function formatComposeDown(data) {
|
|
73
150
|
if (!data.success)
|
|
74
151
|
return "Compose down failed";
|
|
75
|
-
|
|
152
|
+
const parts = [`Compose down: ${data.stopped} stopped, ${data.removed} removed`];
|
|
153
|
+
if (data.volumesRemoved)
|
|
154
|
+
parts[0] += `, ${data.volumesRemoved} volumes removed`;
|
|
155
|
+
if (data.networksRemoved)
|
|
156
|
+
parts[0] += `, ${data.networksRemoved} networks removed`;
|
|
157
|
+
if (data.containers && data.containers.length > 0) {
|
|
158
|
+
for (const c of data.containers) {
|
|
159
|
+
parts.push(` ${c.name}: ${c.action}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return parts.join("\n");
|
|
76
163
|
}
|
|
77
|
-
/** Formats structured Docker pull output into a human-readable summary.
|
|
164
|
+
/** Formats structured Docker pull output into a human-readable summary.
|
|
165
|
+
* #120: Shows size when available. */
|
|
78
166
|
export function formatPull(data) {
|
|
79
|
-
if (!data.success)
|
|
80
|
-
|
|
167
|
+
if (!data.success) {
|
|
168
|
+
const errType = data.errorType ? ` (${data.errorType})` : "";
|
|
169
|
+
const msg = data.errorMessage ? `: ${data.errorMessage}` : "";
|
|
170
|
+
return `Pull failed for ${data.image}:${data.tag}${errType}${msg}`;
|
|
171
|
+
}
|
|
81
172
|
const digest = data.digest ? ` (${data.digest.slice(0, 19)}...)` : "";
|
|
82
|
-
|
|
173
|
+
const size = data.size ? ` [${data.size}]` : "";
|
|
174
|
+
if (data.status === "up-to-date")
|
|
175
|
+
return `${data.image}:${data.tag} is up to date${digest}`;
|
|
176
|
+
return `Pulled ${data.image}:${data.tag}${digest}${size}`;
|
|
83
177
|
}
|
|
84
178
|
export function compactPsMap(data) {
|
|
85
179
|
return {
|
|
@@ -128,14 +222,22 @@ export function compactBuildMap(data) {
|
|
|
128
222
|
...(data.imageId ? { imageId: data.imageId } : {}),
|
|
129
223
|
duration: data.duration,
|
|
130
224
|
errorCount: (data.errors ?? []).length,
|
|
225
|
+
...(data.cacheHits != null ? { cacheHits: data.cacheHits } : {}),
|
|
226
|
+
...(data.cacheMisses != null ? { cacheMisses: data.cacheMisses } : {}),
|
|
131
227
|
};
|
|
132
228
|
}
|
|
133
229
|
export function formatBuildCompact(data) {
|
|
134
230
|
if (data.success) {
|
|
135
231
|
const id = data.imageId ? ` → ${data.imageId}` : "";
|
|
136
|
-
|
|
232
|
+
const cache = data.cacheHits != null || data.cacheMisses != null
|
|
233
|
+
? ` [cache h=${data.cacheHits ?? 0} m=${data.cacheMisses ?? 0}]`
|
|
234
|
+
: "";
|
|
235
|
+
return `Build succeeded in ${data.duration}s${id}${cache}`;
|
|
137
236
|
}
|
|
138
|
-
|
|
237
|
+
const cache = data.cacheHits != null || data.cacheMisses != null
|
|
238
|
+
? `, cache h=${data.cacheHits ?? 0} m=${data.cacheMisses ?? 0}`
|
|
239
|
+
: "";
|
|
240
|
+
return `Build failed (${data.duration}s, ${data.errorCount} errors${cache})`;
|
|
139
241
|
}
|
|
140
242
|
export function compactLogsMap(data) {
|
|
141
243
|
const HEAD_SIZE = 5;
|
|
@@ -160,13 +262,21 @@ export function compactPullMap(data) {
|
|
|
160
262
|
return {
|
|
161
263
|
image: data.image,
|
|
162
264
|
tag: data.tag,
|
|
265
|
+
...(data.digest ? { digest: data.digest } : {}),
|
|
266
|
+
status: data.status,
|
|
163
267
|
success: data.success,
|
|
268
|
+
...(data.errorType ? { errorType: data.errorType } : {}),
|
|
164
269
|
};
|
|
165
270
|
}
|
|
166
271
|
export function formatPullCompact(data) {
|
|
167
|
-
if (!data.success)
|
|
168
|
-
|
|
169
|
-
|
|
272
|
+
if (!data.success) {
|
|
273
|
+
const err = data.errorType ? ` (${data.errorType})` : "";
|
|
274
|
+
return `Pull failed for ${data.image}:${data.tag}${err}`;
|
|
275
|
+
}
|
|
276
|
+
const digest = data.digest ? ` (${data.digest.slice(0, 19)}...)` : "";
|
|
277
|
+
if (data.status === "up-to-date")
|
|
278
|
+
return `${data.image}:${data.tag} is up to date${digest}`;
|
|
279
|
+
return `Pulled ${data.image}:${data.tag}${digest}`;
|
|
170
280
|
}
|
|
171
281
|
export function compactRunMap(data) {
|
|
172
282
|
return {
|
|
@@ -183,21 +293,43 @@ export function compactExecMap(data) {
|
|
|
183
293
|
return {
|
|
184
294
|
exitCode: data.exitCode,
|
|
185
295
|
success: data.success,
|
|
296
|
+
...(data.duration != null ? { duration: data.duration } : {}),
|
|
297
|
+
...(data.timedOut ? { timedOut: true } : {}),
|
|
298
|
+
...(data.isTruncated ? { isTruncated: true } : {}),
|
|
299
|
+
...(data.stdout ? { stdoutPreview: data.stdout.slice(0, 200) } : {}),
|
|
300
|
+
...(data.stderr ? { stderrPreview: data.stderr.slice(0, 200) } : {}),
|
|
186
301
|
};
|
|
187
302
|
}
|
|
188
303
|
export function formatExecCompact(data) {
|
|
189
|
-
|
|
304
|
+
const dur = data.duration != null ? ` in ${data.duration}s` : "";
|
|
305
|
+
const status = data.timedOut
|
|
306
|
+
? "timed out"
|
|
307
|
+
: data.success
|
|
308
|
+
? "succeeded"
|
|
309
|
+
: `failed (exit code ${data.exitCode})`;
|
|
310
|
+
const trunc = data.isTruncated ? " [truncated]" : "";
|
|
311
|
+
const lines = [`Exec ${status}${dur}${trunc}`];
|
|
312
|
+
if (data.stdoutPreview)
|
|
313
|
+
lines.push(data.stdoutPreview);
|
|
314
|
+
if (data.stderrPreview)
|
|
315
|
+
lines.push(`stderr: ${data.stderrPreview}`);
|
|
316
|
+
return lines.join("\n");
|
|
190
317
|
}
|
|
191
318
|
export function compactComposeUpMap(data) {
|
|
192
319
|
return {
|
|
193
320
|
success: data.success,
|
|
194
321
|
started: data.started,
|
|
322
|
+
...(data.networksCreated != null ? { networksCreated: data.networksCreated } : {}),
|
|
323
|
+
...(data.volumesCreated != null ? { volumesCreated: data.volumesCreated } : {}),
|
|
195
324
|
};
|
|
196
325
|
}
|
|
197
326
|
export function formatComposeUpCompact(data) {
|
|
198
327
|
if (!data.success)
|
|
199
328
|
return "Compose up failed";
|
|
200
|
-
|
|
329
|
+
const infra = data.networksCreated != null || data.volumesCreated != null
|
|
330
|
+
? ` (networks=${data.networksCreated ?? 0}, volumes=${data.volumesCreated ?? 0})`
|
|
331
|
+
: "";
|
|
332
|
+
return `Compose up: ${data.started} services started${infra}`;
|
|
201
333
|
}
|
|
202
334
|
export function compactComposeDownMap(data) {
|
|
203
335
|
return {
|
|
@@ -212,8 +344,24 @@ export function formatComposeDownCompact(data) {
|
|
|
212
344
|
return `Compose down: ${data.stopped} stopped, ${data.removed} removed`;
|
|
213
345
|
}
|
|
214
346
|
// ── Inspect ──────────────────────────────────────────────────────────
|
|
215
|
-
/** Formats structured Docker inspect data into a human-readable summary.
|
|
347
|
+
/** Formats structured Docker inspect data into a human-readable summary.
|
|
348
|
+
* Handles both container and image inspect results. */
|
|
216
349
|
export function formatInspect(data) {
|
|
350
|
+
if (data.inspectType === "image") {
|
|
351
|
+
return formatImageInspect(data);
|
|
352
|
+
}
|
|
353
|
+
if (data.inspectType === "volume") {
|
|
354
|
+
return formatVolumeInspect(data);
|
|
355
|
+
}
|
|
356
|
+
if (data.inspectType === "network") {
|
|
357
|
+
return formatNetworkInspect(data);
|
|
358
|
+
}
|
|
359
|
+
return formatContainerInspect(data);
|
|
360
|
+
}
|
|
361
|
+
/** Formats a container inspect result.
|
|
362
|
+
* #111: Shows network settings.
|
|
363
|
+
* #112: Shows mount information. */
|
|
364
|
+
function formatContainerInspect(data) {
|
|
217
365
|
const lines = [`${data.name} (${data.id})`];
|
|
218
366
|
lines.push(` Image: ${data.image}`);
|
|
219
367
|
const state = data.state;
|
|
@@ -222,38 +370,166 @@ export function formatInspect(data) {
|
|
|
222
370
|
if (state.startedAt)
|
|
223
371
|
lines.push(` Started: ${state.startedAt}`);
|
|
224
372
|
}
|
|
373
|
+
if (data.healthStatus)
|
|
374
|
+
lines.push(` Health: ${data.healthStatus}`);
|
|
375
|
+
if (data.restartPolicy)
|
|
376
|
+
lines.push(` Restart: ${data.restartPolicy}`);
|
|
225
377
|
if (data.platform)
|
|
226
378
|
lines.push(` Platform: ${data.platform}`);
|
|
227
379
|
if (data.created)
|
|
228
380
|
lines.push(` Created: ${data.created}`);
|
|
381
|
+
if (data.env && data.env.length > 0) {
|
|
382
|
+
lines.push(` Env: ${data.env.length} variables`);
|
|
383
|
+
}
|
|
384
|
+
// #111: Show network settings
|
|
385
|
+
if (data.networkSettings) {
|
|
386
|
+
lines.push(` Network: IP=${data.networkSettings.ipAddress}`);
|
|
387
|
+
}
|
|
388
|
+
// #112: Show mounts
|
|
389
|
+
if (data.mounts && data.mounts.length > 0) {
|
|
390
|
+
lines.push(` Mounts: ${data.mounts.length} mount(s)`);
|
|
391
|
+
for (const m of data.mounts) {
|
|
392
|
+
const mode = m.mode ? ` [${m.mode}]` : "";
|
|
393
|
+
lines.push(` ${m.source} → ${m.destination}${mode}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (data.relatedTargets && data.relatedTargets.length > 1) {
|
|
397
|
+
lines.push(` Related targets: ${data.relatedTargets.length}`);
|
|
398
|
+
}
|
|
399
|
+
return lines.join("\n");
|
|
400
|
+
}
|
|
401
|
+
/** Formats an image inspect result. */
|
|
402
|
+
function formatImageInspect(data) {
|
|
403
|
+
const lines = [`Image: ${data.name} (${data.id})`];
|
|
404
|
+
if (data.repoTags && data.repoTags.length > 0) {
|
|
405
|
+
lines.push(` Tags: ${data.repoTags.join(", ")}`);
|
|
406
|
+
}
|
|
407
|
+
if (data.repoDigests && data.repoDigests.length > 0) {
|
|
408
|
+
lines.push(` Digests: ${data.repoDigests.map((d) => d.slice(0, 40) + "...").join(", ")}`);
|
|
409
|
+
}
|
|
410
|
+
if (data.size != null) {
|
|
411
|
+
const sizeMB = (data.size / 1024 / 1024).toFixed(1);
|
|
412
|
+
lines.push(` Size: ${sizeMB} MB`);
|
|
413
|
+
}
|
|
414
|
+
if (data.platform)
|
|
415
|
+
lines.push(` Platform: ${data.platform}`);
|
|
416
|
+
if (data.created)
|
|
417
|
+
lines.push(` Created: ${data.created}`);
|
|
418
|
+
if (data.entrypoint && data.entrypoint.length > 0) {
|
|
419
|
+
lines.push(` Entrypoint: ${data.entrypoint.join(" ")}`);
|
|
420
|
+
}
|
|
421
|
+
if (data.cmd && data.cmd.length > 0) {
|
|
422
|
+
lines.push(` Cmd: ${data.cmd.join(" ")}`);
|
|
423
|
+
}
|
|
424
|
+
if (data.env && data.env.length > 0) {
|
|
425
|
+
lines.push(` Env: ${data.env.length} variables`);
|
|
426
|
+
}
|
|
427
|
+
if (data.relatedTargets && data.relatedTargets.length > 1) {
|
|
428
|
+
lines.push(` Related targets: ${data.relatedTargets.length}`);
|
|
429
|
+
}
|
|
430
|
+
return lines.join("\n");
|
|
431
|
+
}
|
|
432
|
+
function formatVolumeInspect(data) {
|
|
433
|
+
const lines = [`Volume: ${data.name} (${data.id})`];
|
|
434
|
+
if (data.driver)
|
|
435
|
+
lines.push(` Driver: ${data.driver}`);
|
|
436
|
+
if (data.scope)
|
|
437
|
+
lines.push(` Scope: ${data.scope}`);
|
|
438
|
+
if (data.mountpoint)
|
|
439
|
+
lines.push(` Mountpoint: ${data.mountpoint}`);
|
|
440
|
+
if (data.created)
|
|
441
|
+
lines.push(` Created: ${data.created}`);
|
|
442
|
+
if (data.labels && Object.keys(data.labels).length > 0) {
|
|
443
|
+
lines.push(` Labels: ${Object.keys(data.labels).length}`);
|
|
444
|
+
}
|
|
445
|
+
return lines.join("\n");
|
|
446
|
+
}
|
|
447
|
+
function formatNetworkInspect(data) {
|
|
448
|
+
const lines = [`Network: ${data.name} (${data.id})`];
|
|
449
|
+
if (data.driver)
|
|
450
|
+
lines.push(` Driver: ${data.driver}`);
|
|
451
|
+
if (data.scope)
|
|
452
|
+
lines.push(` Scope: ${data.scope}`);
|
|
453
|
+
if (data.created)
|
|
454
|
+
lines.push(` Created: ${data.created}`);
|
|
455
|
+
if (data.labels && Object.keys(data.labels).length > 0) {
|
|
456
|
+
lines.push(` Labels: ${Object.keys(data.labels).length}`);
|
|
457
|
+
}
|
|
229
458
|
return lines.join("\n");
|
|
230
459
|
}
|
|
231
460
|
export function compactInspectMap(data) {
|
|
461
|
+
if (data.inspectType === "image") {
|
|
462
|
+
return {
|
|
463
|
+
id: data.id,
|
|
464
|
+
name: data.name,
|
|
465
|
+
inspectType: "image",
|
|
466
|
+
status: "n/a",
|
|
467
|
+
running: false,
|
|
468
|
+
image: data.image,
|
|
469
|
+
...(data.repoTags ? { repoTags: data.repoTags } : {}),
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
if (data.inspectType === "volume" || data.inspectType === "network") {
|
|
473
|
+
return {
|
|
474
|
+
id: data.id,
|
|
475
|
+
name: data.name,
|
|
476
|
+
inspectType: data.inspectType,
|
|
477
|
+
status: data.status ?? "n/a",
|
|
478
|
+
running: false,
|
|
479
|
+
image: data.image,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
232
482
|
return {
|
|
233
483
|
id: data.id,
|
|
234
484
|
name: data.name,
|
|
485
|
+
inspectType: "container",
|
|
235
486
|
status: data.state?.status ?? data.status ?? "unknown",
|
|
236
487
|
running: data.state?.running ?? data.running ?? false,
|
|
237
488
|
image: data.image,
|
|
489
|
+
...(data.healthStatus ? { healthStatus: data.healthStatus } : {}),
|
|
238
490
|
};
|
|
239
491
|
}
|
|
240
492
|
export function formatInspectCompact(data) {
|
|
241
|
-
|
|
493
|
+
if (data.inspectType === "image") {
|
|
494
|
+
const tags = data.repoTags ? ` tags=${data.repoTags.join(",")}` : "";
|
|
495
|
+
return `Image ${data.name} (${data.id})${tags}`;
|
|
496
|
+
}
|
|
497
|
+
if (data.inspectType === "volume") {
|
|
498
|
+
return `Volume ${data.name} (${data.id})`;
|
|
499
|
+
}
|
|
500
|
+
if (data.inspectType === "network") {
|
|
501
|
+
return `Network ${data.name} (${data.id})`;
|
|
502
|
+
}
|
|
503
|
+
const health = data.healthStatus ? ` health=${data.healthStatus}` : "";
|
|
504
|
+
return `${data.name} (${data.id}) ${data.status} [${data.running ? "running" : "stopped"}] image=${data.image}${health}`;
|
|
242
505
|
}
|
|
243
506
|
// ── Network LS ───────────────────────────────────────────────────────
|
|
244
|
-
/** Formats structured Docker network list into a human-readable listing.
|
|
507
|
+
/** Formats structured Docker network list into a human-readable listing.
|
|
508
|
+
* #115: Shows labels when present.
|
|
509
|
+
* #116: Shows boolean flags. */
|
|
245
510
|
export function formatNetworkLs(data) {
|
|
246
511
|
if (data.total === 0)
|
|
247
512
|
return "No networks found.";
|
|
248
513
|
const lines = [`${data.total} networks:`];
|
|
249
514
|
for (const n of data.networks) {
|
|
250
|
-
|
|
515
|
+
const created = n.createdAt ? ` (${n.createdAt})` : "";
|
|
516
|
+
const flags = [];
|
|
517
|
+
if (n.ipv6)
|
|
518
|
+
flags.push("ipv6");
|
|
519
|
+
if (n.internal)
|
|
520
|
+
flags.push("internal");
|
|
521
|
+
if (n.attachable)
|
|
522
|
+
flags.push("attachable");
|
|
523
|
+
const flagStr = flags.length > 0 ? ` [${flags.join(", ")}]` : "";
|
|
524
|
+
const labelCount = n.labels && Object.keys(n.labels).length > 0 ? ` labels=${Object.keys(n.labels).length}` : "";
|
|
525
|
+
lines.push(` ${n.name} (${n.driver}, ${n.scope})${created}${flagStr}${labelCount}`);
|
|
251
526
|
}
|
|
252
527
|
return lines.join("\n");
|
|
253
528
|
}
|
|
254
529
|
export function compactNetworkLsMap(data) {
|
|
255
530
|
return {
|
|
256
531
|
networks: data.networks.map((n) => ({
|
|
532
|
+
...(n.id ? { id: n.id } : {}),
|
|
257
533
|
name: n.name,
|
|
258
534
|
driver: n.driver,
|
|
259
535
|
})),
|
|
@@ -265,18 +541,23 @@ export function formatNetworkLsCompact(data) {
|
|
|
265
541
|
return "No networks found.";
|
|
266
542
|
const lines = [`${data.total} networks:`];
|
|
267
543
|
for (const n of data.networks) {
|
|
268
|
-
|
|
544
|
+
const id = n.id ? ` [${n.id}]` : "";
|
|
545
|
+
lines.push(` ${n.name} (${n.driver})${id}`);
|
|
269
546
|
}
|
|
270
547
|
return lines.join("\n");
|
|
271
548
|
}
|
|
272
549
|
// ── Volume LS ────────────────────────────────────────────────────────
|
|
273
|
-
/** Formats structured Docker volume list into a human-readable listing.
|
|
550
|
+
/** Formats structured Docker volume list into a human-readable listing.
|
|
551
|
+
* #125: Shows labels when present. */
|
|
274
552
|
export function formatVolumeLs(data) {
|
|
275
553
|
if (data.total === 0)
|
|
276
554
|
return "No volumes found.";
|
|
277
555
|
const lines = [`${data.total} volumes:`];
|
|
278
556
|
for (const v of data.volumes) {
|
|
279
|
-
|
|
557
|
+
const created = v.createdAt ? ` (${v.createdAt})` : "";
|
|
558
|
+
const status = v.status ? ` status=${v.status}` : "";
|
|
559
|
+
const labelCount = v.labels && Object.keys(v.labels).length > 0 ? ` labels=${Object.keys(v.labels).length}` : "";
|
|
560
|
+
lines.push(` ${v.name} (${v.driver}, ${v.scope})${created}${status}${labelCount}`);
|
|
280
561
|
}
|
|
281
562
|
return lines.join("\n");
|
|
282
563
|
}
|
|
@@ -285,6 +566,7 @@ export function compactVolumeLsMap(data) {
|
|
|
285
566
|
volumes: data.volumes.map((v) => ({
|
|
286
567
|
name: v.name,
|
|
287
568
|
driver: v.driver,
|
|
569
|
+
...(v.mountpoint ? { mountpoint: v.mountpoint } : {}),
|
|
288
570
|
})),
|
|
289
571
|
total: data.total,
|
|
290
572
|
};
|
|
@@ -294,12 +576,14 @@ export function formatVolumeLsCompact(data) {
|
|
|
294
576
|
return "No volumes found.";
|
|
295
577
|
const lines = [`${data.total} volumes:`];
|
|
296
578
|
for (const v of data.volumes) {
|
|
297
|
-
|
|
579
|
+
const mp = v.mountpoint ? ` @ ${v.mountpoint}` : "";
|
|
580
|
+
lines.push(` ${v.name} (${v.driver})${mp}`);
|
|
298
581
|
}
|
|
299
582
|
return lines.join("\n");
|
|
300
583
|
}
|
|
301
584
|
// ── Compose Build ────────────────────────────────────────────────────
|
|
302
|
-
/** Formats structured Docker Compose build output into a human-readable summary.
|
|
585
|
+
/** Formats structured Docker Compose build output into a human-readable summary.
|
|
586
|
+
* #99: Shows per-service duration. */
|
|
303
587
|
export function formatComposeBuild(data) {
|
|
304
588
|
if (!data.success && data.built === 0) {
|
|
305
589
|
const lines = [`Compose build failed (${data.duration}s)`];
|
|
@@ -313,18 +597,23 @@ export function formatComposeBuild(data) {
|
|
|
313
597
|
for (const s of data.services ?? []) {
|
|
314
598
|
const status = s.success ? "built" : "failed";
|
|
315
599
|
const error = s.error ? ` — ${s.error}` : "";
|
|
316
|
-
|
|
600
|
+
const dur = s.duration != null ? ` (${s.duration}s)` : "";
|
|
601
|
+
const imageId = s.imageId ? ` id=${s.imageId}` : "";
|
|
602
|
+
const image = s.image ? ` image=${s.image}` : "";
|
|
603
|
+
lines.push(` ${s.service}: ${status}${dur}${imageId}${image}${error}`);
|
|
317
604
|
}
|
|
318
605
|
return lines.join("\n");
|
|
319
606
|
}
|
|
320
607
|
// ── Stats ────────────────────────────────────────────────────────────
|
|
321
|
-
/** Formats structured Docker stats data into a human-readable listing with CPU, memory, and I/O.
|
|
608
|
+
/** Formats structured Docker stats data into a human-readable listing with CPU, memory, and I/O.
|
|
609
|
+
* #123/#124: Shows numeric memory and I/O values. */
|
|
322
610
|
export function formatStats(data) {
|
|
323
611
|
if (data.total === 0)
|
|
324
612
|
return "No container stats available.";
|
|
325
613
|
const lines = [`${data.total} containers:`];
|
|
326
614
|
for (const c of data.containers) {
|
|
327
|
-
|
|
615
|
+
const state = c.state ? ` [${c.state}]` : "";
|
|
616
|
+
lines.push(` ${c.name}${state} (${c.id}) CPU: ${c.cpuPercent.toFixed(2)}% Mem: ${c.memoryUsage}/${c.memoryLimit} (${c.memoryPercent.toFixed(2)}%) Net: ${c.netIO} Block: ${c.blockIO} PIDs: ${c.pids}`);
|
|
328
617
|
}
|
|
329
618
|
return lines.join("\n");
|
|
330
619
|
}
|
|
@@ -347,7 +636,9 @@ export function compactStatsMap(data) {
|
|
|
347
636
|
containers: data.containers.map((c) => ({
|
|
348
637
|
id: c.id,
|
|
349
638
|
name: c.name,
|
|
639
|
+
...(c.state ? { state: c.state } : {}),
|
|
350
640
|
cpuPercent: c.cpuPercent,
|
|
641
|
+
memoryUsage: c.memoryUsage,
|
|
351
642
|
memoryPercent: c.memoryPercent,
|
|
352
643
|
pids: c.pids,
|
|
353
644
|
})),
|
|
@@ -359,19 +650,31 @@ export function formatStatsCompact(data) {
|
|
|
359
650
|
return "No container stats available.";
|
|
360
651
|
const lines = [`${data.total} containers:`];
|
|
361
652
|
for (const c of data.containers) {
|
|
362
|
-
|
|
653
|
+
const mem = c.memoryUsage ? ` ${c.memoryUsage}` : "";
|
|
654
|
+
const state = c.state ? ` [${c.state}]` : "";
|
|
655
|
+
lines.push(` ${c.name}${state} (${c.id}) CPU: ${c.cpuPercent.toFixed(2)}% Mem:${mem} (${c.memoryPercent.toFixed(2)}%) PIDs: ${c.pids}`);
|
|
363
656
|
}
|
|
364
657
|
return lines.join("\n");
|
|
365
658
|
}
|
|
366
659
|
// ── Compose PS ───────────────────────────────────────────────────────
|
|
367
|
-
/** Formats structured Docker Compose ps data into a human-readable listing.
|
|
660
|
+
/** Formats structured Docker Compose ps data into a human-readable listing.
|
|
661
|
+
* #105: Shows health status.
|
|
662
|
+
* #106: Shows running/stopped counts. */
|
|
368
663
|
export function formatComposePs(data) {
|
|
369
664
|
if (data.total === 0)
|
|
370
665
|
return "No compose services found.";
|
|
371
|
-
const
|
|
666
|
+
const countInfo = data.running != null && data.stopped != null
|
|
667
|
+
? ` (${data.running} running, ${data.stopped} stopped)`
|
|
668
|
+
: "";
|
|
669
|
+
const lines = [`${data.total} services${countInfo}:`];
|
|
372
670
|
for (const s of data.services) {
|
|
373
|
-
const
|
|
374
|
-
|
|
671
|
+
const portsArr = s.ports ?? [];
|
|
672
|
+
const ports = portsArr.length
|
|
673
|
+
? ` [${portsArr.map((p) => (p.host ? `${p.host}->${p.container}/${p.protocol}` : `${p.container}/${p.protocol}`)).join(", ")}]`
|
|
674
|
+
: "";
|
|
675
|
+
const health = s.health ? ` health=${s.health}` : "";
|
|
676
|
+
const exit = s.exitCode != null ? ` exit=${s.exitCode}` : "";
|
|
677
|
+
lines.push(` ${s.state.padEnd(10)} ${s.name} (${s.service}) ${s.status}${ports}${health}${exit}`);
|
|
375
678
|
}
|
|
376
679
|
return lines.join("\n");
|
|
377
680
|
}
|
|
@@ -381,6 +684,7 @@ export function compactComposePsMap(data) {
|
|
|
381
684
|
name: s.name,
|
|
382
685
|
service: s.service,
|
|
383
686
|
state: s.state,
|
|
687
|
+
...(s.exitCode != null ? { exitCode: s.exitCode } : {}),
|
|
384
688
|
})),
|
|
385
689
|
total: data.total,
|
|
386
690
|
};
|
|
@@ -390,12 +694,14 @@ export function formatComposePsCompact(data) {
|
|
|
390
694
|
return "No compose services found.";
|
|
391
695
|
const lines = [`${data.total} services:`];
|
|
392
696
|
for (const s of data.services) {
|
|
393
|
-
|
|
697
|
+
const exit = s.exitCode != null ? ` exit=${s.exitCode}` : "";
|
|
698
|
+
lines.push(` ${s.state.padEnd(10)} ${s.name} (${s.service})${exit}`);
|
|
394
699
|
}
|
|
395
700
|
return lines.join("\n");
|
|
396
701
|
}
|
|
397
702
|
// ── Compose Logs ─────────────────────────────────────────────────────
|
|
398
|
-
/** Formats structured Docker Compose logs into a human-readable output grouped by service.
|
|
703
|
+
/** Formats structured Docker Compose logs into a human-readable output grouped by service.
|
|
704
|
+
* #104: Shows log level when available. */
|
|
399
705
|
export function formatComposeLogs(data) {
|
|
400
706
|
const header = data.isTruncated
|
|
401
707
|
? `Compose logs: ${data.services.length} services, ${data.total} of ${data.totalEntries} entries (truncated)`
|
|
@@ -403,7 +709,8 @@ export function formatComposeLogs(data) {
|
|
|
403
709
|
const lines = [header];
|
|
404
710
|
for (const entry of data.entries ?? []) {
|
|
405
711
|
const ts = entry.timestamp ? `${entry.timestamp} ` : "";
|
|
406
|
-
|
|
712
|
+
const level = entry.level ? `[${entry.level.toUpperCase()}] ` : "";
|
|
713
|
+
lines.push(` ${entry.service} | ${ts}${level}${entry.message}`);
|
|
407
714
|
}
|
|
408
715
|
return lines.join("\n");
|
|
409
716
|
}
|
|
@@ -414,21 +721,31 @@ export function compactComposeLogsMap(data) {
|
|
|
414
721
|
return {
|
|
415
722
|
services: data.services,
|
|
416
723
|
total: data.total,
|
|
417
|
-
head: entries.slice(0, HEAD_SIZE).map((e) => ({
|
|
724
|
+
head: entries.slice(0, HEAD_SIZE).map((e) => ({
|
|
725
|
+
service: e.service,
|
|
726
|
+
message: e.message,
|
|
727
|
+
...(e.timestamp ? { timestamp: e.timestamp } : {}),
|
|
728
|
+
})),
|
|
418
729
|
tail: data.total > HEAD_SIZE + TAIL_SIZE
|
|
419
|
-
? entries.slice(-TAIL_SIZE).map((e) => ({
|
|
730
|
+
? entries.slice(-TAIL_SIZE).map((e) => ({
|
|
731
|
+
service: e.service,
|
|
732
|
+
message: e.message,
|
|
733
|
+
...(e.timestamp ? { timestamp: e.timestamp } : {}),
|
|
734
|
+
}))
|
|
420
735
|
: [],
|
|
421
736
|
};
|
|
422
737
|
}
|
|
423
738
|
export function formatComposeLogsCompact(data) {
|
|
424
739
|
const parts = [`Compose logs: ${data.services.length} services, ${data.total} entries`];
|
|
425
740
|
for (const e of data.head) {
|
|
426
|
-
|
|
741
|
+
const ts = e.timestamp ? `${e.timestamp} ` : "";
|
|
742
|
+
parts.push(` ${e.service} | ${ts}${e.message}`);
|
|
427
743
|
}
|
|
428
744
|
if (data.tail.length) {
|
|
429
745
|
parts.push(` ... ${data.total - data.head.length - data.tail.length} entries omitted ...`);
|
|
430
746
|
for (const e of data.tail) {
|
|
431
|
-
|
|
747
|
+
const ts = e.timestamp ? `${e.timestamp} ` : "";
|
|
748
|
+
parts.push(` ${e.service} | ${ts}${e.message}`);
|
|
432
749
|
}
|
|
433
750
|
}
|
|
434
751
|
return parts.join("\n");
|