spawnfile 0.1.10 → 0.1.12
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 +13 -2
- package/dist/cli/lifecycleCommands.js +101 -1
- package/dist/cli/modelCommands.js +3 -0
- package/dist/cli/resolveCommandInput.d.ts +19 -0
- package/dist/cli/resolveCommandInput.js +25 -0
- package/dist/cli/runCli.d.ts +4 -1
- package/dist/cli/runCli.js +37 -13
- package/dist/cli/runtimeCommands.js +1 -0
- package/dist/cli/statusCommand.d.ts +42 -0
- package/dist/cli/statusCommand.js +394 -0
- package/dist/cli/surfaceCommands.js +4 -0
- package/dist/compiler/agentSurfaces.js +2 -4
- package/dist/compiler/buildCompilePlanTeams.js +3 -1
- package/dist/compiler/compileProject.js +19 -10
- package/dist/compiler/compileProjectSupport.d.ts +1 -0
- package/dist/compiler/compileProjectSupport.js +1 -1
- package/dist/compiler/containerArtifacts.d.ts +1 -0
- package/dist/compiler/containerArtifacts.js +208 -32
- package/dist/compiler/containerArtifactsPlans.js +7 -4
- package/dist/compiler/containerArtifactsRender.d.ts +7 -1
- package/dist/compiler/containerArtifactsRender.js +6 -0
- package/dist/compiler/containerArtifactsTypes.d.ts +9 -0
- package/dist/compiler/containerEntrypointRender.js +18 -31
- package/dist/compiler/index.d.ts +1 -0
- package/dist/compiler/index.js +1 -0
- package/dist/compiler/moltnetArtifacts.d.ts +2 -0
- package/dist/compiler/moltnetArtifacts.js +58 -99
- package/dist/compiler/moltnetClientConfig.d.ts +2 -2
- package/dist/compiler/moltnetClientConfig.js +16 -23
- package/dist/compiler/moltnetConfigLowering.d.ts +3 -0
- package/dist/compiler/moltnetConfigLowering.js +13 -2
- package/dist/compiler/moltnetNodeConfig.d.ts +27 -0
- package/dist/compiler/moltnetNodeConfig.js +68 -0
- package/dist/compiler/moltnetResolution.js +1 -1
- package/dist/compiler/moltnetRoomMemberships.js +1 -2
- package/dist/compiler/moltnetRoomPolicyCompatibility.d.ts +4 -0
- package/dist/compiler/moltnetRoomPolicyCompatibility.js +28 -0
- package/dist/compiler/moltnetRuntimeConfig.d.ts +1 -1
- package/dist/compiler/moltnetRuntimeConfig.js +1 -13
- package/dist/compiler/networkBinding.d.ts +14 -0
- package/dist/compiler/networkBinding.js +34 -0
- package/dist/compiler/publishProject.d.ts +13 -0
- package/dist/compiler/publishProject.js +72 -0
- package/dist/compiler/runProject.d.ts +26 -12
- package/dist/compiler/runProject.js +102 -125
- package/dist/compiler/runProjectAuth.d.ts +5 -0
- package/dist/compiler/runProjectAuth.js +98 -0
- package/dist/compiler/runProjectDocker.d.ts +19 -0
- package/dist/compiler/runProjectDocker.js +57 -0
- package/dist/compiler/types.d.ts +3 -2
- package/dist/compiler/upProject.d.ts +6 -0
- package/dist/compiler/upProject.js +42 -8
- package/dist/compiler/view/buildOrganizationView.js +27 -3
- package/dist/compiler/view/declarations.d.ts +4 -0
- package/dist/compiler/view/declarations.js +104 -0
- package/dist/compiler/view/renderNetworks.js +12 -3
- package/dist/compiler/view/renderTree.js +16 -3
- package/dist/compiler/view/types.d.ts +77 -1
- package/dist/deployment/dockerInspect.d.ts +29 -0
- package/dist/deployment/dockerInspect.js +130 -0
- package/dist/deployment/dockerLabels.d.ts +16 -0
- package/dist/deployment/dockerLabels.js +32 -0
- package/dist/deployment/dockerLogs.d.ts +24 -0
- package/dist/deployment/dockerLogs.js +96 -0
- package/dist/deployment/dockerManager.d.ts +59 -0
- package/dist/deployment/dockerManager.js +76 -0
- package/dist/deployment/dockerProbeGateway.d.ts +16 -0
- package/dist/deployment/dockerProbeGateway.js +48 -0
- package/dist/deployment/homeStore.d.ts +19 -0
- package/dist/deployment/homeStore.js +130 -0
- package/dist/deployment/index.d.ts +9 -0
- package/dist/deployment/index.js +9 -0
- package/dist/deployment/names.d.ts +3 -0
- package/dist/deployment/names.js +12 -0
- package/dist/deployment/record.d.ts +76 -0
- package/dist/deployment/record.js +174 -0
- package/dist/deployment/target.d.ts +35 -0
- package/dist/deployment/target.js +96 -0
- package/dist/distribution/buildDistributionReport.d.ts +22 -0
- package/dist/distribution/buildDistributionReport.js +51 -0
- package/dist/distribution/consumeImage.d.ts +29 -0
- package/dist/distribution/consumeImage.js +254 -0
- package/dist/distribution/consumeImageSupport.d.ts +18 -0
- package/dist/distribution/consumeImageSupport.js +63 -0
- package/dist/distribution/distributionReportSchema.d.ts +84 -0
- package/dist/distribution/distributionReportSchema.js +107 -0
- package/dist/distribution/dockerRunner.d.ts +5 -0
- package/dist/distribution/dockerRunner.js +21 -0
- package/dist/distribution/extractImage.d.ts +25 -0
- package/dist/distribution/extractImage.js +96 -0
- package/dist/distribution/fingerprint.d.ts +2 -0
- package/dist/distribution/fingerprint.js +14 -0
- package/dist/distribution/imageRef.d.ts +9 -0
- package/dist/distribution/imageRef.js +65 -0
- package/dist/distribution/imageRuntimeAuth.d.ts +19 -0
- package/dist/distribution/imageRuntimeAuth.js +71 -0
- package/dist/distribution/index.d.ts +24 -0
- package/dist/distribution/index.js +16 -0
- package/dist/distribution/preflight.d.ts +11 -0
- package/dist/distribution/preflight.js +65 -0
- package/dist/distribution/projectImageView.d.ts +8 -0
- package/dist/distribution/projectImageView.js +83 -0
- package/dist/distribution/projectName.d.ts +1 -0
- package/dist/distribution/projectName.js +12 -0
- package/dist/distribution/renderImageInterface.d.ts +30 -0
- package/dist/distribution/renderImageInterface.js +86 -0
- package/dist/distribution/tarReader.d.ts +10 -0
- package/dist/distribution/tarReader.js +69 -0
- package/dist/distribution/types.d.ts +80 -0
- package/dist/distribution/types.js +4 -0
- package/dist/distribution/verifyDistributionReport.d.ts +12 -0
- package/dist/distribution/verifyDistributionReport.js +29 -0
- package/dist/filesystem/paths.d.ts +6 -0
- package/dist/filesystem/paths.js +11 -0
- package/dist/manifest/loadManifest.js +22 -2
- package/dist/manifest/renderSpawnfile.js +2 -4
- package/dist/manifest/renderSpawnfileNetworks.js +5 -0
- package/dist/manifest/schemas.d.ts +71 -41
- package/dist/manifest/surfaceSchemas.d.ts +19 -47
- package/dist/manifest/surfaceSchemas.js +3 -6
- package/dist/manifest/teamNetworkAccessSchemas.d.ts +15 -0
- package/dist/manifest/teamNetworkAccessSchemas.js +8 -0
- package/dist/manifest/teamNetworkConsoleSchemas.d.ts +7 -0
- package/dist/manifest/teamNetworkConsoleSchemas.js +19 -0
- package/dist/manifest/teamNetworkSchemas.d.ts +60 -0
- package/dist/manifest/teamNetworkSchemas.js +20 -7
- package/dist/report/createReport.d.ts +7 -1
- package/dist/report/createReport.js +28 -1
- package/dist/report/types.d.ts +43 -0
- package/dist/runtime/container.js +0 -16
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/install.d.ts +0 -10
- package/dist/runtime/install.js +0 -45
- package/dist/runtime/openclaw/adapter.js +9 -1
- package/dist/runtime/openclaw/configAuth.d.ts +15 -0
- package/dist/runtime/openclaw/configAuth.js +46 -0
- package/dist/runtime/openclaw/runAuth.d.ts +6 -0
- package/dist/runtime/openclaw/runAuth.js +7 -55
- package/dist/runtime/openclaw/statusProbes.d.ts +2 -0
- package/dist/runtime/openclaw/statusProbes.js +31 -0
- package/dist/runtime/picoclaw/adapter.js +10 -0
- package/dist/runtime/picoclaw/runAuth.d.ts +7 -0
- package/dist/runtime/picoclaw/runAuth.js +8 -68
- package/dist/runtime/picoclaw/statusProbes.d.ts +2 -0
- package/dist/runtime/picoclaw/statusProbes.js +100 -0
- package/dist/runtime/registry.d.ts +0 -5
- package/dist/runtime/registry.js +2 -12
- package/dist/runtime/statusProbes.d.ts +16 -0
- package/dist/runtime/statusProbes.js +41 -0
- package/dist/runtime/types.d.ts +52 -1
- package/dist/shared/errors.d.ts +6 -0
- package/dist/shared/errors.js +9 -0
- package/dist/status/buildStatus.d.ts +13 -0
- package/dist/status/buildStatus.js +266 -0
- package/dist/status/compileReport.d.ts +96 -0
- package/dist/status/compileReport.js +230 -0
- package/dist/status/compiledContainerObservations.d.ts +5 -0
- package/dist/status/compiledContainerObservations.js +63 -0
- package/dist/status/deploymentLogs.d.ts +12 -0
- package/dist/status/deploymentLogs.js +45 -0
- package/dist/status/deployments.d.ts +14 -0
- package/dist/status/deployments.js +128 -0
- package/dist/status/imageStatusReport.d.ts +10 -0
- package/dist/status/imageStatusReport.js +73 -0
- package/dist/status/index.d.ts +14 -0
- package/dist/status/index.js +14 -0
- package/dist/status/moltnetProbes.d.ts +21 -0
- package/dist/status/moltnetProbes.js +240 -0
- package/dist/status/registryDrift.d.ts +28 -0
- package/dist/status/registryDrift.js +119 -0
- package/dist/status/renderStatus.d.ts +2 -0
- package/dist/status/renderStatus.js +230 -0
- package/dist/status/runtimeProbes.d.ts +13 -0
- package/dist/status/runtimeProbes.js +111 -0
- package/dist/status/selectionSubjects.d.ts +8 -0
- package/dist/status/selectionSubjects.js +77 -0
- package/dist/status/selectors.d.ts +14 -0
- package/dist/status/selectors.js +90 -0
- package/dist/status/traversal.d.ts +6 -0
- package/dist/status/traversal.js +15 -0
- package/dist/status/types.d.ts +113 -0
- package/dist/status/types.js +1 -0
- package/package.json +4 -2
- package/runtimes.yaml +22 -15
- package/dist/runtime/tinyclaw/adapter.d.ts +0 -2
- package/dist/runtime/tinyclaw/adapter.js +0 -284
- package/dist/runtime/tinyclaw/runAuth.d.ts +0 -2
- package/dist/runtime/tinyclaw/runAuth.js +0 -49
- package/dist/runtime/tinyclaw/scaffold-assets/AGENTS.md +0 -160
- package/dist/runtime/tinyclaw/scaffold-assets/CLAUDE.md +0 -5
- package/dist/runtime/tinyclaw/scaffold-assets/SOUL.md +0 -177
- package/dist/runtime/tinyclaw/scaffold.d.ts +0 -2
- package/dist/runtime/tinyclaw/scaffold.js +0 -24
- package/dist/runtime/tinyclaw/schedules.d.ts +0 -9
- package/dist/runtime/tinyclaw/schedules.js +0 -62
- package/dist/runtime/tinyclaw/surfaces.d.ts +0 -8
- package/dist/runtime/tinyclaw/surfaces.js +0 -94
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { createDockerProbeGateway } from "../deployment/index.js";
|
|
2
|
+
const createObservation = (input) => ({
|
|
3
|
+
...input,
|
|
4
|
+
label: `${input.severity.toUpperCase()} ${input.key}`,
|
|
5
|
+
source: "network"
|
|
6
|
+
});
|
|
7
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
+
const toString = (value) => typeof value === "string" ? value : null;
|
|
9
|
+
const toStringArray = (value) => Array.isArray(value)
|
|
10
|
+
? value.filter((entry) => typeof entry === "string")
|
|
11
|
+
: [];
|
|
12
|
+
const parseRooms = (value) => {
|
|
13
|
+
const entries = isRecord(value) && Array.isArray(value.rooms) ? value.rooms : [];
|
|
14
|
+
return entries.flatMap((entry) => {
|
|
15
|
+
if (!isRecord(entry))
|
|
16
|
+
return [];
|
|
17
|
+
const id = toString(entry.id);
|
|
18
|
+
return id ? [{ id, members: toStringArray(entry.members) }] : [];
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
const parseAgents = (value) => {
|
|
22
|
+
const entries = isRecord(value) && Array.isArray(value.agents) ? value.agents : [];
|
|
23
|
+
return entries.flatMap((entry) => {
|
|
24
|
+
if (!isRecord(entry))
|
|
25
|
+
return [];
|
|
26
|
+
const id = toString(entry.id);
|
|
27
|
+
return id
|
|
28
|
+
? [{ connected: entry.connected === true, id, rooms: toStringArray(entry.rooms) }]
|
|
29
|
+
: [];
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
const parseNetwork = (value) => ({
|
|
33
|
+
directMessages: isRecord(value) && isRecord(value.capabilities) && typeof value.capabilities.direct_messages === "boolean"
|
|
34
|
+
? value.capabilities.direct_messages
|
|
35
|
+
: null,
|
|
36
|
+
networkId: isRecord(value) ? toString(value.id) : null
|
|
37
|
+
});
|
|
38
|
+
const defaultFetchJson = async (url, headers, timeoutMs) => {
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
41
|
+
try {
|
|
42
|
+
const response = await fetch(url, { headers, signal: controller.signal });
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
return { error: `HTTP ${response.status}`, json: null, ok: false };
|
|
45
|
+
}
|
|
46
|
+
return { json: await response.json(), ok: true };
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return { error: error instanceof Error ? error.message : String(error), json: null, ok: false };
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const authHeadersFor = (server, authValues = {}) => {
|
|
56
|
+
if (!server.operatorTokenSecret) {
|
|
57
|
+
return {};
|
|
58
|
+
}
|
|
59
|
+
const token = authValues[server.operatorTokenSecret] ?? process.env[server.operatorTokenSecret];
|
|
60
|
+
return token ? { Authorization: `Bearer ${token}` } : null;
|
|
61
|
+
};
|
|
62
|
+
const readManagedJson = async (gateway, server, path, headers) => {
|
|
63
|
+
if (!server.port) {
|
|
64
|
+
return { error: "managed Moltnet server has no internal port", json: null, ok: false };
|
|
65
|
+
}
|
|
66
|
+
const response = await gateway.httpGet(server.port, path, headers);
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
return { error: response.error, json: null, ok: false };
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
return { json: JSON.parse(response.body), ok: true };
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return { error: error instanceof Error ? error.message : String(error), json: null, ok: false };
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const readExternalJson = async (fetchJson, server, path, headers, timeoutMs) => fetchJson(`${server.baseUrl.replace(/\/$/u, "")}${path}`, headers, timeoutMs);
|
|
78
|
+
const createManagedGateway = (deployments, inspections, options) => {
|
|
79
|
+
for (const deployment of deployments) {
|
|
80
|
+
const inspectionMap = inspections.get(deployment.name);
|
|
81
|
+
const unit = deployment.units[0];
|
|
82
|
+
if (!unit)
|
|
83
|
+
continue;
|
|
84
|
+
const inspection = inspectionMap?.get(unit.id);
|
|
85
|
+
if (inspection?.running === true) {
|
|
86
|
+
return createDockerProbeGateway(deployment, unit, {
|
|
87
|
+
execFile: options.execFile,
|
|
88
|
+
inspection,
|
|
89
|
+
timeoutMs: options.timeoutMs
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
};
|
|
95
|
+
const readSnapshot = async (server, options, headers) => {
|
|
96
|
+
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
97
|
+
const gateway = server.mode === "managed"
|
|
98
|
+
? createManagedGateway(options.deployments, options.inspections, options)
|
|
99
|
+
: null;
|
|
100
|
+
if (server.mode === "managed" && !gateway) {
|
|
101
|
+
return { error: "no running deployment unit hosts the managed Moltnet server", json: null, ok: false };
|
|
102
|
+
}
|
|
103
|
+
const readJson = (path) => gateway
|
|
104
|
+
? readManagedJson(gateway, server, path, headers)
|
|
105
|
+
: readExternalJson(options.fetchJson ?? defaultFetchJson, server, path, headers, timeoutMs);
|
|
106
|
+
const health = await readJson("/healthz");
|
|
107
|
+
if (!health.ok)
|
|
108
|
+
return health;
|
|
109
|
+
const network = await readJson("/v1/network");
|
|
110
|
+
if (!network.ok)
|
|
111
|
+
return network;
|
|
112
|
+
const rooms = await readJson("/v1/rooms");
|
|
113
|
+
if (!rooms.ok)
|
|
114
|
+
return rooms;
|
|
115
|
+
const agents = await readJson("/v1/agents");
|
|
116
|
+
if (!agents.ok)
|
|
117
|
+
return agents;
|
|
118
|
+
const parsedNetwork = parseNetwork(network.json);
|
|
119
|
+
return {
|
|
120
|
+
json: null,
|
|
121
|
+
ok: true,
|
|
122
|
+
snapshot: {
|
|
123
|
+
agents: parseAgents(agents.json),
|
|
124
|
+
directMessages: parsedNetwork.directMessages,
|
|
125
|
+
networkId: parsedNetwork.networkId,
|
|
126
|
+
rooms: parseRooms(rooms.json)
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
const compareSnapshot = (server, snapshot) => {
|
|
131
|
+
const observations = [];
|
|
132
|
+
observations.push(createObservation({
|
|
133
|
+
key: "network.reachable",
|
|
134
|
+
message: `${server.networkId} Moltnet server is reachable`,
|
|
135
|
+
severity: "ok",
|
|
136
|
+
subject: `network:${server.networkId}`
|
|
137
|
+
}));
|
|
138
|
+
if (snapshot.networkId && snapshot.networkId !== server.networkId) {
|
|
139
|
+
observations.push(createObservation({
|
|
140
|
+
key: "network.id",
|
|
141
|
+
message: `expected network ${server.networkId}, live network is ${snapshot.networkId}`,
|
|
142
|
+
severity: "error",
|
|
143
|
+
subject: `network:${server.networkId}`
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
const liveRooms = new Map(snapshot.rooms.map((room) => [room.id, room]));
|
|
147
|
+
const liveAgents = new Map(snapshot.agents.map((agent) => [agent.id, agent]));
|
|
148
|
+
for (const room of server.rooms) {
|
|
149
|
+
const liveRoom = liveRooms.get(room.id);
|
|
150
|
+
if (!liveRoom) {
|
|
151
|
+
observations.push(createObservation({
|
|
152
|
+
key: "network.room",
|
|
153
|
+
message: `${server.networkId}/${room.id} is missing from live Moltnet`,
|
|
154
|
+
severity: "error",
|
|
155
|
+
subject: `room:${server.networkId}:${room.id}`
|
|
156
|
+
}));
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
observations.push(createObservation({
|
|
160
|
+
key: "network.room",
|
|
161
|
+
message: `${server.networkId}/${room.id} exists with ${liveRoom.members.length} member(s)`,
|
|
162
|
+
severity: "ok",
|
|
163
|
+
subject: `room:${server.networkId}:${room.id}`
|
|
164
|
+
}));
|
|
165
|
+
for (const member of room.members) {
|
|
166
|
+
const liveAgent = liveAgents.get(member);
|
|
167
|
+
if (!liveAgent) {
|
|
168
|
+
observations.push(createObservation({
|
|
169
|
+
key: "network.member",
|
|
170
|
+
message: `${member} is expected in ${room.id} but is not registered`,
|
|
171
|
+
severity: "warn",
|
|
172
|
+
subject: `agent:${member}`
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
else if (!liveAgent.rooms.includes(room.id)) {
|
|
176
|
+
observations.push(createObservation({
|
|
177
|
+
key: "network.member",
|
|
178
|
+
message: `${member} is registered but not attached to ${room.id}`,
|
|
179
|
+
severity: "warn",
|
|
180
|
+
subject: `agent:${member}`
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
else if (!liveAgent.connected) {
|
|
184
|
+
observations.push(createObservation({
|
|
185
|
+
key: "network.agent.connected",
|
|
186
|
+
message: `${member} is attached to ${room.id} but disconnected`,
|
|
187
|
+
severity: "warn",
|
|
188
|
+
subject: `agent:${member}`
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
observations.push(createObservation({
|
|
193
|
+
key: "network.agent.connected",
|
|
194
|
+
message: `${member} is connected to ${room.id}`,
|
|
195
|
+
severity: "ok",
|
|
196
|
+
subject: `agent:${member}`
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (snapshot.directMessages !== null && server.directMessages !== null && snapshot.directMessages !== server.directMessages) {
|
|
202
|
+
observations.push(createObservation({
|
|
203
|
+
key: "network.direct_messages",
|
|
204
|
+
message: `direct messages expected ${server.directMessages}, live value is ${snapshot.directMessages}`,
|
|
205
|
+
severity: "warn",
|
|
206
|
+
subject: `network:${server.networkId}`
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
return observations;
|
|
210
|
+
};
|
|
211
|
+
export const collectMoltnetProbeObservations = async (options) => {
|
|
212
|
+
if (options.loadedReport.kind !== "loaded") {
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
const observations = [];
|
|
216
|
+
for (const server of options.loadedReport.report.moltnetServers ?? []) {
|
|
217
|
+
const headers = authHeadersFor(server, options.authValues);
|
|
218
|
+
if (headers === null) {
|
|
219
|
+
observations.push(createObservation({
|
|
220
|
+
key: "network.auth",
|
|
221
|
+
message: `${server.networkId} requires operator token ${server.operatorTokenSecret}, but no value is available`,
|
|
222
|
+
severity: "unknown",
|
|
223
|
+
subject: `network:${server.networkId}`
|
|
224
|
+
}));
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const response = await readSnapshot(server, options, headers);
|
|
228
|
+
if (!response.ok || !response.snapshot) {
|
|
229
|
+
observations.push(createObservation({
|
|
230
|
+
key: "network.reachable",
|
|
231
|
+
message: `${server.networkId} Moltnet metadata probe failed: ${response.error ?? "unknown failure"}`,
|
|
232
|
+
severity: "unknown",
|
|
233
|
+
subject: `network:${server.networkId}`
|
|
234
|
+
}));
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
observations.push(...compareSnapshot(server, response.snapshot));
|
|
238
|
+
}
|
|
239
|
+
return observations;
|
|
240
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type DeploymentRecord } from "../deployment/index.js";
|
|
2
|
+
import type { StatusObservation } from "./types.js";
|
|
3
|
+
export type RegistryDigestResolver = (imageRef: string, baseArgs: string[]) => Promise<string | null>;
|
|
4
|
+
export interface RegistryDriftInput {
|
|
5
|
+
deployments: DeploymentRecord[];
|
|
6
|
+
dockerCommand?: string;
|
|
7
|
+
resolveDigest?: RegistryDigestResolver;
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Extracts the comparable digest from `docker manifest inspect --verbose` output.
|
|
12
|
+
*
|
|
13
|
+
* A single-arch tag resolves to one manifest object whose `Descriptor.digest`
|
|
14
|
+
* equals the digest RepoDigests records at deploy time. A multi-arch tag resolves
|
|
15
|
+
* to a JSON ARRAY of per-platform manifests; each `Descriptor.digest` is a CHILD
|
|
16
|
+
* digest, never the manifest-list (index) digest that was recorded — comparing one
|
|
17
|
+
* would report drift on every check. We cannot derive the index digest from this
|
|
18
|
+
* command, so multi-arch (and any unparseable output) yields null → "unknown"
|
|
19
|
+
* rather than a false positive.
|
|
20
|
+
*/
|
|
21
|
+
export declare const parseManifestInspectDigest: (output: string) => string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Compares each image deployment's recorded source.digest against the digest
|
|
24
|
+
* the registry tag currently resolves to. Networked: runs only behind the
|
|
25
|
+
* explicit --pull-check flag. A digest-pinned ref needs no lookup; a null
|
|
26
|
+
* recorded digest renders unknown.
|
|
27
|
+
*/
|
|
28
|
+
export declare const collectRegistryDriftObservations: (input: RegistryDriftInput) => Promise<StatusObservation[]>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { dockerContextNameForTarget, dockerHostValueForTarget } from "../deployment/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Extracts the comparable digest from `docker manifest inspect --verbose` output.
|
|
5
|
+
*
|
|
6
|
+
* A single-arch tag resolves to one manifest object whose `Descriptor.digest`
|
|
7
|
+
* equals the digest RepoDigests records at deploy time. A multi-arch tag resolves
|
|
8
|
+
* to a JSON ARRAY of per-platform manifests; each `Descriptor.digest` is a CHILD
|
|
9
|
+
* digest, never the manifest-list (index) digest that was recorded — comparing one
|
|
10
|
+
* would report drift on every check. We cannot derive the index digest from this
|
|
11
|
+
* command, so multi-arch (and any unparseable output) yields null → "unknown"
|
|
12
|
+
* rather than a false positive.
|
|
13
|
+
*/
|
|
14
|
+
export const parseManifestInspectDigest = (output) => {
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(output);
|
|
17
|
+
if (Array.isArray(parsed)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const digest = parsed.Descriptor?.digest ??
|
|
21
|
+
parsed.digest;
|
|
22
|
+
return typeof digest === "string" && digest.length > 0 ? digest : null;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
/* v8 ignore start -- the docker spawn boundary is covered by distribution E2E */
|
|
29
|
+
const defaultResolveDigest = async (imageRef, baseArgs) => new Promise((resolve) => {
|
|
30
|
+
const child = spawn("docker", [...baseArgs, "manifest", "inspect", "--verbose", imageRef], {
|
|
31
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
32
|
+
});
|
|
33
|
+
const stdout = [];
|
|
34
|
+
child.stdout.on("data", (chunk) => stdout.push(String(chunk)));
|
|
35
|
+
child.on("error", () => resolve(null));
|
|
36
|
+
child.on("close", (code) => {
|
|
37
|
+
resolve(code === 0 ? parseManifestInspectDigest(stdout.join("")) : null);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
/* v8 ignore stop */
|
|
41
|
+
const baseArgsForTarget = (record) => {
|
|
42
|
+
const context = dockerContextNameForTarget(record.target);
|
|
43
|
+
if (context) {
|
|
44
|
+
return ["--context", context];
|
|
45
|
+
}
|
|
46
|
+
const host = dockerHostValueForTarget(record.target);
|
|
47
|
+
return host ? ["--host", host] : [];
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Compares each image deployment's recorded source.digest against the digest
|
|
51
|
+
* the registry tag currently resolves to. Networked: runs only behind the
|
|
52
|
+
* explicit --pull-check flag. A digest-pinned ref needs no lookup; a null
|
|
53
|
+
* recorded digest renders unknown.
|
|
54
|
+
*/
|
|
55
|
+
export const collectRegistryDriftObservations = async (input) => {
|
|
56
|
+
const resolveDigest = input.resolveDigest ?? defaultResolveDigest;
|
|
57
|
+
const observations = [];
|
|
58
|
+
for (const record of input.deployments) {
|
|
59
|
+
if (record.source.kind !== "image") {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const subject = `deployment-unit:${record.name}`;
|
|
63
|
+
const ref = record.source.ref;
|
|
64
|
+
if (ref.includes("@sha256:")) {
|
|
65
|
+
observations.push({
|
|
66
|
+
key: "registry.drift",
|
|
67
|
+
label: "registry",
|
|
68
|
+
message: `${ref} is digest-pinned; registry drift does not apply`,
|
|
69
|
+
severity: "ok",
|
|
70
|
+
source: "deployment",
|
|
71
|
+
subject
|
|
72
|
+
});
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (!record.source.digest) {
|
|
76
|
+
observations.push({
|
|
77
|
+
key: "registry.drift",
|
|
78
|
+
label: "registry",
|
|
79
|
+
message: `${ref} has no recorded registry digest; drift is unknown`,
|
|
80
|
+
severity: "unknown",
|
|
81
|
+
source: "deployment",
|
|
82
|
+
subject
|
|
83
|
+
});
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const current = await resolveDigest(ref, baseArgsForTarget(record));
|
|
87
|
+
if (!current) {
|
|
88
|
+
observations.push({
|
|
89
|
+
key: "registry.drift",
|
|
90
|
+
label: "registry",
|
|
91
|
+
message: `Unable to resolve the current registry digest for ${ref}`,
|
|
92
|
+
severity: "unknown",
|
|
93
|
+
source: "deployment",
|
|
94
|
+
subject
|
|
95
|
+
});
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (current === record.source.digest) {
|
|
99
|
+
observations.push({
|
|
100
|
+
key: "registry.drift",
|
|
101
|
+
label: "registry",
|
|
102
|
+
message: `${ref} matches the recorded registry digest`,
|
|
103
|
+
severity: "ok",
|
|
104
|
+
source: "deployment",
|
|
105
|
+
subject
|
|
106
|
+
});
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
observations.push({
|
|
110
|
+
key: "registry.drift",
|
|
111
|
+
label: "registry",
|
|
112
|
+
message: `A newer build of ${ref} has been published since this deployment`,
|
|
113
|
+
severity: "warn",
|
|
114
|
+
source: "deployment",
|
|
115
|
+
subject
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return observations;
|
|
119
|
+
};
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { renderOrganizationTree } from "../compiler/index.js";
|
|
3
|
+
import { countObservations, getVisibleObservations } from "./buildStatus.js";
|
|
4
|
+
import { flattenOrganizationNodes } from "./traversal.js";
|
|
5
|
+
const SEVERITY_ORDER = ["error", "warn", "unknown", "ok"];
|
|
6
|
+
const relativePath = (value, root) => {
|
|
7
|
+
if (!root) {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
const relative = path.relative(root, value) || ".";
|
|
11
|
+
// A relative path that escapes the project root (e.g. an --out under /tmp) is
|
|
12
|
+
// never more readable than the absolute path — show the absolute one instead.
|
|
13
|
+
return relative.startsWith("..") ? value : relative;
|
|
14
|
+
};
|
|
15
|
+
const formatCounts = (counts) => `${counts.ok} ok, ${counts.warn} warn, ${counts.error} error, ${counts.unknown} unknown`;
|
|
16
|
+
const severityForSubject = (observations, subject, source) => {
|
|
17
|
+
const subjectObservations = observations.filter((observation) => observation.subject === subject && (!source || observation.source === source));
|
|
18
|
+
for (const severity of SEVERITY_ORDER) {
|
|
19
|
+
if (subjectObservations.some((observation) => observation.severity === severity)) {
|
|
20
|
+
return severity;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return "unknown";
|
|
24
|
+
};
|
|
25
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26
|
+
const humanizeMessagePaths = (message, projectRoot) => {
|
|
27
|
+
if (!projectRoot) {
|
|
28
|
+
return message;
|
|
29
|
+
}
|
|
30
|
+
return message.replace(new RegExp(escapeRegExp(projectRoot), "g"), ".");
|
|
31
|
+
};
|
|
32
|
+
const formatObservation = (observation, projectRoot) => `[${observation.severity}] ${observation.subject} ${observation.key}: ${humanizeMessagePaths(observation.message, projectRoot)}`;
|
|
33
|
+
const nonOkObservations = (observations) => observations.filter((observation) => observation.severity !== "ok");
|
|
34
|
+
const observationsWithDetails = (observations) => observations.filter((observation) => observation.details && Object.keys(observation.details).length > 0);
|
|
35
|
+
const formatDetailValue = (value) => typeof value === "string" ? value : JSON.stringify(value);
|
|
36
|
+
const formatObservationDetails = (observation, projectRoot) => {
|
|
37
|
+
const details = observation.details ?? {};
|
|
38
|
+
const lines = [` ${formatObservation(observation, projectRoot)}`];
|
|
39
|
+
const logTail = details.log_tail;
|
|
40
|
+
if (typeof logTail === "string") {
|
|
41
|
+
lines.push(" log_tail:");
|
|
42
|
+
const text = logTail.replace(/\n$/u, "");
|
|
43
|
+
if (text.length === 0) {
|
|
44
|
+
lines.push(" (empty)");
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
lines.push(...text.split("\n").map((line) => ` ${humanizeMessagePaths(line, projectRoot)}`));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const [key, value] of Object.entries(details)) {
|
|
51
|
+
if (key === "log_tail") {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
lines.push(` ${key}: ${humanizeMessagePaths(formatDetailValue(value), projectRoot)}`);
|
|
55
|
+
}
|
|
56
|
+
return lines;
|
|
57
|
+
};
|
|
58
|
+
const formatDocRoles = (node) => node.declared?.docs.map((doc) => doc.role).sort().join(", ") || "none";
|
|
59
|
+
const formatNodeDetails = (node, observations, projectRoot) => {
|
|
60
|
+
const declared = node.declared;
|
|
61
|
+
const details = [
|
|
62
|
+
` ${node.name} (${node.id})${node.runtimeName ? ` runtime=${node.runtimeName}` : ""}`,
|
|
63
|
+
` source: ${relativePath(node.source, projectRoot)}`,
|
|
64
|
+
` declared: ${severityForSubject(observations, node.id, "declared")}`,
|
|
65
|
+
` compiled: ${severityForSubject(observations, node.id, "compile_report")}`,
|
|
66
|
+
` docs: ${formatDocRoles(node)}`
|
|
67
|
+
];
|
|
68
|
+
if (!declared) {
|
|
69
|
+
return details;
|
|
70
|
+
}
|
|
71
|
+
details.push(` skills: ${declared.skills.map((skill) => skill.name).join(", ") || "none"}`);
|
|
72
|
+
details.push(` resources: ${declared.resources.map((resource) => `${resource.id}@${resource.mount}(${resource.sharing})`).join(", ") || "none"}`);
|
|
73
|
+
details.push(` packages: ${declared.packages.map((pkg) => pkg.id).join(", ") || "none"}`);
|
|
74
|
+
details.push(` mcp: ${declared.mcpServers.map((server) => `${server.name}:${server.transport}`).join(", ") || "none"}`);
|
|
75
|
+
details.push(` model: ${declared.model
|
|
76
|
+
? `${declared.model.provider}/${declared.model.name} auth=${declared.model.authMethod}`
|
|
77
|
+
: "none"}`);
|
|
78
|
+
details.push(` schedule: ${declared.schedule
|
|
79
|
+
? `${declared.schedule.kind}${declared.schedule.expression ? ` ${declared.schedule.expression}` : ""}`
|
|
80
|
+
: "none"}`);
|
|
81
|
+
details.push(` surfaces: ${declared.surfaces.map((surface) => `${surface.name}[${surface.scopes.join(",")}]`).join(", ") || "none"}`);
|
|
82
|
+
details.push(` policy: mode=${declared.policy.mode ?? "default"} on_degrade=${declared.policy.onDegrade ?? "default"}`);
|
|
83
|
+
return details;
|
|
84
|
+
};
|
|
85
|
+
const formatNetwork = (network, observations) => [
|
|
86
|
+
` ${network.id} "${network.name}"`,
|
|
87
|
+
` declared: ${severityForSubject(observations, `network:${network.id}`, "declared")}`,
|
|
88
|
+
` provider: ${network.provider}`,
|
|
89
|
+
` server: ${network.serverMode ?? "unspecified"}`,
|
|
90
|
+
` rooms: ${network.rooms.map((room) => `${room.id}[${room.declaredMembers.join(",")}]`).join(", ") || "none"}`
|
|
91
|
+
];
|
|
92
|
+
const formatRuntime = (runtime, observations) => [
|
|
93
|
+
` ${runtime.name}`,
|
|
94
|
+
` declared: ${severityForSubject(observations, `runtime:${runtime.name}`, "declared")}`,
|
|
95
|
+
` nodes: ${runtime.nodeIds.join(", ") || "none"}`
|
|
96
|
+
];
|
|
97
|
+
const formatDeployment = (deployment, projectRoot) => [
|
|
98
|
+
` ${deployment.name} ${deployment.manager} ${deployment.target}`,
|
|
99
|
+
` record: ${relativePath(deployment.recordPath, projectRoot)}`,
|
|
100
|
+
` compile: ${deployment.compileFingerprint}`,
|
|
101
|
+
` created: ${deployment.createdAt}`,
|
|
102
|
+
` auth-profile: ${deployment.authProfile ?? "none"}`,
|
|
103
|
+
...deployment.units.flatMap((unit) => [
|
|
104
|
+
` unit ${unit.id}`,
|
|
105
|
+
` image: ${unit.imageTag}${unit.imageId ? ` (${unit.imageId})` : ""}`,
|
|
106
|
+
` container: ${unit.containerName ?? "none"}${unit.containerId ? ` (${unit.containerId})` : ""}`,
|
|
107
|
+
` contains: ${unit.contains.map((entry) => entry.id).join(", ") || "none"}`,
|
|
108
|
+
` runtimes: ${unit.runtimeInstances.join(", ") || "none"}`,
|
|
109
|
+
` live: ${unit.live ? unit.live.message : "not checked"}`
|
|
110
|
+
])
|
|
111
|
+
];
|
|
112
|
+
const renderHeader = (status, observations) => {
|
|
113
|
+
const counts = countObservations(observations);
|
|
114
|
+
const count = (value, noun) => `${value} ${noun}${value === 1 ? "" : "s"}`;
|
|
115
|
+
return [
|
|
116
|
+
`Spawnfile status: ${relativePath(status.inputPath, status.projectRoot)}`,
|
|
117
|
+
`Output: ${relativePath(status.outputDirectory, status.projectRoot)}`,
|
|
118
|
+
`Summary: ${count(status.summary.agents, "agent")}, ${count(status.summary.teams, "team")}, ${count(status.summary.networks, "network")}, ${count(status.summary.runtimes, "runtime")}, ${count(status.summary.deployments, "deployment")}`,
|
|
119
|
+
`Status: ${formatCounts(counts)}`,
|
|
120
|
+
status.live.requested ? `Live: requested${status.live.deploymentName ? ` deployment=${status.live.deploymentName}` : ""}` : "",
|
|
121
|
+
status.selection ? `Selection: ${status.selection.kind} ${status.selection.label}` : ""
|
|
122
|
+
].filter(Boolean);
|
|
123
|
+
};
|
|
124
|
+
const selectedNodes = (status) => {
|
|
125
|
+
const nodes = flattenOrganizationNodes(status.view);
|
|
126
|
+
if (!status.selection) {
|
|
127
|
+
return nodes;
|
|
128
|
+
}
|
|
129
|
+
if (status.selection.kind === "agent" || status.selection.kind === "team") {
|
|
130
|
+
const subjects = new Set(status.selection.subjectKeys);
|
|
131
|
+
return nodes.filter((node) => subjects.has(node.id));
|
|
132
|
+
}
|
|
133
|
+
if (status.selection.kind === "runtime") {
|
|
134
|
+
const subjects = new Set(status.selection.subjectKeys);
|
|
135
|
+
return nodes.filter((node) => subjects.has(node.id));
|
|
136
|
+
}
|
|
137
|
+
return [];
|
|
138
|
+
};
|
|
139
|
+
const selectedNetworks = (status) => {
|
|
140
|
+
if (status.selection?.kind !== "network") {
|
|
141
|
+
return status.selection ? [] : status.view.networks;
|
|
142
|
+
}
|
|
143
|
+
const subjects = new Set(status.selection.subjectKeys);
|
|
144
|
+
return status.view.networks.filter((network) => subjects.has(`network:${network.id}`));
|
|
145
|
+
};
|
|
146
|
+
const selectedRuntimes = (status) => {
|
|
147
|
+
if (status.selection?.kind !== "runtime") {
|
|
148
|
+
return status.selection ? [] : status.view.runtimes;
|
|
149
|
+
}
|
|
150
|
+
return status.view.runtimes.filter((runtime) => runtime.name === status.selection?.value);
|
|
151
|
+
};
|
|
152
|
+
const selectedDeployments = (status) => {
|
|
153
|
+
if (!status.selection) {
|
|
154
|
+
return status.deployments;
|
|
155
|
+
}
|
|
156
|
+
if (status.selection.kind === "network") {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
const subjects = new Set(status.selection.subjectKeys);
|
|
160
|
+
return status.deployments.filter((deployment) => deployment.units.some((unit) => unit.contains.some((entry) => subjects.has(entry.id))
|
|
161
|
+
|| unit.runtimeInstances.some((runtimeInstance) => subjects.has(`runtime-instance:${runtimeInstance}`))));
|
|
162
|
+
};
|
|
163
|
+
const renderPrettyStatus = (status) => {
|
|
164
|
+
const observations = getVisibleObservations(status);
|
|
165
|
+
const annotations = (subject) => {
|
|
166
|
+
const severity = severityForSubject(observations, subject);
|
|
167
|
+
return [`status=${severity}`];
|
|
168
|
+
};
|
|
169
|
+
const lines = [
|
|
170
|
+
...renderHeader(status, observations),
|
|
171
|
+
"",
|
|
172
|
+
`Compile: ${status.compile.present ? "present" : "missing"} ${relativePath(status.compile.path, status.projectRoot)}`,
|
|
173
|
+
...(status.compile.present ? [] : [" run 'spawnfile compile' to populate compiled status"])
|
|
174
|
+
];
|
|
175
|
+
if (!status.selection) {
|
|
176
|
+
lines.push("", "Organization", renderOrganizationTree(status.view, { annotationFor: annotations }));
|
|
177
|
+
}
|
|
178
|
+
const deployments = selectedDeployments(status);
|
|
179
|
+
if (deployments.length > 0) {
|
|
180
|
+
lines.push("", "Deployments", ...deployments.flatMap((deployment) => formatDeployment(deployment, status.projectRoot)));
|
|
181
|
+
}
|
|
182
|
+
const nodes = selectedNodes(status);
|
|
183
|
+
if (nodes.length > 0) {
|
|
184
|
+
lines.push("", "Nodes", ...nodes.flatMap((node) => formatNodeDetails(node, observations, status.projectRoot)));
|
|
185
|
+
}
|
|
186
|
+
const networks = selectedNetworks(status);
|
|
187
|
+
if (networks.length > 0) {
|
|
188
|
+
lines.push("", "Networks", ...networks.flatMap((network) => formatNetwork(network, observations)));
|
|
189
|
+
}
|
|
190
|
+
const runtimes = selectedRuntimes(status);
|
|
191
|
+
if (runtimes.length > 0) {
|
|
192
|
+
lines.push("", "Runtimes", ...runtimes.flatMap((runtime) => formatRuntime(runtime, observations)));
|
|
193
|
+
}
|
|
194
|
+
const notable = nonOkObservations(observations);
|
|
195
|
+
if (notable.length > 0) {
|
|
196
|
+
lines.push("", "Observations", ...notable.map((observation) => ` ${formatObservation(observation, status.projectRoot)}`));
|
|
197
|
+
}
|
|
198
|
+
const details = observationsWithDetails(observations);
|
|
199
|
+
if (details.length > 0) {
|
|
200
|
+
lines.push("", "Details", ...details.flatMap((observation) => formatObservationDetails(observation, status.projectRoot)));
|
|
201
|
+
}
|
|
202
|
+
return lines.join("\n");
|
|
203
|
+
};
|
|
204
|
+
const renderQuietStatus = (status) => {
|
|
205
|
+
const observations = getVisibleObservations(status);
|
|
206
|
+
const notable = nonOkObservations(observations);
|
|
207
|
+
return [
|
|
208
|
+
...renderHeader(status, observations),
|
|
209
|
+
...notable.map((observation) => formatObservation(observation, status.projectRoot))
|
|
210
|
+
].join("\n");
|
|
211
|
+
};
|
|
212
|
+
const renderJsonStatus = (status) => JSON.stringify({
|
|
213
|
+
compile: status.compile,
|
|
214
|
+
deployments: status.deployments,
|
|
215
|
+
input_path: status.inputPath,
|
|
216
|
+
live: status.live,
|
|
217
|
+
observations: getVisibleObservations(status),
|
|
218
|
+
output_directory: status.outputDirectory,
|
|
219
|
+
project_root: status.projectRoot,
|
|
220
|
+
selection: status.selection,
|
|
221
|
+
summary: status.summary,
|
|
222
|
+
version: status.version,
|
|
223
|
+
view: status.view
|
|
224
|
+
}, null, 2);
|
|
225
|
+
export const renderStatus = (status, options) => {
|
|
226
|
+
if (options.mode === "json") {
|
|
227
|
+
return renderJsonStatus(status);
|
|
228
|
+
}
|
|
229
|
+
return options.mode === "quiet" ? renderQuietStatus(status) : renderPrettyStatus(status);
|
|
230
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DeploymentRecord, DockerInspectionResult, DockerProbeExecFile } from "../deployment/index.js";
|
|
2
|
+
import type { LoadedCompileReport } from "./compileReport.js";
|
|
3
|
+
import type { StatusObservation } from "./types.js";
|
|
4
|
+
export type StatusRuntimeInspections = Map<string, DockerInspectionResult>;
|
|
5
|
+
export interface CollectRuntimeProbeOptions {
|
|
6
|
+
deployments: DeploymentRecord[];
|
|
7
|
+
dockerCommand?: string;
|
|
8
|
+
execFile?: DockerProbeExecFile;
|
|
9
|
+
inspections: StatusRuntimeInspections;
|
|
10
|
+
loadedReport: LoadedCompileReport;
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const collectRuntimeProbeObservations: (options: CollectRuntimeProbeOptions) => Promise<StatusObservation[]>;
|