spawnfile 0.1.0 → 0.1.2
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 +80 -397
- package/dist/cli/modelCommands.d.ts +3 -0
- package/dist/cli/modelCommands.js +68 -0
- package/dist/cli/runCli.d.ts +6 -1
- package/dist/cli/runCli.js +12 -67
- package/dist/cli/runtimeCommands.d.ts +3 -0
- package/dist/cli/runtimeCommands.js +20 -0
- package/dist/cli/surfaceCommands.d.ts +3 -0
- package/dist/cli/surfaceCommands.js +98 -0
- package/dist/compiler/agentSurfaces.js +51 -5
- package/dist/compiler/buildCompilePlan.js +36 -40
- package/dist/compiler/buildCompilePlanRuntime.d.ts +14 -0
- package/dist/compiler/buildCompilePlanRuntime.js +39 -0
- package/dist/compiler/buildCompilePlanTeams.d.ts +5 -0
- package/dist/compiler/buildCompilePlanTeams.js +38 -0
- package/dist/compiler/compilePlanHelpers.js +4 -1
- package/dist/compiler/compileProject.js +62 -13
- package/dist/compiler/compileProjectSupport.d.ts +17 -0
- package/dist/compiler/compileProjectSupport.js +136 -0
- package/dist/compiler/containerArtifacts.d.ts +6 -1
- package/dist/compiler/containerArtifacts.js +26 -4
- package/dist/compiler/containerArtifactsPlans.js +16 -1
- package/dist/compiler/containerArtifactsRender.d.ts +4 -2
- package/dist/compiler/containerArtifactsRender.js +21 -126
- package/dist/compiler/containerArtifactsTypes.d.ts +7 -0
- package/dist/compiler/containerEntrypointRender.d.ts +12 -0
- package/dist/compiler/containerEntrypointRender.js +186 -0
- package/dist/compiler/index.d.ts +2 -0
- package/dist/compiler/index.js +2 -0
- package/dist/compiler/interactiveSurfaceScopes.d.ts +2 -0
- package/dist/compiler/interactiveSurfaceScopes.js +21 -0
- package/dist/compiler/moltnetArtifacts.d.ts +27 -0
- package/dist/compiler/moltnetArtifacts.js +204 -0
- package/dist/compiler/moltnetBinaries.d.ts +4 -0
- package/dist/compiler/moltnetBinaries.js +103 -0
- package/dist/compiler/moltnetClientConfig.d.ts +11 -0
- package/dist/compiler/moltnetClientConfig.js +89 -0
- package/dist/compiler/moltnetRepresentativeResolution.d.ts +16 -0
- package/dist/compiler/moltnetRepresentativeResolution.js +86 -0
- package/dist/compiler/moltnetResolution.d.ts +3 -0
- package/dist/compiler/moltnetResolution.js +201 -0
- package/dist/compiler/runProject.js +1 -1
- package/dist/compiler/surfaceDefinitions.d.ts +55 -0
- package/dist/compiler/surfaceDefinitions.js +204 -0
- package/dist/compiler/teamContextHelpers.d.ts +18 -0
- package/dist/compiler/teamContextHelpers.js +112 -0
- package/dist/compiler/teamContextSupport.d.ts +4 -0
- package/dist/compiler/teamContextSupport.js +264 -0
- package/dist/compiler/teamContextSupport.testHelpers.d.ts +16 -0
- package/dist/compiler/teamContextSupport.testHelpers.js +68 -0
- package/dist/compiler/teamContextTypes.d.ts +28 -0
- package/dist/compiler/teamRoster.d.ts +12 -0
- package/dist/compiler/teamRoster.js +48 -0
- package/dist/compiler/teamRosterEntries.d.ts +13 -0
- package/dist/compiler/teamRosterEntries.js +230 -0
- package/dist/compiler/teamRosterTypes.d.ts +45 -0
- package/dist/compiler/types.d.ts +72 -6
- package/dist/compiler/updateProjectRuntime.d.ts +9 -0
- package/dist/compiler/updateProjectRuntime.js +67 -0
- package/dist/compiler/updateProjectSurfaces.d.ts +8 -0
- package/dist/compiler/updateProjectSurfaces.js +106 -0
- package/dist/manifest/loadManifest.js +4 -4
- package/dist/manifest/renderSpawnfile.js +74 -8
- package/dist/manifest/scaffold.js +1 -3
- package/dist/manifest/schemas.d.ts +227 -17
- package/dist/manifest/schemas.js +62 -20
- package/dist/manifest/surfaceSchemas.d.ts +154 -0
- package/dist/manifest/surfaceSchemas.js +77 -5
- package/dist/runtime/common.js +3 -0
- package/dist/runtime/openclaw/adapter.js +38 -5
- package/dist/runtime/openclaw/moltnet.d.ts +12 -0
- package/dist/runtime/openclaw/moltnet.js +124 -0
- package/dist/runtime/openclaw/surfaces.js +3 -0
- package/dist/runtime/picoclaw/adapter.js +27 -8
- package/dist/runtime/picoclaw/pico.d.ts +2 -0
- package/dist/runtime/picoclaw/pico.js +2 -0
- package/dist/runtime/picoclaw/surfaces.js +11 -0
- package/dist/runtime/tinyclaw/adapter.js +22 -8
- package/dist/runtime/tinyclaw/runAuth.js +28 -1
- package/dist/runtime/tinyclaw/surfaces.js +8 -0
- package/dist/runtime/types.d.ts +11 -0
- package/package.json +10 -3
- package/runtimes.yaml +4 -4
- package/dist/.env.example +0 -5
- package/dist/Dockerfile +0 -21
- package/dist/compiler/discordSurface.d.ts +0 -4
- package/dist/compiler/discordSurface.js +0 -28
- package/dist/container/rootfs/var/lib/spawnfile/instances/picoclaw/agent-assistant/picoclaw/config.json +0 -16
- package/dist/container/rootfs/var/lib/spawnfile/instances/picoclaw/agent-assistant/picoclaw/workspace/AGENTS.md +0 -1
- package/dist/e2e/cli.js +0 -40
- package/dist/e2e/dockerAuth.d.ts +0 -18
- package/dist/e2e/dockerAuth.js +0 -212
- package/dist/e2e/fixtures.d.ts +0 -2
- package/dist/e2e/fixtures.js +0 -49
- package/dist/e2e/index.d.ts +0 -4
- package/dist/e2e/index.js +0 -4
- package/dist/e2e/runtimePrompts.d.ts +0 -13
- package/dist/e2e/runtimePrompts.js +0 -132
- package/dist/e2e/scenarios.d.ts +0 -3
- package/dist/e2e/scenarios.js +0 -84
- package/dist/e2e/types.d.ts +0 -35
- package/dist/entrypoint.sh +0 -71
- package/dist/runtimes/picoclaw/agents/assistant/config.json +0 -16
- package/dist/runtimes/picoclaw/agents/assistant/workspace/AGENTS.md +0 -1
- package/dist/spawnfile-report.json +0 -71
- /package/dist/{e2e/cli.d.ts → compiler/teamContextTypes.js} +0 -0
- /package/dist/{e2e/types.js → compiler/teamRosterTypes.js} +0 -0
package/dist/cli/runCli.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { importClaudeCodeAuth, importCodexAuth, importEnvFile, requireAuthProfile } from "../auth/index.js";
|
|
3
|
-
import { addAgentProject, addProjectModelFallback, addSubagentProject, addTeamProject, buildCompilePlan, buildProject, clearProjectModelFallbacks, compileProject, initProject, runProject, setProjectPrimaryModel, syncProjectAuth } from "../compiler/index.js";
|
|
3
|
+
import { addAgentProject, addProjectSurface, addProjectModelFallback, addSubagentProject, addTeamProject, buildCompilePlan, buildProject, clearProjectModelFallbacks, compileProject, initProject, removeProjectSurface, runProject, setProjectPrimaryModel, setProjectRuntime, setProjectSurfaceAccess, showProjectSurfaces, syncProjectAuth } from "../compiler/index.js";
|
|
4
4
|
import { isSpawnfileError } from "../shared/index.js";
|
|
5
5
|
import { listRuntimeAdapters } from "../runtime/index.js";
|
|
6
|
+
import { registerModelCommands } from "./modelCommands.js";
|
|
7
|
+
import { registerRuntimeCommands } from "./runtimeCommands.js";
|
|
8
|
+
import { registerSurfaceCommands } from "./surfaceCommands.js";
|
|
6
9
|
const createDefaultStreams = () => ({
|
|
7
10
|
stderr: (message) => process.stderr.write(`${message}\n`),
|
|
8
11
|
stdout: (message) => process.stdout.write(`${message}\n`)
|
|
@@ -13,6 +16,7 @@ const createDefaultHandlers = () => ({
|
|
|
13
16
|
compileProject,
|
|
14
17
|
addAgentProject,
|
|
15
18
|
addProjectModelFallback,
|
|
19
|
+
addProjectSurface,
|
|
16
20
|
addSubagentProject,
|
|
17
21
|
addTeamProject,
|
|
18
22
|
clearProjectModelFallbacks,
|
|
@@ -21,9 +25,13 @@ const createDefaultHandlers = () => ({
|
|
|
21
25
|
importEnvFile,
|
|
22
26
|
initProject,
|
|
23
27
|
listRuntimeAdapters,
|
|
28
|
+
removeProjectSurface,
|
|
24
29
|
requireAuthProfile,
|
|
25
30
|
runProject,
|
|
26
31
|
setProjectPrimaryModel,
|
|
32
|
+
setProjectRuntime,
|
|
33
|
+
setProjectSurfaceAccess,
|
|
34
|
+
showProjectSurfaces,
|
|
27
35
|
syncProjectAuth
|
|
28
36
|
});
|
|
29
37
|
const formatPlanSummary = (plan) => [
|
|
@@ -155,72 +163,9 @@ export const runCli = async (argv, streams = createDefaultStreams(), handlerOver
|
|
|
155
163
|
streams.stdout(`created ${filePath}`);
|
|
156
164
|
}
|
|
157
165
|
});
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
modelCommand
|
|
162
|
-
.command("set")
|
|
163
|
-
.argument("<provider>", "Model provider")
|
|
164
|
-
.argument("<name>", "Model name")
|
|
165
|
-
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
166
|
-
.option("--auth <method>", "Model auth method")
|
|
167
|
-
.option("--key <env>", "Environment variable for api_key auth")
|
|
168
|
-
.option("--compat <compatibility>", "Endpoint compatibility for custom/local models")
|
|
169
|
-
.option("--base-url <url>", "Endpoint base URL for custom/local models")
|
|
170
|
-
.option("--recursive", "Update the target project and its descendants")
|
|
171
|
-
.action(async (provider, name, inputPath, options) => {
|
|
172
|
-
const result = await handlers.setProjectPrimaryModel({
|
|
173
|
-
authKey: options.key,
|
|
174
|
-
authMethod: options.auth,
|
|
175
|
-
endpointBaseUrl: options.baseUrl,
|
|
176
|
-
endpointCompatibility: options.compat,
|
|
177
|
-
name,
|
|
178
|
-
path: inputPath,
|
|
179
|
-
provider,
|
|
180
|
-
recursive: options.recursive
|
|
181
|
-
});
|
|
182
|
-
for (const filePath of result.updatedFiles) {
|
|
183
|
-
streams.stdout(`updated ${filePath}`);
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
modelCommand
|
|
187
|
-
.command("add-fallback")
|
|
188
|
-
.argument("<provider>", "Model provider")
|
|
189
|
-
.argument("<name>", "Model name")
|
|
190
|
-
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
191
|
-
.option("--auth <method>", "Model auth method")
|
|
192
|
-
.option("--key <env>", "Environment variable for api_key auth")
|
|
193
|
-
.option("--compat <compatibility>", "Endpoint compatibility for custom/local models")
|
|
194
|
-
.option("--base-url <url>", "Endpoint base URL for custom/local models")
|
|
195
|
-
.option("--recursive", "Update the target project and its descendants")
|
|
196
|
-
.action(async (provider, name, inputPath, options) => {
|
|
197
|
-
const result = await handlers.addProjectModelFallback({
|
|
198
|
-
authKey: options.key,
|
|
199
|
-
authMethod: options.auth,
|
|
200
|
-
endpointBaseUrl: options.baseUrl,
|
|
201
|
-
endpointCompatibility: options.compat,
|
|
202
|
-
name,
|
|
203
|
-
path: inputPath,
|
|
204
|
-
provider,
|
|
205
|
-
recursive: options.recursive
|
|
206
|
-
});
|
|
207
|
-
for (const filePath of result.updatedFiles) {
|
|
208
|
-
streams.stdout(`updated ${filePath}`);
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
modelCommand
|
|
212
|
-
.command("clear-fallbacks")
|
|
213
|
-
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
214
|
-
.option("--recursive", "Update the target project and its descendants")
|
|
215
|
-
.action(async (inputPath, options) => {
|
|
216
|
-
const result = await handlers.clearProjectModelFallbacks({
|
|
217
|
-
path: inputPath,
|
|
218
|
-
recursive: options.recursive
|
|
219
|
-
});
|
|
220
|
-
for (const filePath of result.updatedFiles) {
|
|
221
|
-
streams.stdout(`updated ${filePath}`);
|
|
222
|
-
}
|
|
223
|
-
});
|
|
166
|
+
registerModelCommands(program, handlers, streams);
|
|
167
|
+
registerRuntimeCommands(program, handlers, streams);
|
|
168
|
+
registerSurfaceCommands(program, handlers, streams);
|
|
224
169
|
program
|
|
225
170
|
.command("validate")
|
|
226
171
|
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const registerRuntimeCommands = (program, handlers, streams) => {
|
|
2
|
+
const runtimeCommand = program
|
|
3
|
+
.command("runtime")
|
|
4
|
+
.description("Edit runtime declarations");
|
|
5
|
+
runtimeCommand
|
|
6
|
+
.command("set")
|
|
7
|
+
.argument("<name>", "Runtime name")
|
|
8
|
+
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
9
|
+
.option("--recursive", "Update the target project and its descendants")
|
|
10
|
+
.action(async (runtime, inputPath, options) => {
|
|
11
|
+
const result = await handlers.setProjectRuntime({
|
|
12
|
+
path: inputPath,
|
|
13
|
+
recursive: options.recursive,
|
|
14
|
+
runtime
|
|
15
|
+
});
|
|
16
|
+
for (const filePath of result.updatedFiles) {
|
|
17
|
+
streams.stdout(`updated ${filePath}`);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import YAML from "yaml";
|
|
2
|
+
import { resolvePortableSurfaceName } from "../compiler/index.js";
|
|
3
|
+
const collectStringOption = (value, previous) => [...previous, value];
|
|
4
|
+
const emitSurfaceSummaries = (streams, entries) => {
|
|
5
|
+
entries.forEach((entry, index) => {
|
|
6
|
+
if (index > 0) {
|
|
7
|
+
streams.stdout("");
|
|
8
|
+
}
|
|
9
|
+
streams.stdout(`path: ${entry.manifestPath}`);
|
|
10
|
+
streams.stdout(`kind: ${entry.kind}`);
|
|
11
|
+
streams.stdout(`name: ${entry.name}`);
|
|
12
|
+
if (!entry.surfaces) {
|
|
13
|
+
streams.stdout("surfaces: none");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
streams.stdout(YAML.stringify({ surfaces: entry.surfaces }).trimEnd());
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
export const registerSurfaceCommands = (program, handlers, streams) => {
|
|
20
|
+
const surfaceCommand = program
|
|
21
|
+
.command("surface")
|
|
22
|
+
.description("Edit agent surfaces");
|
|
23
|
+
surfaceCommand
|
|
24
|
+
.command("add")
|
|
25
|
+
.argument("<surface>", "Surface name")
|
|
26
|
+
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
27
|
+
.option("--bot-token-secret <env>", "Override the bot token env var name")
|
|
28
|
+
.option("--app-token-secret <env>", "Override the Slack app token env var name")
|
|
29
|
+
.option("--recursive", "Update the target project and its descendants")
|
|
30
|
+
.action(async (surface, inputPath, options) => {
|
|
31
|
+
const surfaceName = resolvePortableSurfaceName(surface);
|
|
32
|
+
const result = await handlers.addProjectSurface({
|
|
33
|
+
appTokenSecret: options.appTokenSecret,
|
|
34
|
+
botTokenSecret: options.botTokenSecret,
|
|
35
|
+
path: inputPath,
|
|
36
|
+
recursive: options.recursive,
|
|
37
|
+
surface: surfaceName
|
|
38
|
+
});
|
|
39
|
+
for (const filePath of result.updatedFiles) {
|
|
40
|
+
streams.stdout(`updated ${filePath}`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
surfaceCommand
|
|
44
|
+
.command("remove")
|
|
45
|
+
.argument("<surface>", "Surface name")
|
|
46
|
+
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
47
|
+
.option("--recursive", "Update the target project and its descendants")
|
|
48
|
+
.action(async (surface, inputPath, options) => {
|
|
49
|
+
const surfaceName = resolvePortableSurfaceName(surface);
|
|
50
|
+
const result = await handlers.removeProjectSurface({
|
|
51
|
+
path: inputPath,
|
|
52
|
+
recursive: options.recursive,
|
|
53
|
+
surface: surfaceName
|
|
54
|
+
});
|
|
55
|
+
for (const filePath of result.updatedFiles) {
|
|
56
|
+
streams.stdout(`updated ${filePath}`);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
surfaceCommand
|
|
60
|
+
.command("set-access")
|
|
61
|
+
.argument("<surface>", "Surface name")
|
|
62
|
+
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
63
|
+
.requiredOption("--mode <mode>", "Access mode")
|
|
64
|
+
.option("--user <id>", "Allowlisted user id", collectStringOption, [])
|
|
65
|
+
.option("--channel <id>", "Allowlisted channel id", collectStringOption, [])
|
|
66
|
+
.option("--guild <id>", "Allowlisted guild id", collectStringOption, [])
|
|
67
|
+
.option("--chat <id>", "Allowlisted chat id", collectStringOption, [])
|
|
68
|
+
.option("--group <id>", "Allowlisted group id", collectStringOption, [])
|
|
69
|
+
.option("--recursive", "Update the target project and its descendants")
|
|
70
|
+
.action(async (surface, inputPath, options) => {
|
|
71
|
+
const surfaceName = resolvePortableSurfaceName(surface);
|
|
72
|
+
const result = await handlers.setProjectSurfaceAccess({
|
|
73
|
+
channels: options.channel,
|
|
74
|
+
chats: options.chat,
|
|
75
|
+
groups: options.group,
|
|
76
|
+
guilds: options.guild,
|
|
77
|
+
mode: options.mode,
|
|
78
|
+
path: inputPath,
|
|
79
|
+
recursive: options.recursive,
|
|
80
|
+
surface: surfaceName,
|
|
81
|
+
users: options.user
|
|
82
|
+
});
|
|
83
|
+
for (const filePath of result.updatedFiles) {
|
|
84
|
+
streams.stdout(`updated ${filePath}`);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
surfaceCommand
|
|
88
|
+
.command("show")
|
|
89
|
+
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
90
|
+
.option("--recursive", "Show descendant agent surfaces too")
|
|
91
|
+
.action(async (inputPath, options) => {
|
|
92
|
+
const result = await handlers.showProjectSurfaces({
|
|
93
|
+
path: inputPath,
|
|
94
|
+
recursive: options.recursive
|
|
95
|
+
});
|
|
96
|
+
emitSurfaceSummaries(streams, result.entries);
|
|
97
|
+
});
|
|
98
|
+
};
|
|
@@ -5,6 +5,7 @@ export const resolveAgentSurfaces = (surfaces) => {
|
|
|
5
5
|
}
|
|
6
6
|
const resolved = {};
|
|
7
7
|
if (surfaces.discord) {
|
|
8
|
+
const identity = surfaces.discord.identity;
|
|
8
9
|
resolved.discord = {
|
|
9
10
|
...(surfaces.discord.access
|
|
10
11
|
? {
|
|
@@ -16,10 +17,41 @@ export const resolveAgentSurfaces = (surfaces) => {
|
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
: {}),
|
|
19
|
-
botTokenSecret: surfaces.discord.bot_token_secret ?? DEFAULT_DISCORD_BOT_TOKEN_SECRET
|
|
20
|
+
botTokenSecret: surfaces.discord.bot_token_secret ?? DEFAULT_DISCORD_BOT_TOKEN_SECRET,
|
|
21
|
+
...(identity ? { identity: { userId: identity.user_id } } : {})
|
|
20
22
|
};
|
|
21
23
|
}
|
|
24
|
+
if (surfaces.moltnet) {
|
|
25
|
+
resolved.moltnet = surfaces.moltnet.map((attachment) => ({
|
|
26
|
+
...(attachment.dms
|
|
27
|
+
? {
|
|
28
|
+
dms: {
|
|
29
|
+
enabled: attachment.dms.enabled,
|
|
30
|
+
...(attachment.dms.read ? { read: attachment.dms.read } : {}),
|
|
31
|
+
...(attachment.dms.reply ? { reply: attachment.dms.reply } : {})
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
: {}),
|
|
35
|
+
memberId: null,
|
|
36
|
+
network: attachment.network,
|
|
37
|
+
...(attachment.rooms
|
|
38
|
+
? {
|
|
39
|
+
rooms: Object.fromEntries(Object.entries(attachment.rooms)
|
|
40
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
41
|
+
.map(([roomId, behavior]) => [
|
|
42
|
+
roomId,
|
|
43
|
+
{
|
|
44
|
+
...(behavior.read ? { read: behavior.read } : {}),
|
|
45
|
+
...(behavior.reply ? { reply: behavior.reply } : {})
|
|
46
|
+
}
|
|
47
|
+
]))
|
|
48
|
+
}
|
|
49
|
+
: {}),
|
|
50
|
+
teamSource: null
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
22
53
|
if (surfaces.telegram) {
|
|
54
|
+
const identity = surfaces.telegram.identity;
|
|
23
55
|
resolved.telegram = {
|
|
24
56
|
...(surfaces.telegram.access
|
|
25
57
|
? {
|
|
@@ -30,21 +62,34 @@ export const resolveAgentSurfaces = (surfaces) => {
|
|
|
30
62
|
}
|
|
31
63
|
}
|
|
32
64
|
: {}),
|
|
33
|
-
botTokenSecret: surfaces.telegram.bot_token_secret ?? DEFAULT_TELEGRAM_BOT_TOKEN_SECRET
|
|
65
|
+
botTokenSecret: surfaces.telegram.bot_token_secret ?? DEFAULT_TELEGRAM_BOT_TOKEN_SECRET,
|
|
66
|
+
...(identity
|
|
67
|
+
? {
|
|
68
|
+
identity: {
|
|
69
|
+
...(identity.user_id ? { userId: identity.user_id } : {}),
|
|
70
|
+
...(identity.username ? { username: identity.username } : {})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
: {})
|
|
34
74
|
};
|
|
35
75
|
}
|
|
36
76
|
if (surfaces.whatsapp) {
|
|
77
|
+
const identity = surfaces.whatsapp.identity;
|
|
37
78
|
resolved.whatsapp = surfaces.whatsapp.access
|
|
38
79
|
? {
|
|
39
80
|
access: {
|
|
40
81
|
groups: [...(surfaces.whatsapp.access.groups ?? [])],
|
|
41
82
|
mode: surfaces.whatsapp.access.mode ?? "allowlist",
|
|
42
83
|
users: [...(surfaces.whatsapp.access.users ?? [])]
|
|
43
|
-
}
|
|
84
|
+
},
|
|
85
|
+
...(identity ? { identity: { phone: identity.phone } } : {})
|
|
44
86
|
}
|
|
45
|
-
: {
|
|
87
|
+
: {
|
|
88
|
+
...(identity ? { identity: { phone: identity.phone } } : {})
|
|
89
|
+
};
|
|
46
90
|
}
|
|
47
91
|
if (surfaces.slack) {
|
|
92
|
+
const identity = surfaces.slack.identity;
|
|
48
93
|
resolved.slack = {
|
|
49
94
|
...(surfaces.slack.access
|
|
50
95
|
? {
|
|
@@ -56,7 +101,8 @@ export const resolveAgentSurfaces = (surfaces) => {
|
|
|
56
101
|
}
|
|
57
102
|
: {}),
|
|
58
103
|
appTokenSecret: surfaces.slack.app_token_secret ?? DEFAULT_SLACK_APP_TOKEN_SECRET,
|
|
59
|
-
botTokenSecret: surfaces.slack.bot_token_secret ?? DEFAULT_SLACK_BOT_TOKEN_SECRET
|
|
104
|
+
botTokenSecret: surfaces.slack.bot_token_secret ?? DEFAULT_SLACK_BOT_TOKEN_SECRET,
|
|
105
|
+
...(identity ? { identity: { userId: identity.user_id } } : {})
|
|
60
106
|
};
|
|
61
107
|
}
|
|
62
108
|
return Object.keys(resolved).length > 0 ? resolved : undefined;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getCanonicalManifestPath, getManifestPath, resolveProjectPath } from "../filesystem/index.js";
|
|
2
|
-
import { isAgentManifest, isTeamManifest, loadManifest, mergeExecution
|
|
3
|
-
import { assertRuntimeCanCompile } from "../runtime/index.js";
|
|
2
|
+
import { isAgentManifest, isTeamManifest, loadManifest, mergeExecution } from "../manifest/index.js";
|
|
4
3
|
import { SpawnfileError } from "../shared/index.js";
|
|
5
4
|
import { getAgentFingerprint, getMcpNames, getTeamFingerprint, validateEffectiveSkillRequirements } from "./compilePlanHelpers.js";
|
|
6
5
|
import { resolveAgentSurfaces } from "./agentSurfaces.js";
|
|
@@ -9,30 +8,16 @@ import { loadResolvedDocuments, mergeResolvedSkills, loadResolvedSkills, mergeSh
|
|
|
9
8
|
import { assertRuntimeSupportsExecutionModelAuth } from "./modelAuth.js";
|
|
10
9
|
import { assertRuntimeSupportsAgentSurfaces } from "./surfaceSupport.js";
|
|
11
10
|
import { applyExecutionDefaults } from "./executionDefaults.js";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (!context.inheritedRuntime) {
|
|
16
|
-
throw new SpawnfileError("runtime_error", `Subagent ${manifest.name} is missing inherited runtime context`);
|
|
17
|
-
}
|
|
18
|
-
if (localRuntime &&
|
|
19
|
-
localRuntime.name !== context.inheritedRuntime.name) {
|
|
20
|
-
throw new SpawnfileError("runtime_error", `Subagent ${manifest.name} must match parent runtime`);
|
|
21
|
-
}
|
|
22
|
-
return context.inheritedRuntime;
|
|
23
|
-
}
|
|
24
|
-
if (!localRuntime) {
|
|
25
|
-
throw new SpawnfileError("runtime_error", `Agent ${manifest.name} does not declare a runtime`);
|
|
26
|
-
}
|
|
27
|
-
await assertRuntimeCanCompile(localRuntime.name);
|
|
28
|
-
return localRuntime;
|
|
29
|
-
};
|
|
11
|
+
import { resolvePlanMoltnetAttachments } from "./moltnetResolution.js";
|
|
12
|
+
import { normalizeDescription, resolveDescription, resolveRuntime } from "./buildCompilePlanRuntime.js";
|
|
13
|
+
import { resolveTeamExternalIds, resolveTeamNetworks, validateTeamNetworkRooms } from "./buildCompilePlanTeams.js";
|
|
30
14
|
export const buildCompilePlan = async (inputPath) => {
|
|
31
15
|
const rootManifestPath = getCanonicalManifestPath(getManifestPath(inputPath));
|
|
32
16
|
const loadCache = new Map();
|
|
33
17
|
const nodeCache = new Map();
|
|
34
18
|
const fingerprintCache = new Map();
|
|
35
19
|
const edges = [];
|
|
20
|
+
const memberships = new Map();
|
|
36
21
|
const visitStack = [];
|
|
37
22
|
const getLoadedManifest = (manifestPath) => {
|
|
38
23
|
const canonicalPath = getCanonicalManifestPath(manifestPath);
|
|
@@ -70,10 +55,13 @@ export const buildCompilePlan = async (inputPath) => {
|
|
|
70
55
|
const localSkills = await loadResolvedSkills(canonicalPath, loadedManifest.manifest.skills);
|
|
71
56
|
const skills = mergeResolvedSkills(inheritedSkills, localSkills);
|
|
72
57
|
validateEffectiveSkillRequirements(loadedManifest.manifest.name, getMcpNames(sharedSurface.mcpServers), skills);
|
|
58
|
+
const docs = await loadResolvedDocuments(canonicalPath, loadedManifest.manifest.docs);
|
|
73
59
|
const candidate = {
|
|
74
|
-
|
|
60
|
+
description: resolveDescription(loadedManifest.manifest.description, docs),
|
|
61
|
+
docs,
|
|
75
62
|
env: sharedSurface.env,
|
|
76
63
|
execution,
|
|
64
|
+
expose: loadedManifest.manifest.expose ?? false,
|
|
77
65
|
kind: "agent",
|
|
78
66
|
mcpServers: sharedSurface.mcpServers,
|
|
79
67
|
name: loadedManifest.manifest.name,
|
|
@@ -135,31 +123,29 @@ export const buildCompilePlan = async (inputPath) => {
|
|
|
135
123
|
}
|
|
136
124
|
const sharedSkills = await loadResolvedSkills(canonicalPath, loadedManifest.manifest.shared?.skills);
|
|
137
125
|
validateEffectiveSkillRequirements(loadedManifest.manifest.name, getMcpNames(loadedManifest.manifest.shared?.mcp_servers ?? []), sharedSkills);
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
?? (structure.mode === "hierarchical" && structure.leader
|
|
142
|
-
? [structure.leader]
|
|
143
|
-
: memberIds);
|
|
126
|
+
const manifest = loadedManifest.manifest;
|
|
127
|
+
const resolvedExternal = resolveTeamExternalIds(manifest);
|
|
128
|
+
const docs = await loadResolvedDocuments(canonicalPath, manifest.docs);
|
|
144
129
|
const candidate = {
|
|
145
|
-
|
|
130
|
+
description: manifest.description ? normalizeDescription(manifest.description) : "",
|
|
131
|
+
docs,
|
|
132
|
+
external: resolvedExternal,
|
|
133
|
+
externalExplicit: manifest.external !== undefined,
|
|
146
134
|
kind: "team",
|
|
135
|
+
lead: manifest.lead ?? null,
|
|
147
136
|
members: [],
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
137
|
+
mode: manifest.mode,
|
|
138
|
+
name: manifest.name,
|
|
139
|
+
networks: resolveTeamNetworks(manifest),
|
|
140
|
+
policyMode: manifest.policy?.mode ?? null,
|
|
141
|
+
policyOnDegrade: manifest.policy?.on_degrade ?? null,
|
|
151
142
|
shared: {
|
|
152
|
-
env:
|
|
153
|
-
mcpServers:
|
|
154
|
-
secrets:
|
|
143
|
+
env: manifest.shared?.env ?? {},
|
|
144
|
+
mcpServers: manifest.shared?.mcp_servers ?? [],
|
|
145
|
+
secrets: manifest.shared?.secrets ?? [],
|
|
155
146
|
skills: sharedSkills
|
|
156
147
|
},
|
|
157
148
|
source: canonicalPath,
|
|
158
|
-
structure: {
|
|
159
|
-
external: resolvedExternal,
|
|
160
|
-
leader: structure.leader ?? null,
|
|
161
|
-
mode: structure.mode
|
|
162
|
-
}
|
|
163
149
|
};
|
|
164
150
|
const fingerprint = getTeamFingerprint(candidate);
|
|
165
151
|
const existingFingerprint = fingerprintCache.get(canonicalPath);
|
|
@@ -195,6 +181,12 @@ export const buildCompilePlan = async (inputPath) => {
|
|
|
195
181
|
nodeSource: resolvedAgent.source,
|
|
196
182
|
runtimeName: resolvedAgent.runtime.name
|
|
197
183
|
};
|
|
184
|
+
memberships.set(`${canonicalPath}::${member.id}::${resolvedAgent.source}`, {
|
|
185
|
+
agentSource: resolvedAgent.source,
|
|
186
|
+
memberId: member.id,
|
|
187
|
+
teamName: candidate.name,
|
|
188
|
+
teamSource: canonicalPath
|
|
189
|
+
});
|
|
198
190
|
}
|
|
199
191
|
else {
|
|
200
192
|
const resolvedTeam = await visitTeam(childManifestPath);
|
|
@@ -213,6 +205,7 @@ export const buildCompilePlan = async (inputPath) => {
|
|
|
213
205
|
to: resolvedMember.nodeSource
|
|
214
206
|
});
|
|
215
207
|
}
|
|
208
|
+
validateTeamNetworkRooms(candidate);
|
|
216
209
|
visitStack.pop();
|
|
217
210
|
return candidate;
|
|
218
211
|
};
|
|
@@ -249,10 +242,13 @@ export const buildCompilePlan = async (inputPath) => {
|
|
|
249
242
|
groups[node.runtimeName] = group;
|
|
250
243
|
return groups;
|
|
251
244
|
}, {});
|
|
252
|
-
|
|
245
|
+
const compilePlan = {
|
|
253
246
|
edges: compilePlanEdges,
|
|
247
|
+
memberships: [...memberships.values()].sort((left, right) => `${left.agentSource}:${left.teamSource}:${left.memberId}`.localeCompare(`${right.agentSource}:${right.teamSource}:${right.memberId}`)),
|
|
254
248
|
nodes: compilePlanNodes,
|
|
255
249
|
root: rootManifestPath,
|
|
256
250
|
runtimes
|
|
257
251
|
};
|
|
252
|
+
resolvePlanMoltnetAttachments(compilePlan);
|
|
253
|
+
return compilePlan;
|
|
258
254
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type AgentManifest, type ExecutionBlock, type SharedSurface } from "../manifest/index.js";
|
|
2
|
+
import type { ResolvedDocument, ResolvedRuntime } from "./types.js";
|
|
3
|
+
export interface AgentVisitContext {
|
|
4
|
+
inheritedExecution?: ExecutionBlock;
|
|
5
|
+
inheritedShared?: {
|
|
6
|
+
manifestPath: string;
|
|
7
|
+
surface: SharedSurface | undefined;
|
|
8
|
+
};
|
|
9
|
+
inheritedRuntime?: ResolvedRuntime;
|
|
10
|
+
isSubagent: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const resolveRuntime: (manifest: AgentManifest, context: AgentVisitContext) => Promise<ResolvedRuntime>;
|
|
13
|
+
export declare const normalizeDescription: (description: string) => string;
|
|
14
|
+
export declare const resolveDescription: (description: string | undefined, docs: ResolvedDocument[]) => string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { normalizeRuntimeBinding } from "../manifest/index.js";
|
|
2
|
+
import { assertRuntimeCanCompile } from "../runtime/index.js";
|
|
3
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
4
|
+
export const resolveRuntime = async (manifest, context) => {
|
|
5
|
+
const localRuntime = normalizeRuntimeBinding(manifest.runtime);
|
|
6
|
+
if (context.isSubagent) {
|
|
7
|
+
if (!context.inheritedRuntime) {
|
|
8
|
+
throw new SpawnfileError("runtime_error", `Subagent ${manifest.name} is missing inherited runtime context`);
|
|
9
|
+
}
|
|
10
|
+
if (localRuntime &&
|
|
11
|
+
localRuntime.name !== context.inheritedRuntime.name) {
|
|
12
|
+
throw new SpawnfileError("runtime_error", `Subagent ${manifest.name} must match parent runtime`);
|
|
13
|
+
}
|
|
14
|
+
return context.inheritedRuntime;
|
|
15
|
+
}
|
|
16
|
+
if (!localRuntime) {
|
|
17
|
+
throw new SpawnfileError("runtime_error", `Agent ${manifest.name} does not declare a runtime`);
|
|
18
|
+
}
|
|
19
|
+
await assertRuntimeCanCompile(localRuntime.name);
|
|
20
|
+
return localRuntime;
|
|
21
|
+
};
|
|
22
|
+
export const normalizeDescription = (description) => description.replace(/\s+/g, " ").trim();
|
|
23
|
+
const deriveDescriptionFromDocs = (docs) => {
|
|
24
|
+
const identity = docs.find((doc) => doc.role === "identity")?.content;
|
|
25
|
+
if (!identity) {
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
const paragraph = identity
|
|
29
|
+
.split(/\n\s*\n/)
|
|
30
|
+
.map((block) => normalizeDescription(block))
|
|
31
|
+
.find((block) => block.length > 0 && !/^#{1,6}\s+/.test(block));
|
|
32
|
+
if (!paragraph) {
|
|
33
|
+
return "";
|
|
34
|
+
}
|
|
35
|
+
return paragraph.length > 200 ? paragraph.slice(0, 200).trimEnd() : paragraph;
|
|
36
|
+
};
|
|
37
|
+
export const resolveDescription = (description, docs) => description !== undefined
|
|
38
|
+
? normalizeDescription(description)
|
|
39
|
+
: deriveDescriptionFromDocs(docs);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TeamManifest } from "../manifest/index.js";
|
|
2
|
+
import type { ResolvedTeamNetwork, ResolvedTeamNode } from "./types.js";
|
|
3
|
+
export declare const resolveTeamExternalIds: (manifest: TeamManifest) => string[];
|
|
4
|
+
export declare const resolveTeamNetworks: (manifest: TeamManifest) => ResolvedTeamNetwork[];
|
|
5
|
+
export declare const validateTeamNetworkRooms: (teamNode: ResolvedTeamNode) => void;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
2
|
+
export const resolveTeamExternalIds = (manifest) => {
|
|
3
|
+
const memberIds = manifest.members.map((member) => member.id);
|
|
4
|
+
if (manifest.lead && !memberIds.includes(manifest.lead)) {
|
|
5
|
+
throw new SpawnfileError("validation_error", `Team ${manifest.name} lead references unknown member ${manifest.lead}`);
|
|
6
|
+
}
|
|
7
|
+
for (const externalMemberId of manifest.external ?? []) {
|
|
8
|
+
if (!memberIds.includes(externalMemberId)) {
|
|
9
|
+
throw new SpawnfileError("validation_error", `Team ${manifest.name} external representative references unknown member ${externalMemberId}`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return manifest.external
|
|
13
|
+
?? (manifest.mode === "hierarchical" && manifest.lead
|
|
14
|
+
? [manifest.lead]
|
|
15
|
+
: memberIds);
|
|
16
|
+
};
|
|
17
|
+
export const resolveTeamNetworks = (manifest) => (manifest.networks ?? []).map((network) => ({
|
|
18
|
+
expose: network.expose ?? false,
|
|
19
|
+
id: network.id,
|
|
20
|
+
name: network.name ?? network.id,
|
|
21
|
+
provider: network.provider,
|
|
22
|
+
rooms: network.rooms.map((room) => ({
|
|
23
|
+
id: room.id,
|
|
24
|
+
members: [...room.members]
|
|
25
|
+
}))
|
|
26
|
+
}));
|
|
27
|
+
export const validateTeamNetworkRooms = (teamNode) => {
|
|
28
|
+
for (const network of teamNode.networks ?? []) {
|
|
29
|
+
for (const room of network.rooms) {
|
|
30
|
+
for (const roomMemberId of room.members) {
|
|
31
|
+
const resolvedMember = teamNode.members.find((member) => member.id === roomMemberId);
|
|
32
|
+
if (!resolvedMember) {
|
|
33
|
+
throw new SpawnfileError("validation_error", `Team ${teamNode.name} Moltnet room ${room.id} references unknown member ${roomMemberId}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
@@ -25,7 +25,10 @@ export const getAgentFingerprint = (node) => stableStringify({
|
|
|
25
25
|
});
|
|
26
26
|
export const getTeamFingerprint = (node) => stableStringify({
|
|
27
27
|
members: node.members,
|
|
28
|
-
|
|
28
|
+
mode: node.mode,
|
|
29
|
+
lead: node.lead,
|
|
30
|
+
external: node.external,
|
|
31
|
+
networks: node.networks ?? [],
|
|
29
32
|
shared: {
|
|
30
33
|
env: node.shared.env,
|
|
31
34
|
mcpServers: node.shared.mcpServers,
|