spawnfile 0.1.1 → 0.1.3
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 -396
- package/dist/cli/index.js +0 -0
- package/dist/cli/modelCommands.d.ts +3 -0
- package/dist/cli/modelCommands.js +68 -0
- package/dist/cli/runCli.d.ts +23 -2
- package/dist/cli/runCli.js +78 -122
- 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/cli/viewCommand.d.ts +3 -0
- package/dist/cli/viewCommand.js +87 -0
- package/dist/compiler/agentSurfaces.js +51 -5
- package/dist/compiler/buildCompilePlan.js +38 -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 +4 -0
- package/dist/compiler/index.js +4 -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 +208 -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 +182 -0
- package/dist/compiler/moltnetRoomMemberships.d.ts +3 -0
- package/dist/compiler/moltnetRoomMemberships.js +140 -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/teamContextTypes.js +1 -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/teamRosterTypes.js +1 -0
- package/dist/compiler/types.d.ts +90 -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/compiler/view/buildOrganizationView.d.ts +2 -0
- package/dist/compiler/view/buildOrganizationView.js +180 -0
- package/dist/compiler/view/index.d.ts +4 -0
- package/dist/compiler/view/index.js +4 -0
- package/dist/compiler/view/renderNetworks.d.ts +2 -0
- package/dist/compiler/view/renderNetworks.js +93 -0
- package/dist/compiler/view/renderTree.d.ts +2 -0
- package/dist/compiler/view/renderTree.js +59 -0
- package/dist/compiler/view/sourcePaths.d.ts +2 -0
- package/dist/compiler/view/sourcePaths.js +19 -0
- package/dist/compiler/view/types.d.ts +80 -0
- package/dist/compiler/view/types.js +1 -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 +5 -3
- package/runtimes.yaml +4 -4
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { SpawnfileError } from "../../shared/index.js";
|
|
3
|
+
import { buildCompilePlan } from "../buildCompilePlan.js";
|
|
4
|
+
import { resolveMoltnetRoomMemberships } from "../moltnetRoomMemberships.js";
|
|
5
|
+
const createNameCounts = (plan) => {
|
|
6
|
+
const counts = new Map();
|
|
7
|
+
for (const node of plan.nodes) {
|
|
8
|
+
const key = `${node.kind}:${node.value.name}`;
|
|
9
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
10
|
+
}
|
|
11
|
+
return counts;
|
|
12
|
+
};
|
|
13
|
+
const formatDisplayName = (node, nameCounts) => {
|
|
14
|
+
const key = `${node.kind}:${node.value.name}`;
|
|
15
|
+
return (nameCounts.get(key) ?? 0) > 1
|
|
16
|
+
? `${node.value.name} [${node.id}]`
|
|
17
|
+
: node.value.name;
|
|
18
|
+
};
|
|
19
|
+
const groupEdgesBySource = (edges) => {
|
|
20
|
+
const groups = new Map();
|
|
21
|
+
for (const edge of edges) {
|
|
22
|
+
const group = groups.get(edge.from) ?? [];
|
|
23
|
+
group.push(edge);
|
|
24
|
+
groups.set(edge.from, group);
|
|
25
|
+
}
|
|
26
|
+
return groups;
|
|
27
|
+
};
|
|
28
|
+
const buildTreeNetworkSummaries = (node) => {
|
|
29
|
+
if (node.value.kind !== "team") {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return (node.value.networks ?? []).map((network) => ({
|
|
33
|
+
expose: network.expose ?? false,
|
|
34
|
+
id: network.id,
|
|
35
|
+
name: network.name,
|
|
36
|
+
provider: network.provider,
|
|
37
|
+
rooms: network.rooms.map((room) => ({
|
|
38
|
+
declaredMembers: [...room.members],
|
|
39
|
+
id: room.id
|
|
40
|
+
}))
|
|
41
|
+
}));
|
|
42
|
+
};
|
|
43
|
+
const buildTreeNode = (node, nodeById, edgesBySource, nameCounts, ancestors = []) => {
|
|
44
|
+
if (ancestors.includes(node.id)) {
|
|
45
|
+
throw new SpawnfileError("compile_error", `Cycle detected while building view tree for ${node.id}`);
|
|
46
|
+
}
|
|
47
|
+
const children = (edgesBySource.get(node.id) ?? []).map((edge) => {
|
|
48
|
+
const child = nodeById.get(edge.to);
|
|
49
|
+
if (!child) {
|
|
50
|
+
throw new SpawnfileError("compile_error", `Unable to find view node ${edge.to}`);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
label: edge.label,
|
|
54
|
+
node: buildTreeNode(child, nodeById, edgesBySource, nameCounts, [...ancestors, node.id]),
|
|
55
|
+
relation: edge.kind
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
children,
|
|
60
|
+
displayName: formatDisplayName(node, nameCounts),
|
|
61
|
+
...(node.value.kind === "team"
|
|
62
|
+
? {
|
|
63
|
+
external: [...node.value.external],
|
|
64
|
+
lead: node.value.lead,
|
|
65
|
+
mode: node.value.mode
|
|
66
|
+
}
|
|
67
|
+
: {}),
|
|
68
|
+
id: node.id,
|
|
69
|
+
kind: node.kind,
|
|
70
|
+
name: node.value.name,
|
|
71
|
+
networks: buildTreeNetworkSummaries(node),
|
|
72
|
+
runtimeName: node.runtimeName,
|
|
73
|
+
source: node.value.source
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
const sortNetworkMembers = (declaredMembers, members) => {
|
|
77
|
+
const declaredOrder = new Map(declaredMembers.map((member, index) => [member, index]));
|
|
78
|
+
return [...members].sort((left, right) => (declaredOrder.get(left.declaredSlot) ?? Number.MAX_SAFE_INTEGER)
|
|
79
|
+
- (declaredOrder.get(right.declaredSlot) ?? Number.MAX_SAFE_INTEGER)
|
|
80
|
+
|| (left.representativePath ?? []).join("/").localeCompare((right.representativePath ?? []).join("/"))
|
|
81
|
+
|| left.concreteMemberId.localeCompare(right.concreteMemberId));
|
|
82
|
+
};
|
|
83
|
+
const toNetworkMemberView = (membership) => ({
|
|
84
|
+
agentName: membership.agentName,
|
|
85
|
+
agentSource: membership.agentSource,
|
|
86
|
+
concreteMemberId: membership.concreteMemberId,
|
|
87
|
+
declaredSlot: membership.declaredSlot,
|
|
88
|
+
directTeamName: membership.directTeamName,
|
|
89
|
+
directTeamSource: membership.directTeamSource,
|
|
90
|
+
...(membership.policy ? { policy: { ...membership.policy } } : {}),
|
|
91
|
+
...(membership.representedSlot ? { representedSlot: membership.representedSlot } : {}),
|
|
92
|
+
...(membership.representedTeamName
|
|
93
|
+
? { representedTeamName: membership.representedTeamName }
|
|
94
|
+
: {}),
|
|
95
|
+
...(membership.representedTeamSource
|
|
96
|
+
? { representedTeamSource: membership.representedTeamSource }
|
|
97
|
+
: {}),
|
|
98
|
+
...(membership.representativePath
|
|
99
|
+
? { representativePath: [...membership.representativePath] }
|
|
100
|
+
: {})
|
|
101
|
+
});
|
|
102
|
+
const createNetworkKey = (provider, networkId) => `${provider}::${networkId}`;
|
|
103
|
+
const buildNetworkDeclaration = (teamNode, network, roomMemberships) => ({
|
|
104
|
+
declaringTeamName: teamNode.name,
|
|
105
|
+
declaringTeamSource: teamNode.source,
|
|
106
|
+
expose: network.expose ?? false,
|
|
107
|
+
name: network.name,
|
|
108
|
+
rooms: network.rooms.map((room) => {
|
|
109
|
+
const members = roomMemberships
|
|
110
|
+
.filter((membership) => membership.declaringTeamSource === teamNode.source
|
|
111
|
+
&& membership.networkId === network.id
|
|
112
|
+
&& membership.roomId === room.id)
|
|
113
|
+
.map(toNetworkMemberView);
|
|
114
|
+
return {
|
|
115
|
+
declaredMembers: [...room.members],
|
|
116
|
+
id: room.id,
|
|
117
|
+
members: sortNetworkMembers(room.members, members)
|
|
118
|
+
};
|
|
119
|
+
})
|
|
120
|
+
});
|
|
121
|
+
const buildNetworks = (plan) => {
|
|
122
|
+
const roomMemberships = plan.moltnetRoomMemberships
|
|
123
|
+
?? resolveMoltnetRoomMemberships(plan);
|
|
124
|
+
const groups = new Map();
|
|
125
|
+
for (const node of plan.nodes) {
|
|
126
|
+
if (node.value.kind !== "team") {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const teamNode = node.value;
|
|
130
|
+
for (const network of teamNode.networks ?? []) {
|
|
131
|
+
const key = createNetworkKey(network.provider, network.id);
|
|
132
|
+
const declaration = buildNetworkDeclaration(teamNode, network, roomMemberships);
|
|
133
|
+
const existing = groups.get(key);
|
|
134
|
+
if (existing) {
|
|
135
|
+
existing.declarations = [...(existing.declarations ?? []), declaration];
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
groups.set(key, {
|
|
139
|
+
declaringTeamName: declaration.declaringTeamName,
|
|
140
|
+
declaringTeamSource: declaration.declaringTeamSource,
|
|
141
|
+
declarations: [declaration],
|
|
142
|
+
expose: declaration.expose,
|
|
143
|
+
id: network.id,
|
|
144
|
+
name: declaration.name,
|
|
145
|
+
provider: network.provider,
|
|
146
|
+
rooms: declaration.rooms
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const networks = [...groups.values()];
|
|
151
|
+
for (const network of networks) {
|
|
152
|
+
const firstDeclaration = network.declarations?.[0];
|
|
153
|
+
if (firstDeclaration) {
|
|
154
|
+
network.declaringTeamName = firstDeclaration.declaringTeamName;
|
|
155
|
+
network.declaringTeamSource = firstDeclaration.declaringTeamSource;
|
|
156
|
+
network.expose = firstDeclaration.expose;
|
|
157
|
+
network.name = firstDeclaration.name;
|
|
158
|
+
network.rooms = firstDeclaration.rooms;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return networks;
|
|
162
|
+
};
|
|
163
|
+
export const buildOrganizationView = async (inputPath) => {
|
|
164
|
+
const plan = await buildCompilePlan(inputPath);
|
|
165
|
+
const rootNode = plan.nodes.find((node) => node.value.source === plan.root);
|
|
166
|
+
if (!rootNode) {
|
|
167
|
+
throw new SpawnfileError("compile_error", `Unable to find root view node for ${plan.root}`);
|
|
168
|
+
}
|
|
169
|
+
const nodeById = new Map(plan.nodes.map((node) => [node.id, node]));
|
|
170
|
+
const root = buildTreeNode(rootNode, nodeById, groupEdgesBySource(plan.edges), createNameCounts(plan));
|
|
171
|
+
return {
|
|
172
|
+
contexts: [],
|
|
173
|
+
diagnostics: [],
|
|
174
|
+
inputPath,
|
|
175
|
+
networks: buildNetworks(plan),
|
|
176
|
+
projectRoot: path.dirname(plan.root),
|
|
177
|
+
root,
|
|
178
|
+
runtimes: []
|
|
179
|
+
};
|
|
180
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { formatSourceMeta } from "./sourcePaths.js";
|
|
2
|
+
const color = (value, code, options) => options.color ? `\u001b[${code}m${value}\u001b[0m` : value;
|
|
3
|
+
const glyphsFor = (options) => options.ascii
|
|
4
|
+
? { branch: "|-- ", last: "`-- ", pipe: "| ", space: " " }
|
|
5
|
+
: { branch: "├── ", last: "└── ", pipe: "│ ", space: " " };
|
|
6
|
+
const formatPolicy = (member) => {
|
|
7
|
+
if (!member.policy) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
return [
|
|
11
|
+
member.policy.read ? `read=${member.policy.read}` : undefined,
|
|
12
|
+
member.policy.reply ? `reply=${member.policy.reply}` : undefined
|
|
13
|
+
].filter((entry) => entry !== undefined);
|
|
14
|
+
};
|
|
15
|
+
const formatMember = (member, options, projectRoot) => {
|
|
16
|
+
const metadata = member.representedSlot
|
|
17
|
+
? [
|
|
18
|
+
`represents=${member.representedSlot}`,
|
|
19
|
+
member.representedTeamName && member.representedTeamName !== member.representedSlot
|
|
20
|
+
? `team=${member.representedTeamName}`
|
|
21
|
+
: undefined,
|
|
22
|
+
`member=${member.concreteMemberId}`
|
|
23
|
+
]
|
|
24
|
+
: [
|
|
25
|
+
`team=${member.directTeamName}`,
|
|
26
|
+
`member=${member.concreteMemberId}`
|
|
27
|
+
];
|
|
28
|
+
const source = options.paths
|
|
29
|
+
? formatSourceMeta("source", member.agentSource, projectRoot)
|
|
30
|
+
: "";
|
|
31
|
+
const details = [
|
|
32
|
+
...metadata,
|
|
33
|
+
...formatPolicy(member)
|
|
34
|
+
].filter((entry) => entry !== undefined);
|
|
35
|
+
return `${member.agentName} ${details.join(" ")}${source}`;
|
|
36
|
+
};
|
|
37
|
+
const formatNetwork = (network, options) => {
|
|
38
|
+
const id = color(network.id, "36", options);
|
|
39
|
+
return `${network.provider} ${id}`;
|
|
40
|
+
};
|
|
41
|
+
const getDeclarations = (network) => network.declarations ?? [
|
|
42
|
+
{
|
|
43
|
+
declaringTeamName: network.declaringTeamName,
|
|
44
|
+
declaringTeamSource: network.declaringTeamSource,
|
|
45
|
+
expose: network.expose,
|
|
46
|
+
name: network.name,
|
|
47
|
+
rooms: network.rooms
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
const formatDeclaration = (network, declaration, options, projectRoot) => {
|
|
51
|
+
const exposed = declaration.expose ? " exposed" : "";
|
|
52
|
+
const source = options.paths
|
|
53
|
+
? formatSourceMeta("declared_source", declaration.declaringTeamSource, projectRoot)
|
|
54
|
+
: "";
|
|
55
|
+
return `${network.id} "${declaration.name}" on ${declaration.declaringTeamName}${exposed}${source}`;
|
|
56
|
+
};
|
|
57
|
+
export const renderOrganizationNetworks = (view, options = {}) => {
|
|
58
|
+
if (view.networks.length === 0) {
|
|
59
|
+
return "No Moltnet networks.";
|
|
60
|
+
}
|
|
61
|
+
const glyphs = glyphsFor(options);
|
|
62
|
+
const lines = ["Moltnet networks"];
|
|
63
|
+
view.networks.forEach((network, networkIndex) => {
|
|
64
|
+
const networkLast = networkIndex === view.networks.length - 1;
|
|
65
|
+
const networkPrefix = networkLast ? glyphs.space : glyphs.pipe;
|
|
66
|
+
lines.push(`${networkLast ? glyphs.last : glyphs.branch}${formatNetwork(network, options)}`);
|
|
67
|
+
const declarations = getDeclarations(network);
|
|
68
|
+
declarations.forEach((declaration, declarationIndex) => {
|
|
69
|
+
const declarationLast = declarationIndex === declarations.length - 1;
|
|
70
|
+
const declarationPrefix = `${networkPrefix}${declarationLast ? glyphs.space : glyphs.pipe}`;
|
|
71
|
+
lines.push(`${networkPrefix}${declarationLast ? glyphs.last : glyphs.branch}${formatDeclaration(network, declaration, options, view.projectRoot)}`);
|
|
72
|
+
declaration.rooms.forEach((room, roomIndex) => {
|
|
73
|
+
const roomLast = roomIndex === declaration.rooms.length - 1;
|
|
74
|
+
const roomPrefix = `${declarationPrefix}${roomLast ? glyphs.space : glyphs.pipe}`;
|
|
75
|
+
const legacyDeclared = options.declared && !view.projectRoot
|
|
76
|
+
? ` room ${room.id} declared [${room.declaredMembers.join(", ")}]`
|
|
77
|
+
: "";
|
|
78
|
+
lines.push(`${declarationPrefix}${roomLast ? glyphs.last : glyphs.branch}#${room.id}${legacyDeclared}`);
|
|
79
|
+
if (options.declared) {
|
|
80
|
+
const declaredMembers = room.declaredMembers.length > 0
|
|
81
|
+
? room.declaredMembers.join(", ")
|
|
82
|
+
: "(none)";
|
|
83
|
+
lines.push(`${roomPrefix}${glyphs.branch}declared members: ${declaredMembers}`);
|
|
84
|
+
}
|
|
85
|
+
room.members.forEach((member, memberIndex) => {
|
|
86
|
+
const memberLast = memberIndex === room.members.length - 1;
|
|
87
|
+
lines.push(`${roomPrefix}${memberLast ? glyphs.last : glyphs.branch}${formatMember(member, options, view.projectRoot)}`);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
return lines.join("\n");
|
|
93
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { formatSourceMeta } from "./sourcePaths.js";
|
|
2
|
+
const color = (value, code, options) => options.color ? `\u001b[${code}m${value}\u001b[0m` : value;
|
|
3
|
+
const formatNode = (node, options, projectRoot) => {
|
|
4
|
+
const kind = color(node.kind, node.kind === "team" ? "36" : "32", options);
|
|
5
|
+
const metadata = node.kind === "team"
|
|
6
|
+
? [
|
|
7
|
+
node.mode ? `mode=${node.mode}` : undefined,
|
|
8
|
+
node.lead ? `lead=${node.lead}` : undefined,
|
|
9
|
+
node.external && node.external.length > 0
|
|
10
|
+
? `external=${node.external.join(",")}`
|
|
11
|
+
: undefined
|
|
12
|
+
]
|
|
13
|
+
: [
|
|
14
|
+
node.runtimeName ? `[${node.runtimeName}]` : undefined,
|
|
15
|
+
node.runtimeName ? `runtime=${node.runtimeName}` : undefined
|
|
16
|
+
];
|
|
17
|
+
const details = metadata.filter((entry) => entry !== undefined);
|
|
18
|
+
const source = options.paths ? formatSourceMeta("source", node.source, projectRoot) : "";
|
|
19
|
+
const separator = details[0]?.startsWith("[") ? " " : " ";
|
|
20
|
+
return `${kind} ${node.displayName}${details.length > 0 ? `${separator}${details.join(" ")}` : ""}${source}`;
|
|
21
|
+
};
|
|
22
|
+
const formatEdgeLabel = (edge) => edge.relation === "subagent"
|
|
23
|
+
? `subagent ${edge.label}`
|
|
24
|
+
: edge.label;
|
|
25
|
+
const formatNetworkSummary = (network, options) => {
|
|
26
|
+
const id = color(network.id, "36", options);
|
|
27
|
+
const exposed = network.expose ? " exposed" : "";
|
|
28
|
+
const rooms = network.rooms
|
|
29
|
+
.map((room) => `${room.id} [${room.declaredMembers.join(", ")}]`)
|
|
30
|
+
.join("; ");
|
|
31
|
+
return `network ${id} "${network.name}"${exposed}: ${rooms}`;
|
|
32
|
+
};
|
|
33
|
+
const renderChildren = (node, options, projectRoot, prefix = "") => {
|
|
34
|
+
const glyphs = options.ascii
|
|
35
|
+
? { branch: "|-- ", last: "`-- ", pipe: "| ", space: " " }
|
|
36
|
+
: { branch: "├── ", last: "└── ", pipe: "│ ", space: " " };
|
|
37
|
+
const items = [
|
|
38
|
+
...(node.networks ?? []).map((network) => ({ kind: "network", network })),
|
|
39
|
+
...node.children.map((edge) => ({ kind: "edge", edge }))
|
|
40
|
+
];
|
|
41
|
+
return items.flatMap((item, index) => {
|
|
42
|
+
const isLast = index === items.length - 1;
|
|
43
|
+
const connector = isLast ? glyphs.last : glyphs.branch;
|
|
44
|
+
const nextPrefix = `${prefix}${isLast ? glyphs.space : glyphs.pipe}`;
|
|
45
|
+
if (item.kind === "network") {
|
|
46
|
+
return [`${prefix}${connector}${formatNetworkSummary(item.network, options)}`];
|
|
47
|
+
}
|
|
48
|
+
const edge = item.edge;
|
|
49
|
+
const line = `${prefix}${connector}${formatEdgeLabel(edge)}: ${formatNode(edge.node, options, projectRoot)}`;
|
|
50
|
+
return [
|
|
51
|
+
line,
|
|
52
|
+
...renderChildren(edge.node, options, projectRoot, nextPrefix)
|
|
53
|
+
];
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
export const renderOrganizationTree = (view, options = {}) => [
|
|
57
|
+
formatNode(view.root, options, view.projectRoot),
|
|
58
|
+
...renderChildren(view.root, options, view.projectRoot)
|
|
59
|
+
].join("\n");
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export const formatSourcePath = (sourcePath, projectRoot) => {
|
|
3
|
+
if (!projectRoot || !path.isAbsolute(sourcePath) || !path.isAbsolute(projectRoot)) {
|
|
4
|
+
return sourcePath;
|
|
5
|
+
}
|
|
6
|
+
const relativePath = path.relative(projectRoot, sourcePath);
|
|
7
|
+
if (relativePath === ""
|
|
8
|
+
|| relativePath.startsWith("..")
|
|
9
|
+
|| path.isAbsolute(relativePath)) {
|
|
10
|
+
return sourcePath;
|
|
11
|
+
}
|
|
12
|
+
return relativePath.split(path.sep).join("/");
|
|
13
|
+
};
|
|
14
|
+
export const formatSourceMeta = (label, sourcePath, projectRoot) => {
|
|
15
|
+
const formattedPath = formatSourcePath(sourcePath, projectRoot);
|
|
16
|
+
return projectRoot
|
|
17
|
+
? ` ${label}=${formattedPath}`
|
|
18
|
+
: ` <${formattedPath}>`;
|
|
19
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ResolvedMoltnetRoomPolicy } from "../types.js";
|
|
2
|
+
export interface OrganizationViewTreeNode {
|
|
3
|
+
children: OrganizationViewTreeEdge[];
|
|
4
|
+
displayName: string;
|
|
5
|
+
external?: string[];
|
|
6
|
+
id: string;
|
|
7
|
+
kind: "agent" | "team";
|
|
8
|
+
lead?: string | null;
|
|
9
|
+
mode?: "hierarchical" | "swarm";
|
|
10
|
+
name: string;
|
|
11
|
+
networks?: OrganizationTreeNetworkSummary[];
|
|
12
|
+
runtimeName: string | null;
|
|
13
|
+
source: string;
|
|
14
|
+
}
|
|
15
|
+
export interface OrganizationTreeNetworkRoomSummary {
|
|
16
|
+
declaredMembers: string[];
|
|
17
|
+
id: string;
|
|
18
|
+
}
|
|
19
|
+
export interface OrganizationTreeNetworkSummary {
|
|
20
|
+
expose: boolean;
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
provider: "moltnet";
|
|
24
|
+
rooms: OrganizationTreeNetworkRoomSummary[];
|
|
25
|
+
}
|
|
26
|
+
export interface OrganizationViewTreeEdge {
|
|
27
|
+
label: string;
|
|
28
|
+
node: OrganizationViewTreeNode;
|
|
29
|
+
relation: "subagent" | "team_member";
|
|
30
|
+
}
|
|
31
|
+
export interface OrganizationNetworkMemberView {
|
|
32
|
+
agentName: string;
|
|
33
|
+
agentSource: string;
|
|
34
|
+
concreteMemberId: string;
|
|
35
|
+
declaredSlot: string;
|
|
36
|
+
directTeamName: string;
|
|
37
|
+
directTeamSource: string;
|
|
38
|
+
policy?: ResolvedMoltnetRoomPolicy;
|
|
39
|
+
representedSlot?: string;
|
|
40
|
+
representedTeamName?: string;
|
|
41
|
+
representedTeamSource?: string;
|
|
42
|
+
representativePath?: string[];
|
|
43
|
+
}
|
|
44
|
+
export interface OrganizationNetworkDeclarationView {
|
|
45
|
+
declaringTeamName: string;
|
|
46
|
+
declaringTeamSource: string;
|
|
47
|
+
expose: boolean;
|
|
48
|
+
name: string;
|
|
49
|
+
rooms: OrganizationNetworkRoomView[];
|
|
50
|
+
}
|
|
51
|
+
export interface OrganizationNetworkRoomView {
|
|
52
|
+
declaredMembers: string[];
|
|
53
|
+
id: string;
|
|
54
|
+
members: OrganizationNetworkMemberView[];
|
|
55
|
+
}
|
|
56
|
+
export interface OrganizationNetworkView {
|
|
57
|
+
declaringTeamName: string;
|
|
58
|
+
declaringTeamSource: string;
|
|
59
|
+
expose: boolean;
|
|
60
|
+
id: string;
|
|
61
|
+
name: string;
|
|
62
|
+
provider: "moltnet";
|
|
63
|
+
rooms: OrganizationNetworkRoomView[];
|
|
64
|
+
declarations?: OrganizationNetworkDeclarationView[];
|
|
65
|
+
}
|
|
66
|
+
export interface OrganizationView {
|
|
67
|
+
contexts: [];
|
|
68
|
+
diagnostics: [];
|
|
69
|
+
inputPath: string;
|
|
70
|
+
networks: OrganizationNetworkView[];
|
|
71
|
+
projectRoot?: string;
|
|
72
|
+
root: OrganizationViewTreeNode;
|
|
73
|
+
runtimes: [];
|
|
74
|
+
}
|
|
75
|
+
export interface RenderOrganizationViewOptions {
|
|
76
|
+
ascii?: boolean;
|
|
77
|
+
color?: boolean;
|
|
78
|
+
declared?: boolean;
|
|
79
|
+
paths?: boolean;
|
|
80
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -102,12 +102,12 @@ const validateLocalTeamManifest = async (manifestPath, manifest) => {
|
|
|
102
102
|
await validateManifestRefs(manifestPath, manifest.members.map((member) => member.ref), "Member");
|
|
103
103
|
validateSkillRequirements(manifest.shared?.skills, getSharedMcpNames(manifest.shared));
|
|
104
104
|
const memberIds = new Set(manifest.members.map((member) => member.id));
|
|
105
|
-
if (manifest.
|
|
106
|
-
throw new SpawnfileError("validation_error", `
|
|
105
|
+
if (manifest.lead && !memberIds.has(manifest.lead)) {
|
|
106
|
+
throw new SpawnfileError("validation_error", `Lead is not a declared team member: ${manifest.lead}`);
|
|
107
107
|
}
|
|
108
|
-
for (const id of manifest.
|
|
108
|
+
for (const id of manifest.external ?? []) {
|
|
109
109
|
if (!memberIds.has(id)) {
|
|
110
|
-
throw new SpawnfileError("validation_error", `
|
|
110
|
+
throw new SpawnfileError("validation_error", `External references undeclared member: ${id}`);
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
};
|
|
@@ -29,7 +29,8 @@ const orderDiscordSurface = (surface) => {
|
|
|
29
29
|
}
|
|
30
30
|
return withDefinedEntries([
|
|
31
31
|
["access", orderDiscordSurfaceAccess(surface.access)],
|
|
32
|
-
["bot_token_secret", surface.bot_token_secret]
|
|
32
|
+
["bot_token_secret", surface.bot_token_secret],
|
|
33
|
+
["identity", surface.identity]
|
|
33
34
|
]);
|
|
34
35
|
};
|
|
35
36
|
const orderDiscordSurfaceAccess = (access) => {
|
|
@@ -49,7 +50,8 @@ const orderTelegramSurface = (surface) => {
|
|
|
49
50
|
}
|
|
50
51
|
return withDefinedEntries([
|
|
51
52
|
["access", orderTelegramSurfaceAccess(surface.access)],
|
|
52
|
-
["bot_token_secret", surface.bot_token_secret]
|
|
53
|
+
["bot_token_secret", surface.bot_token_secret],
|
|
54
|
+
["identity", surface.identity]
|
|
53
55
|
]);
|
|
54
56
|
};
|
|
55
57
|
const orderTelegramSurfaceAccess = (access) => {
|
|
@@ -62,11 +64,55 @@ const orderTelegramSurfaceAccess = (access) => {
|
|
|
62
64
|
["chats", access.chats]
|
|
63
65
|
]);
|
|
64
66
|
};
|
|
67
|
+
const orderWebhookSurface = (surface) => {
|
|
68
|
+
if (!surface) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
return withDefinedEntries([
|
|
72
|
+
["url", surface.url],
|
|
73
|
+
["signing_secret", surface.signing_secret]
|
|
74
|
+
]);
|
|
75
|
+
};
|
|
76
|
+
const orderMoltnetRoomBehavior = (behavior) => {
|
|
77
|
+
if (!behavior) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
return withDefinedEntries([
|
|
81
|
+
["read", behavior.read],
|
|
82
|
+
["reply", behavior.reply]
|
|
83
|
+
]);
|
|
84
|
+
};
|
|
85
|
+
const orderMoltnetDm = (dms) => {
|
|
86
|
+
if (!dms) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
return withDefinedEntries([
|
|
90
|
+
["enabled", dms.enabled],
|
|
91
|
+
["read", dms.read],
|
|
92
|
+
["reply", dms.reply]
|
|
93
|
+
]);
|
|
94
|
+
};
|
|
95
|
+
const orderMoltnetAttachment = (attachment) => withDefinedEntries([
|
|
96
|
+
["network", attachment.network],
|
|
97
|
+
[
|
|
98
|
+
"rooms",
|
|
99
|
+
attachment.rooms
|
|
100
|
+
? Object.fromEntries(Object.entries(attachment.rooms)
|
|
101
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
102
|
+
.map(([roomId, behavior]) => [roomId, orderMoltnetRoomBehavior(behavior)]))
|
|
103
|
+
: undefined
|
|
104
|
+
],
|
|
105
|
+
["dms", orderMoltnetDm(attachment.dms)]
|
|
106
|
+
]);
|
|
107
|
+
const orderMoltnetSurface = (surface) => surface?.map(orderMoltnetAttachment);
|
|
65
108
|
const orderWhatsAppSurface = (surface) => {
|
|
66
109
|
if (!surface) {
|
|
67
110
|
return undefined;
|
|
68
111
|
}
|
|
69
|
-
return withDefinedEntries([
|
|
112
|
+
return withDefinedEntries([
|
|
113
|
+
["access", orderWhatsAppSurfaceAccess(surface.access)],
|
|
114
|
+
["identity", surface.identity]
|
|
115
|
+
]);
|
|
70
116
|
};
|
|
71
117
|
const orderWhatsAppSurfaceAccess = (access) => {
|
|
72
118
|
if (!access) {
|
|
@@ -85,7 +131,8 @@ const orderSlackSurface = (surface) => {
|
|
|
85
131
|
return withDefinedEntries([
|
|
86
132
|
["access", orderSlackSurfaceAccess(surface.access)],
|
|
87
133
|
["bot_token_secret", surface.bot_token_secret],
|
|
88
|
-
["app_token_secret", surface.app_token_secret]
|
|
134
|
+
["app_token_secret", surface.app_token_secret],
|
|
135
|
+
["identity", surface.identity]
|
|
89
136
|
]);
|
|
90
137
|
};
|
|
91
138
|
const orderSlackSurfaceAccess = (access) => {
|
|
@@ -156,9 +203,24 @@ const orderSurfaces = (surfaces) => {
|
|
|
156
203
|
["discord", orderDiscordSurface(surfaces.discord)],
|
|
157
204
|
["telegram", orderTelegramSurface(surfaces.telegram)],
|
|
158
205
|
["whatsapp", orderWhatsAppSurface(surfaces.whatsapp)],
|
|
159
|
-
["slack", orderSlackSurface(surfaces.slack)]
|
|
206
|
+
["slack", orderSlackSurface(surfaces.slack)],
|
|
207
|
+
["webhook", orderWebhookSurface(surfaces.webhook)],
|
|
208
|
+
["moltnet", orderMoltnetSurface(surfaces.moltnet)]
|
|
160
209
|
]);
|
|
161
210
|
};
|
|
211
|
+
const orderTeamNetworks = (networks) => networks?.map((network) => withDefinedEntries([
|
|
212
|
+
["id", network.id],
|
|
213
|
+
["provider", network.provider],
|
|
214
|
+
["name", network.name],
|
|
215
|
+
["expose", network.expose],
|
|
216
|
+
[
|
|
217
|
+
"rooms",
|
|
218
|
+
network.rooms.map((room) => withDefinedEntries([
|
|
219
|
+
["id", room.id],
|
|
220
|
+
["members", room.members]
|
|
221
|
+
]))
|
|
222
|
+
]
|
|
223
|
+
]));
|
|
162
224
|
const renderSections = (sections) => sections
|
|
163
225
|
.filter(hasEntries)
|
|
164
226
|
.map((section) => YAML.stringify(section))
|
|
@@ -167,7 +229,8 @@ const orderAgentManifestSections = (manifest) => [
|
|
|
167
229
|
withDefinedEntries([
|
|
168
230
|
["spawnfile_version", manifest.spawnfile_version],
|
|
169
231
|
["kind", manifest.kind],
|
|
170
|
-
["name", manifest.name]
|
|
232
|
+
["name", manifest.name],
|
|
233
|
+
["expose", manifest.expose]
|
|
171
234
|
]),
|
|
172
235
|
withDefinedEntries([["runtime", orderRuntimeBinding(manifest.runtime)]]),
|
|
173
236
|
withDefinedEntries([["execution", orderExecution(manifest.execution)]]),
|
|
@@ -192,7 +255,8 @@ const orderTeamManifestSections = (manifest) => [
|
|
|
192
255
|
withDefinedEntries([["execution", orderExecution(manifest.execution)]]),
|
|
193
256
|
withDefinedEntries([
|
|
194
257
|
["docs", orderDocs(manifest.docs)],
|
|
195
|
-
["shared", orderSharedSurface(manifest.shared)]
|
|
258
|
+
["shared", orderSharedSurface(manifest.shared)],
|
|
259
|
+
["networks", orderTeamNetworks(manifest.networks)]
|
|
196
260
|
]),
|
|
197
261
|
withDefinedEntries([
|
|
198
262
|
["skills", manifest.skills],
|
|
@@ -203,7 +267,9 @@ const orderTeamManifestSections = (manifest) => [
|
|
|
203
267
|
]),
|
|
204
268
|
withDefinedEntries([
|
|
205
269
|
["members", manifest.members],
|
|
206
|
-
["
|
|
270
|
+
["mode", manifest.mode],
|
|
271
|
+
["lead", manifest.lead],
|
|
272
|
+
["external", manifest.external]
|
|
207
273
|
])
|
|
208
274
|
];
|
|
209
275
|
export const renderSpawnfile = (manifest) => renderSections(manifest.kind === "agent"
|