spawnfile 0.1.11 → 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/compileProject.js +19 -10
- 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.js +5 -0
- package/dist/compiler/moltnetClientConfig.d.ts +1 -1
- package/dist/compiler/moltnetClientConfig.js +3 -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 +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 +1 -2
- package/dist/compiler/upProject.d.ts +6 -0
- package/dist/compiler/upProject.js +42 -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/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/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/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,19 @@
|
|
|
1
|
+
export interface DockerRunInvocation {
|
|
2
|
+
args: string[];
|
|
3
|
+
command: string;
|
|
4
|
+
containerName: string | null;
|
|
5
|
+
cwd: string;
|
|
6
|
+
detach: boolean;
|
|
7
|
+
deploymentName?: string | null;
|
|
8
|
+
dockerContext?: string | null;
|
|
9
|
+
dockerHost?: string | null;
|
|
10
|
+
envFilePath: string;
|
|
11
|
+
imageTag: string;
|
|
12
|
+
supportDirectory: string;
|
|
13
|
+
}
|
|
14
|
+
export interface DockerRunResult {
|
|
15
|
+
containerId?: string;
|
|
16
|
+
imageId?: string;
|
|
17
|
+
}
|
|
18
|
+
export type DockerRunRunner = (invocation: DockerRunInvocation) => Promise<DockerRunResult | void>;
|
|
19
|
+
export declare const runDockerContainer: DockerRunRunner;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { execFile as execFileCallback, spawn } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
4
|
+
const execFile = promisify(execFileCallback);
|
|
5
|
+
const inspectImageArgs = (invocation, containerId) => {
|
|
6
|
+
const base = invocation.dockerContext
|
|
7
|
+
? ["--context", invocation.dockerContext, "inspect"]
|
|
8
|
+
: invocation.dockerHost
|
|
9
|
+
? ["--host", invocation.dockerHost, "inspect"]
|
|
10
|
+
: ["inspect"];
|
|
11
|
+
return [...base, "--format", "{{.Image}}", containerId];
|
|
12
|
+
};
|
|
13
|
+
const inspectDetachedImageId = async (invocation, containerId) => {
|
|
14
|
+
try {
|
|
15
|
+
const { stdout } = await execFile(invocation.command, inspectImageArgs(invocation, containerId), {
|
|
16
|
+
cwd: invocation.cwd,
|
|
17
|
+
timeout: 10_000
|
|
18
|
+
});
|
|
19
|
+
return stdout.trim() || undefined;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
23
|
+
throw new SpawnfileError("runtime_error", `Unable to inspect detached container ${containerId} image id: ${message}`);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export const runDockerContainer = async (invocation) => new Promise((resolve, reject) => {
|
|
27
|
+
let stdout = "";
|
|
28
|
+
const child = spawn(invocation.command, invocation.args, {
|
|
29
|
+
cwd: invocation.cwd,
|
|
30
|
+
stdio: invocation.detach ? ["ignore", "pipe", "inherit"] : "inherit"
|
|
31
|
+
});
|
|
32
|
+
if (invocation.detach && child.stdout) {
|
|
33
|
+
child.stdout.setEncoding("utf8");
|
|
34
|
+
child.stdout.on("data", (chunk) => {
|
|
35
|
+
stdout += chunk;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
child.once("error", (error) => {
|
|
39
|
+
reject(new SpawnfileError("runtime_error", `Unable to start docker run for ${invocation.imageTag}: ${error.message}`));
|
|
40
|
+
});
|
|
41
|
+
child.once("exit", (code, signal) => {
|
|
42
|
+
if (code === 0) {
|
|
43
|
+
const containerId = stdout.trim().split(/\s+/)[0];
|
|
44
|
+
if (!invocation.detach || !containerId) {
|
|
45
|
+
resolve(undefined);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
inspectDetachedImageId(invocation, containerId)
|
|
49
|
+
.then((imageId) => resolve({ containerId, ...(imageId ? { imageId } : {}) }))
|
|
50
|
+
.catch(reject);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
reject(new SpawnfileError("runtime_error", signal
|
|
54
|
+
? `Docker run for ${invocation.imageTag} exited from signal ${signal}`
|
|
55
|
+
: `Docker run for ${invocation.imageTag} failed with exit code ${code ?? "unknown"}`));
|
|
56
|
+
});
|
|
57
|
+
});
|
package/dist/compiler/types.d.ts
CHANGED
|
@@ -82,8 +82,7 @@ export interface ResolvedHttpSurface {
|
|
|
82
82
|
port?: number;
|
|
83
83
|
}
|
|
84
84
|
export interface ResolvedMoltnetRoomPolicy {
|
|
85
|
-
|
|
86
|
-
reply?: "auto" | "never";
|
|
85
|
+
wake?: "all" | "mentions" | "thread_only" | "never";
|
|
87
86
|
}
|
|
88
87
|
export interface ResolvedMoltnetDMConfig extends ResolvedMoltnetRoomPolicy {
|
|
89
88
|
enabled: boolean;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type DockerTargetExecFile } from "../deployment/index.js";
|
|
1
2
|
import { type BuildProjectResult, type DockerBuildRunner } from "./buildProject.js";
|
|
2
3
|
import { type DockerRunRunner } from "./runProject.js";
|
|
3
4
|
import { CompileProjectOptions } from "./compileProject.js";
|
|
@@ -6,14 +7,19 @@ export interface UpProjectOptions extends CompileProjectOptions {
|
|
|
6
7
|
buildRunner?: DockerBuildRunner;
|
|
7
8
|
containerName?: string;
|
|
8
9
|
detach?: boolean;
|
|
10
|
+
deploymentName?: string;
|
|
9
11
|
dockerCommand?: string;
|
|
12
|
+
dockerContext?: string;
|
|
13
|
+
dockerHost?: string;
|
|
10
14
|
envFilePath?: string;
|
|
11
15
|
imageTag?: string;
|
|
12
16
|
runRunner?: DockerRunRunner;
|
|
17
|
+
targetExecFile?: DockerTargetExecFile;
|
|
13
18
|
}
|
|
14
19
|
export interface UpProjectResult extends BuildProjectResult {
|
|
15
20
|
authProfileName: string | null;
|
|
16
21
|
containerName: string | null;
|
|
22
|
+
deploymentRecordPath?: string | null;
|
|
17
23
|
supportDirectory: string | null;
|
|
18
24
|
}
|
|
19
25
|
export declare const upProject: (inputPath: string, options?: UpProjectOptions) => Promise<UpProjectResult>;
|
|
@@ -1,37 +1,71 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { removeDirectory } from "../filesystem/index.js";
|
|
3
|
+
import { writeDockerDeploymentRecordForRun } from "../deployment/index.js";
|
|
2
4
|
import { buildProject } from "./buildProject.js";
|
|
3
|
-
import { createDockerRunInvocation, runDockerContainer } from "./runProject.js";
|
|
5
|
+
import { createDockerRunInvocation, resolveDetachedDeploymentOptions, runDockerContainer } from "./runProject.js";
|
|
4
6
|
import { requireAuthProfile } from "../auth/index.js";
|
|
7
|
+
import { DEFAULT_OUTPUT_DIRECTORY } from "../shared/index.js";
|
|
5
8
|
export const upProject = async (inputPath, options = {}) => {
|
|
9
|
+
const resolvedOptions = await resolveDetachedDeploymentOptions(path.resolve(options.outputDirectory ?? DEFAULT_OUTPUT_DIRECTORY), {
|
|
10
|
+
authProfile: options.authProfile,
|
|
11
|
+
containerName: options.containerName,
|
|
12
|
+
deploymentName: options.deploymentName,
|
|
13
|
+
detach: options.detach,
|
|
14
|
+
dockerCommand: options.dockerCommand,
|
|
15
|
+
dockerContext: options.dockerContext,
|
|
16
|
+
dockerHost: options.dockerHost,
|
|
17
|
+
envFilePath: options.envFilePath,
|
|
18
|
+
imageTag: options.imageTag,
|
|
19
|
+
targetExecFile: options.targetExecFile
|
|
20
|
+
});
|
|
6
21
|
const buildResult = await buildProject(inputPath, {
|
|
7
22
|
buildRunner: options.buildRunner,
|
|
8
23
|
clean: options.clean,
|
|
9
24
|
dockerCommand: options.dockerCommand,
|
|
10
|
-
imageTag:
|
|
25
|
+
imageTag: resolvedOptions.imageTag,
|
|
11
26
|
outputDirectory: options.outputDirectory
|
|
12
27
|
});
|
|
13
|
-
const authProfile =
|
|
14
|
-
? await requireAuthProfile(
|
|
28
|
+
const authProfile = resolvedOptions.authProfile
|
|
29
|
+
? await requireAuthProfile(resolvedOptions.authProfile)
|
|
15
30
|
: null;
|
|
16
|
-
const
|
|
31
|
+
const imageTag = resolvedOptions.imageTag ?? buildResult.imageTag;
|
|
32
|
+
const invocation = await createDockerRunInvocation(buildResult, imageTag, {
|
|
17
33
|
authProfile,
|
|
18
|
-
containerName:
|
|
34
|
+
containerName: resolvedOptions.containerName,
|
|
19
35
|
detach: options.detach,
|
|
36
|
+
deploymentName: resolvedOptions.deploymentName,
|
|
20
37
|
dockerCommand: options.dockerCommand,
|
|
21
|
-
|
|
38
|
+
dockerContext: resolvedOptions.dockerContext,
|
|
39
|
+
dockerHost: resolvedOptions.dockerHost,
|
|
40
|
+
envFilePath: resolvedOptions.envFilePath
|
|
22
41
|
});
|
|
42
|
+
let runMetadata;
|
|
23
43
|
try {
|
|
24
|
-
await (options.runRunner ?? runDockerContainer)(invocation);
|
|
44
|
+
runMetadata = await (options.runRunner ?? runDockerContainer)(invocation);
|
|
25
45
|
}
|
|
26
46
|
finally {
|
|
27
47
|
if (!invocation.detach) {
|
|
28
48
|
await removeDirectory(invocation.supportDirectory);
|
|
29
49
|
}
|
|
30
50
|
}
|
|
51
|
+
const deploymentRecordPath = invocation.detach && invocation.deploymentName
|
|
52
|
+
? await writeDockerDeploymentRecordForRun({
|
|
53
|
+
authProfileName: authProfile?.name ?? null,
|
|
54
|
+
envFilePath: resolvedOptions.envFilePath,
|
|
55
|
+
imageTag,
|
|
56
|
+
invocation,
|
|
57
|
+
outputDirectory: buildResult.outputDirectory,
|
|
58
|
+
report: buildResult.report,
|
|
59
|
+
runMetadata: runMetadata ?? undefined,
|
|
60
|
+
targetExecFile: options.targetExecFile
|
|
61
|
+
})
|
|
62
|
+
: null;
|
|
31
63
|
return {
|
|
32
64
|
...buildResult,
|
|
33
65
|
authProfileName: authProfile?.name ?? null,
|
|
34
66
|
containerName: invocation.containerName,
|
|
67
|
+
deploymentRecordPath,
|
|
68
|
+
imageTag,
|
|
35
69
|
supportDirectory: invocation.supportDirectory
|
|
36
70
|
};
|
|
37
71
|
};
|
|
@@ -2,6 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { SpawnfileError } from "../../shared/index.js";
|
|
3
3
|
import { buildCompilePlan } from "../buildCompilePlan.js";
|
|
4
4
|
import { resolveMoltnetRoomMemberships } from "../moltnetRoomMemberships.js";
|
|
5
|
+
import { buildDeclaredNodeView, buildRuntimeViews } from "./declarations.js";
|
|
5
6
|
const createNameCounts = (plan) => {
|
|
6
7
|
const counts = new Map();
|
|
7
8
|
for (const node of plan.nodes) {
|
|
@@ -82,7 +83,9 @@ const buildTreeNode = (node, nodeById, edgesBySource, nameCounts, ancestors = []
|
|
|
82
83
|
kind: node.kind,
|
|
83
84
|
name: node.value.name,
|
|
84
85
|
networks: buildTreeNetworkSummaries(node),
|
|
86
|
+
declared: buildDeclaredNodeView(node),
|
|
85
87
|
runtimeName: node.runtimeName,
|
|
88
|
+
slug: node.slug,
|
|
86
89
|
source: node.value.source
|
|
87
90
|
};
|
|
88
91
|
};
|
|
@@ -219,6 +222,6 @@ export const buildOrganizationView = async (inputPath) => {
|
|
|
219
222
|
networks: buildNetworks(plan),
|
|
220
223
|
projectRoot: path.dirname(plan.root),
|
|
221
224
|
root,
|
|
222
|
-
runtimes:
|
|
225
|
+
runtimes: buildRuntimeViews(plan)
|
|
223
226
|
};
|
|
224
227
|
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CompilePlan, CompilePlanNode } from "../types.js";
|
|
2
|
+
import type { OrganizationRuntimeView, OrganizationViewDeclaredSummary } from "./types.js";
|
|
3
|
+
export declare const buildDeclaredNodeView: (node: CompilePlanNode) => OrganizationViewDeclaredSummary;
|
|
4
|
+
export declare const buildRuntimeViews: (plan: CompilePlan) => OrganizationRuntimeView[];
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { resolveEffectiveModelTarget } from "../modelEnv.js";
|
|
2
|
+
import { listInteractiveSurfaceScopes } from "../interactiveSurfaceScopes.js";
|
|
3
|
+
const summarizeModel = (node) => {
|
|
4
|
+
if (node.value.kind !== "agent" || !node.value.execution?.model?.primary) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const target = resolveEffectiveModelTarget(node.value.execution.model.primary, node.value.execution);
|
|
8
|
+
return {
|
|
9
|
+
authMethod: target.auth.method,
|
|
10
|
+
name: target.name,
|
|
11
|
+
provider: target.provider
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
const summarizeSchedule = (node) => {
|
|
15
|
+
if (node.value.kind !== "agent" || !node.value.schedule) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const schedule = node.value.schedule;
|
|
19
|
+
if (schedule.kind === "cron") {
|
|
20
|
+
return {
|
|
21
|
+
expression: schedule.cron,
|
|
22
|
+
kind: schedule.kind,
|
|
23
|
+
...(schedule.timezone ? { timezone: schedule.timezone } : {})
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (schedule.kind === "every") {
|
|
27
|
+
return {
|
|
28
|
+
expression: schedule.every,
|
|
29
|
+
kind: schedule.kind,
|
|
30
|
+
...(schedule.timezone ? { timezone: schedule.timezone } : {})
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return { kind: schedule.kind };
|
|
34
|
+
};
|
|
35
|
+
const surfaceSummaryFor = (name, surfaces) => {
|
|
36
|
+
if (!surfaces[name]) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const scopes = name === "moltnet"
|
|
40
|
+
? listInteractiveSurfaceScopes(surfaces).filter((scope) => scope.startsWith("moltnet:"))
|
|
41
|
+
: listInteractiveSurfaceScopes(surfaces).filter((scope) => scope === name);
|
|
42
|
+
return {
|
|
43
|
+
name,
|
|
44
|
+
scopes: scopes.length > 0 ? scopes : [name]
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
const summarizeSurfaces = (node) => {
|
|
48
|
+
const surfaces = node.value.kind === "agent" ? node.value.surfaces : undefined;
|
|
49
|
+
if (!surfaces) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
return ["discord", "http", "moltnet", "slack", "telegram", "webhook", "whatsapp"]
|
|
53
|
+
.map((name) => surfaceSummaryFor(name, surfaces))
|
|
54
|
+
.filter((surface) => surface !== null);
|
|
55
|
+
};
|
|
56
|
+
const summarizePackages = (node) => {
|
|
57
|
+
const packages = node.value.kind === "agent"
|
|
58
|
+
? node.value.packages ?? []
|
|
59
|
+
: node.value.shared.packages ?? [];
|
|
60
|
+
return packages.map((pkg) => ({
|
|
61
|
+
id: pkg.id,
|
|
62
|
+
manager: pkg.manager,
|
|
63
|
+
name: pkg.name,
|
|
64
|
+
...(pkg.scope ? { scope: pkg.scope } : {}),
|
|
65
|
+
...(pkg.version ? { version: pkg.version } : {})
|
|
66
|
+
}));
|
|
67
|
+
};
|
|
68
|
+
const summarizeResources = (node) => (node.value.workspaceResources ?? []).map((resource) => ({
|
|
69
|
+
id: resource.id,
|
|
70
|
+
kind: resource.kind,
|
|
71
|
+
mode: resource.mode,
|
|
72
|
+
mount: resource.mount,
|
|
73
|
+
sharing: resource.sharing
|
|
74
|
+
}));
|
|
75
|
+
export const buildDeclaredNodeView = (node) => {
|
|
76
|
+
const skills = node.value.kind === "agent" ? node.value.skills : node.value.shared.skills;
|
|
77
|
+
const mcpServers = node.value.kind === "agent"
|
|
78
|
+
? node.value.mcpServers
|
|
79
|
+
: node.value.shared.mcpServers;
|
|
80
|
+
return {
|
|
81
|
+
docs: node.value.docs.map((doc) => ({ role: doc.role, source: doc.sourcePath })),
|
|
82
|
+
mcpServers: mcpServers.map((server) => ({
|
|
83
|
+
name: server.name,
|
|
84
|
+
transport: server.transport
|
|
85
|
+
})),
|
|
86
|
+
model: summarizeModel(node),
|
|
87
|
+
packages: summarizePackages(node),
|
|
88
|
+
policy: {
|
|
89
|
+
mode: node.value.policyMode,
|
|
90
|
+
onDegrade: node.value.policyOnDegrade
|
|
91
|
+
},
|
|
92
|
+
resources: summarizeResources(node),
|
|
93
|
+
schedule: summarizeSchedule(node),
|
|
94
|
+
skills: skills.map((skill) => ({
|
|
95
|
+
name: skill.name,
|
|
96
|
+
ref: skill.ref,
|
|
97
|
+
requiresMcp: [...skill.requiresMcp]
|
|
98
|
+
})),
|
|
99
|
+
surfaces: summarizeSurfaces(node)
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
export const buildRuntimeViews = (plan) => Object.entries(plan.runtimes)
|
|
103
|
+
.map(([name, runtime]) => ({ name, nodeIds: [...runtime.nodeIds].sort() }))
|
|
104
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
@@ -8,8 +8,7 @@ const formatPolicy = (member) => {
|
|
|
8
8
|
return [];
|
|
9
9
|
}
|
|
10
10
|
return [
|
|
11
|
-
member.policy.
|
|
12
|
-
member.policy.reply ? `reply=${member.policy.reply}` : undefined
|
|
11
|
+
member.policy.wake ? `wake=${member.policy.wake}` : undefined
|
|
13
12
|
].filter((entry) => entry !== undefined);
|
|
14
13
|
};
|
|
15
14
|
const formatMember = (member, options, projectRoot) => {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { formatSourceMeta } from "./sourcePaths.js";
|
|
2
2
|
const color = (value, code, options) => options.color ? `\u001b[${code}m${value}\u001b[0m` : value;
|
|
3
|
+
const formatAnnotations = (subjectKey, options) => {
|
|
4
|
+
const annotations = options.annotationFor?.(subjectKey) ?? [];
|
|
5
|
+
return annotations.length > 0 ? ` ${annotations.join(" ")}` : "";
|
|
6
|
+
};
|
|
3
7
|
const formatNode = (node, options, projectRoot) => {
|
|
4
8
|
const kind = color(node.kind, node.kind === "team" ? "36" : "32", options);
|
|
5
9
|
const metadata = node.kind === "team"
|
|
@@ -17,7 +21,7 @@ const formatNode = (node, options, projectRoot) => {
|
|
|
17
21
|
const details = metadata.filter((entry) => entry !== undefined);
|
|
18
22
|
const source = options.paths ? formatSourceMeta("source", node.source, projectRoot) : "";
|
|
19
23
|
const separator = details[0]?.startsWith("[") ? " " : " ";
|
|
20
|
-
return `${kind} ${node.displayName}${details.length > 0 ? `${separator}${details.join(" ")}` : ""}${source}`;
|
|
24
|
+
return `${kind} ${node.displayName}${details.length > 0 ? `${separator}${details.join(" ")}` : ""}${source}${formatAnnotations(node.id, options)}`;
|
|
21
25
|
};
|
|
22
26
|
const formatEdgeLabel = (edge) => edge.relation === "subagent"
|
|
23
27
|
? `subagent ${edge.label}`
|
|
@@ -44,7 +48,7 @@ const formatNetworkSummary = (network, options) => {
|
|
|
44
48
|
return `${room.id}${roomMetadata.length > 0 ? ` ${roomMetadata.join(" ")}` : ""} [${room.declaredMembers.join(", ")}]`;
|
|
45
49
|
})
|
|
46
50
|
.join("; ");
|
|
47
|
-
return `network ${id} "${network.name}"${metadata.length > 0 ? ` ${metadata.join(" ")}` : ""}: ${rooms}`;
|
|
51
|
+
return `network ${id} "${network.name}"${metadata.length > 0 ? ` ${metadata.join(" ")}` : ""}: ${rooms}${formatAnnotations(`network:${network.id}`, options)}`;
|
|
48
52
|
};
|
|
49
53
|
const renderChildren = (node, options, projectRoot, prefix = "") => {
|
|
50
54
|
const glyphs = options.ascii
|
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
import type { ResolvedMoltnetRoomPolicy } from "../types.js";
|
|
2
|
+
export interface OrganizationViewDocSummary {
|
|
3
|
+
role: string;
|
|
4
|
+
source: string;
|
|
5
|
+
}
|
|
6
|
+
export interface OrganizationViewSkillSummary {
|
|
7
|
+
name: string;
|
|
8
|
+
ref: string;
|
|
9
|
+
requiresMcp: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface OrganizationViewResourceSummary {
|
|
12
|
+
id: string;
|
|
13
|
+
kind: "git" | "volume";
|
|
14
|
+
mode: "mutable" | "readonly";
|
|
15
|
+
mount: string;
|
|
16
|
+
sharing: "per_agent" | "team";
|
|
17
|
+
}
|
|
18
|
+
export interface OrganizationViewPackageSummary {
|
|
19
|
+
id: string;
|
|
20
|
+
manager: string;
|
|
21
|
+
name: string;
|
|
22
|
+
scope?: string;
|
|
23
|
+
version?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface OrganizationViewMcpSummary {
|
|
26
|
+
name: string;
|
|
27
|
+
transport: "sse" | "stdio" | "streamable_http";
|
|
28
|
+
}
|
|
29
|
+
export interface OrganizationViewModelSummary {
|
|
30
|
+
authMethod: string;
|
|
31
|
+
name: string;
|
|
32
|
+
provider: string;
|
|
33
|
+
}
|
|
34
|
+
export interface OrganizationViewScheduleSummary {
|
|
35
|
+
expression?: string;
|
|
36
|
+
kind: "cron" | "disabled" | "every";
|
|
37
|
+
timezone?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface OrganizationViewSurfaceSummary {
|
|
40
|
+
name: string;
|
|
41
|
+
scopes: string[];
|
|
42
|
+
}
|
|
43
|
+
export interface OrganizationViewPolicySummary {
|
|
44
|
+
mode: string | null;
|
|
45
|
+
onDegrade: string | null;
|
|
46
|
+
}
|
|
47
|
+
export interface OrganizationViewDeclaredSummary {
|
|
48
|
+
docs: OrganizationViewDocSummary[];
|
|
49
|
+
mcpServers: OrganizationViewMcpSummary[];
|
|
50
|
+
model: OrganizationViewModelSummary | null;
|
|
51
|
+
packages: OrganizationViewPackageSummary[];
|
|
52
|
+
policy: OrganizationViewPolicySummary;
|
|
53
|
+
resources: OrganizationViewResourceSummary[];
|
|
54
|
+
schedule: OrganizationViewScheduleSummary | null;
|
|
55
|
+
skills: OrganizationViewSkillSummary[];
|
|
56
|
+
surfaces: OrganizationViewSurfaceSummary[];
|
|
57
|
+
}
|
|
2
58
|
export interface OrganizationViewTreeNode {
|
|
3
59
|
children: OrganizationViewTreeEdge[];
|
|
60
|
+
declared?: OrganizationViewDeclaredSummary;
|
|
4
61
|
displayName: string;
|
|
5
62
|
external?: string[];
|
|
6
63
|
id: string;
|
|
@@ -10,6 +67,7 @@ export interface OrganizationViewTreeNode {
|
|
|
10
67
|
name: string;
|
|
11
68
|
networks?: OrganizationTreeNetworkSummary[];
|
|
12
69
|
runtimeName: string | null;
|
|
70
|
+
slug?: string;
|
|
13
71
|
source: string;
|
|
14
72
|
}
|
|
15
73
|
export interface OrganizationTreeNetworkRoomSummary {
|
|
@@ -94,6 +152,10 @@ export interface OrganizationNetworkView {
|
|
|
94
152
|
url?: string;
|
|
95
153
|
declarations?: OrganizationNetworkDeclarationView[];
|
|
96
154
|
}
|
|
155
|
+
export interface OrganizationRuntimeView {
|
|
156
|
+
name: string;
|
|
157
|
+
nodeIds: string[];
|
|
158
|
+
}
|
|
97
159
|
export interface OrganizationView {
|
|
98
160
|
contexts: [];
|
|
99
161
|
diagnostics: [];
|
|
@@ -101,9 +163,10 @@ export interface OrganizationView {
|
|
|
101
163
|
networks: OrganizationNetworkView[];
|
|
102
164
|
projectRoot?: string;
|
|
103
165
|
root: OrganizationViewTreeNode;
|
|
104
|
-
runtimes: [];
|
|
166
|
+
runtimes: OrganizationRuntimeView[];
|
|
105
167
|
}
|
|
106
168
|
export interface RenderOrganizationViewOptions {
|
|
169
|
+
annotationFor?: (subjectKey: string) => string[];
|
|
107
170
|
ascii?: boolean;
|
|
108
171
|
color?: boolean;
|
|
109
172
|
declared?: boolean;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { DeploymentRecord } from "./record.js";
|
|
2
|
+
export type DockerInspectExecFile = (file: string, args: string[], options: {
|
|
3
|
+
timeout: number;
|
|
4
|
+
}) => Promise<{
|
|
5
|
+
stderr: string;
|
|
6
|
+
stdout: string;
|
|
7
|
+
}>;
|
|
8
|
+
export interface DockerUnitInspection {
|
|
9
|
+
containerId: string | null;
|
|
10
|
+
drift: string[];
|
|
11
|
+
exists: boolean | null;
|
|
12
|
+
exitCode: number | null;
|
|
13
|
+
finishedAt: string | null;
|
|
14
|
+
imageId: string | null;
|
|
15
|
+
message: string;
|
|
16
|
+
restartCount: number | null;
|
|
17
|
+
running: boolean | null;
|
|
18
|
+
severity: "error" | "ok" | "unknown" | "warn";
|
|
19
|
+
startedAt: string | null;
|
|
20
|
+
status: string | null;
|
|
21
|
+
unitId: string;
|
|
22
|
+
}
|
|
23
|
+
export type DockerInspectionResult = Map<string, DockerUnitInspection>;
|
|
24
|
+
export interface DockerInspectOptions {
|
|
25
|
+
dockerCommand?: string;
|
|
26
|
+
execFile?: DockerInspectExecFile;
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
}
|
|
29
|
+
export declare const inspectDockerDeployment: (record: DeploymentRecord, options?: DockerInspectOptions) => Promise<DockerInspectionResult>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { dockerContextNameForTarget, verifyDockerDeploymentTarget } from "./target.js";
|
|
4
|
+
const execFile = promisify(execFileCallback);
|
|
5
|
+
const toStringOrNull = (value) => typeof value === "string" && value.length > 0 ? value : null;
|
|
6
|
+
const toNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
7
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
+
const targetRefForUnit = (unit) => unit.container_id ?? unit.container_name;
|
|
9
|
+
const argsForInspect = (record, targetRef) => {
|
|
10
|
+
const context = dockerContextNameForTarget(record.target);
|
|
11
|
+
if (context) {
|
|
12
|
+
return ["--context", context, "inspect", targetRef];
|
|
13
|
+
}
|
|
14
|
+
if (record.target.kind === "host") {
|
|
15
|
+
return ["--host", record.target.value, "inspect", targetRef];
|
|
16
|
+
}
|
|
17
|
+
return ["inspect", targetRef];
|
|
18
|
+
};
|
|
19
|
+
const matchesDockerId = (recorded, live) => !recorded
|
|
20
|
+
|| !live
|
|
21
|
+
|| recorded === live
|
|
22
|
+
|| live.startsWith(recorded)
|
|
23
|
+
|| recorded.startsWith(live);
|
|
24
|
+
const createDriftMessages = (unit, inspected) => {
|
|
25
|
+
const containerId = toStringOrNull(inspected.Id);
|
|
26
|
+
const imageId = toStringOrNull(inspected.Image);
|
|
27
|
+
const drift = [];
|
|
28
|
+
if (!matchesDockerId(unit.container_id, containerId)) {
|
|
29
|
+
drift.push(`container id drift: recorded ${unit.container_id}, live ${containerId}`);
|
|
30
|
+
}
|
|
31
|
+
if (!matchesDockerId(unit.image_id, imageId)) {
|
|
32
|
+
drift.push(`image id drift: recorded ${unit.image_id}, live ${imageId}`);
|
|
33
|
+
}
|
|
34
|
+
return { containerId, drift, imageId };
|
|
35
|
+
};
|
|
36
|
+
const inspectionFromState = (unitId, unit, inspected, state) => {
|
|
37
|
+
const running = typeof state.Running === "boolean" ? state.Running : null;
|
|
38
|
+
const status = toStringOrNull(state.Status);
|
|
39
|
+
const { containerId, drift, imageId } = createDriftMessages(unit, inspected);
|
|
40
|
+
const baseMessage = running === true
|
|
41
|
+
? `container is running (${status ?? "running"})`
|
|
42
|
+
: `container is not running (${status ?? "unknown"})`;
|
|
43
|
+
return {
|
|
44
|
+
containerId,
|
|
45
|
+
drift,
|
|
46
|
+
exists: true,
|
|
47
|
+
exitCode: toNumberOrNull(state.ExitCode),
|
|
48
|
+
finishedAt: toStringOrNull(state.FinishedAt),
|
|
49
|
+
imageId,
|
|
50
|
+
message: drift.length > 0 ? `${baseMessage}; ${drift.join("; ")}` : baseMessage,
|
|
51
|
+
restartCount: toNumberOrNull(state.RestartCount),
|
|
52
|
+
running,
|
|
53
|
+
severity: drift.length > 0 ? "warn" : running === false ? "error" : "ok",
|
|
54
|
+
startedAt: toStringOrNull(state.StartedAt),
|
|
55
|
+
status,
|
|
56
|
+
unitId
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
const missingInspection = (unitId, message) => ({
|
|
60
|
+
containerId: null,
|
|
61
|
+
drift: [],
|
|
62
|
+
exists: false,
|
|
63
|
+
exitCode: null,
|
|
64
|
+
finishedAt: null,
|
|
65
|
+
imageId: null,
|
|
66
|
+
message,
|
|
67
|
+
restartCount: null,
|
|
68
|
+
running: false,
|
|
69
|
+
severity: "warn",
|
|
70
|
+
startedAt: null,
|
|
71
|
+
status: null,
|
|
72
|
+
unitId
|
|
73
|
+
});
|
|
74
|
+
const unknownInspection = (unitId, message, severity = "unknown") => ({
|
|
75
|
+
containerId: null,
|
|
76
|
+
drift: [],
|
|
77
|
+
exists: null,
|
|
78
|
+
exitCode: null,
|
|
79
|
+
finishedAt: null,
|
|
80
|
+
imageId: null,
|
|
81
|
+
message,
|
|
82
|
+
restartCount: null,
|
|
83
|
+
running: null,
|
|
84
|
+
severity,
|
|
85
|
+
startedAt: null,
|
|
86
|
+
status: null,
|
|
87
|
+
unitId
|
|
88
|
+
});
|
|
89
|
+
const inspectUnit = async (record, unit, options) => {
|
|
90
|
+
const targetRef = targetRefForUnit(unit);
|
|
91
|
+
if (!targetRef) {
|
|
92
|
+
return unknownInspection(unit.id, "deployment unit has no recorded container id or name");
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const { stdout } = await options.execFile(options.dockerCommand, argsForInspect(record, targetRef), { timeout: options.timeoutMs });
|
|
96
|
+
const parsed = JSON.parse(stdout);
|
|
97
|
+
const [first] = Array.isArray(parsed) ? parsed : [];
|
|
98
|
+
const state = isRecord(first) && isRecord(first.State) ? first.State : null;
|
|
99
|
+
return state
|
|
100
|
+
? inspectionFromState(unit.id, unit, first, state)
|
|
101
|
+
: unknownInspection(unit.id, `docker inspect returned no state for ${targetRef}`);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
const stderr = isRecord(error) ? toStringOrNull(error.stderr) : null;
|
|
105
|
+
const message = stderr ?? (error instanceof Error ? error.message : String(error));
|
|
106
|
+
if (/No such object/i.test(message)) {
|
|
107
|
+
return missingInspection(unit.id, `recorded container ${targetRef} is missing`);
|
|
108
|
+
}
|
|
109
|
+
return unknownInspection(unit.id, `unable to inspect container ${targetRef}: ${message}`, "error");
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
export const inspectDockerDeployment = async (record, options = {}) => {
|
|
113
|
+
const resolvedOptions = {
|
|
114
|
+
dockerCommand: options.dockerCommand ?? "docker",
|
|
115
|
+
execFile: options.execFile ?? execFile,
|
|
116
|
+
timeoutMs: options.timeoutMs ?? 10_000
|
|
117
|
+
};
|
|
118
|
+
try {
|
|
119
|
+
await verifyDockerDeploymentTarget(record.target, resolvedOptions);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
123
|
+
return new Map(record.units.map((unit) => [
|
|
124
|
+
unit.id,
|
|
125
|
+
unknownInspection(unit.id, `unable to verify recorded Docker target: ${message}`, "error")
|
|
126
|
+
]));
|
|
127
|
+
}
|
|
128
|
+
const inspections = await Promise.all(record.units.map(async (unit) => inspectUnit(record, unit, resolvedOptions)));
|
|
129
|
+
return new Map(inspections.map((inspection) => [inspection.unitId, inspection]));
|
|
130
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const dockerDeploymentLabelKeys: {
|
|
2
|
+
readonly compileFingerprint: "com.spawnfile.compile_fingerprint";
|
|
3
|
+
readonly deployment: "com.spawnfile.deployment";
|
|
4
|
+
readonly project: "com.spawnfile.project";
|
|
5
|
+
readonly unit: "com.spawnfile.unit";
|
|
6
|
+
readonly version: "com.spawnfile.version";
|
|
7
|
+
};
|
|
8
|
+
export interface DockerDeploymentLabelInput {
|
|
9
|
+
compileFingerprint: string;
|
|
10
|
+
deployment: string;
|
|
11
|
+
project: string;
|
|
12
|
+
unit: string;
|
|
13
|
+
version: string;
|
|
14
|
+
}
|
|
15
|
+
export declare const createDockerDeploymentLabels: (input: DockerDeploymentLabelInput) => Record<string, string>;
|
|
16
|
+
export declare const appendDockerLabelArgs: (args: string[], labels: Record<string, string>) => void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
2
|
+
export const dockerDeploymentLabelKeys = {
|
|
3
|
+
compileFingerprint: "com.spawnfile.compile_fingerprint",
|
|
4
|
+
deployment: "com.spawnfile.deployment",
|
|
5
|
+
project: "com.spawnfile.project",
|
|
6
|
+
unit: "com.spawnfile.unit",
|
|
7
|
+
version: "com.spawnfile.version"
|
|
8
|
+
};
|
|
9
|
+
const dockerLabelValuePattern = /^[A-Za-z0-9][A-Za-z0-9_.:-]*$/;
|
|
10
|
+
const assertIdentifierLabelValue = (key, value) => {
|
|
11
|
+
if (!dockerLabelValuePattern.test(value)) {
|
|
12
|
+
throw new SpawnfileError("validation_error", `Docker deployment label ${key} must be an identifier`);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export const createDockerDeploymentLabels = (input) => {
|
|
16
|
+
const labels = {
|
|
17
|
+
[dockerDeploymentLabelKeys.compileFingerprint]: input.compileFingerprint,
|
|
18
|
+
[dockerDeploymentLabelKeys.deployment]: input.deployment,
|
|
19
|
+
[dockerDeploymentLabelKeys.project]: input.project,
|
|
20
|
+
[dockerDeploymentLabelKeys.unit]: input.unit,
|
|
21
|
+
[dockerDeploymentLabelKeys.version]: input.version
|
|
22
|
+
};
|
|
23
|
+
for (const [key, value] of Object.entries(labels)) {
|
|
24
|
+
assertIdentifierLabelValue(key, value);
|
|
25
|
+
}
|
|
26
|
+
return labels;
|
|
27
|
+
};
|
|
28
|
+
export const appendDockerLabelArgs = (args, labels) => {
|
|
29
|
+
for (const [key, value] of Object.entries(labels).sort(([left], [right]) => left.localeCompare(right))) {
|
|
30
|
+
args.push("--label", `${key}=${value}`);
|
|
31
|
+
}
|
|
32
|
+
};
|