[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
187
domain/systemManager/inspectView.ts
Normal file
187
domain/systemManager/inspectView.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* View models for `docker inspect` payloads (the summarized objects produced
|
||||
* by electron/bridges/systemManager/dockerOps.cjs). All fields are optional —
|
||||
* the raw payload shape varies across docker versions, so every accessor is
|
||||
* defensive.
|
||||
*/
|
||||
|
||||
export interface ContainerInspectView {
|
||||
id?: string;
|
||||
image?: string;
|
||||
status?: string;
|
||||
startedAt?: string;
|
||||
createdAt?: string;
|
||||
restartPolicy?: string;
|
||||
command?: string;
|
||||
ports: string[];
|
||||
networks: string[];
|
||||
mounts: string[];
|
||||
env: string[];
|
||||
labels: string[];
|
||||
}
|
||||
|
||||
export interface ImageInspectView {
|
||||
id?: string;
|
||||
tags: string[];
|
||||
digests: string[];
|
||||
createdAt?: string;
|
||||
size?: string;
|
||||
platform?: string;
|
||||
entrypoint?: string;
|
||||
cmd?: string;
|
||||
workdir?: string;
|
||||
exposedPorts: string[];
|
||||
env: string[];
|
||||
labels: string[];
|
||||
}
|
||||
|
||||
type Dict = Record<string, unknown>;
|
||||
|
||||
function str(value: unknown): string | undefined {
|
||||
return typeof value === 'string' && value.trim() ? value : undefined;
|
||||
}
|
||||
|
||||
function rec(value: unknown): Dict | undefined {
|
||||
return value && typeof value === 'object' && !Array.isArray(value)
|
||||
? (value as Dict)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function strArray(value: unknown): string[] {
|
||||
return Array.isArray(value) ? value.map((v) => String(v)).filter(Boolean) : [];
|
||||
}
|
||||
|
||||
export function shortDockerId(value: unknown): string | undefined {
|
||||
const raw = str(value);
|
||||
if (!raw) return undefined;
|
||||
return raw.replace(/^sha256:/, '').slice(0, 12);
|
||||
}
|
||||
|
||||
function formatIsoDate(value: unknown): string | undefined {
|
||||
const raw = str(value);
|
||||
if (!raw) return undefined;
|
||||
const date = new Date(raw);
|
||||
if (Number.isNaN(date.getTime())) return raw;
|
||||
// Docker uses 0001-01-01T00:00:00Z for "never".
|
||||
if (date.getTime() <= 0) return undefined;
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
export function formatBytes(value: unknown): string | undefined {
|
||||
const num = typeof value === 'number' ? value : Number(value);
|
||||
if (!Number.isFinite(num) || num < 0) return undefined;
|
||||
if (num >= 1024 ** 3) return `${(num / 1024 ** 3).toFixed(2)} GB`;
|
||||
if (num >= 1024 ** 2) return `${(num / 1024 ** 2).toFixed(1)} MB`;
|
||||
if (num >= 1024) return `${(num / 1024).toFixed(1)} KB`;
|
||||
return `${num} B`;
|
||||
}
|
||||
|
||||
function labelLines(value: unknown): string[] {
|
||||
const labels = rec(value);
|
||||
if (!labels) return [];
|
||||
return Object.entries(labels).map(([key, val]) => `${key}=${String(val ?? '')}`);
|
||||
}
|
||||
|
||||
/** "0.0.0.0:8080->80/tcp" for published ports, "80/tcp" for exposed-only. */
|
||||
function portLines(portsMap: unknown): string[] {
|
||||
const ports = rec(portsMap);
|
||||
if (!ports) return [];
|
||||
const lines: string[] = [];
|
||||
for (const [containerPort, bindings] of Object.entries(ports)) {
|
||||
if (!Array.isArray(bindings) || bindings.length === 0) {
|
||||
lines.push(containerPort);
|
||||
continue;
|
||||
}
|
||||
for (const binding of bindings) {
|
||||
const bind = rec(binding);
|
||||
const hostIp = str(bind?.HostIp) ?? '0.0.0.0';
|
||||
const hostPort = str(bind?.HostPort);
|
||||
lines.push(hostPort ? `${hostIp}:${hostPort} -> ${containerPort}` : containerPort);
|
||||
}
|
||||
}
|
||||
return [...new Set(lines)];
|
||||
}
|
||||
|
||||
function commandLine(path: unknown, args: unknown): string | undefined {
|
||||
const parts = [str(path), ...strArray(args)].filter(Boolean) as string[];
|
||||
return parts.length ? parts.join(' ') : undefined;
|
||||
}
|
||||
|
||||
function joinCommand(value: unknown): string | undefined {
|
||||
if (Array.isArray(value)) {
|
||||
const joined = value.map((v) => String(v)).join(' ').trim();
|
||||
return joined || undefined;
|
||||
}
|
||||
return str(value);
|
||||
}
|
||||
|
||||
export function buildContainerInspectView(data: Dict): ContainerInspectView {
|
||||
const state = rec(data.state);
|
||||
const network = rec(data.network);
|
||||
|
||||
let status = str(state?.Status);
|
||||
const exitCode = typeof state?.ExitCode === 'number' ? state.ExitCode : undefined;
|
||||
if (status && status !== 'running' && exitCode !== undefined && exitCode !== 0) {
|
||||
status = `${status} (exit ${exitCode})`;
|
||||
}
|
||||
|
||||
const networks: string[] = [];
|
||||
const networksMap = rec(network?.Networks);
|
||||
if (networksMap) {
|
||||
for (const [name, info] of Object.entries(networksMap)) {
|
||||
const ip = str(rec(info)?.IPAddress);
|
||||
networks.push(ip ? `${name} · ${ip}` : name);
|
||||
}
|
||||
} else {
|
||||
const ip = str(network?.IPAddress);
|
||||
if (ip) networks.push(ip);
|
||||
}
|
||||
|
||||
const mounts = Array.isArray(data.mounts)
|
||||
? data.mounts.map((mount) => {
|
||||
const m = rec(mount);
|
||||
const source = str(m?.Source) ?? str(m?.Name) ?? str(m?.Type) ?? '?';
|
||||
const destination = str(m?.Destination) ?? '?';
|
||||
const mode = m?.RW === false ? 'ro' : 'rw';
|
||||
return `${source} -> ${destination} (${mode})`;
|
||||
})
|
||||
: [];
|
||||
|
||||
const restartPolicy = str(rec(data.restartPolicy)?.Name);
|
||||
|
||||
return {
|
||||
id: shortDockerId(data.id),
|
||||
image: str(data.image),
|
||||
status,
|
||||
startedAt: formatIsoDate(state?.StartedAt),
|
||||
createdAt: formatIsoDate(data.created),
|
||||
restartPolicy,
|
||||
command: commandLine(data.path, data.args),
|
||||
ports: portLines(network?.Ports),
|
||||
networks,
|
||||
mounts,
|
||||
env: strArray(data.env),
|
||||
labels: labelLines(data.labels),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildImageInspectView(data: Dict): ImageInspectView {
|
||||
const config = rec(data.config);
|
||||
const os = str(data.os);
|
||||
const arch = str(data.architecture);
|
||||
|
||||
return {
|
||||
id: shortDockerId(data.id),
|
||||
tags: strArray(data.repoTags),
|
||||
digests: strArray(data.repoDigests),
|
||||
createdAt: formatIsoDate(data.created),
|
||||
size: formatBytes(data.size),
|
||||
platform: os && arch ? `${os}/${arch}` : os ?? arch,
|
||||
entrypoint: joinCommand(config?.entrypoint),
|
||||
cmd: joinCommand(config?.cmd),
|
||||
workdir: str(config?.workingDir),
|
||||
exposedPorts: Object.keys(rec(config?.exposedPorts) ?? {}),
|
||||
env: strArray(config?.env),
|
||||
labels: labelLines(config?.labels),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user