spawnfile 0.1.4 → 0.1.6
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/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 +4 -48
- 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 +34 -24
- 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/surfaces.d.ts +3 -13
- package/dist/compiler/surfaces.js +1 -6
- package/dist/compiler/syncProjectAuth.js +14 -0
- 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/filesystem/paths.js +1 -10
- 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/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
package/dist/compiler/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { ExecutionBlock, McpServer, ModelEndpoint, Secret } from "../manifest/index.js";
|
|
1
|
+
import { AgentSchedule, ExecutionBlock, McpServer, ModelEndpoint, Secret, TeamNetworkServer } from "../manifest/index.js";
|
|
2
2
|
import { ModelAuthMethod, StringMap } from "../shared/index.js";
|
|
3
|
+
import type { ResolvedWorkspaceResource } from "./workspaceResources.js";
|
|
3
4
|
export interface ResolvedDocument {
|
|
4
5
|
content: string;
|
|
5
6
|
role: string;
|
|
@@ -12,6 +13,13 @@ export interface ResolvedSkill {
|
|
|
12
13
|
requiresMcp: string[];
|
|
13
14
|
sourcePath: string;
|
|
14
15
|
}
|
|
16
|
+
export interface ResolvedPackage {
|
|
17
|
+
id: string;
|
|
18
|
+
manager: string;
|
|
19
|
+
name: string;
|
|
20
|
+
scope?: string;
|
|
21
|
+
version?: string;
|
|
22
|
+
}
|
|
15
23
|
export interface ResolvedRuntime {
|
|
16
24
|
name: string;
|
|
17
25
|
options: Record<string, unknown>;
|
|
@@ -104,6 +112,7 @@ export interface ResolvedAgentSurfaces {
|
|
|
104
112
|
export interface ResolvedTeamNetworkRoom {
|
|
105
113
|
id: string;
|
|
106
114
|
members: string[];
|
|
115
|
+
name?: string;
|
|
107
116
|
}
|
|
108
117
|
export interface ResolvedTeamNetwork {
|
|
109
118
|
expose?: boolean;
|
|
@@ -111,6 +120,7 @@ export interface ResolvedTeamNetwork {
|
|
|
111
120
|
name: string;
|
|
112
121
|
provider: "moltnet";
|
|
113
122
|
rooms: ResolvedTeamNetworkRoom[];
|
|
123
|
+
server?: TeamNetworkServer;
|
|
114
124
|
}
|
|
115
125
|
export interface EffectiveModelTarget {
|
|
116
126
|
auth: {
|
|
@@ -140,14 +150,17 @@ export interface ResolvedAgentNode {
|
|
|
140
150
|
kind: "agent";
|
|
141
151
|
mcpServers: McpServer[];
|
|
142
152
|
name: string;
|
|
153
|
+
packages?: ResolvedPackage[];
|
|
143
154
|
policyMode: string | null;
|
|
144
155
|
policyOnDegrade: string | null;
|
|
145
156
|
runtime: ResolvedRuntime;
|
|
157
|
+
schedule?: AgentSchedule;
|
|
146
158
|
secrets: Secret[];
|
|
147
159
|
skills: ResolvedSkill[];
|
|
148
160
|
source: string;
|
|
149
161
|
surfaces?: ResolvedAgentSurfaces;
|
|
150
162
|
subagents: ResolvedSubagentRef[];
|
|
163
|
+
workspaceResources?: ResolvedWorkspaceResource[];
|
|
151
164
|
}
|
|
152
165
|
export interface ResolvedTeamNode {
|
|
153
166
|
description: string;
|
|
@@ -162,11 +175,13 @@ export interface ResolvedTeamNode {
|
|
|
162
175
|
networks?: ResolvedTeamNetwork[];
|
|
163
176
|
policyMode: string | null;
|
|
164
177
|
policyOnDegrade: string | null;
|
|
178
|
+
workspaceResources?: ResolvedWorkspaceResource[];
|
|
165
179
|
shared: {
|
|
166
180
|
env: StringMap;
|
|
167
181
|
mcpServers: McpServer[];
|
|
168
182
|
secrets: Secret[];
|
|
169
183
|
skills: ResolvedSkill[];
|
|
184
|
+
packages?: ResolvedPackage[];
|
|
170
185
|
};
|
|
171
186
|
source: string;
|
|
172
187
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type BuildProjectResult, type DockerBuildRunner } from "./buildProject.js";
|
|
2
|
+
import { type DockerRunRunner } from "./runProject.js";
|
|
3
|
+
import { CompileProjectOptions } from "./compileProject.js";
|
|
4
|
+
export interface UpProjectOptions extends CompileProjectOptions {
|
|
5
|
+
authProfile?: string;
|
|
6
|
+
buildRunner?: DockerBuildRunner;
|
|
7
|
+
containerName?: string;
|
|
8
|
+
detach?: boolean;
|
|
9
|
+
dockerCommand?: string;
|
|
10
|
+
envFilePath?: string;
|
|
11
|
+
imageTag?: string;
|
|
12
|
+
runRunner?: DockerRunRunner;
|
|
13
|
+
}
|
|
14
|
+
export interface UpProjectResult extends BuildProjectResult {
|
|
15
|
+
authProfileName: string | null;
|
|
16
|
+
containerName: string | null;
|
|
17
|
+
supportDirectory: string | null;
|
|
18
|
+
}
|
|
19
|
+
export declare const upProject: (inputPath: string, options?: UpProjectOptions) => Promise<UpProjectResult>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { removeDirectory } from "../filesystem/index.js";
|
|
2
|
+
import { buildProject } from "./buildProject.js";
|
|
3
|
+
import { createDockerRunInvocation, runDockerContainer } from "./runProject.js";
|
|
4
|
+
import { requireAuthProfile } from "../auth/index.js";
|
|
5
|
+
export const upProject = async (inputPath, options = {}) => {
|
|
6
|
+
const buildResult = await buildProject(inputPath, {
|
|
7
|
+
buildRunner: options.buildRunner,
|
|
8
|
+
clean: options.clean,
|
|
9
|
+
dockerCommand: options.dockerCommand,
|
|
10
|
+
imageTag: options.imageTag,
|
|
11
|
+
outputDirectory: options.outputDirectory
|
|
12
|
+
});
|
|
13
|
+
const authProfile = options.authProfile
|
|
14
|
+
? await requireAuthProfile(options.authProfile)
|
|
15
|
+
: null;
|
|
16
|
+
const invocation = await createDockerRunInvocation(buildResult, buildResult.imageTag, {
|
|
17
|
+
authProfile,
|
|
18
|
+
containerName: options.containerName,
|
|
19
|
+
detach: options.detach,
|
|
20
|
+
dockerCommand: options.dockerCommand,
|
|
21
|
+
envFilePath: options.envFilePath
|
|
22
|
+
});
|
|
23
|
+
try {
|
|
24
|
+
await (options.runRunner ?? runDockerContainer)(invocation);
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
if (!invocation.detach) {
|
|
28
|
+
await removeDirectory(invocation.supportDirectory);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
...buildResult,
|
|
33
|
+
authProfileName: authProfile?.name ?? null,
|
|
34
|
+
containerName: invocation.containerName,
|
|
35
|
+
supportDirectory: invocation.supportDirectory
|
|
36
|
+
};
|
|
37
|
+
};
|
|
@@ -30,10 +30,15 @@ const buildTreeNetworkSummaries = (node) => {
|
|
|
30
30
|
return [];
|
|
31
31
|
}
|
|
32
32
|
return (node.value.networks ?? []).map((network) => ({
|
|
33
|
-
|
|
33
|
+
authMode: network.server?.auth.mode,
|
|
34
|
+
directMessages: network.server?.mode === "managed" ? network.server.direct_messages : undefined,
|
|
35
|
+
expose: network.server?.mode === "managed" ? network.server.human_ingress : undefined,
|
|
36
|
+
httpEnabled: network.server?.mode === "managed" ? network.server.human_ingress === true : false,
|
|
34
37
|
id: network.id,
|
|
35
38
|
name: network.name,
|
|
36
39
|
provider: network.provider,
|
|
40
|
+
serverMode: network.server?.mode,
|
|
41
|
+
url: network.server?.url,
|
|
37
42
|
rooms: network.rooms.map((room) => ({
|
|
38
43
|
declaredMembers: [...room.members],
|
|
39
44
|
id: room.id
|
|
@@ -101,10 +106,15 @@ const toNetworkMemberView = (membership) => ({
|
|
|
101
106
|
});
|
|
102
107
|
const createNetworkKey = (provider, networkId) => `${provider}::${networkId}`;
|
|
103
108
|
const buildNetworkDeclaration = (teamNode, network, roomMemberships) => ({
|
|
109
|
+
authMode: network.server?.auth.mode,
|
|
104
110
|
declaringTeamName: teamNode.name,
|
|
105
111
|
declaringTeamSource: teamNode.source,
|
|
106
|
-
|
|
112
|
+
directMessages: network.server?.mode === "managed" ? network.server.direct_messages : undefined,
|
|
113
|
+
expose: network.server?.mode === "managed" ? network.server.human_ingress : undefined,
|
|
114
|
+
httpEnabled: network.server?.mode === "managed" ? network.server.human_ingress === true : false,
|
|
107
115
|
name: network.name,
|
|
116
|
+
serverMode: network.server?.mode,
|
|
117
|
+
url: network.server?.url,
|
|
108
118
|
rooms: network.rooms.map((room) => {
|
|
109
119
|
const members = roomMemberships
|
|
110
120
|
.filter((membership) => membership.declaringTeamSource === teamNode.source
|
|
@@ -139,10 +149,15 @@ const buildNetworks = (plan) => {
|
|
|
139
149
|
declaringTeamName: declaration.declaringTeamName,
|
|
140
150
|
declaringTeamSource: declaration.declaringTeamSource,
|
|
141
151
|
declarations: [declaration],
|
|
152
|
+
authMode: declaration.authMode,
|
|
153
|
+
directMessages: declaration.directMessages,
|
|
142
154
|
expose: declaration.expose,
|
|
155
|
+
httpEnabled: declaration.httpEnabled,
|
|
143
156
|
id: network.id,
|
|
144
157
|
name: declaration.name,
|
|
145
158
|
provider: network.provider,
|
|
159
|
+
serverMode: declaration.serverMode,
|
|
160
|
+
url: declaration.url,
|
|
146
161
|
rooms: declaration.rooms
|
|
147
162
|
});
|
|
148
163
|
}
|
|
@@ -153,8 +168,13 @@ const buildNetworks = (plan) => {
|
|
|
153
168
|
if (firstDeclaration) {
|
|
154
169
|
network.declaringTeamName = firstDeclaration.declaringTeamName;
|
|
155
170
|
network.declaringTeamSource = firstDeclaration.declaringTeamSource;
|
|
171
|
+
network.authMode = firstDeclaration.authMode;
|
|
172
|
+
network.directMessages = firstDeclaration.directMessages;
|
|
156
173
|
network.expose = firstDeclaration.expose;
|
|
174
|
+
network.httpEnabled = firstDeclaration.httpEnabled;
|
|
157
175
|
network.name = firstDeclaration.name;
|
|
176
|
+
network.serverMode = firstDeclaration.serverMode;
|
|
177
|
+
network.url = firstDeclaration.url;
|
|
158
178
|
network.rooms = firstDeclaration.rooms;
|
|
159
179
|
}
|
|
160
180
|
}
|
|
@@ -40,19 +40,30 @@ const formatNetwork = (network, options) => {
|
|
|
40
40
|
};
|
|
41
41
|
const getDeclarations = (network) => network.declarations ?? [
|
|
42
42
|
{
|
|
43
|
+
authMode: network.authMode,
|
|
43
44
|
declaringTeamName: network.declaringTeamName,
|
|
44
45
|
declaringTeamSource: network.declaringTeamSource,
|
|
46
|
+
directMessages: network.directMessages,
|
|
45
47
|
expose: network.expose,
|
|
48
|
+
httpEnabled: network.httpEnabled,
|
|
46
49
|
name: network.name,
|
|
47
|
-
rooms: network.rooms
|
|
50
|
+
rooms: network.rooms,
|
|
51
|
+
serverMode: network.serverMode,
|
|
52
|
+
url: network.url
|
|
48
53
|
}
|
|
49
54
|
];
|
|
50
55
|
const formatDeclaration = (network, declaration, options, projectRoot) => {
|
|
51
|
-
const
|
|
56
|
+
const metadata = [
|
|
57
|
+
declaration.serverMode ? `server=${declaration.serverMode}` : undefined,
|
|
58
|
+
declaration.url ? `url=${declaration.url}` : undefined,
|
|
59
|
+
declaration.authMode ? `auth=${declaration.authMode}` : undefined,
|
|
60
|
+
declaration.directMessages === false ? "dms=disabled" : undefined,
|
|
61
|
+
declaration.expose === true || declaration.httpEnabled ? "human_ingress" : undefined
|
|
62
|
+
].filter((entry) => entry !== undefined);
|
|
52
63
|
const source = options.paths
|
|
53
64
|
? formatSourceMeta("declared_source", declaration.declaringTeamSource, projectRoot)
|
|
54
65
|
: "";
|
|
55
|
-
return `${network.id} "${declaration.name}" on ${declaration.declaringTeamName}${
|
|
66
|
+
return `${network.id} "${declaration.name}" on ${declaration.declaringTeamName}${metadata.length > 0 ? ` ${metadata.join(" ")}` : ""}${source}`;
|
|
56
67
|
};
|
|
57
68
|
export const renderOrganizationNetworks = (view, options = {}) => {
|
|
58
69
|
if (view.networks.length === 0) {
|
|
@@ -24,11 +24,17 @@ const formatEdgeLabel = (edge) => edge.relation === "subagent"
|
|
|
24
24
|
: edge.label;
|
|
25
25
|
const formatNetworkSummary = (network, options) => {
|
|
26
26
|
const id = color(network.id, "36", options);
|
|
27
|
-
const
|
|
27
|
+
const metadata = [
|
|
28
|
+
network.serverMode ? `server=${network.serverMode}` : undefined,
|
|
29
|
+
network.url ? `url=${network.url}` : undefined,
|
|
30
|
+
network.authMode ? `auth=${network.authMode}` : undefined,
|
|
31
|
+
network.directMessages === false ? "dms=disabled" : undefined,
|
|
32
|
+
network.expose === true || network.httpEnabled ? "human_ingress" : undefined
|
|
33
|
+
].filter((entry) => entry !== undefined);
|
|
28
34
|
const rooms = network.rooms
|
|
29
35
|
.map((room) => `${room.id} [${room.declaredMembers.join(", ")}]`)
|
|
30
36
|
.join("; ");
|
|
31
|
-
return `network ${id} "${network.name}"${
|
|
37
|
+
return `network ${id} "${network.name}"${metadata.length > 0 ? ` ${metadata.join(" ")}` : ""}: ${rooms}`;
|
|
32
38
|
};
|
|
33
39
|
const renderChildren = (node, options, projectRoot, prefix = "") => {
|
|
34
40
|
const glyphs = options.ascii
|
|
@@ -17,10 +17,15 @@ export interface OrganizationTreeNetworkRoomSummary {
|
|
|
17
17
|
id: string;
|
|
18
18
|
}
|
|
19
19
|
export interface OrganizationTreeNetworkSummary {
|
|
20
|
-
|
|
20
|
+
authMode?: "bearer" | "none" | "open";
|
|
21
|
+
directMessages?: boolean;
|
|
22
|
+
expose?: boolean;
|
|
23
|
+
httpEnabled?: boolean;
|
|
21
24
|
id: string;
|
|
22
25
|
name: string;
|
|
23
26
|
provider: "moltnet";
|
|
27
|
+
serverMode?: "external" | "managed";
|
|
28
|
+
url?: string;
|
|
24
29
|
rooms: OrganizationTreeNetworkRoomSummary[];
|
|
25
30
|
}
|
|
26
31
|
export interface OrganizationViewTreeEdge {
|
|
@@ -42,11 +47,16 @@ export interface OrganizationNetworkMemberView {
|
|
|
42
47
|
representativePath?: string[];
|
|
43
48
|
}
|
|
44
49
|
export interface OrganizationNetworkDeclarationView {
|
|
50
|
+
authMode?: "bearer" | "none" | "open";
|
|
45
51
|
declaringTeamName: string;
|
|
46
52
|
declaringTeamSource: string;
|
|
47
|
-
|
|
53
|
+
directMessages?: boolean;
|
|
54
|
+
expose?: boolean;
|
|
55
|
+
httpEnabled?: boolean;
|
|
48
56
|
name: string;
|
|
49
57
|
rooms: OrganizationNetworkRoomView[];
|
|
58
|
+
serverMode?: "external" | "managed";
|
|
59
|
+
url?: string;
|
|
50
60
|
}
|
|
51
61
|
export interface OrganizationNetworkRoomView {
|
|
52
62
|
declaredMembers: string[];
|
|
@@ -54,13 +64,18 @@ export interface OrganizationNetworkRoomView {
|
|
|
54
64
|
members: OrganizationNetworkMemberView[];
|
|
55
65
|
}
|
|
56
66
|
export interface OrganizationNetworkView {
|
|
67
|
+
authMode?: "bearer" | "none" | "open";
|
|
57
68
|
declaringTeamName: string;
|
|
58
69
|
declaringTeamSource: string;
|
|
59
|
-
|
|
70
|
+
directMessages?: boolean;
|
|
71
|
+
expose?: boolean;
|
|
72
|
+
httpEnabled?: boolean;
|
|
60
73
|
id: string;
|
|
61
74
|
name: string;
|
|
62
75
|
provider: "moltnet";
|
|
63
76
|
rooms: OrganizationNetworkRoomView[];
|
|
77
|
+
serverMode?: "external" | "managed";
|
|
78
|
+
url?: string;
|
|
64
79
|
declarations?: OrganizationNetworkDeclarationView[];
|
|
65
80
|
}
|
|
66
81
|
export interface OrganizationView {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { TeamWorkspaceResource } from "../manifest/index.js";
|
|
2
|
+
export type WorkspaceResourceSharing = "per_agent" | "team";
|
|
3
|
+
export interface WorkspaceResourceScope {
|
|
4
|
+
kind: "agent" | "team";
|
|
5
|
+
key: string;
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
export type ResolvedWorkspaceResource = TeamWorkspaceResource & {
|
|
9
|
+
scope: WorkspaceResourceScope;
|
|
10
|
+
sharing: WorkspaceResourceSharing;
|
|
11
|
+
};
|
|
12
|
+
export interface WorkspaceResourcePlan {
|
|
13
|
+
backingPath: string;
|
|
14
|
+
branch?: string;
|
|
15
|
+
id: string;
|
|
16
|
+
kind: "git" | "volume";
|
|
17
|
+
linkPath: string;
|
|
18
|
+
mode: "mutable" | "readonly";
|
|
19
|
+
mount: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
ref?: string;
|
|
22
|
+
sharing: WorkspaceResourceSharing;
|
|
23
|
+
tag?: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare const mergeWorkspaceResources: (inherited: ResolvedWorkspaceResource[] | undefined, local: TeamWorkspaceResource[] | undefined, ownerName: string, ownerScope: WorkspaceResourceScope) => ResolvedWorkspaceResource[];
|
|
27
|
+
export declare const toWorkspaceResourcePlan: (resource: ResolvedWorkspaceResource, context: {
|
|
28
|
+
targetId: string;
|
|
29
|
+
workspacePath: string;
|
|
30
|
+
}) => WorkspaceResourcePlan;
|
|
31
|
+
export declare const mergeWorkspaceResourcePlans: (resources: ResolvedWorkspaceResource[], ownerName: string, context: {
|
|
32
|
+
targetId: string;
|
|
33
|
+
workspacePath: string;
|
|
34
|
+
}) => WorkspaceResourcePlan[];
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
3
|
+
import { createShortHash, slugify } from "./helpers.js";
|
|
4
|
+
const normalizeMount = (value) => {
|
|
5
|
+
const trimmed = value.trim();
|
|
6
|
+
const workspaceRelative = trimmed.startsWith("${workspace}/")
|
|
7
|
+
? `./${trimmed.slice("${workspace}/".length)}`
|
|
8
|
+
: trimmed;
|
|
9
|
+
const collapsed = workspaceRelative.replace(/\/+/g, "/");
|
|
10
|
+
if (collapsed.startsWith("./")) {
|
|
11
|
+
const relativePath = collapsed.slice(2).replace(/\/+$/u, "");
|
|
12
|
+
return `./${relativePath}`;
|
|
13
|
+
}
|
|
14
|
+
return collapsed.length > 1 ? collapsed.replace(/\/+$/u, "") : "/";
|
|
15
|
+
};
|
|
16
|
+
const normalizeResourceIdentity = (resource) => {
|
|
17
|
+
if (resource.kind === "git") {
|
|
18
|
+
return JSON.stringify({
|
|
19
|
+
branch: resource.branch?.trim() ?? "",
|
|
20
|
+
kind: "git",
|
|
21
|
+
mode: resource.mode,
|
|
22
|
+
mount: normalizeMount(resource.mount),
|
|
23
|
+
ref: resource.ref?.trim() ?? "",
|
|
24
|
+
sharing: resource.sharing,
|
|
25
|
+
tag: resource.tag?.trim() ?? "",
|
|
26
|
+
url: resource.url.trim()
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return JSON.stringify({
|
|
30
|
+
kind: "volume",
|
|
31
|
+
mode: resource.mode,
|
|
32
|
+
mount: normalizeMount(resource.mount),
|
|
33
|
+
name: resource.name?.trim() ?? "",
|
|
34
|
+
scope: resource.sharing === "team" ? resource.scope.key : "",
|
|
35
|
+
sharing: resource.sharing
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
const mountsOverlap = (left, right) => left === right ||
|
|
39
|
+
left.startsWith(`${right}/`) ||
|
|
40
|
+
right.startsWith(`${left}/`);
|
|
41
|
+
const resolveSharing = (resource) => resource.sharing ?? "per_agent";
|
|
42
|
+
const toResolvedResource = (resource, scope) => ({
|
|
43
|
+
...resource,
|
|
44
|
+
mount: normalizeMount(resource.mount),
|
|
45
|
+
scope,
|
|
46
|
+
sharing: resolveSharing(resource)
|
|
47
|
+
});
|
|
48
|
+
const createPathSegment = (value) => {
|
|
49
|
+
const slug = slugify(value);
|
|
50
|
+
const hash = createShortHash(value);
|
|
51
|
+
return slug ? `${slug}-${hash}` : hash;
|
|
52
|
+
};
|
|
53
|
+
const createScopeSegment = (scope) => `${scope.kind}-${createPathSegment(`${scope.name}:${scope.key}`)}`;
|
|
54
|
+
const createResourceSegment = (resource) => createPathSegment(resource.kind === "volume" && resource.name ? resource.name : resource.id);
|
|
55
|
+
const resolveLinkPath = (mount, workspacePath) => mount.startsWith("./")
|
|
56
|
+
? path.posix.join(workspacePath, mount.slice(2))
|
|
57
|
+
: mount;
|
|
58
|
+
const resolveBackingPath = (resource, targetId) => {
|
|
59
|
+
const resourceSegment = createResourceSegment(resource);
|
|
60
|
+
if (resource.sharing === "team") {
|
|
61
|
+
return path.posix.join("/var/lib/spawnfile/resources/teams", createScopeSegment(resource.scope), resourceSegment);
|
|
62
|
+
}
|
|
63
|
+
return path.posix.join("/var/lib/spawnfile/resources/instances", createPathSegment(targetId), resourceSegment);
|
|
64
|
+
};
|
|
65
|
+
export const mergeWorkspaceResources = (inherited = [], local = [], ownerName, ownerScope) => {
|
|
66
|
+
const merged = [];
|
|
67
|
+
const identityById = new Map();
|
|
68
|
+
const resources = [
|
|
69
|
+
...inherited,
|
|
70
|
+
...local.map((resource) => toResolvedResource(resource, ownerScope))
|
|
71
|
+
];
|
|
72
|
+
for (const resource of resources) {
|
|
73
|
+
const mount = normalizeMount(resource.mount);
|
|
74
|
+
const identity = normalizeResourceIdentity(resource);
|
|
75
|
+
const existingIdentity = identityById.get(resource.id);
|
|
76
|
+
if (existingIdentity) {
|
|
77
|
+
if (existingIdentity !== identity) {
|
|
78
|
+
throw new SpawnfileError("validation_error", `Workspace resource ${resource.id} resolves differently for ${ownerName}`);
|
|
79
|
+
}
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const overlapping = merged.find((candidate) => candidate.id !== resource.id &&
|
|
83
|
+
mountsOverlap(normalizeMount(candidate.mount), mount));
|
|
84
|
+
if (overlapping) {
|
|
85
|
+
throw new SpawnfileError("validation_error", `Workspace resources ${overlapping.id} and ${resource.id} use overlapping mounts for ${ownerName}`);
|
|
86
|
+
}
|
|
87
|
+
identityById.set(resource.id, identity);
|
|
88
|
+
merged.push({ ...resource, mount });
|
|
89
|
+
}
|
|
90
|
+
return merged.sort((left, right) => left.id.localeCompare(right.id));
|
|
91
|
+
};
|
|
92
|
+
export const toWorkspaceResourcePlan = (resource, context) => resource.kind === "git"
|
|
93
|
+
? {
|
|
94
|
+
backingPath: resolveBackingPath(resource, context.targetId),
|
|
95
|
+
...(resource.branch ? { branch: resource.branch } : {}),
|
|
96
|
+
id: resource.id,
|
|
97
|
+
kind: "git",
|
|
98
|
+
linkPath: resolveLinkPath(normalizeMount(resource.mount), context.workspacePath),
|
|
99
|
+
mode: resource.mode,
|
|
100
|
+
mount: normalizeMount(resource.mount),
|
|
101
|
+
...(resource.ref ? { ref: resource.ref } : {}),
|
|
102
|
+
sharing: resource.sharing,
|
|
103
|
+
...(resource.tag ? { tag: resource.tag } : {}),
|
|
104
|
+
url: resource.url
|
|
105
|
+
}
|
|
106
|
+
: {
|
|
107
|
+
backingPath: resolveBackingPath(resource, context.targetId),
|
|
108
|
+
id: resource.id,
|
|
109
|
+
kind: "volume",
|
|
110
|
+
linkPath: resolveLinkPath(normalizeMount(resource.mount), context.workspacePath),
|
|
111
|
+
mode: resource.mode,
|
|
112
|
+
mount: normalizeMount(resource.mount),
|
|
113
|
+
...(resource.name ? { name: resource.name } : {}),
|
|
114
|
+
sharing: resource.sharing
|
|
115
|
+
};
|
|
116
|
+
export const mergeWorkspaceResourcePlans = (resources, ownerName, context) => mergeWorkspaceResources(resources, [], ownerName, {
|
|
117
|
+
kind: "agent",
|
|
118
|
+
key: context.targetId,
|
|
119
|
+
name: context.targetId
|
|
120
|
+
}).map((resource) => toWorkspaceResourcePlan(resource, context));
|
package/dist/filesystem/paths.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { SpawnfileError } from "../shared/index.js";
|
|
3
|
-
const INVALID_PATH_SEGMENT = "..";
|
|
4
3
|
export const assertPortableRelativePath = (inputPath) => {
|
|
5
4
|
if (inputPath.includes("\\")) {
|
|
6
5
|
throw new SpawnfileError("validation_error", `Paths must use forward slashes: ${inputPath}`);
|
|
@@ -8,10 +7,6 @@ export const assertPortableRelativePath = (inputPath) => {
|
|
|
8
7
|
if (path.isAbsolute(inputPath)) {
|
|
9
8
|
throw new SpawnfileError("validation_error", `Absolute paths are not allowed: ${inputPath}`);
|
|
10
9
|
}
|
|
11
|
-
const segments = inputPath.split("/");
|
|
12
|
-
if (segments.includes(INVALID_PATH_SEGMENT)) {
|
|
13
|
-
throw new SpawnfileError("validation_error", `Path traversal is not allowed: ${inputPath}`);
|
|
14
|
-
}
|
|
15
10
|
};
|
|
16
11
|
export const getCanonicalManifestPath = (filePath) => path.resolve(filePath);
|
|
17
12
|
export const getManifestPath = (inputPath) => path.basename(inputPath) === "Spawnfile"
|
|
@@ -21,10 +16,6 @@ export const getProjectRoot = (manifestPath) => path.dirname(manifestPath);
|
|
|
21
16
|
export const resolveProjectPath = (manifestPath, relativePath) => {
|
|
22
17
|
assertPortableRelativePath(relativePath);
|
|
23
18
|
const projectRoot = getProjectRoot(manifestPath);
|
|
24
|
-
|
|
25
|
-
if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {
|
|
26
|
-
throw new SpawnfileError("validation_error", `Path escapes project root: ${relativePath}`);
|
|
27
|
-
}
|
|
28
|
-
return resolved;
|
|
19
|
+
return path.resolve(projectRoot, relativePath);
|
|
29
20
|
};
|
|
30
21
|
export const toPosixPath = (value) => value.split(path.sep).join("/");
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const modelEntryAuthSchema: z.ZodObject<{
|
|
3
|
+
key: z.ZodOptional<z.ZodString>;
|
|
4
|
+
method: z.ZodOptional<z.ZodEnum<{
|
|
5
|
+
api_key: "api_key";
|
|
6
|
+
"claude-code": "claude-code";
|
|
7
|
+
codex: "codex";
|
|
8
|
+
none: "none";
|
|
9
|
+
}>>;
|
|
10
|
+
}, z.core.$strict>;
|
|
11
|
+
export declare const modelEndpointSchema: z.ZodObject<{
|
|
12
|
+
base_url: z.ZodString;
|
|
13
|
+
compatibility: z.ZodEnum<{
|
|
14
|
+
anthropic: "anthropic";
|
|
15
|
+
openai: "openai";
|
|
16
|
+
}>;
|
|
17
|
+
}, z.core.$strict>;
|
|
18
|
+
export declare const modelTargetSchema: z.ZodObject<{
|
|
19
|
+
auth: z.ZodOptional<z.ZodObject<{
|
|
20
|
+
key: z.ZodOptional<z.ZodString>;
|
|
21
|
+
method: z.ZodOptional<z.ZodEnum<{
|
|
22
|
+
api_key: "api_key";
|
|
23
|
+
"claude-code": "claude-code";
|
|
24
|
+
codex: "codex";
|
|
25
|
+
none: "none";
|
|
26
|
+
}>>;
|
|
27
|
+
}, z.core.$strict>>;
|
|
28
|
+
endpoint: z.ZodOptional<z.ZodObject<{
|
|
29
|
+
base_url: z.ZodString;
|
|
30
|
+
compatibility: z.ZodEnum<{
|
|
31
|
+
anthropic: "anthropic";
|
|
32
|
+
openai: "openai";
|
|
33
|
+
}>;
|
|
34
|
+
}, z.core.$strict>>;
|
|
35
|
+
name: z.ZodString;
|
|
36
|
+
provider: z.ZodString;
|
|
37
|
+
}, z.core.$strict>;
|
|
38
|
+
export declare const executionSchema: z.ZodObject<{
|
|
39
|
+
model: z.ZodOptional<z.ZodObject<{
|
|
40
|
+
auth: z.ZodOptional<z.ZodObject<{
|
|
41
|
+
method: z.ZodOptional<z.ZodEnum<{
|
|
42
|
+
api_key: "api_key";
|
|
43
|
+
"claude-code": "claude-code";
|
|
44
|
+
codex: "codex";
|
|
45
|
+
none: "none";
|
|
46
|
+
}>>;
|
|
47
|
+
methods: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
48
|
+
api_key: "api_key";
|
|
49
|
+
"claude-code": "claude-code";
|
|
50
|
+
codex: "codex";
|
|
51
|
+
none: "none";
|
|
52
|
+
}>>>;
|
|
53
|
+
}, z.core.$strict>>;
|
|
54
|
+
fallback: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
55
|
+
auth: z.ZodOptional<z.ZodObject<{
|
|
56
|
+
key: z.ZodOptional<z.ZodString>;
|
|
57
|
+
method: z.ZodOptional<z.ZodEnum<{
|
|
58
|
+
api_key: "api_key";
|
|
59
|
+
"claude-code": "claude-code";
|
|
60
|
+
codex: "codex";
|
|
61
|
+
none: "none";
|
|
62
|
+
}>>;
|
|
63
|
+
}, z.core.$strict>>;
|
|
64
|
+
endpoint: z.ZodOptional<z.ZodObject<{
|
|
65
|
+
base_url: z.ZodString;
|
|
66
|
+
compatibility: z.ZodEnum<{
|
|
67
|
+
anthropic: "anthropic";
|
|
68
|
+
openai: "openai";
|
|
69
|
+
}>;
|
|
70
|
+
}, z.core.$strict>>;
|
|
71
|
+
name: z.ZodString;
|
|
72
|
+
provider: z.ZodString;
|
|
73
|
+
}, z.core.$strict>>>;
|
|
74
|
+
primary: z.ZodObject<{
|
|
75
|
+
auth: z.ZodOptional<z.ZodObject<{
|
|
76
|
+
key: z.ZodOptional<z.ZodString>;
|
|
77
|
+
method: z.ZodOptional<z.ZodEnum<{
|
|
78
|
+
api_key: "api_key";
|
|
79
|
+
"claude-code": "claude-code";
|
|
80
|
+
codex: "codex";
|
|
81
|
+
none: "none";
|
|
82
|
+
}>>;
|
|
83
|
+
}, z.core.$strict>>;
|
|
84
|
+
endpoint: z.ZodOptional<z.ZodObject<{
|
|
85
|
+
base_url: z.ZodString;
|
|
86
|
+
compatibility: z.ZodEnum<{
|
|
87
|
+
anthropic: "anthropic";
|
|
88
|
+
openai: "openai";
|
|
89
|
+
}>;
|
|
90
|
+
}, z.core.$strict>>;
|
|
91
|
+
name: z.ZodString;
|
|
92
|
+
provider: z.ZodString;
|
|
93
|
+
}, z.core.$strict>;
|
|
94
|
+
}, z.core.$strict>>;
|
|
95
|
+
sandbox: z.ZodOptional<z.ZodObject<{
|
|
96
|
+
mode: z.ZodEnum<{
|
|
97
|
+
sandboxed: "sandboxed";
|
|
98
|
+
unrestricted: "unrestricted";
|
|
99
|
+
workspace: "workspace";
|
|
100
|
+
}>;
|
|
101
|
+
}, z.core.$strict>>;
|
|
102
|
+
}, z.core.$strict>;
|
|
103
|
+
export type ExecutionBlock = z.infer<typeof executionSchema>;
|
|
104
|
+
export type ModelEndpoint = z.infer<typeof modelEndpointSchema>;
|
|
105
|
+
export type ModelEntryAuth = z.infer<typeof modelEntryAuthSchema>;
|
|
106
|
+
export type ModelTarget = z.infer<typeof modelTargetSchema>;
|