spawnfile 0.1.3 → 0.1.5
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 +2 -0
- package/dist/cli/lifecycleCommands.d.ts +3 -0
- package/dist/cli/lifecycleCommands.js +80 -0
- package/dist/cli/runCli.d.ts +2 -1
- package/dist/cli/runCli.js +5 -47
- package/dist/compiler/buildCompilePlan.js +12 -202
- package/dist/compiler/buildCompilePlanRuntime.d.ts +6 -1
- package/dist/compiler/buildCompilePlanRuntime.js +9 -0
- package/dist/compiler/buildCompilePlanTeams.js +16 -10
- package/dist/compiler/buildCompilePlanTraversal.d.ts +16 -0
- package/dist/compiler/buildCompilePlanTraversal.js +214 -0
- package/dist/compiler/buildCompilePlanTraversalHelpers.d.ts +18 -0
- package/dist/compiler/buildCompilePlanTraversalHelpers.js +22 -0
- package/dist/compiler/compilePlanHelpers.d.ts +3 -1
- package/dist/compiler/compilePlanHelpers.js +37 -1
- package/dist/compiler/compileProject.js +14 -0
- package/dist/compiler/containerArtifacts.js +18 -3
- package/dist/compiler/containerArtifactsPlans.js +32 -0
- package/dist/compiler/containerArtifactsRender.js +86 -3
- package/dist/compiler/containerArtifactsTypes.d.ts +7 -3
- package/dist/compiler/containerEntrypointRender.d.ts +1 -1
- package/dist/compiler/containerEntrypointRender.js +37 -27
- package/dist/compiler/containerTargetResources.d.ts +4 -0
- package/dist/compiler/containerTargetResources.js +54 -0
- package/dist/compiler/containerWorkspaceResourceRender.d.ts +3 -0
- package/dist/compiler/containerWorkspaceResourceRender.js +128 -0
- package/dist/compiler/executionDefaults.js +0 -3
- package/dist/compiler/helpers.d.ts +1 -1
- package/dist/compiler/index.d.ts +1 -0
- package/dist/compiler/index.js +1 -0
- package/dist/compiler/moltnetArtifacts.d.ts +11 -5
- package/dist/compiler/moltnetArtifacts.js +133 -117
- package/dist/compiler/moltnetClientConfig.js +8 -2
- package/dist/compiler/moltnetConfigLowering.d.ts +36 -0
- package/dist/compiler/moltnetConfigLowering.js +125 -0
- package/dist/compiler/moltnetRuntimeConfig.d.ts +2 -0
- package/dist/compiler/moltnetRuntimeConfig.js +69 -0
- package/dist/compiler/runProject.d.ts +2 -0
- package/dist/compiler/runProject.js +20 -6
- package/dist/compiler/surfaces.d.ts +3 -13
- package/dist/compiler/surfaces.js +1 -6
- package/dist/compiler/syncProjectAuth.js +67 -19
- package/dist/compiler/types.d.ts +16 -1
- package/dist/compiler/upProject.d.ts +19 -0
- package/dist/compiler/upProject.js +37 -0
- package/dist/compiler/view/buildOrganizationView.js +22 -2
- package/dist/compiler/view/renderNetworks.js +14 -3
- package/dist/compiler/view/renderTree.js +8 -2
- package/dist/compiler/view/types.d.ts +18 -3
- package/dist/compiler/workspaceResources.d.ts +34 -0
- package/dist/compiler/workspaceResources.js +120 -0
- package/dist/manifest/executionSchemas.d.ts +106 -0
- package/dist/manifest/executionSchemas.js +140 -0
- package/dist/manifest/loadManifest.js +15 -27
- package/dist/manifest/renderSpawnfile.js +44 -52
- package/dist/manifest/renderSpawnfileNetworks.d.ts +2 -0
- package/dist/manifest/renderSpawnfileNetworks.js +63 -0
- package/dist/manifest/renderSpawnfileWorkspace.d.ts +2 -0
- package/dist/manifest/renderSpawnfileWorkspace.js +47 -0
- package/dist/manifest/scaffold.js +12 -6
- package/dist/manifest/scheduleSchemas.d.ts +15 -0
- package/dist/manifest/scheduleSchemas.js +26 -0
- package/dist/manifest/schemas.d.ts +626 -368
- package/dist/manifest/schemas.js +51 -191
- package/dist/manifest/teamNetworkSchemas.d.ts +228 -0
- package/dist/manifest/teamNetworkSchemas.js +295 -0
- package/dist/manifest/workspaceSchemas.d.ts +96 -0
- package/dist/manifest/workspaceSchemas.js +166 -0
- package/dist/report/types.d.ts +10 -0
- package/dist/runtime/common.d.ts +2 -1
- package/dist/runtime/common.js +3 -3
- package/dist/runtime/picoclaw/adapter.js +7 -0
- package/dist/runtime/picoclaw/runAuth.js +5 -41
- package/dist/runtime/tinyclaw/adapter.js +9 -2
- package/dist/runtime/tinyclaw/schedules.d.ts +9 -0
- package/dist/runtime/tinyclaw/schedules.js +62 -0
- package/dist/runtime/types.d.ts +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { getCanonicalManifestPath, getManifestPath, resolveProjectPath } from "../filesystem/index.js";
|
|
2
|
+
import { isAgentManifest, isTeamManifest, mergeExecution } from "../manifest/index.js";
|
|
3
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
4
|
+
import { getAgentFingerprint, getMcpNames, getTeamFingerprint, validateEffectiveSkillRequirements } from "./compilePlanHelpers.js";
|
|
5
|
+
import { resolveAgentSurfaces } from "./agentSurfaces.js";
|
|
6
|
+
import { mergeResolvedSkills, loadResolvedSkills } from "./surfaces.js";
|
|
7
|
+
import { assertRuntimeSupportsExecutionModelAuth } from "./modelAuth.js";
|
|
8
|
+
import { assertRuntimeSupportsAgentSurfaces } from "./surfaceSupport.js";
|
|
9
|
+
import { applyExecutionDefaults } from "./executionDefaults.js";
|
|
10
|
+
import { normalizeDescription, resolveDescription, resolveRuntime } from "./buildCompilePlanRuntime.js";
|
|
11
|
+
import { resolveTeamExternalIds, resolveTeamNetworks, validateTeamNetworkRooms } from "./buildCompilePlanTeams.js";
|
|
12
|
+
import { mergeWorkspaceResources } from "./workspaceResources.js";
|
|
13
|
+
import { DEFAULT_POLICY_MODE, DEFAULT_POLICY_ON_DEGRADE, mergeResolvedDocuments, resolveEffectiveEnvironment } from "./buildCompilePlanTraversalHelpers.js";
|
|
14
|
+
export const createCompilePlanTraversal = ({ getLoadedManifest, nodeCache, fingerprintCache, edges, memberships }) => {
|
|
15
|
+
const visitStack = [];
|
|
16
|
+
const visitAgent = async (manifestPath, context) => {
|
|
17
|
+
const canonicalPath = getCanonicalManifestPath(manifestPath);
|
|
18
|
+
if (visitStack.includes(canonicalPath)) {
|
|
19
|
+
throw new SpawnfileError("compile_error", `Cycle detected while visiting ${canonicalPath}`);
|
|
20
|
+
}
|
|
21
|
+
const loadedManifest = await getLoadedManifest(canonicalPath);
|
|
22
|
+
if (!isAgentManifest(loadedManifest.manifest)) {
|
|
23
|
+
throw new SpawnfileError("compile_error", `Expected agent manifest, got ${loadedManifest.manifest.kind} at ${canonicalPath}`);
|
|
24
|
+
}
|
|
25
|
+
const runtime = await resolveRuntime(loadedManifest.manifest, context);
|
|
26
|
+
const execution = applyExecutionDefaults(context.isSubagent
|
|
27
|
+
? mergeExecution(context.inheritedExecution, loadedManifest.manifest.execution)
|
|
28
|
+
: loadedManifest.manifest.execution);
|
|
29
|
+
assertRuntimeSupportsExecutionModelAuth(runtime.name, execution, loadedManifest.manifest.name);
|
|
30
|
+
const environment = resolveEffectiveEnvironment(context.inheritedShared?.surface?.environment, loadedManifest.manifest.environment);
|
|
31
|
+
const inheritedSkills = context.inheritedShared
|
|
32
|
+
? await loadResolvedSkills(context.inheritedShared.manifestPath, context.inheritedShared.surface?.workspace?.skills)
|
|
33
|
+
: [];
|
|
34
|
+
const localSkills = await loadResolvedSkills(canonicalPath, loadedManifest.manifest.workspace?.skills);
|
|
35
|
+
const skills = mergeResolvedSkills(inheritedSkills, localSkills);
|
|
36
|
+
validateEffectiveSkillRequirements(loadedManifest.manifest.name, getMcpNames(environment.mcpServers), skills);
|
|
37
|
+
const docs = await mergeResolvedDocuments(canonicalPath, loadedManifest.manifest.workspace?.docs, context.inheritedShared?.manifestPath, context.inheritedShared?.surface?.workspace?.docs);
|
|
38
|
+
const workspaceResources = mergeWorkspaceResources(context.inheritedResources, loadedManifest.manifest.workspace?.resources, loadedManifest.manifest.name, {
|
|
39
|
+
kind: "agent",
|
|
40
|
+
key: canonicalPath,
|
|
41
|
+
name: loadedManifest.manifest.name
|
|
42
|
+
});
|
|
43
|
+
const candidate = {
|
|
44
|
+
description: resolveDescription(loadedManifest.manifest.description, docs),
|
|
45
|
+
docs,
|
|
46
|
+
env: environment.env,
|
|
47
|
+
execution,
|
|
48
|
+
expose: loadedManifest.manifest.expose ?? false,
|
|
49
|
+
kind: "agent",
|
|
50
|
+
mcpServers: environment.mcpServers,
|
|
51
|
+
name: loadedManifest.manifest.name,
|
|
52
|
+
policyMode: loadedManifest.manifest.policy?.mode ?? DEFAULT_POLICY_MODE,
|
|
53
|
+
policyOnDegrade: loadedManifest.manifest.policy?.on_degrade ?? DEFAULT_POLICY_ON_DEGRADE,
|
|
54
|
+
runtime,
|
|
55
|
+
schedule: loadedManifest.manifest.schedule,
|
|
56
|
+
secrets: environment.secrets,
|
|
57
|
+
packages: environment.packages,
|
|
58
|
+
skills,
|
|
59
|
+
source: canonicalPath,
|
|
60
|
+
surfaces: resolveAgentSurfaces(loadedManifest.manifest.surfaces),
|
|
61
|
+
subagents: [],
|
|
62
|
+
workspaceResources
|
|
63
|
+
};
|
|
64
|
+
assertRuntimeSupportsAgentSurfaces(runtime.name, candidate.surfaces, loadedManifest.manifest.name);
|
|
65
|
+
const fingerprint = getAgentFingerprint(candidate);
|
|
66
|
+
const existingFingerprint = fingerprintCache.get(canonicalPath);
|
|
67
|
+
if (existingFingerprint && existingFingerprint !== fingerprint) {
|
|
68
|
+
throw new SpawnfileError("compile_error", `Manifest ${canonicalPath} resolves differently across compile contexts`);
|
|
69
|
+
}
|
|
70
|
+
const cachedNode = nodeCache.get(canonicalPath);
|
|
71
|
+
if (cachedNode) {
|
|
72
|
+
return cachedNode.value;
|
|
73
|
+
}
|
|
74
|
+
fingerprintCache.set(canonicalPath, fingerprint);
|
|
75
|
+
nodeCache.set(canonicalPath, {
|
|
76
|
+
runtimeName: runtime.name,
|
|
77
|
+
source: canonicalPath,
|
|
78
|
+
value: candidate
|
|
79
|
+
});
|
|
80
|
+
visitStack.push(canonicalPath);
|
|
81
|
+
for (const subagent of loadedManifest.manifest.subagents ?? []) {
|
|
82
|
+
const childManifestPath = getManifestPath(resolveProjectPath(canonicalPath, subagent.ref));
|
|
83
|
+
const resolvedSubagent = await visitAgent(childManifestPath, {
|
|
84
|
+
inheritedExecution: execution,
|
|
85
|
+
inheritedResources: candidate.workspaceResources,
|
|
86
|
+
inheritedRuntime: runtime,
|
|
87
|
+
isSubagent: true
|
|
88
|
+
});
|
|
89
|
+
candidate.subagents.push({
|
|
90
|
+
id: subagent.id,
|
|
91
|
+
nodeSource: resolvedSubagent.source
|
|
92
|
+
});
|
|
93
|
+
edges.push({
|
|
94
|
+
from: canonicalPath,
|
|
95
|
+
kind: "subagent",
|
|
96
|
+
label: subagent.id,
|
|
97
|
+
to: resolvedSubagent.source
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
visitStack.pop();
|
|
101
|
+
return candidate;
|
|
102
|
+
};
|
|
103
|
+
const visitTeam = async (manifestPath, inheritedResources = []) => {
|
|
104
|
+
const canonicalPath = getCanonicalManifestPath(manifestPath);
|
|
105
|
+
if (visitStack.includes(canonicalPath)) {
|
|
106
|
+
throw new SpawnfileError("compile_error", `Cycle detected while visiting ${canonicalPath}`);
|
|
107
|
+
}
|
|
108
|
+
const loadedManifest = await getLoadedManifest(canonicalPath);
|
|
109
|
+
if (!isTeamManifest(loadedManifest.manifest)) {
|
|
110
|
+
throw new SpawnfileError("compile_error", `Expected team manifest, got ${loadedManifest.manifest.kind} at ${canonicalPath}`);
|
|
111
|
+
}
|
|
112
|
+
const manifest = loadedManifest.manifest;
|
|
113
|
+
const sharedWorkspace = manifest.shared?.workspace;
|
|
114
|
+
const sharedEnvironment = manifest.shared?.environment;
|
|
115
|
+
const sharedSkills = await loadResolvedSkills(canonicalPath, sharedWorkspace?.skills);
|
|
116
|
+
validateEffectiveSkillRequirements(loadedManifest.manifest.name, getMcpNames(sharedEnvironment?.mcp_servers ?? []), sharedSkills);
|
|
117
|
+
const resolvedExternal = resolveTeamExternalIds(manifest);
|
|
118
|
+
const docs = await mergeResolvedDocuments(canonicalPath, sharedWorkspace?.docs, undefined, undefined);
|
|
119
|
+
const workspaceResources = mergeWorkspaceResources(inheritedResources, sharedWorkspace?.resources, manifest.name, {
|
|
120
|
+
kind: "team",
|
|
121
|
+
key: canonicalPath,
|
|
122
|
+
name: manifest.name
|
|
123
|
+
});
|
|
124
|
+
const candidate = {
|
|
125
|
+
description: manifest.description ? normalizeDescription(manifest.description) : "",
|
|
126
|
+
docs,
|
|
127
|
+
external: resolvedExternal,
|
|
128
|
+
externalExplicit: manifest.external !== undefined,
|
|
129
|
+
kind: "team",
|
|
130
|
+
lead: manifest.lead ?? null,
|
|
131
|
+
members: [],
|
|
132
|
+
mode: manifest.mode,
|
|
133
|
+
name: manifest.name,
|
|
134
|
+
networks: resolveTeamNetworks(manifest),
|
|
135
|
+
policyMode: manifest.policy?.mode ?? DEFAULT_POLICY_MODE,
|
|
136
|
+
policyOnDegrade: manifest.policy?.on_degrade ?? DEFAULT_POLICY_ON_DEGRADE,
|
|
137
|
+
workspaceResources,
|
|
138
|
+
shared: {
|
|
139
|
+
env: sharedEnvironment?.env ?? {},
|
|
140
|
+
mcpServers: sharedEnvironment?.mcp_servers ?? [],
|
|
141
|
+
packages: sharedEnvironment?.packages,
|
|
142
|
+
secrets: sharedEnvironment?.secrets ?? [],
|
|
143
|
+
skills: sharedSkills
|
|
144
|
+
},
|
|
145
|
+
source: canonicalPath,
|
|
146
|
+
};
|
|
147
|
+
const fingerprint = getTeamFingerprint(candidate);
|
|
148
|
+
const existingFingerprint = fingerprintCache.get(canonicalPath);
|
|
149
|
+
if (existingFingerprint && existingFingerprint !== fingerprint) {
|
|
150
|
+
throw new SpawnfileError("compile_error", `Team manifest ${canonicalPath} resolves differently across compile contexts`);
|
|
151
|
+
}
|
|
152
|
+
const cachedNode = nodeCache.get(canonicalPath);
|
|
153
|
+
if (cachedNode) {
|
|
154
|
+
return cachedNode.value;
|
|
155
|
+
}
|
|
156
|
+
fingerprintCache.set(canonicalPath, fingerprint);
|
|
157
|
+
nodeCache.set(canonicalPath, {
|
|
158
|
+
runtimeName: null,
|
|
159
|
+
source: canonicalPath,
|
|
160
|
+
value: candidate
|
|
161
|
+
});
|
|
162
|
+
visitStack.push(canonicalPath);
|
|
163
|
+
for (const member of loadedManifest.manifest.members) {
|
|
164
|
+
const childManifestPath = getManifestPath(resolveProjectPath(canonicalPath, member.ref));
|
|
165
|
+
const childManifest = await getLoadedManifest(childManifestPath);
|
|
166
|
+
let resolvedMember;
|
|
167
|
+
if (isAgentManifest(childManifest.manifest)) {
|
|
168
|
+
const resolvedAgent = await visitAgent(childManifestPath, {
|
|
169
|
+
inheritedShared: {
|
|
170
|
+
manifestPath: canonicalPath,
|
|
171
|
+
surface: loadedManifest.manifest.shared
|
|
172
|
+
},
|
|
173
|
+
inheritedResources: candidate.workspaceResources,
|
|
174
|
+
isSubagent: false
|
|
175
|
+
});
|
|
176
|
+
resolvedMember = {
|
|
177
|
+
id: member.id,
|
|
178
|
+
kind: "agent",
|
|
179
|
+
nodeSource: resolvedAgent.source,
|
|
180
|
+
runtimeName: resolvedAgent.runtime.name
|
|
181
|
+
};
|
|
182
|
+
memberships.set(`${canonicalPath}::${member.id}::${resolvedAgent.source}`, {
|
|
183
|
+
agentSource: resolvedAgent.source,
|
|
184
|
+
memberId: member.id,
|
|
185
|
+
teamName: candidate.name,
|
|
186
|
+
teamSource: canonicalPath
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
const resolvedTeam = await visitTeam(childManifestPath, candidate.workspaceResources);
|
|
191
|
+
resolvedMember = {
|
|
192
|
+
id: member.id,
|
|
193
|
+
kind: "team",
|
|
194
|
+
nodeSource: resolvedTeam.source,
|
|
195
|
+
runtimeName: null
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
candidate.members.push(resolvedMember);
|
|
199
|
+
edges.push({
|
|
200
|
+
from: canonicalPath,
|
|
201
|
+
kind: "team_member",
|
|
202
|
+
label: member.id,
|
|
203
|
+
to: resolvedMember.nodeSource
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
validateTeamNetworkRooms(candidate);
|
|
207
|
+
visitStack.pop();
|
|
208
|
+
return candidate;
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
visitAgent,
|
|
212
|
+
visitTeam
|
|
213
|
+
};
|
|
214
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Environment, type McpServer, type Secret, type TeamWorkspace } from "../manifest/index.js";
|
|
2
|
+
import { ResolvedDocument, ResolvedPackage, ResolvedAgentNode, ResolvedTeamNode } from "./types.js";
|
|
3
|
+
export declare const DEFAULT_POLICY_MODE = "warn";
|
|
4
|
+
export declare const DEFAULT_POLICY_ON_DEGRADE = "warn";
|
|
5
|
+
export type InternalNode = {
|
|
6
|
+
runtimeName: string | null;
|
|
7
|
+
source: string;
|
|
8
|
+
value: ResolvedAgentNode | ResolvedTeamNode;
|
|
9
|
+
};
|
|
10
|
+
type ResolvedPackageArray = ResolvedPackage[] | undefined;
|
|
11
|
+
export declare const mergeResolvedDocuments: (primaryPath: string, primaryDocs: TeamWorkspace["docs"] | undefined, fallbackPath: string | undefined, fallbackDocs: TeamWorkspace["docs"] | undefined) => Promise<ResolvedDocument[]>;
|
|
12
|
+
export declare const resolveEffectiveEnvironment: (sharedEnvironment: Environment | undefined, localEnvironment: Environment | undefined) => {
|
|
13
|
+
env: Record<string, string>;
|
|
14
|
+
mcpServers: McpServer[];
|
|
15
|
+
packages: ResolvedPackageArray;
|
|
16
|
+
secrets: Secret[];
|
|
17
|
+
};
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { loadResolvedDocuments, mergeEnv, mergeMcpServers, mergePackages, mergeSecrets } from "./surfaces.js";
|
|
2
|
+
export const DEFAULT_POLICY_MODE = "warn";
|
|
3
|
+
export const DEFAULT_POLICY_ON_DEGRADE = "warn";
|
|
4
|
+
export const mergeResolvedDocuments = async (primaryPath, primaryDocs, fallbackPath, fallbackDocs) => {
|
|
5
|
+
const fallbackResolved = fallbackPath && fallbackDocs
|
|
6
|
+
? await loadResolvedDocuments(fallbackPath, fallbackDocs)
|
|
7
|
+
: [];
|
|
8
|
+
const primaryResolved = await loadResolvedDocuments(primaryPath, primaryDocs);
|
|
9
|
+
const merged = new Map(primaryResolved.map((doc) => [doc.role, doc]));
|
|
10
|
+
for (const doc of fallbackResolved) {
|
|
11
|
+
if (!merged.has(doc.role)) {
|
|
12
|
+
merged.set(doc.role, doc);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return [...merged.values()];
|
|
16
|
+
};
|
|
17
|
+
export const resolveEffectiveEnvironment = (sharedEnvironment, localEnvironment) => ({
|
|
18
|
+
env: mergeEnv(sharedEnvironment?.env, localEnvironment?.env),
|
|
19
|
+
mcpServers: mergeMcpServers(sharedEnvironment?.mcp_servers, localEnvironment?.mcp_servers),
|
|
20
|
+
packages: mergePackages(sharedEnvironment?.packages, localEnvironment?.packages),
|
|
21
|
+
secrets: mergeSecrets(sharedEnvironment?.secrets, localEnvironment?.secrets)
|
|
22
|
+
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
export { mergePackages } from "./surfaces.js";
|
|
2
|
+
import type { CompilePlanNode, ResolvedAgentNode, ResolvedSkill, ResolvedTeamNode } from "./types.js";
|
|
2
3
|
export declare const getMcpNames: (servers: Array<{
|
|
3
4
|
name: string;
|
|
4
5
|
}>) => Set<string>;
|
|
5
6
|
export declare const validateEffectiveSkillRequirements: (nodeName: string, mcpNames: Set<string>, skills: ResolvedSkill[]) => void;
|
|
6
7
|
export declare const getAgentFingerprint: (node: ResolvedAgentNode) => string;
|
|
7
8
|
export declare const getTeamFingerprint: (node: ResolvedTeamNode) => string;
|
|
9
|
+
export declare const listMoltnetNetworkSecretNames: (nodes: CompilePlanNode[]) => string[];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SpawnfileError } from "../shared/index.js";
|
|
2
2
|
import { stableStringify } from "./helpers.js";
|
|
3
|
+
export { mergePackages } from "./surfaces.js";
|
|
3
4
|
export const getMcpNames = (servers) => new Set(servers.map((server) => server.name));
|
|
4
5
|
export const validateEffectiveSkillRequirements = (nodeName, mcpNames, skills) => {
|
|
5
6
|
for (const skill of skills) {
|
|
@@ -15,13 +16,16 @@ export const getAgentFingerprint = (node) => stableStringify({
|
|
|
15
16
|
execution: node.execution,
|
|
16
17
|
mcpServers: node.mcpServers,
|
|
17
18
|
runtime: node.runtime,
|
|
19
|
+
schedule: node.schedule,
|
|
20
|
+
packages: node.packages,
|
|
18
21
|
secrets: node.secrets,
|
|
19
22
|
skills: node.skills.map((skill) => ({
|
|
20
23
|
name: skill.name,
|
|
21
24
|
ref: skill.ref,
|
|
22
25
|
requiresMcp: skill.requiresMcp
|
|
23
26
|
})),
|
|
24
|
-
surfaces: node.surfaces
|
|
27
|
+
surfaces: node.surfaces,
|
|
28
|
+
workspaceResources: node.workspaceResources
|
|
25
29
|
});
|
|
26
30
|
export const getTeamFingerprint = (node) => stableStringify({
|
|
27
31
|
members: node.members,
|
|
@@ -29,9 +33,11 @@ export const getTeamFingerprint = (node) => stableStringify({
|
|
|
29
33
|
lead: node.lead,
|
|
30
34
|
external: node.external,
|
|
31
35
|
networks: node.networks ?? [],
|
|
36
|
+
workspaceResources: node.workspaceResources,
|
|
32
37
|
shared: {
|
|
33
38
|
env: node.shared.env,
|
|
34
39
|
mcpServers: node.shared.mcpServers,
|
|
40
|
+
packages: node.shared.packages,
|
|
35
41
|
secrets: node.shared.secrets,
|
|
36
42
|
skills: node.shared.skills.map((skill) => ({
|
|
37
43
|
name: skill.name,
|
|
@@ -40,3 +46,33 @@ export const getTeamFingerprint = (node) => stableStringify({
|
|
|
40
46
|
}))
|
|
41
47
|
}
|
|
42
48
|
});
|
|
49
|
+
const collectMoltnetSecretNames = (server, secretNames) => {
|
|
50
|
+
for (const token of server.auth.tokens ?? []) {
|
|
51
|
+
secretNames.add(token.secret);
|
|
52
|
+
}
|
|
53
|
+
if (server.mode === "managed") {
|
|
54
|
+
for (const pairing of server.pairings ?? []) {
|
|
55
|
+
secretNames.add(pairing.token_secret);
|
|
56
|
+
}
|
|
57
|
+
if (server.store.kind === "postgres") {
|
|
58
|
+
secretNames.add(server.store.dsn_secret);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (server.auth.client?.token_env) {
|
|
62
|
+
secretNames.add(server.auth.client.token_env);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
export const listMoltnetNetworkSecretNames = (nodes) => {
|
|
66
|
+
const secretNames = new Set();
|
|
67
|
+
for (const node of nodes) {
|
|
68
|
+
if (node.value.kind !== "team" || !node.value.networks) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
for (const network of node.value.networks) {
|
|
72
|
+
if (network.server) {
|
|
73
|
+
collectMoltnetSecretNames(network.server, secretNames);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return [...secretNames].sort();
|
|
78
|
+
};
|
|
@@ -136,11 +136,17 @@ const enforcePolicy = (nodeReport, policyMode, onDegrade) => {
|
|
|
136
136
|
if (policyMode === "strict") {
|
|
137
137
|
throw new Error(`Policy violation: ${capability.key} is unsupported for ${nodeReport.id} (strict mode)${capability.message ? `: ${capability.message}` : ""}`);
|
|
138
138
|
}
|
|
139
|
+
if (policyMode === "warn") {
|
|
140
|
+
nodeReport.diagnostics.push(createDiagnostic("warn", `Policy warning: ${capability.key} is unsupported for ${nodeReport.id}${capability.message ? `: ${capability.message}` : ""}`));
|
|
141
|
+
}
|
|
139
142
|
}
|
|
140
143
|
if (capability.outcome === "degraded") {
|
|
141
144
|
if (onDegrade === "error") {
|
|
142
145
|
throw new Error(`Policy violation: ${capability.key} is degraded for ${nodeReport.id} (on_degrade: error)${capability.message ? `: ${capability.message}` : ""}`);
|
|
143
146
|
}
|
|
147
|
+
if (onDegrade === "warn") {
|
|
148
|
+
nodeReport.diagnostics.push(createDiagnostic("warn", `Policy warning: ${capability.key} is degraded for ${nodeReport.id}${capability.message ? `: ${capability.message}` : ""}`));
|
|
149
|
+
}
|
|
144
150
|
}
|
|
145
151
|
}
|
|
146
152
|
};
|
|
@@ -174,8 +180,16 @@ const createIdentityCapabilities = (node) => [
|
|
|
174
180
|
}]
|
|
175
181
|
: [])
|
|
176
182
|
];
|
|
183
|
+
const createWorkspaceResourceCapabilities = (node) => (node.workspaceResources?.length ?? 0) > 0
|
|
184
|
+
? [{
|
|
185
|
+
key: "workspace.resources",
|
|
186
|
+
message: `${node.workspaceResources?.length ?? 0} workspace resource(s) will be prepared at startup`,
|
|
187
|
+
outcome: "supported"
|
|
188
|
+
}]
|
|
189
|
+
: [];
|
|
177
190
|
const augmentNodeReports = (compiledNodes, support) => {
|
|
178
191
|
for (const compiled of compiledNodes) {
|
|
192
|
+
compiled.report.capabilities.push(...createWorkspaceResourceCapabilities(compiled.value));
|
|
179
193
|
if (compiled.value.kind === "team") {
|
|
180
194
|
compiled.report.capabilities.push(...(support.capabilitiesByTeamSource.get(compiled.value.source) ?? []));
|
|
181
195
|
compiled.report.diagnostics.push(...(support.diagnosticsByTeamSource.get(compiled.value.source) ?? []));
|
|
@@ -30,7 +30,7 @@ export const createContainerArtifacts = async (plan, compiledNodes, options = {}
|
|
|
30
30
|
content: renderEntrypoint(runtimePlans, requiredSecrets.filter((secretName) => !modelSecretsRequired.includes(secretName)), {
|
|
31
31
|
moltnet: options.moltnet
|
|
32
32
|
? {
|
|
33
|
-
|
|
33
|
+
nodePlans: options.moltnet.nodePlans,
|
|
34
34
|
serverPlans: options.moltnet.serverPlans
|
|
35
35
|
}
|
|
36
36
|
: undefined
|
|
@@ -59,13 +59,27 @@ export const createContainerArtifacts = async (plan, compiledNodes, options = {}
|
|
|
59
59
|
}))
|
|
60
60
|
.sort((left, right) => left.id.localeCompare(right.id));
|
|
61
61
|
const runtimesInstalled = [...new Set(runtimePlans.map((plan) => plan.runtimeName))].sort();
|
|
62
|
+
const workspaceResources = [
|
|
63
|
+
...new Map(runtimePlans.flatMap((plan) => (plan.resources ?? []).map((resource) => [
|
|
64
|
+
`${resource.kind}:${resource.id}:${resource.linkPath}`,
|
|
65
|
+
{
|
|
66
|
+
backing_path: resource.backingPath,
|
|
67
|
+
id: resource.id,
|
|
68
|
+
kind: resource.kind,
|
|
69
|
+
link_path: resource.linkPath,
|
|
70
|
+
mode: resource.mode,
|
|
71
|
+
mount: resource.mount,
|
|
72
|
+
sharing: resource.sharing
|
|
73
|
+
}
|
|
74
|
+
]))).values()
|
|
75
|
+
].sort((left, right) => left.link_path.localeCompare(right.link_path) || left.id.localeCompare(right.id));
|
|
62
76
|
return {
|
|
63
77
|
executablePaths: ["entrypoint.sh"],
|
|
64
78
|
files,
|
|
65
79
|
...(options.moltnet
|
|
66
80
|
? {
|
|
67
81
|
moltnet: {
|
|
68
|
-
|
|
82
|
+
nodePlans: options.moltnet.nodePlans,
|
|
69
83
|
serverPlans: options.moltnet.serverPlans
|
|
70
84
|
}
|
|
71
85
|
}
|
|
@@ -80,7 +94,8 @@ export const createContainerArtifacts = async (plan, compiledNodes, options = {}
|
|
|
80
94
|
runtime_homes: runtimeHomes,
|
|
81
95
|
runtime_secrets_required: runtimeSecretsRequired,
|
|
82
96
|
runtimes_installed: runtimesInstalled,
|
|
83
|
-
secrets_required: requiredSecrets
|
|
97
|
+
secrets_required: requiredSecrets,
|
|
98
|
+
...(workspaceResources.length > 0 ? { workspace_resources: workspaceResources } : {})
|
|
84
99
|
}
|
|
85
100
|
};
|
|
86
101
|
};
|
|
@@ -3,6 +3,7 @@ import { createRuntimeInstallRecipe, getRuntimeAdapter } from "../runtime/index.
|
|
|
3
3
|
import { SpawnfileError } from "../shared/index.js";
|
|
4
4
|
import { listAgentSurfaceSecretNames } from "./agentSurfaces.js";
|
|
5
5
|
import { listExecutionModelSecretNames, resolveExecutionModelAuthMethods } from "./modelEnv.js";
|
|
6
|
+
import { resolveTargetResources } from "./containerTargetResources.js";
|
|
6
7
|
const CONFIG_FILE_PLACEHOLDER = "<config-file>";
|
|
7
8
|
const INSTANCE_ROOT_PLACEHOLDER = "<instance-root>";
|
|
8
9
|
const createDefaultTargets = (inputs) => inputs.map((input) => ({
|
|
@@ -20,6 +21,34 @@ const assertTargetHasConfig = (runtimeName, targetId, meta, files) => {
|
|
|
20
21
|
throw new SpawnfileError("runtime_error", `Container target ${targetId} for ${runtimeName} is missing ${meta.configFileName}`);
|
|
21
22
|
}
|
|
22
23
|
};
|
|
24
|
+
const createPackageIdentity = (pkg) => `${pkg.manager}\u0000${pkg.name}\u0000${pkg.version ?? ""}\u0000${pkg.scope ?? ""}`;
|
|
25
|
+
const createPackageConflictIdentity = (pkg) => `${pkg.manager}\u0000${pkg.name}`;
|
|
26
|
+
const createPackageLabel = (pkg) => `${pkg.manager} package ${pkg.name}`;
|
|
27
|
+
const resolveTargetPackages = (target, inputs) => {
|
|
28
|
+
const sourceIds = new Set(target.sourceIds ?? []);
|
|
29
|
+
if (sourceIds.size === 0) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const byIdentity = new Map();
|
|
33
|
+
for (const input of inputs) {
|
|
34
|
+
if (!sourceIds.has(input.id) || input.value.kind !== "agent") {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const candidatePackages = input.value.packages ?? [];
|
|
38
|
+
for (const currentPackage of candidatePackages) {
|
|
39
|
+
const conflictIdentity = createPackageConflictIdentity(currentPackage);
|
|
40
|
+
const existingPackage = byIdentity.get(conflictIdentity);
|
|
41
|
+
if (!existingPackage) {
|
|
42
|
+
byIdentity.set(conflictIdentity, currentPackage);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (createPackageIdentity(existingPackage) !== createPackageIdentity(currentPackage)) {
|
|
46
|
+
throw new SpawnfileError("validation_error", `Container target ${target.id} declares conflicting package definitions for ${createPackageLabel(currentPackage)}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return [...byIdentity.values()].sort((left, right) => createPackageIdentity(left).localeCompare(createPackageIdentity(right)));
|
|
51
|
+
};
|
|
23
52
|
const replaceContainerPathTemplate = (template, instanceRoot, configFileName) => template
|
|
24
53
|
.replaceAll(INSTANCE_ROOT_PLACEHOLDER, instanceRoot)
|
|
25
54
|
.replaceAll(CONFIG_FILE_PLACEHOLDER, configFileName);
|
|
@@ -30,6 +59,7 @@ const resolveInstancePaths = (runtimeName, targetId, meta) => {
|
|
|
30
59
|
homePath: meta.instancePaths.homePathTemplate
|
|
31
60
|
? replaceContainerPathTemplate(meta.instancePaths.homePathTemplate, instanceRoot, meta.configFileName)
|
|
32
61
|
: undefined,
|
|
62
|
+
instanceRoot,
|
|
33
63
|
workspacePath: replaceContainerPathTemplate(meta.instancePaths.workspacePathTemplate, instanceRoot, meta.configFileName)
|
|
34
64
|
};
|
|
35
65
|
};
|
|
@@ -149,6 +179,7 @@ export const createRuntimeTargetPlans = async (plan, compiledNodes) => {
|
|
|
149
179
|
runtimePlans.push({
|
|
150
180
|
configEnvBindings: resolveTargetConfigEnvBindings(adapter.container, target) ?? [],
|
|
151
181
|
envFiles: resolveTargetEnvFiles(instancePaths.configPath, target),
|
|
182
|
+
packages: resolveTargetPackages(target, targetInputs),
|
|
152
183
|
id: target.id,
|
|
153
184
|
instancePaths,
|
|
154
185
|
meta: adapter.container,
|
|
@@ -158,6 +189,7 @@ export const createRuntimeTargetPlans = async (plan, compiledNodes) => {
|
|
|
158
189
|
publishedPort: resolveTargetExposure(target, targetInputs) && adapter.container.port
|
|
159
190
|
? adapter.container.port + (index * portStride)
|
|
160
191
|
: undefined,
|
|
192
|
+
resources: resolveTargetResources(target, targetInputs, instancePaths, adapter.container),
|
|
161
193
|
runtimeName,
|
|
162
194
|
runtimeRoot: recipe.runtimeRoot,
|
|
163
195
|
targetConfigEnvBindings: target.configEnvBindings,
|
|
@@ -11,6 +11,70 @@ const shellQuote = (value) => `'${value.replace(/'/g, `'\"'\"'`)}'`;
|
|
|
11
11
|
const extractNodeMajorVersion = (image) => Number(image.match(/^node:(\d+)/)?.[1] ?? "0");
|
|
12
12
|
const createPackageInstallCommand = (packages) => `RUN apt-get update && apt-get install -y --no-install-recommends ${packages.join(" ")} && rm -rf /var/lib/apt/lists/*`;
|
|
13
13
|
const createNpmPackageInstallCommand = (packages) => `RUN npm install -g --omit=dev --no-fund --no-audit ${packages.join(" ")}`;
|
|
14
|
+
const createPipxPackageInstallCommand = (packages) => `RUN PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install ${packages.join(" ")}`;
|
|
15
|
+
const dedupePackages = (packages) => {
|
|
16
|
+
const seen = new Map();
|
|
17
|
+
for (const currentPackage of packages) {
|
|
18
|
+
seen.set(`${currentPackage.manager}\u0000${currentPackage.name}\u0000${currentPackage.version ?? ""}\u0000${currentPackage.scope ?? ""}`, currentPackage);
|
|
19
|
+
}
|
|
20
|
+
return [...seen.values()].sort((left, right) => `${left.manager}\u0000${left.name}\u0000${left.version ?? ""}\u0000${left.scope ?? ""}`.localeCompare(`${right.manager}\u0000${right.name}\u0000${right.version ?? ""}\u0000${right.scope ?? ""}`));
|
|
21
|
+
};
|
|
22
|
+
const createPackageIdentity = (pkg) => `${pkg.manager}\u0000${pkg.name}\u0000${pkg.version ?? ""}\u0000${pkg.scope ?? ""}`;
|
|
23
|
+
const createPackageConflictIdentity = (pkg) => `${pkg.manager}\u0000${pkg.name}`;
|
|
24
|
+
const assertImagePackageCompatibility = (packages) => {
|
|
25
|
+
const byName = new Map();
|
|
26
|
+
for (const currentPackage of packages) {
|
|
27
|
+
const conflictIdentity = createPackageConflictIdentity(currentPackage);
|
|
28
|
+
const existingPackage = byName.get(conflictIdentity);
|
|
29
|
+
if (!existingPackage) {
|
|
30
|
+
byName.set(conflictIdentity, currentPackage);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (createPackageIdentity(existingPackage) !== createPackageIdentity(currentPackage)) {
|
|
34
|
+
throw new SpawnfileError("validation_error", `Generated container declares conflicting package definitions for ${currentPackage.manager} package ${currentPackage.name}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const createAptPackageInstallItem = (pkg) => pkg.version ? `${pkg.name}=${pkg.version}` : pkg.name;
|
|
39
|
+
const createNpmPackageInstallItem = (pkg) => pkg.version ? `${pkg.name}@${pkg.version}` : pkg.name;
|
|
40
|
+
const createPipxPackageInstallItem = (pkg) => pkg.version ? `${pkg.name}==${pkg.version}` : pkg.name;
|
|
41
|
+
const getNpmInstallItemName = (item) => {
|
|
42
|
+
if (!item.startsWith("@")) {
|
|
43
|
+
const versionMarker = item.indexOf("@");
|
|
44
|
+
return versionMarker === -1 ? item : item.slice(0, versionMarker);
|
|
45
|
+
}
|
|
46
|
+
const slashIndex = item.indexOf("/");
|
|
47
|
+
if (slashIndex === -1) {
|
|
48
|
+
return item;
|
|
49
|
+
}
|
|
50
|
+
const versionMarker = item.indexOf("@", slashIndex);
|
|
51
|
+
return versionMarker === -1 ? item : item.slice(0, versionMarker);
|
|
52
|
+
};
|
|
53
|
+
const collectPackagesByManager = (runtimePlans) => {
|
|
54
|
+
const packagesByManager = {
|
|
55
|
+
apt: [],
|
|
56
|
+
npm: [],
|
|
57
|
+
pipx: []
|
|
58
|
+
};
|
|
59
|
+
const imagePackages = runtimePlans.flatMap((plan) => plan.packages ?? []);
|
|
60
|
+
assertImagePackageCompatibility(imagePackages);
|
|
61
|
+
const resolvedPackages = dedupePackages(imagePackages);
|
|
62
|
+
for (const packageConfig of resolvedPackages) {
|
|
63
|
+
if (packageConfig.manager === "apt") {
|
|
64
|
+
packagesByManager.apt.push(packageConfig);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (packageConfig.manager === "npm") {
|
|
68
|
+
packagesByManager.npm.push(packageConfig);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (packageConfig.manager === "pipx") {
|
|
72
|
+
packagesByManager.pipx.push(packageConfig);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return packagesByManager;
|
|
77
|
+
};
|
|
14
78
|
const selectBaseImage = (runtimePlans) => {
|
|
15
79
|
const firstRuntimeMeta = runtimePlans[0]?.meta;
|
|
16
80
|
if (runtimePlans.length <= 1) {
|
|
@@ -52,30 +116,49 @@ export const renderDockerfile = async (runtimePlans, options = {}) => {
|
|
|
52
116
|
const runtimeRecipes = await Promise.all(runtimeNames.map((runtimeName) => createRuntimeInstallRecipe(runtimeName)));
|
|
53
117
|
const baseImage = selectBaseImage(runtimePlans);
|
|
54
118
|
const needsJsonEnvWriter = runtimePlans.some((plan) => (plan.configEnvBindings?.length ?? 0) > 0);
|
|
119
|
+
const needsGit = runtimePlans.some((plan) => (plan.resources ?? []).some((resource) => resource.kind === "git"));
|
|
55
120
|
const systemDeps = [
|
|
56
121
|
...new Set([
|
|
57
122
|
...runtimePlans.flatMap((plan) => plan.meta.systemDeps),
|
|
123
|
+
...(needsGit ? ["git"] : []),
|
|
58
124
|
...(options.hasMoltnet && !options.hasStagedMoltnetBinaries
|
|
59
125
|
? ["ca-certificates", "curl", "tar"]
|
|
60
126
|
: []),
|
|
61
127
|
...(needsJsonEnvWriter ? ["python3"] : [])
|
|
62
128
|
])
|
|
63
129
|
].sort();
|
|
130
|
+
const { apt: aptPackages, npm: npmPackages, pipx: pipxPackages } = collectPackagesByManager(runtimePlans);
|
|
131
|
+
const aptDependencies = [
|
|
132
|
+
...systemDeps,
|
|
133
|
+
...aptPackages.map((pkg) => createAptPackageInstallItem(pkg)),
|
|
134
|
+
...(pipxPackages.length > 0 ? ["pipx"] : [])
|
|
135
|
+
];
|
|
136
|
+
const aptInstallPackages = [...new Set(aptDependencies)].sort();
|
|
137
|
+
const projectNpmPackages = npmPackages.map((pkg) => createNpmPackageInstallItem(pkg));
|
|
138
|
+
const projectNpmPackageNames = new Set(projectNpmPackages.map(getNpmInstallItemName));
|
|
139
|
+
const runtimeNpmPackages = runtimePlans
|
|
140
|
+
.flatMap((plan) => plan.meta.globalNpmPackages ?? [])
|
|
141
|
+
.filter((pkg) => !projectNpmPackageNames.has(getNpmInstallItemName(pkg)));
|
|
64
142
|
const globalNpmPackages = [
|
|
65
|
-
...new Set(
|
|
143
|
+
...new Set([...runtimeNpmPackages, ...projectNpmPackages])
|
|
66
144
|
].sort();
|
|
145
|
+
const pipxInstallPackages = [...new Set(pipxPackages.map((pkg) => createPipxPackageInstallItem(pkg)))]
|
|
146
|
+
.sort();
|
|
67
147
|
const runtimePorts = runtimePlans.flatMap((plan) => plan.publishedPort ? [plan.publishedPort] : []);
|
|
68
148
|
const moltnetPorts = options.moltnetPublishedPorts ?? [];
|
|
69
149
|
const exposedPorts = [...new Set([...runtimePorts, ...moltnetPorts])].sort((left, right) => left - right);
|
|
70
150
|
const lines = [];
|
|
71
151
|
lines.push(`FROM ${baseImage}`);
|
|
72
152
|
lines.push("USER root", "", "WORKDIR /opt/spawnfile");
|
|
73
|
-
if (
|
|
74
|
-
lines.push(createPackageInstallCommand(
|
|
153
|
+
if (aptInstallPackages.length > 0) {
|
|
154
|
+
lines.push(createPackageInstallCommand(aptInstallPackages), "");
|
|
75
155
|
}
|
|
76
156
|
if (globalNpmPackages.length > 0) {
|
|
77
157
|
lines.push(createNpmPackageInstallCommand(globalNpmPackages), "");
|
|
78
158
|
}
|
|
159
|
+
if (pipxInstallPackages.length > 0) {
|
|
160
|
+
lines.push(createPipxPackageInstallCommand(pipxInstallPackages), "");
|
|
161
|
+
}
|
|
79
162
|
if (options.hasMoltnet && options.hasStagedMoltnetBinaries) {
|
|
80
163
|
lines.push(`COPY ${MOLTNET_BIN_DIRECTORY}/ /usr/local/bin/`, `RUN chmod +x ${MOLTNET_BINARY_NAMES.map((binaryName) => `/usr/local/bin/${binaryName}`).join(" ")}`, "");
|
|
81
164
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ContainerReport } from "../report/index.js";
|
|
2
2
|
import type { EmittedFile, RuntimeContainerConfigEnvBinding, RuntimeContainerMeta } from "../runtime/index.js";
|
|
3
3
|
import type { ModelAuthMethod } from "../shared/index.js";
|
|
4
|
-
import type { ResolvedAgentNode, ResolvedTeamNode } from "./types.js";
|
|
5
|
-
import type {
|
|
4
|
+
import type { ResolvedAgentNode, ResolvedPackage, ResolvedTeamNode } from "./types.js";
|
|
5
|
+
import type { MoltnetNodePlan, MoltnetServerPlan } from "./moltnetArtifacts.js";
|
|
6
|
+
import type { WorkspaceResourcePlan } from "./workspaceResources.js";
|
|
6
7
|
export interface ContainerEnvVariable {
|
|
7
8
|
categories: Array<"model" | "project" | "runtime" | "surface">;
|
|
8
9
|
description: string;
|
|
@@ -11,6 +12,7 @@ export interface ContainerEnvVariable {
|
|
|
11
12
|
}
|
|
12
13
|
export interface RuntimeTargetPlan {
|
|
13
14
|
configEnvBindings?: RuntimeContainerConfigEnvBinding[];
|
|
15
|
+
packages?: ResolvedPackage[];
|
|
14
16
|
envFiles: Array<{
|
|
15
17
|
envName: string;
|
|
16
18
|
filePath: string;
|
|
@@ -19,6 +21,7 @@ export interface RuntimeTargetPlan {
|
|
|
19
21
|
instancePaths: {
|
|
20
22
|
configPath: string;
|
|
21
23
|
homePath?: string;
|
|
24
|
+
instanceRoot?: string;
|
|
22
25
|
workspacePath: string;
|
|
23
26
|
};
|
|
24
27
|
meta: RuntimeContainerMeta;
|
|
@@ -26,6 +29,7 @@ export interface RuntimeTargetPlan {
|
|
|
26
29
|
modelSecretsRequired: string[];
|
|
27
30
|
port?: number;
|
|
28
31
|
publishedPort?: number;
|
|
32
|
+
resources?: WorkspaceResourcePlan[];
|
|
29
33
|
runtimeName: string;
|
|
30
34
|
runtimeRoot: string;
|
|
31
35
|
targetConfigEnvBindings?: RuntimeContainerConfigEnvBinding[];
|
|
@@ -42,7 +46,7 @@ export interface GeneratedContainerArtifacts {
|
|
|
42
46
|
executablePaths: string[];
|
|
43
47
|
files: EmittedFile[];
|
|
44
48
|
moltnet?: {
|
|
45
|
-
|
|
49
|
+
nodePlans: MoltnetNodePlan[];
|
|
46
50
|
serverPlans: MoltnetServerPlan[];
|
|
47
51
|
};
|
|
48
52
|
report: ContainerReport;
|