spawnfile 0.1.11 → 0.1.13
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 +21 -3
- package/dist/auth/runtimeCredentials.js +18 -2
- package/dist/cli/devCommands.d.ts +3 -0
- package/dist/cli/devCommands.js +115 -0
- package/dist/cli/lifecycleCommands.js +103 -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 +10 -1
- package/dist/cli/runCli.js +41 -13
- package/dist/cli/runtimeCommands.js +1 -0
- package/dist/cli/statusCommand.d.ts +43 -0
- package/dist/cli/statusCommand.js +428 -0
- package/dist/cli/surfaceCommands.js +4 -0
- package/dist/compiler/agentSurfaces.js +2 -4
- package/dist/compiler/buildProject.d.ts +9 -1
- package/dist/compiler/buildProject.js +44 -5
- package/dist/compiler/compileProject.d.ts +2 -0
- package/dist/compiler/compileProject.js +22 -11
- 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 +31 -69
- package/dist/compiler/containerArtifactsTypes.d.ts +9 -0
- package/dist/compiler/containerEntrypointRender.js +22 -32
- package/dist/compiler/containerPackageRender.d.ts +15 -0
- package/dist/compiler/containerPackageRender.js +63 -0
- package/dist/compiler/containerTargetResources.js +3 -1
- package/dist/compiler/index.d.ts +1 -0
- package/dist/compiler/index.js +1 -0
- package/dist/compiler/moltnetArtifacts.js +21 -1
- package/dist/compiler/moltnetBinaries.d.ts +4 -1
- package/dist/compiler/moltnetBinaries.js +24 -3
- package/dist/compiler/moltnetClientConfig.d.ts +1 -1
- package/dist/compiler/moltnetClientConfig.js +14 -16
- package/dist/compiler/moltnetNodeConfig.js +2 -4
- package/dist/compiler/moltnetResolution.js +1 -1
- package/dist/compiler/moltnetRoomMemberships.js +1 -2
- package/dist/compiler/moltnetRuntimeConfig.d.ts +1 -1
- package/dist/compiler/moltnetRuntimeConfig.js +9 -10
- 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 +109 -126
- 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 +202 -0
- package/dist/compiler/types.d.ts +1 -2
- package/dist/compiler/upProject.d.ts +6 -0
- package/dist/compiler/upProject.js +43 -8
- package/dist/compiler/view/buildOrganizationView.js +4 -1
- package/dist/compiler/view/declarations.d.ts +4 -0
- package/dist/compiler/view/declarations.js +104 -0
- package/dist/compiler/view/renderNetworks.js +1 -2
- package/dist/compiler/view/renderTree.js +6 -2
- package/dist/compiler/view/types.d.ts +64 -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/dockerRecover.d.ts +17 -0
- package/dist/deployment/dockerRecover.js +129 -0
- package/dist/deployment/homeStore.d.ts +19 -0
- package/dist/deployment/homeStore.js +130 -0
- package/dist/deployment/index.d.ts +10 -0
- package/dist/deployment/index.js +10 -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/dev/activity.d.ts +14 -0
- package/dist/dev/activity.js +48 -0
- package/dist/dev/docker.d.ts +16 -0
- package/dist/dev/docker.js +71 -0
- package/dist/dev/index.d.ts +2 -0
- package/dist/dev/index.js +2 -0
- package/dist/dev/project.d.ts +35 -0
- package/dist/dev/project.js +164 -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/schemas.d.ts +17 -41
- package/dist/manifest/surfaceSchemas.d.ts +19 -47
- package/dist/manifest/surfaceSchemas.js +3 -6
- 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/common.d.ts +2 -0
- package/dist/runtime/common.js +1 -1
- package/dist/runtime/container.d.ts +1 -0
- package/dist/runtime/container.js +82 -15
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/install.d.ts +1 -11
- package/dist/runtime/install.js +27 -30
- 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/pi/adapter.d.ts +4 -0
- package/dist/runtime/pi/adapter.js +198 -0
- package/dist/runtime/pi/appActivitySource.d.ts +1 -0
- package/dist/runtime/pi/appActivitySource.js +87 -0
- package/dist/runtime/pi/appCoreSource.d.ts +1 -0
- package/dist/runtime/pi/appCoreSource.js +354 -0
- package/dist/runtime/pi/appPreludeSource.d.ts +1 -0
- package/dist/runtime/pi/appPreludeSource.js +103 -0
- package/dist/runtime/pi/appSource.d.ts +1 -0
- package/dist/runtime/pi/appSource.js +8 -0
- package/dist/runtime/pi/appTemplate.d.ts +28 -0
- package/dist/runtime/pi/appTemplate.js +144 -0
- package/dist/runtime/pi/runAuth.d.ts +2 -0
- package/dist/runtime/pi/runAuth.js +68 -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 +5 -12
- package/dist/runtime/statusProbes.d.ts +16 -0
- package/dist/runtime/statusProbes.js +41 -0
- package/dist/runtime/types.d.ts +53 -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 +121 -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 +9 -2
- package/runtimes.yaml +44 -21
- 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
|
@@ -4,6 +4,7 @@ export const registerRuntimeCommands = (program, handlers, streams) => {
|
|
|
4
4
|
.description("Edit runtime declarations");
|
|
5
5
|
runtimeCommand
|
|
6
6
|
.command("set")
|
|
7
|
+
.description("Set an agent's runtime binding")
|
|
7
8
|
.argument("<name>", "Runtime name")
|
|
8
9
|
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
9
10
|
.option("--recursive", "Update the target project and its descendants")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { inspectDockerDeployment, recoverDockerDeploymentRecords } from "../deployment/index.js";
|
|
3
|
+
import { collectDeploymentLogObservations, collectMoltnetProbeObservations, collectRuntimeProbeObservations, type StatusCommandResult, type StatusExitCode } from "../status/index.js";
|
|
4
|
+
import type { CliHandlers, CliStreams } from "./runCli.js";
|
|
5
|
+
type StatusCommandHandlers = Pick<CliHandlers, "buildOrganizationView"> & Partial<Pick<CliHandlers, "requireAuthProfile">>;
|
|
6
|
+
type StatusCommandLiveHandlers = {
|
|
7
|
+
collectDeploymentLogObservations?: typeof collectDeploymentLogObservations;
|
|
8
|
+
collectMoltnetProbeObservations?: typeof collectMoltnetProbeObservations;
|
|
9
|
+
collectRuntimeProbeObservations?: typeof collectRuntimeProbeObservations;
|
|
10
|
+
inspectDockerDeployment?: typeof inspectDockerDeployment;
|
|
11
|
+
recoverDockerDeploymentRecords?: typeof recoverDockerDeploymentRecords;
|
|
12
|
+
};
|
|
13
|
+
type StatusCommandHandlersWithLive = StatusCommandHandlers & StatusCommandLiveHandlers;
|
|
14
|
+
export interface StatusCommandOptions {
|
|
15
|
+
agent?: string;
|
|
16
|
+
context?: string;
|
|
17
|
+
deployment?: string;
|
|
18
|
+
dockerCommand?: string;
|
|
19
|
+
image?: boolean;
|
|
20
|
+
json?: boolean;
|
|
21
|
+
live?: boolean;
|
|
22
|
+
logs?: boolean;
|
|
23
|
+
network?: string;
|
|
24
|
+
out?: string;
|
|
25
|
+
pretty?: boolean;
|
|
26
|
+
pull?: boolean;
|
|
27
|
+
pullCheck?: boolean;
|
|
28
|
+
quiet?: boolean;
|
|
29
|
+
recover?: boolean;
|
|
30
|
+
runtime?: string;
|
|
31
|
+
team?: string;
|
|
32
|
+
timeout?: string;
|
|
33
|
+
watch?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface StatusWatchOptions {
|
|
36
|
+
intervalMs?: number;
|
|
37
|
+
iterations?: number;
|
|
38
|
+
sleep?: (ms: number) => Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
export declare const executeStatusWatch: (inputPath: string, options: StatusCommandOptions, handlers: StatusCommandHandlersWithLive, streams: CliStreams, setExitCode: (exitCode: StatusExitCode) => void, watchOptions?: StatusWatchOptions) => Promise<void>;
|
|
41
|
+
export declare const executeStatusCommand: (inputPath: string, options: StatusCommandOptions, handlers: StatusCommandHandlersWithLive) => Promise<StatusCommandResult>;
|
|
42
|
+
export declare const registerStatusCommand: (program: Command, handlers: CliHandlers, streams: CliStreams, setExitCode: (exitCode: StatusExitCode) => void) => void;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { inspectDockerDeployment, listDeploymentRecords, listHomeDeploymentRecords, readHomeDeploymentRecord, readHomeDeploymentReport, recoverDockerDeploymentRecords } from "../deployment/index.js";
|
|
2
|
+
import { extractImageReport, normalizeProjectLabelSlug, parseDistributionReport, projectImageOrganizationView, renderImageInterface } from "../distribution/index.js";
|
|
3
|
+
import { collectRegistryDriftObservations } from "../status/index.js";
|
|
4
|
+
import { resolveProjectOutputDirectory } from "../filesystem/index.js";
|
|
5
|
+
import { DEFAULT_OUTPUT_DIRECTORY, errorExitCode, SpawnfileError } from "../shared/index.js";
|
|
6
|
+
import { resolveCommandInput } from "./resolveCommandInput.js";
|
|
7
|
+
import { loadedImageCompileReport } from "../status/index.js";
|
|
8
|
+
import { createStaticStatus, createDeploymentSummaries, collectDeploymentLogObservations, collectMoltnetProbeObservations, collectRuntimeProbeObservations, exitCodeForStatus, flattenOrganizationNodes, loadCompileReport, renderStatus, resolveStatusSelector } from "../status/index.js";
|
|
9
|
+
const inputFailure = (message) => ({
|
|
10
|
+
error: message,
|
|
11
|
+
exitCode: 2
|
|
12
|
+
});
|
|
13
|
+
const resolveOutputMode = (options) => {
|
|
14
|
+
const modes = [
|
|
15
|
+
...(options.json ? ["json"] : []),
|
|
16
|
+
...(options.pretty ? ["pretty"] : []),
|
|
17
|
+
...(options.quiet ? ["quiet"] : [])
|
|
18
|
+
];
|
|
19
|
+
if (modes.length > 1) {
|
|
20
|
+
return inputFailure("Choose only one status output mode: --pretty, --json, or --quiet");
|
|
21
|
+
}
|
|
22
|
+
return modes[0] ?? "pretty";
|
|
23
|
+
};
|
|
24
|
+
const resolveSelectorInput = (options) => {
|
|
25
|
+
const selectors = [
|
|
26
|
+
...(options.agent ? [{ kind: "agent", value: options.agent }] : []),
|
|
27
|
+
...(options.team ? [{ kind: "team", value: options.team }] : []),
|
|
28
|
+
...(options.network ? [{ kind: "network", value: options.network }] : []),
|
|
29
|
+
...(options.runtime ? [{ kind: "runtime", value: options.runtime }] : [])
|
|
30
|
+
];
|
|
31
|
+
if (selectors.length > 1) {
|
|
32
|
+
return inputFailure("Choose only one status selector: --agent, --team, --network, or --runtime");
|
|
33
|
+
}
|
|
34
|
+
return selectors[0] ?? null;
|
|
35
|
+
};
|
|
36
|
+
const resolveTimeoutMs = (options) => {
|
|
37
|
+
if (!options.timeout) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
const parsed = Number(options.timeout);
|
|
41
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
42
|
+
return inputFailure("status --timeout must be a positive integer number of milliseconds");
|
|
43
|
+
}
|
|
44
|
+
return parsed;
|
|
45
|
+
};
|
|
46
|
+
const resolveDeploymentRecords = (records, options) => {
|
|
47
|
+
if (options.deployment) {
|
|
48
|
+
const record = records.find((entry) => entry.record.name === options.deployment);
|
|
49
|
+
return record
|
|
50
|
+
? [record]
|
|
51
|
+
: inputFailure(`Unknown deployment "${options.deployment}". Valid deployments: ${records.map((entry) => entry.record.name).sort().join(", ") || "none"}`);
|
|
52
|
+
}
|
|
53
|
+
if (options.live && records.length > 1) {
|
|
54
|
+
return inputFailure(`status --live requires --deployment when multiple records exist: ${records.map((entry) => entry.record.name).sort().join(", ")}`);
|
|
55
|
+
}
|
|
56
|
+
return records;
|
|
57
|
+
};
|
|
58
|
+
const inspectDeployments = async (records, handlers, options, timeoutMs) => {
|
|
59
|
+
if (!options.live) {
|
|
60
|
+
return new Map();
|
|
61
|
+
}
|
|
62
|
+
const inspect = handlers.inspectDockerDeployment ?? inspectDockerDeployment;
|
|
63
|
+
const inspections = await Promise.all(records.map(async ({ record }) => [
|
|
64
|
+
record.name,
|
|
65
|
+
await inspect(record, {
|
|
66
|
+
dockerCommand: options.dockerCommand,
|
|
67
|
+
timeoutMs
|
|
68
|
+
})
|
|
69
|
+
]));
|
|
70
|
+
return new Map(inspections);
|
|
71
|
+
};
|
|
72
|
+
const containsFromView = (view) => flattenOrganizationNodes(view).map((node) => ({ id: node.id, kind: node.kind }));
|
|
73
|
+
const runtimeInstanceIdsFromReport = (loadedReport) => loadedReport.kind === "loaded"
|
|
74
|
+
? loadedReport.report.runtimeInstances.map((instance) => instance.id).sort()
|
|
75
|
+
: [];
|
|
76
|
+
const recoverContextDeployments = async (input) => {
|
|
77
|
+
const recover = input.handlers.recoverDockerDeploymentRecords ?? recoverDockerDeploymentRecords;
|
|
78
|
+
return recover({
|
|
79
|
+
contains: containsFromView(input.view),
|
|
80
|
+
context: input.options.context,
|
|
81
|
+
dockerCommand: input.options.dockerCommand,
|
|
82
|
+
outputDirectory: input.outputDirectory,
|
|
83
|
+
projectLabel: input.projectLabel,
|
|
84
|
+
runtimeInstanceIds: runtimeInstanceIdsFromReport(input.loadedReport),
|
|
85
|
+
sourceRoot: input.sourceRoot,
|
|
86
|
+
timeoutMs: input.timeoutMs
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
const resolveAuthValues = async (records, handlers) => {
|
|
90
|
+
const values = {};
|
|
91
|
+
if (!handlers.requireAuthProfile) {
|
|
92
|
+
return values;
|
|
93
|
+
}
|
|
94
|
+
const profileNames = [...new Set(records
|
|
95
|
+
.map(({ record }) => record.auth_profile)
|
|
96
|
+
.filter((profileName) => typeof profileName === "string" && profileName.length > 0))];
|
|
97
|
+
for (const profileName of profileNames) {
|
|
98
|
+
try {
|
|
99
|
+
const profile = await handlers.requireAuthProfile(profileName);
|
|
100
|
+
Object.assign(values, profile.env);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Missing profile values are reported by the metadata layer as unknown credentials.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return values;
|
|
107
|
+
};
|
|
108
|
+
const emitOutput = (streams, output) => {
|
|
109
|
+
for (const line of output.split("\n")) {
|
|
110
|
+
streams.stdout(line);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const wait = (ms) => new Promise((resolve) => {
|
|
114
|
+
setTimeout(resolve, ms);
|
|
115
|
+
});
|
|
116
|
+
export const executeStatusWatch = async (inputPath, options, handlers, streams, setExitCode, watchOptions = {}) => {
|
|
117
|
+
const intervalMs = watchOptions.intervalMs ?? 5_000;
|
|
118
|
+
const sleep = watchOptions.sleep ?? wait;
|
|
119
|
+
let iteration = 0;
|
|
120
|
+
while (watchOptions.iterations === undefined || iteration < watchOptions.iterations) {
|
|
121
|
+
if (iteration > 0) {
|
|
122
|
+
await sleep(intervalMs);
|
|
123
|
+
streams.stdout("");
|
|
124
|
+
}
|
|
125
|
+
const result = await executeStatusCommand(inputPath, options, handlers);
|
|
126
|
+
setExitCode(result.exitCode);
|
|
127
|
+
if (result.error) {
|
|
128
|
+
streams.stderr(`error: ${result.error}`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (result.output) {
|
|
132
|
+
emitOutput(streams, result.output);
|
|
133
|
+
}
|
|
134
|
+
iteration += 1;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
/* v8 ignore start -- docker extraction is covered by distribution E2E */
|
|
138
|
+
const runStaticImageStatus = async (imageRef, options, json) => {
|
|
139
|
+
try {
|
|
140
|
+
const inspection = await extractImageReport(imageRef, {
|
|
141
|
+
dockerCommand: options.dockerCommand,
|
|
142
|
+
dockerContext: options.context,
|
|
143
|
+
pull: options.pull
|
|
144
|
+
});
|
|
145
|
+
return {
|
|
146
|
+
exitCode: 0,
|
|
147
|
+
output: renderImageInterface(inspection.report, { imageRef, json })
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
// Input errors (bad labels, non-JSON report, fingerprint mismatch) exit 2;
|
|
152
|
+
// Docker/runtime failures (daemon down, pull/create failure) exit 1.
|
|
153
|
+
return {
|
|
154
|
+
error: error instanceof Error ? error.message : String(error),
|
|
155
|
+
exitCode: errorExitCode(error)
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
/* v8 ignore stop */
|
|
160
|
+
const runHomeDeploymentStatus = async (options, handlers, mode, timeoutMs) => {
|
|
161
|
+
let records;
|
|
162
|
+
try {
|
|
163
|
+
records = await listHomeDeploymentRecords();
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
return { error: error instanceof Error ? error.message : String(error), exitCode: errorExitCode(error) };
|
|
167
|
+
}
|
|
168
|
+
if (!options.deployment) {
|
|
169
|
+
if (records.length === 0) {
|
|
170
|
+
return inputFailure("No image deployments found in the home store");
|
|
171
|
+
}
|
|
172
|
+
if (records.length > 1) {
|
|
173
|
+
return inputFailure(`status --deployment is required: ${records.map((entry) => entry.record.name).sort().join(", ")}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const targetName = options.deployment ?? records[0].record.name;
|
|
177
|
+
const match = records.find((entry) => entry.record.name === targetName);
|
|
178
|
+
if (!match) {
|
|
179
|
+
return inputFailure(`Unknown deployment "${targetName}". Valid deployments: ${records.map((entry) => entry.record.name).sort().join(", ") || "none"}`);
|
|
180
|
+
}
|
|
181
|
+
let report;
|
|
182
|
+
try {
|
|
183
|
+
const record = await readHomeDeploymentRecord(targetName);
|
|
184
|
+
void record;
|
|
185
|
+
const raw = await readHomeDeploymentReport(targetName);
|
|
186
|
+
let parsed;
|
|
187
|
+
try {
|
|
188
|
+
parsed = JSON.parse(raw);
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
// A corrupt cached report is bad input, not a runtime failure: exit 2.
|
|
192
|
+
throw new SpawnfileError("validation_error", `Cached report for "${targetName}" is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
193
|
+
}
|
|
194
|
+
report = parseDistributionReport(parsed);
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
return { error: error instanceof Error ? error.message : String(error), exitCode: errorExitCode(error) };
|
|
198
|
+
}
|
|
199
|
+
const view = projectImageOrganizationView(report, match.record.source.kind === "image"
|
|
200
|
+
? match.record.source.ref
|
|
201
|
+
: targetName);
|
|
202
|
+
const reportPath = match.path.replace(/record\.json$/, "spawnfile-report.json");
|
|
203
|
+
const loadedReport = loadedImageCompileReport(report, reportPath);
|
|
204
|
+
const selectedRecords = [match];
|
|
205
|
+
const deploymentInspections = await inspectDeployments(selectedRecords, handlers, options, timeoutMs);
|
|
206
|
+
const recordValues = selectedRecords.map(({ record }) => record);
|
|
207
|
+
const authValues = options.live ? await resolveAuthValues(selectedRecords, handlers) : {};
|
|
208
|
+
const collectRuntimeProbes = handlers.collectRuntimeProbeObservations ?? collectRuntimeProbeObservations;
|
|
209
|
+
const collectMoltnetProbes = handlers.collectMoltnetProbeObservations ?? collectMoltnetProbeObservations;
|
|
210
|
+
const collectDeploymentLogs = handlers.collectDeploymentLogObservations ?? collectDeploymentLogObservations;
|
|
211
|
+
const liveObservations = options.live && !options.recover
|
|
212
|
+
? [
|
|
213
|
+
...await collectRuntimeProbes({
|
|
214
|
+
deployments: recordValues,
|
|
215
|
+
inspections: deploymentInspections,
|
|
216
|
+
loadedReport,
|
|
217
|
+
timeoutMs
|
|
218
|
+
}),
|
|
219
|
+
...await collectMoltnetProbes({
|
|
220
|
+
authValues,
|
|
221
|
+
deployments: recordValues,
|
|
222
|
+
inspections: deploymentInspections,
|
|
223
|
+
loadedReport,
|
|
224
|
+
timeoutMs
|
|
225
|
+
}),
|
|
226
|
+
...(options.logs
|
|
227
|
+
? await collectDeploymentLogs({
|
|
228
|
+
deployments: recordValues,
|
|
229
|
+
dockerCommand: options.dockerCommand,
|
|
230
|
+
loadedReport,
|
|
231
|
+
timeoutMs
|
|
232
|
+
})
|
|
233
|
+
: []),
|
|
234
|
+
...(options.pullCheck
|
|
235
|
+
? await collectRegistryDriftObservations({
|
|
236
|
+
deployments: recordValues,
|
|
237
|
+
dockerCommand: options.dockerCommand,
|
|
238
|
+
timeoutMs
|
|
239
|
+
})
|
|
240
|
+
: [])
|
|
241
|
+
]
|
|
242
|
+
: [];
|
|
243
|
+
const status = createStaticStatus(view, loadedReport, {
|
|
244
|
+
deployments: createDeploymentSummaries(selectedRecords, deploymentInspections),
|
|
245
|
+
inputPath: view.inputPath,
|
|
246
|
+
live: {
|
|
247
|
+
context: null,
|
|
248
|
+
deploymentName: targetName,
|
|
249
|
+
logs: options.logs ?? false,
|
|
250
|
+
recover: false,
|
|
251
|
+
requested: options.live ?? false
|
|
252
|
+
},
|
|
253
|
+
liveObservations,
|
|
254
|
+
outputDirectory: "",
|
|
255
|
+
selection: null
|
|
256
|
+
});
|
|
257
|
+
return { exitCode: exitCodeForStatus(status), output: renderStatus(status, { mode }), status };
|
|
258
|
+
};
|
|
259
|
+
export const executeStatusCommand = async (inputPath, options, handlers) => {
|
|
260
|
+
const mode = resolveOutputMode(options);
|
|
261
|
+
if (typeof mode !== "string") {
|
|
262
|
+
return mode;
|
|
263
|
+
}
|
|
264
|
+
if (options.logs && !options.live) {
|
|
265
|
+
return inputFailure("status --logs requires --live");
|
|
266
|
+
}
|
|
267
|
+
// A positional image reference without --deployment renders the static
|
|
268
|
+
// interface from the embedded report. Image ref with --deployment, or a bare
|
|
269
|
+
// --deployment on the default path, reads the home store.
|
|
270
|
+
// A clear image reference (tag/digest/registry, or forced with --image)
|
|
271
|
+
// without --deployment renders the static interface; any other positional is
|
|
272
|
+
// treated as a project path (the view builder reports a missing path).
|
|
273
|
+
const commandInput = resolveCommandInput(inputPath, { forceImage: options.image });
|
|
274
|
+
const usedDefaultPath = inputPath === process.cwd();
|
|
275
|
+
if (commandInput.kind === "image" && !options.deployment) {
|
|
276
|
+
return runStaticImageStatus(commandInput.ref, options, mode === "json");
|
|
277
|
+
}
|
|
278
|
+
if (options.deployment && !options.context && (commandInput.kind === "image" || usedDefaultPath)) {
|
|
279
|
+
const timeoutForHome = resolveTimeoutMs(options);
|
|
280
|
+
if (typeof timeoutForHome !== "number" && timeoutForHome !== undefined) {
|
|
281
|
+
return timeoutForHome;
|
|
282
|
+
}
|
|
283
|
+
return runHomeDeploymentStatus(options, handlers, mode, timeoutForHome);
|
|
284
|
+
}
|
|
285
|
+
if (options.context && !options.live) {
|
|
286
|
+
return inputFailure("status --context requires --live");
|
|
287
|
+
}
|
|
288
|
+
if (options.logs && !options.live) {
|
|
289
|
+
return inputFailure("status --logs requires --live");
|
|
290
|
+
}
|
|
291
|
+
if (options.recover && !options.context) {
|
|
292
|
+
return inputFailure("status --recover requires --context");
|
|
293
|
+
}
|
|
294
|
+
const timeoutMs = resolveTimeoutMs(options);
|
|
295
|
+
if (typeof timeoutMs !== "number" && timeoutMs !== undefined) {
|
|
296
|
+
return timeoutMs;
|
|
297
|
+
}
|
|
298
|
+
const selectorInput = resolveSelectorInput(options);
|
|
299
|
+
if (selectorInput && "exitCode" in selectorInput) {
|
|
300
|
+
return selectorInput;
|
|
301
|
+
}
|
|
302
|
+
const outputDirectory = resolveProjectOutputDirectory(inputPath, options.out, DEFAULT_OUTPUT_DIRECTORY);
|
|
303
|
+
const view = await handlers.buildOrganizationView(inputPath);
|
|
304
|
+
const selectorResult = resolveStatusSelector(view, selectorInput);
|
|
305
|
+
if (selectorResult?.kind === "failure") {
|
|
306
|
+
return { error: selectorResult.failure.message, exitCode: 2 };
|
|
307
|
+
}
|
|
308
|
+
const loadedReport = await loadCompileReport(outputDirectory);
|
|
309
|
+
if (loadedReport.kind === "failure") {
|
|
310
|
+
return { error: loadedReport.failure.message, exitCode: 2 };
|
|
311
|
+
}
|
|
312
|
+
let deploymentRecords;
|
|
313
|
+
try {
|
|
314
|
+
deploymentRecords = options.context
|
|
315
|
+
? await recoverContextDeployments({
|
|
316
|
+
handlers,
|
|
317
|
+
loadedReport,
|
|
318
|
+
options,
|
|
319
|
+
outputDirectory,
|
|
320
|
+
projectLabel: normalizeProjectLabelSlug(view.root.name),
|
|
321
|
+
sourceRoot: view.root.source,
|
|
322
|
+
timeoutMs,
|
|
323
|
+
view
|
|
324
|
+
})
|
|
325
|
+
: await listDeploymentRecords(outputDirectory);
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
return {
|
|
329
|
+
error: error instanceof Error ? error.message : String(error),
|
|
330
|
+
exitCode: errorExitCode(error)
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
const selectedDeploymentRecords = resolveDeploymentRecords(deploymentRecords, options);
|
|
334
|
+
if ("exitCode" in selectedDeploymentRecords) {
|
|
335
|
+
return selectedDeploymentRecords;
|
|
336
|
+
}
|
|
337
|
+
const deploymentInspections = await inspectDeployments(selectedDeploymentRecords, handlers, options, timeoutMs);
|
|
338
|
+
const selectedDeploymentRecordValues = selectedDeploymentRecords.map(({ record }) => record);
|
|
339
|
+
const authValues = options.live
|
|
340
|
+
? await resolveAuthValues(selectedDeploymentRecords, handlers)
|
|
341
|
+
: {};
|
|
342
|
+
const collectRuntimeProbes = handlers.collectRuntimeProbeObservations ?? collectRuntimeProbeObservations;
|
|
343
|
+
const collectMoltnetProbes = handlers.collectMoltnetProbeObservations ?? collectMoltnetProbeObservations;
|
|
344
|
+
const collectDeploymentLogs = handlers.collectDeploymentLogObservations ?? collectDeploymentLogObservations;
|
|
345
|
+
const liveObservations = options.live && !options.recover
|
|
346
|
+
? [
|
|
347
|
+
...await collectRuntimeProbes({
|
|
348
|
+
deployments: selectedDeploymentRecordValues,
|
|
349
|
+
inspections: deploymentInspections,
|
|
350
|
+
loadedReport,
|
|
351
|
+
timeoutMs
|
|
352
|
+
}),
|
|
353
|
+
...await collectMoltnetProbes({
|
|
354
|
+
authValues,
|
|
355
|
+
deployments: selectedDeploymentRecordValues,
|
|
356
|
+
inspections: deploymentInspections,
|
|
357
|
+
loadedReport,
|
|
358
|
+
timeoutMs
|
|
359
|
+
}),
|
|
360
|
+
...(options.logs
|
|
361
|
+
? await collectDeploymentLogs({
|
|
362
|
+
deployments: selectedDeploymentRecordValues,
|
|
363
|
+
dockerCommand: options.dockerCommand,
|
|
364
|
+
loadedReport,
|
|
365
|
+
timeoutMs
|
|
366
|
+
})
|
|
367
|
+
: [])
|
|
368
|
+
]
|
|
369
|
+
: [];
|
|
370
|
+
const status = createStaticStatus(view, loadedReport, {
|
|
371
|
+
deployments: createDeploymentSummaries(selectedDeploymentRecords, deploymentInspections),
|
|
372
|
+
inputPath,
|
|
373
|
+
live: {
|
|
374
|
+
context: options.context ?? null,
|
|
375
|
+
deploymentName: options.deployment ?? null,
|
|
376
|
+
logs: options.logs ?? false,
|
|
377
|
+
recover: options.recover === true || Boolean(options.context),
|
|
378
|
+
requested: options.live ?? false
|
|
379
|
+
},
|
|
380
|
+
liveObservations,
|
|
381
|
+
outputDirectory,
|
|
382
|
+
selection: selectorResult?.selection ?? null
|
|
383
|
+
});
|
|
384
|
+
return {
|
|
385
|
+
exitCode: exitCodeForStatus(status),
|
|
386
|
+
output: renderStatus(status, { mode }),
|
|
387
|
+
status
|
|
388
|
+
};
|
|
389
|
+
};
|
|
390
|
+
export const registerStatusCommand = (program, handlers, streams, setExitCode) => {
|
|
391
|
+
program
|
|
392
|
+
.command("status")
|
|
393
|
+
.description("Show static Spawnfile organization status")
|
|
394
|
+
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
395
|
+
.option("--out <dir>", "Compile output directory")
|
|
396
|
+
.option("--json", "Render machine-readable JSON")
|
|
397
|
+
.option("--pretty", "Render human output")
|
|
398
|
+
.option("--quiet", "Render only summary and non-ok observations")
|
|
399
|
+
.option("--live", "Inspect the recorded live deployment")
|
|
400
|
+
.option("--deployment <name>", "Deployment record name")
|
|
401
|
+
.option("--image", "Interpret the argument as an image reference")
|
|
402
|
+
.option("--pull", "Pull the image before inspecting (image references only)")
|
|
403
|
+
.option("--pull-check", "Check the registry for a newer image digest (networked)")
|
|
404
|
+
.option("--docker-command <command>", "Docker command")
|
|
405
|
+
.option("--context <name>", "Docker context for live remote deployment recovery")
|
|
406
|
+
.option("--recover", "Recover live status from labels instead of a deployment record")
|
|
407
|
+
.option("--logs", "Include redacted logs when supported")
|
|
408
|
+
.option("--timeout <ms>", "Bound live Docker/runtime checks in milliseconds")
|
|
409
|
+
.option("--watch", "Refresh status every five seconds until interrupted")
|
|
410
|
+
.option("--agent <id>", "Show one agent")
|
|
411
|
+
.option("--team <id>", "Show one team")
|
|
412
|
+
.option("--network <id>", "Show one network")
|
|
413
|
+
.option("--runtime <name>", "Show one runtime")
|
|
414
|
+
.action(async (inputPath, options) => {
|
|
415
|
+
if (options.watch) {
|
|
416
|
+
await executeStatusWatch(inputPath, options, handlers, streams, setExitCode);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const result = await executeStatusCommand(inputPath, options, handlers);
|
|
420
|
+
setExitCode(result.exitCode);
|
|
421
|
+
if (result.error) {
|
|
422
|
+
streams.stderr(`error: ${result.error}`);
|
|
423
|
+
}
|
|
424
|
+
if (result.output) {
|
|
425
|
+
emitOutput(streams, result.output);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
};
|
|
@@ -22,6 +22,7 @@ export const registerSurfaceCommands = (program, handlers, streams) => {
|
|
|
22
22
|
.description("Edit agent surfaces");
|
|
23
23
|
surfaceCommand
|
|
24
24
|
.command("add")
|
|
25
|
+
.description("Attach a surface to an agent")
|
|
25
26
|
.argument("<surface>", "Surface name")
|
|
26
27
|
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
27
28
|
.option("--bot-token-secret <env>", "Override the bot token env var name")
|
|
@@ -42,6 +43,7 @@ export const registerSurfaceCommands = (program, handlers, streams) => {
|
|
|
42
43
|
});
|
|
43
44
|
surfaceCommand
|
|
44
45
|
.command("remove")
|
|
46
|
+
.description("Detach a surface from an agent")
|
|
45
47
|
.argument("<surface>", "Surface name")
|
|
46
48
|
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
47
49
|
.option("--recursive", "Update the target project and its descendants")
|
|
@@ -58,6 +60,7 @@ export const registerSurfaceCommands = (program, handlers, streams) => {
|
|
|
58
60
|
});
|
|
59
61
|
surfaceCommand
|
|
60
62
|
.command("set-access")
|
|
63
|
+
.description("Set an agent's access mode for a surface")
|
|
61
64
|
.argument("<surface>", "Surface name")
|
|
62
65
|
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
63
66
|
.requiredOption("--mode <mode>", "Access mode")
|
|
@@ -86,6 +89,7 @@ export const registerSurfaceCommands = (program, handlers, streams) => {
|
|
|
86
89
|
});
|
|
87
90
|
surfaceCommand
|
|
88
91
|
.command("show")
|
|
92
|
+
.description("Show an agent's resolved surfaces")
|
|
89
93
|
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
90
94
|
.option("--recursive", "Show descendant agent surfaces too")
|
|
91
95
|
.action(async (inputPath, options) => {
|
|
@@ -27,8 +27,7 @@ export const resolveAgentSurfaces = (surfaces) => {
|
|
|
27
27
|
? {
|
|
28
28
|
dms: {
|
|
29
29
|
enabled: attachment.dms.enabled,
|
|
30
|
-
...(attachment.dms.
|
|
31
|
-
...(attachment.dms.reply ? { reply: attachment.dms.reply } : {})
|
|
30
|
+
...(attachment.dms.wake ? { wake: attachment.dms.wake } : {})
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
: {}),
|
|
@@ -41,8 +40,7 @@ export const resolveAgentSurfaces = (surfaces) => {
|
|
|
41
40
|
.map(([roomId, behavior]) => [
|
|
42
41
|
roomId,
|
|
43
42
|
{
|
|
44
|
-
...(behavior.
|
|
45
|
-
...(behavior.reply ? { reply: behavior.reply } : {})
|
|
43
|
+
...(behavior.wake ? { wake: behavior.wake } : {})
|
|
46
44
|
}
|
|
47
45
|
]))
|
|
48
46
|
}
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { type CompileProjectOptions, type CompileProjectResult } from "./compileProject.js";
|
|
2
|
+
import type { MoltnetTargetArchitecture } from "./moltnetBinaries.js";
|
|
2
3
|
export interface DockerBuildInvocation {
|
|
3
4
|
args: string[];
|
|
4
5
|
command: string;
|
|
5
6
|
cwd: string;
|
|
7
|
+
dockerContext?: string | null;
|
|
6
8
|
imageTag: string;
|
|
7
9
|
}
|
|
8
10
|
export type DockerBuildRunner = (invocation: DockerBuildInvocation) => Promise<void>;
|
|
9
11
|
export interface BuildProjectOptions extends CompileProjectOptions {
|
|
10
12
|
buildRunner?: DockerBuildRunner;
|
|
11
13
|
dockerCommand?: string;
|
|
14
|
+
dockerContext?: string;
|
|
12
15
|
imageTag?: string;
|
|
13
16
|
}
|
|
14
17
|
export interface BuildProjectResult extends CompileProjectResult {
|
|
15
18
|
imageTag: string;
|
|
16
19
|
}
|
|
17
20
|
export declare const createDefaultImageTag: (projectRoot: string) => string;
|
|
18
|
-
export declare const createDockerBuildInvocation: (outputDirectory: string, imageTag: string,
|
|
21
|
+
export declare const createDockerBuildInvocation: (outputDirectory: string, imageTag: string, options?: string | {
|
|
22
|
+
dockerCommand?: string;
|
|
23
|
+
dockerContext?: string;
|
|
24
|
+
}) => DockerBuildInvocation;
|
|
25
|
+
export declare const normalizeDockerArchitecture: (architecture: string) => MoltnetTargetArchitecture;
|
|
26
|
+
export declare const resolveDockerBuildArchitecture: (options: Pick<BuildProjectOptions, "dockerCommand" | "dockerContext">) => Promise<MoltnetTargetArchitecture | undefined>;
|
|
19
27
|
export declare const runDockerBuild: DockerBuildRunner;
|
|
20
28
|
export declare const buildProject: (inputPath: string, options?: BuildProjectOptions) => Promise<BuildProjectResult>;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
2
|
+
import { execFile as execFileCallback, spawn } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
3
4
|
import { SpawnfileError } from "../shared/index.js";
|
|
4
5
|
import { compileProject } from "./compileProject.js";
|
|
5
6
|
import { slugify } from "./helpers.js";
|
|
7
|
+
const execFile = promisify(execFileCallback);
|
|
6
8
|
export const createDefaultImageTag = (projectRoot) => {
|
|
7
9
|
const baseName = slugify(path.basename(projectRoot));
|
|
8
10
|
return `spawnfile-${baseName || "project"}`;
|
|
@@ -13,12 +15,44 @@ const resolveImageTagRoot = (inputPath) => {
|
|
|
13
15
|
? path.dirname(resolvedPath)
|
|
14
16
|
: resolvedPath;
|
|
15
17
|
};
|
|
16
|
-
export const createDockerBuildInvocation = (outputDirectory, imageTag,
|
|
17
|
-
args:
|
|
18
|
-
|
|
18
|
+
export const createDockerBuildInvocation = (outputDirectory, imageTag, options = "docker") => ({
|
|
19
|
+
args: typeof options === "string" || !options.dockerContext
|
|
20
|
+
? ["build", "-t", imageTag, "."]
|
|
21
|
+
: ["--context", options.dockerContext, "build", "-t", imageTag, "."],
|
|
22
|
+
command: typeof options === "string" ? options : options.dockerCommand ?? "docker",
|
|
19
23
|
cwd: outputDirectory,
|
|
24
|
+
dockerContext: typeof options === "string" ? null : options.dockerContext ?? null,
|
|
20
25
|
imageTag
|
|
21
26
|
});
|
|
27
|
+
export const normalizeDockerArchitecture = (architecture) => {
|
|
28
|
+
switch (architecture.trim()) {
|
|
29
|
+
case "amd64":
|
|
30
|
+
case "x64":
|
|
31
|
+
case "x86_64":
|
|
32
|
+
return "amd64";
|
|
33
|
+
case "aarch64":
|
|
34
|
+
case "arm64":
|
|
35
|
+
return "arm64";
|
|
36
|
+
default:
|
|
37
|
+
throw new SpawnfileError("compile_error", `Docker target architecture ${architecture.trim() || "unknown"} is not supported by staged Moltnet binaries`);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
export const resolveDockerBuildArchitecture = async (options) => {
|
|
41
|
+
if (!options.dockerContext) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const { stdout } = await execFile(options.dockerCommand ?? "docker", ["--context", options.dockerContext, "info", "--format", "{{.Architecture}}"], { timeout: 10_000 });
|
|
46
|
+
return normalizeDockerArchitecture(stdout);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error instanceof SpawnfileError) {
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
53
|
+
throw new SpawnfileError("compile_error", `Unable to resolve Docker context ${options.dockerContext} architecture: ${reason}`);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
22
56
|
export const runDockerBuild = async (invocation) => new Promise((resolve, reject) => {
|
|
23
57
|
const child = spawn(invocation.command, invocation.args, {
|
|
24
58
|
cwd: invocation.cwd,
|
|
@@ -38,12 +72,17 @@ export const runDockerBuild = async (invocation) => new Promise((resolve, reject
|
|
|
38
72
|
});
|
|
39
73
|
});
|
|
40
74
|
export const buildProject = async (inputPath, options = {}) => {
|
|
75
|
+
const targetArchitecture = options.containerArchitecture ?? await resolveDockerBuildArchitecture(options);
|
|
41
76
|
const compileResult = await compileProject(inputPath, {
|
|
42
77
|
clean: options.clean,
|
|
78
|
+
containerArchitecture: targetArchitecture,
|
|
43
79
|
outputDirectory: options.outputDirectory
|
|
44
80
|
});
|
|
45
81
|
const imageTag = options.imageTag ?? createDefaultImageTag(resolveImageTagRoot(inputPath));
|
|
46
|
-
const invocation = createDockerBuildInvocation(compileResult.outputDirectory, imageTag,
|
|
82
|
+
const invocation = createDockerBuildInvocation(compileResult.outputDirectory, imageTag, {
|
|
83
|
+
dockerCommand: options.dockerCommand,
|
|
84
|
+
dockerContext: options.dockerContext
|
|
85
|
+
});
|
|
47
86
|
await (options.buildRunner ?? runDockerBuild)(invocation);
|
|
48
87
|
return {
|
|
49
88
|
...compileResult,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CompileReport } from "../report/index.js";
|
|
2
|
+
import { type MoltnetTargetArchitecture } from "./moltnetBinaries.js";
|
|
2
3
|
export interface CompileProjectOptions {
|
|
3
4
|
clean?: boolean;
|
|
5
|
+
containerArchitecture?: MoltnetTargetArchitecture;
|
|
4
6
|
outputDirectory?: string;
|
|
5
7
|
}
|
|
6
8
|
export interface CompileProjectResult {
|