spawnfile 0.1.2 → 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 +1 -0
- package/dist/cli/index.js +0 -0
- package/dist/cli/runCli.d.ts +18 -2
- package/dist/cli/runCli.js +72 -61
- package/dist/cli/viewCommand.d.ts +3 -0
- package/dist/cli/viewCommand.js +87 -0
- package/dist/compiler/buildCompilePlan.js +2 -0
- package/dist/compiler/index.d.ts +2 -0
- package/dist/compiler/index.js +2 -0
- package/dist/compiler/moltnetArtifacts.js +7 -3
- package/dist/compiler/moltnetResolution.js +29 -48
- package/dist/compiler/moltnetRoomMemberships.d.ts +3 -0
- package/dist/compiler/moltnetRoomMemberships.js +140 -0
- package/dist/compiler/types.d.ts +18 -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/package.json +2 -2
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ Node.js 22+ required. See [source install](#from-source) for local development.
|
|
|
34
34
|
```bash
|
|
35
35
|
spawnfile init # scaffold an agent (defaults to openclaw)
|
|
36
36
|
spawnfile validate # check the graph
|
|
37
|
+
spawnfile view . # read-only graph view; writes no files
|
|
37
38
|
spawnfile compile # lower to runtime-native output
|
|
38
39
|
spawnfile auth sync --profile dev --env-file .env
|
|
39
40
|
spawnfile build --tag my-agent # compile + docker build
|
package/dist/cli/index.js
CHANGED
|
File without changes
|
package/dist/cli/runCli.d.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { importClaudeCodeAuth, importCodexAuth, importEnvFile, requireAuthProfile } from "../auth/index.js";
|
|
2
|
-
import { addAgentProject, addProjectSurface, addProjectModelFallback, addSubagentProject, addTeamProject, buildCompilePlan, buildProject, clearProjectModelFallbacks, compileProject, initProject, removeProjectSurface, runProject, setProjectPrimaryModel, setProjectRuntime, setProjectSurfaceAccess, showProjectSurfaces, syncProjectAuth } from "../compiler/index.js";
|
|
2
|
+
import { addAgentProject, addProjectSurface, addProjectModelFallback, addSubagentProject, addTeamProject, buildOrganizationView, buildCompilePlan, buildProject, clearProjectModelFallbacks, compileProject, initProject, removeProjectSurface, runProject, setProjectPrimaryModel, setProjectRuntime, setProjectSurfaceAccess, showProjectSurfaces, syncProjectAuth } from "../compiler/index.js";
|
|
3
3
|
import { listRuntimeAdapters } from "../runtime/index.js";
|
|
4
4
|
export interface CliStreams {
|
|
5
5
|
stderr: (message: string) => void;
|
|
6
6
|
stdout: (message: string) => void;
|
|
7
7
|
}
|
|
8
|
+
export interface CliRenderEnvironment {
|
|
9
|
+
ci: boolean;
|
|
10
|
+
noColor: boolean;
|
|
11
|
+
stdoutIsTty: boolean;
|
|
12
|
+
}
|
|
8
13
|
export interface CliHandlers {
|
|
9
14
|
buildCompilePlan: typeof buildCompilePlan;
|
|
15
|
+
buildOrganizationView: typeof buildOrganizationView;
|
|
10
16
|
buildProject: typeof buildProject;
|
|
11
17
|
compileProject: typeof compileProject;
|
|
12
18
|
addAgentProject: typeof addAgentProject;
|
|
@@ -29,4 +35,14 @@ export interface CliHandlers {
|
|
|
29
35
|
showProjectSurfaces: typeof showProjectSurfaces;
|
|
30
36
|
syncProjectAuth: typeof syncProjectAuth;
|
|
31
37
|
}
|
|
32
|
-
export
|
|
38
|
+
export interface RunCliOptions {
|
|
39
|
+
handlers?: Partial<CliHandlers>;
|
|
40
|
+
renderEnvironment?: CliRenderEnvironment;
|
|
41
|
+
streams?: CliStreams;
|
|
42
|
+
}
|
|
43
|
+
type RunCli = {
|
|
44
|
+
(argv: string[], options?: RunCliOptions): Promise<number>;
|
|
45
|
+
(argv: string[], streams?: CliStreams, handlerOverrides?: Partial<CliHandlers>): Promise<number>;
|
|
46
|
+
};
|
|
47
|
+
export declare const runCli: RunCli;
|
|
48
|
+
export {};
|
package/dist/cli/runCli.js
CHANGED
|
@@ -1,39 +1,60 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { importClaudeCodeAuth, importCodexAuth, importEnvFile, requireAuthProfile } from "../auth/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";
|
|
3
|
+
import { addAgentProject, addProjectSurface, addProjectModelFallback, addSubagentProject, addTeamProject, buildOrganizationView, 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
6
|
import { registerModelCommands } from "./modelCommands.js";
|
|
7
7
|
import { registerRuntimeCommands } from "./runtimeCommands.js";
|
|
8
8
|
import { registerSurfaceCommands } from "./surfaceCommands.js";
|
|
9
|
+
import { registerViewCommand } from "./viewCommand.js";
|
|
9
10
|
const createDefaultStreams = () => ({
|
|
10
11
|
stderr: (message) => process.stderr.write(`${message}\n`),
|
|
11
12
|
stdout: (message) => process.stdout.write(`${message}\n`)
|
|
12
13
|
});
|
|
14
|
+
const createDefaultRenderEnvironment = () => ({
|
|
15
|
+
ci: process.env.CI !== undefined && process.env.CI !== "" && process.env.CI !== "0",
|
|
16
|
+
noColor: process.env.NO_COLOR !== undefined && process.env.NO_COLOR !== "",
|
|
17
|
+
stdoutIsTty: process.stdout.isTTY === true
|
|
18
|
+
});
|
|
13
19
|
const createDefaultHandlers = () => ({
|
|
14
|
-
buildCompilePlan,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
addTeamProject,
|
|
22
|
-
clearProjectModelFallbacks,
|
|
23
|
-
importClaudeCodeAuth,
|
|
24
|
-
importCodexAuth,
|
|
25
|
-
importEnvFile,
|
|
26
|
-
initProject,
|
|
27
|
-
listRuntimeAdapters,
|
|
28
|
-
removeProjectSurface,
|
|
29
|
-
requireAuthProfile,
|
|
30
|
-
runProject,
|
|
31
|
-
setProjectPrimaryModel,
|
|
32
|
-
setProjectRuntime,
|
|
33
|
-
setProjectSurfaceAccess,
|
|
34
|
-
showProjectSurfaces,
|
|
35
|
-
syncProjectAuth
|
|
20
|
+
buildCompilePlan, buildOrganizationView, buildProject, compileProject,
|
|
21
|
+
addAgentProject, addProjectModelFallback, addProjectSurface,
|
|
22
|
+
addSubagentProject, addTeamProject, clearProjectModelFallbacks,
|
|
23
|
+
importClaudeCodeAuth, importCodexAuth, importEnvFile,
|
|
24
|
+
initProject, listRuntimeAdapters, removeProjectSurface, requireAuthProfile,
|
|
25
|
+
runProject, setProjectPrimaryModel, setProjectRuntime,
|
|
26
|
+
setProjectSurfaceAccess, showProjectSurfaces, syncProjectAuth
|
|
36
27
|
});
|
|
28
|
+
const isCliStreams = (value) => {
|
|
29
|
+
const candidate = value;
|
|
30
|
+
return typeof candidate?.stderr === "function" && typeof candidate.stdout === "function";
|
|
31
|
+
};
|
|
32
|
+
const normalizeRunCliOptions = (optionsOrStreams, handlerOverrides = {}) => isCliStreams(optionsOrStreams)
|
|
33
|
+
? {
|
|
34
|
+
handlers: handlerOverrides,
|
|
35
|
+
renderEnvironment: createDefaultRenderEnvironment(),
|
|
36
|
+
streams: optionsOrStreams
|
|
37
|
+
}
|
|
38
|
+
: {
|
|
39
|
+
handlers: optionsOrStreams?.handlers ?? handlerOverrides,
|
|
40
|
+
renderEnvironment: optionsOrStreams?.renderEnvironment ?? createDefaultRenderEnvironment(),
|
|
41
|
+
streams: optionsOrStreams?.streams ?? createDefaultStreams()
|
|
42
|
+
};
|
|
43
|
+
const writeCommanderOutput = (write, message) => {
|
|
44
|
+
const normalized = message.replace(/\n$/, "");
|
|
45
|
+
if (normalized.length > 0) {
|
|
46
|
+
write(normalized);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const isCommanderError = (error) => {
|
|
50
|
+
if (typeof error !== "object" || error === null) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const candidate = error;
|
|
54
|
+
return typeof candidate.code === "string"
|
|
55
|
+
&& candidate.code.startsWith("commander.")
|
|
56
|
+
&& typeof candidate.exitCode === "number";
|
|
57
|
+
};
|
|
37
58
|
const formatPlanSummary = (plan) => [
|
|
38
59
|
`root: ${plan.root}`,
|
|
39
60
|
`nodes: ${plan.nodes.length}`,
|
|
@@ -48,10 +69,20 @@ const formatAuthProfileSummary = (profile) => {
|
|
|
48
69
|
`imports: ${importedKinds.length > 0 ? importedKinds.join(", ") : "none"}`
|
|
49
70
|
];
|
|
50
71
|
};
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
const emitLines = (streams, lines) => lines.forEach((line) => streams.stdout(line));
|
|
73
|
+
const emitFileLines = (streams, label, filePaths) => emitLines(streams, filePaths.map((filePath) => `${label} ${filePath}`));
|
|
74
|
+
export const runCli = async (argv, optionsOrStreams, handlerOverrides = {}) => {
|
|
75
|
+
const cliOptions = normalizeRunCliOptions(optionsOrStreams, handlerOverrides);
|
|
76
|
+
const streams = cliOptions.streams;
|
|
77
|
+
const handlers = { ...createDefaultHandlers(), ...cliOptions.handlers };
|
|
53
78
|
const program = new Command();
|
|
54
79
|
program.name("spawnfile").description("Spawnfile v0.1 compiler");
|
|
80
|
+
program.exitOverride();
|
|
81
|
+
program.configureOutput({
|
|
82
|
+
outputError: (message, write) => write(message),
|
|
83
|
+
writeErr: (message) => writeCommanderOutput(streams.stderr, message),
|
|
84
|
+
writeOut: (message) => writeCommanderOutput(streams.stdout, message)
|
|
85
|
+
});
|
|
55
86
|
program
|
|
56
87
|
.command("compile")
|
|
57
88
|
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
@@ -108,9 +139,7 @@ export const runCli = async (argv, streams = createDefaultStreams(), handlerOver
|
|
|
108
139
|
team: options.team
|
|
109
140
|
});
|
|
110
141
|
streams.stdout(`initialized ${result.directory}`);
|
|
111
|
-
|
|
112
|
-
streams.stdout(`created ${filePath}`);
|
|
113
|
-
}
|
|
142
|
+
emitFileLines(streams, "created", result.createdFiles);
|
|
114
143
|
});
|
|
115
144
|
const addCommand = program.command("add").description("Add children to an existing Spawnfile project");
|
|
116
145
|
addCommand
|
|
@@ -124,12 +153,8 @@ export const runCli = async (argv, streams = createDefaultStreams(), handlerOver
|
|
|
124
153
|
path: inputPath,
|
|
125
154
|
runtime: options.runtime
|
|
126
155
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
for (const filePath of result.createdFiles) {
|
|
131
|
-
streams.stdout(`created ${filePath}`);
|
|
132
|
-
}
|
|
156
|
+
emitFileLines(streams, "updated", result.updatedFiles);
|
|
157
|
+
emitFileLines(streams, "created", result.createdFiles);
|
|
133
158
|
});
|
|
134
159
|
addCommand
|
|
135
160
|
.command("subagent")
|
|
@@ -140,12 +165,8 @@ export const runCli = async (argv, streams = createDefaultStreams(), handlerOver
|
|
|
140
165
|
id,
|
|
141
166
|
path: inputPath
|
|
142
167
|
});
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
for (const filePath of result.createdFiles) {
|
|
147
|
-
streams.stdout(`created ${filePath}`);
|
|
148
|
-
}
|
|
168
|
+
emitFileLines(streams, "updated", result.updatedFiles);
|
|
169
|
+
emitFileLines(streams, "created", result.createdFiles);
|
|
149
170
|
});
|
|
150
171
|
addCommand
|
|
151
172
|
.command("team")
|
|
@@ -156,16 +177,13 @@ export const runCli = async (argv, streams = createDefaultStreams(), handlerOver
|
|
|
156
177
|
id,
|
|
157
178
|
path: inputPath
|
|
158
179
|
});
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
for (const filePath of result.createdFiles) {
|
|
163
|
-
streams.stdout(`created ${filePath}`);
|
|
164
|
-
}
|
|
180
|
+
emitFileLines(streams, "updated", result.updatedFiles);
|
|
181
|
+
emitFileLines(streams, "created", result.createdFiles);
|
|
165
182
|
});
|
|
166
183
|
registerModelCommands(program, handlers, streams);
|
|
167
184
|
registerRuntimeCommands(program, handlers, streams);
|
|
168
185
|
registerSurfaceCommands(program, handlers, streams);
|
|
186
|
+
registerViewCommand(program, handlers, streams, cliOptions.renderEnvironment);
|
|
169
187
|
program
|
|
170
188
|
.command("validate")
|
|
171
189
|
.argument("[path]", "Project directory or Spawnfile path", process.cwd())
|
|
@@ -192,9 +210,7 @@ export const runCli = async (argv, streams = createDefaultStreams(), handlerOver
|
|
|
192
210
|
.option("-p, --profile <name>", "Auth profile name", "default")
|
|
193
211
|
.action(async (filePath, options) => {
|
|
194
212
|
const profile = await handlers.importEnvFile(options.profile, filePath);
|
|
195
|
-
|
|
196
|
-
streams.stdout(line);
|
|
197
|
-
}
|
|
213
|
+
emitLines(streams, formatAuthProfileSummary(profile));
|
|
198
214
|
});
|
|
199
215
|
authImportCommand
|
|
200
216
|
.command("claude-code")
|
|
@@ -202,9 +218,7 @@ export const runCli = async (argv, streams = createDefaultStreams(), handlerOver
|
|
|
202
218
|
.option("--from <directory>", "Source Claude Code config directory")
|
|
203
219
|
.action(async (options) => {
|
|
204
220
|
const profile = await handlers.importClaudeCodeAuth(options.profile, options.from);
|
|
205
|
-
|
|
206
|
-
streams.stdout(line);
|
|
207
|
-
}
|
|
221
|
+
emitLines(streams, formatAuthProfileSummary(profile));
|
|
208
222
|
});
|
|
209
223
|
authImportCommand
|
|
210
224
|
.command("codex")
|
|
@@ -212,9 +226,7 @@ export const runCli = async (argv, streams = createDefaultStreams(), handlerOver
|
|
|
212
226
|
.option("--from <directory>", "Source Codex config directory")
|
|
213
227
|
.action(async (options) => {
|
|
214
228
|
const profile = await handlers.importCodexAuth(options.profile, options.from);
|
|
215
|
-
|
|
216
|
-
streams.stdout(line);
|
|
217
|
-
}
|
|
229
|
+
emitLines(streams, formatAuthProfileSummary(profile));
|
|
218
230
|
});
|
|
219
231
|
authCommand
|
|
220
232
|
.command("sync")
|
|
@@ -230,24 +242,23 @@ export const runCli = async (argv, streams = createDefaultStreams(), handlerOver
|
|
|
230
242
|
envFilePath: options.envFile,
|
|
231
243
|
profileName: options.profile
|
|
232
244
|
});
|
|
233
|
-
|
|
234
|
-
streams.stdout(line);
|
|
235
|
-
}
|
|
245
|
+
emitLines(streams, formatAuthProfileSummary(profile));
|
|
236
246
|
});
|
|
237
247
|
authCommand
|
|
238
248
|
.command("show")
|
|
239
249
|
.option("-p, --profile <name>", "Auth profile name", "default")
|
|
240
250
|
.action(async (options) => {
|
|
241
251
|
const profile = await handlers.requireAuthProfile(options.profile);
|
|
242
|
-
|
|
243
|
-
streams.stdout(line);
|
|
244
|
-
}
|
|
252
|
+
emitLines(streams, formatAuthProfileSummary(profile));
|
|
245
253
|
});
|
|
246
254
|
try {
|
|
247
255
|
await program.parseAsync(argv, { from: "user" });
|
|
248
256
|
return 0;
|
|
249
257
|
}
|
|
250
258
|
catch (error) {
|
|
259
|
+
if (isCommanderError(error)) {
|
|
260
|
+
return error.exitCode === 0 ? 0 : 1;
|
|
261
|
+
}
|
|
251
262
|
const message = isSpawnfileError(error)
|
|
252
263
|
? `${error.code}: ${error.message}`
|
|
253
264
|
: error instanceof Error
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import type { CliHandlers, CliRenderEnvironment, CliStreams } from "./runCli.js";
|
|
3
|
+
export declare const registerViewCommand: (program: Command, handlers: CliHandlers, streams: CliStreams, renderEnvironment: CliRenderEnvironment) => void;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Option } from "commander";
|
|
4
|
+
import { renderOrganizationNetworks, renderOrganizationTree } from "../compiler/index.js";
|
|
5
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
6
|
+
const VIEW_MODES = ["tree", "networks"];
|
|
7
|
+
const VIEW_SHOW_OPTIONS = ["paths", "declared"];
|
|
8
|
+
const VIEW_COLOR_OPTIONS = ["auto", "always", "never"];
|
|
9
|
+
const isViewMode = (value) => VIEW_MODES.includes(value);
|
|
10
|
+
const isViewShow = (value) => VIEW_SHOW_OPTIONS.includes(value);
|
|
11
|
+
const manifestPathFor = (inputPath) => path.basename(inputPath) === "Spawnfile"
|
|
12
|
+
? path.resolve(inputPath)
|
|
13
|
+
: path.resolve(inputPath, "Spawnfile");
|
|
14
|
+
const hasResolvedSpawnfile = async (inputPath) => {
|
|
15
|
+
try {
|
|
16
|
+
return (await stat(manifestPathFor(inputPath))).isFile();
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const suggestModeOptionForPathToken = async (inputPath) => {
|
|
23
|
+
if (!inputPath || !isViewMode(inputPath) || await hasResolvedSpawnfile(inputPath)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
throw new SpawnfileError("validation_error", `No Spawnfile found for path "${inputPath}". Did you mean "spawnfile view --mode ${inputPath}"?`);
|
|
27
|
+
};
|
|
28
|
+
const resolveColor = (color, environment) => {
|
|
29
|
+
if (color === "always") {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (color === "never") {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return environment.stdoutIsTty && !environment.ci && !environment.noColor;
|
|
36
|
+
};
|
|
37
|
+
const parseShowLayers = (value) => {
|
|
38
|
+
const layers = new Set();
|
|
39
|
+
if (!value) {
|
|
40
|
+
return layers;
|
|
41
|
+
}
|
|
42
|
+
for (const layer of value.split(",")) {
|
|
43
|
+
const normalized = layer.trim();
|
|
44
|
+
if (!isViewShow(normalized)) {
|
|
45
|
+
throw new SpawnfileError("validation_error", `Unsupported view detail layer "${normalized}". Supported layers: paths, declared.`);
|
|
46
|
+
}
|
|
47
|
+
layers.add(normalized);
|
|
48
|
+
}
|
|
49
|
+
return layers;
|
|
50
|
+
};
|
|
51
|
+
const toRenderOptions = (options, environment) => {
|
|
52
|
+
const showLayers = parseShowLayers(options.show);
|
|
53
|
+
if (options.paths) {
|
|
54
|
+
showLayers.add("paths");
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
ascii: options.ascii,
|
|
58
|
+
color: resolveColor(options.color, environment),
|
|
59
|
+
declared: showLayers.has("declared"),
|
|
60
|
+
paths: showLayers.has("paths")
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const emitRenderedView = (streams, output) => {
|
|
64
|
+
for (const line of output.split("\n")) {
|
|
65
|
+
streams.stdout(line);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
export const registerViewCommand = (program, handlers, streams, renderEnvironment) => {
|
|
69
|
+
program
|
|
70
|
+
.command("view")
|
|
71
|
+
.description("Render a read-only organization view")
|
|
72
|
+
.argument("[path]", "Project directory or Spawnfile path")
|
|
73
|
+
.addOption(new Option("--mode <mode>", "View mode").choices(VIEW_MODES).default("tree"))
|
|
74
|
+
.option("--show <show>", "Comma-separated additional details")
|
|
75
|
+
.option("--ascii", "Use ASCII tree connectors")
|
|
76
|
+
.addOption(new Option("--color <when>", "Color output").choices(VIEW_COLOR_OPTIONS).default("auto"))
|
|
77
|
+
.option("--paths", "Show source paths")
|
|
78
|
+
.action(async (inputPath, options) => {
|
|
79
|
+
await suggestModeOptionForPathToken(inputPath);
|
|
80
|
+
const renderOptions = toRenderOptions(options, renderEnvironment);
|
|
81
|
+
const view = await handlers.buildOrganizationView(inputPath ?? process.cwd());
|
|
82
|
+
const output = options.mode === "networks"
|
|
83
|
+
? renderOrganizationNetworks(view, renderOptions)
|
|
84
|
+
: renderOrganizationTree(view, renderOptions);
|
|
85
|
+
emitRenderedView(streams, output);
|
|
86
|
+
});
|
|
87
|
+
};
|
|
@@ -9,6 +9,7 @@ import { assertRuntimeSupportsExecutionModelAuth } from "./modelAuth.js";
|
|
|
9
9
|
import { assertRuntimeSupportsAgentSurfaces } from "./surfaceSupport.js";
|
|
10
10
|
import { applyExecutionDefaults } from "./executionDefaults.js";
|
|
11
11
|
import { resolvePlanMoltnetAttachments } from "./moltnetResolution.js";
|
|
12
|
+
import { resolveMoltnetRoomMemberships } from "./moltnetRoomMemberships.js";
|
|
12
13
|
import { normalizeDescription, resolveDescription, resolveRuntime } from "./buildCompilePlanRuntime.js";
|
|
13
14
|
import { resolveTeamExternalIds, resolveTeamNetworks, validateTeamNetworkRooms } from "./buildCompilePlanTeams.js";
|
|
14
15
|
export const buildCompilePlan = async (inputPath) => {
|
|
@@ -249,6 +250,7 @@ export const buildCompilePlan = async (inputPath) => {
|
|
|
249
250
|
root: rootManifestPath,
|
|
250
251
|
runtimes
|
|
251
252
|
};
|
|
253
|
+
compilePlan.moltnetRoomMemberships = resolveMoltnetRoomMemberships(compilePlan);
|
|
252
254
|
resolvePlanMoltnetAttachments(compilePlan);
|
|
253
255
|
return compilePlan;
|
|
254
256
|
};
|
package/dist/compiler/index.d.ts
CHANGED
|
@@ -3,9 +3,11 @@ export * from "./buildCompilePlan.js";
|
|
|
3
3
|
export * from "./buildProject.js";
|
|
4
4
|
export * from "./compileProject.js";
|
|
5
5
|
export * from "./initProject.js";
|
|
6
|
+
export * from "./moltnetRoomMemberships.js";
|
|
6
7
|
export * from "./runProject.js";
|
|
7
8
|
export * from "./syncProjectAuth.js";
|
|
8
9
|
export * from "./types.js";
|
|
9
10
|
export * from "./updateProjectModels.js";
|
|
10
11
|
export * from "./updateProjectRuntime.js";
|
|
11
12
|
export * from "./updateProjectSurfaces.js";
|
|
13
|
+
export * from "./view/index.js";
|
package/dist/compiler/index.js
CHANGED
|
@@ -3,9 +3,11 @@ export * from "./buildCompilePlan.js";
|
|
|
3
3
|
export * from "./buildProject.js";
|
|
4
4
|
export * from "./compileProject.js";
|
|
5
5
|
export * from "./initProject.js";
|
|
6
|
+
export * from "./moltnetRoomMemberships.js";
|
|
6
7
|
export * from "./runProject.js";
|
|
7
8
|
export * from "./syncProjectAuth.js";
|
|
8
9
|
export * from "./types.js";
|
|
9
10
|
export * from "./updateProjectModels.js";
|
|
10
11
|
export * from "./updateProjectRuntime.js";
|
|
11
12
|
export * from "./updateProjectSurfaces.js";
|
|
13
|
+
export * from "./view/index.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getRuntimeAdapter } from "../runtime/index.js";
|
|
2
2
|
import { SpawnfileError } from "../shared/index.js";
|
|
3
|
+
import { listConcreteMoltnetRoomMemberIds } from "./moltnetRoomMemberships.js";
|
|
3
4
|
const DEFAULT_MOLTNET_PORT = 8787;
|
|
4
5
|
const DEFAULT_TINYCLAW_PORT = 3777;
|
|
5
6
|
const ROOTFS_PREFIX = "container/rootfs";
|
|
@@ -86,14 +87,17 @@ export const generateMoltnetArtifacts = async (plan) => {
|
|
|
86
87
|
const existingPlan = serverPlans.get(serverKey);
|
|
87
88
|
if (existingPlan) {
|
|
88
89
|
for (const room of network.rooms) {
|
|
90
|
+
const concreteMembers = listConcreteMoltnetRoomMemberIds(plan, teamNode.value, network.id, room);
|
|
89
91
|
const existingRoom = existingPlan.rooms.find((entry) => entry.id === room.id);
|
|
90
92
|
if (existingRoom) {
|
|
91
|
-
existingRoom.members = [
|
|
93
|
+
existingRoom.members = [
|
|
94
|
+
...new Set([...existingRoom.members, ...concreteMembers])
|
|
95
|
+
].sort();
|
|
92
96
|
}
|
|
93
97
|
else {
|
|
94
98
|
existingPlan.rooms.push({
|
|
95
99
|
id: room.id,
|
|
96
|
-
members:
|
|
100
|
+
members: concreteMembers
|
|
97
101
|
});
|
|
98
102
|
}
|
|
99
103
|
}
|
|
@@ -107,7 +111,7 @@ export const generateMoltnetArtifacts = async (plan) => {
|
|
|
107
111
|
port: nextPort,
|
|
108
112
|
rooms: network.rooms.map((room) => ({
|
|
109
113
|
id: room.id,
|
|
110
|
-
members:
|
|
114
|
+
members: listConcreteMoltnetRoomMemberIds(plan, teamNode.value, network.id, room)
|
|
111
115
|
})),
|
|
112
116
|
teamSource: teamNode.value.source
|
|
113
117
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SpawnfileError } from "../shared/index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveMoltnetRoomMemberships } from "./moltnetRoomMemberships.js";
|
|
3
|
+
import { resolveMoltnetAttachments } from "./moltnetRepresentativeResolution.js";
|
|
3
4
|
export { resolveMoltnetAttachments, resolveTeamRepresentatives } from "./moltnetRepresentativeResolution.js";
|
|
4
5
|
const findTeamBySource = (plan, source) => {
|
|
5
6
|
const node = plan.nodes.find((entry) => entry.kind === "team" && entry.value.source === source);
|
|
@@ -32,52 +33,26 @@ const validateGlobalMemberIds = (plan) => {
|
|
|
32
33
|
seen.set(context.memberId, label);
|
|
33
34
|
}
|
|
34
35
|
};
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
const teamNode = node.value;
|
|
42
|
-
for (const network of teamNode.networks ?? []) {
|
|
43
|
-
for (const room of network.rooms) {
|
|
44
|
-
const expandedMembers = [];
|
|
45
|
-
for (const roomMemberId of room.members) {
|
|
46
|
-
const member = teamNode.members.find((entry) => entry.id === roomMemberId);
|
|
47
|
-
if (!member) {
|
|
48
|
-
throw new SpawnfileError("validation_error", `Team ${teamNode.name} Moltnet room ${room.id} references unknown member ${roomMemberId}`);
|
|
49
|
-
}
|
|
50
|
-
if (member.kind === "agent") {
|
|
51
|
-
expandedMembers.push(member.id);
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
const childTeam = findTeamBySource(plan, member.nodeSource);
|
|
55
|
-
const representatives = resolveTeamRepresentatives(plan, childTeam);
|
|
56
|
-
if (representatives.length === 0) {
|
|
57
|
-
throw new SpawnfileError("validation_error", `Team ${childTeam.name} has no concrete representative for Moltnet room ${room.id} on ${teamNode.name}`);
|
|
58
|
-
}
|
|
59
|
-
for (const representative of representatives) {
|
|
60
|
-
expandedMembers.push(representative.memberId);
|
|
61
|
-
synthesizedAttachments.push({
|
|
62
|
-
contextRooms: {
|
|
63
|
-
[teamNode.source]: [room.id]
|
|
64
|
-
},
|
|
65
|
-
memberId: representative.memberId,
|
|
66
|
-
network: network.id,
|
|
67
|
-
rooms: {
|
|
68
|
-
[room.id]: {}
|
|
69
|
-
},
|
|
70
|
-
teamSource: teamNode.source
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
room.members = [...new Set(expandedMembers)];
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return synthesizedAttachments;
|
|
36
|
+
const getRoomMemberships = (plan) => {
|
|
37
|
+
const memberships = plan.moltnetRoomMemberships ?? resolveMoltnetRoomMemberships(plan);
|
|
38
|
+
plan.moltnetRoomMemberships = memberships;
|
|
39
|
+
return memberships;
|
|
79
40
|
};
|
|
41
|
+
const synthesizeRepresentativeAttachments = (memberships) => memberships
|
|
42
|
+
.filter((membership) => membership.representedSlot !== undefined)
|
|
43
|
+
.map((membership) => ({
|
|
44
|
+
contextRooms: {
|
|
45
|
+
[membership.declaringTeamSource]: [membership.roomId]
|
|
46
|
+
},
|
|
47
|
+
memberId: membership.concreteMemberId,
|
|
48
|
+
network: membership.networkId,
|
|
49
|
+
rooms: {
|
|
50
|
+
[membership.roomId]: {}
|
|
51
|
+
},
|
|
52
|
+
teamSource: membership.declaringTeamSource
|
|
53
|
+
}));
|
|
80
54
|
const roomPolicyKey = (policy) => JSON.stringify(policy ?? {});
|
|
55
|
+
const hasRoomPolicy = (policy) => policy.read !== undefined || policy.reply !== undefined;
|
|
81
56
|
const mergeAttachment = (target, next, nodeName) => {
|
|
82
57
|
if (target.dms &&
|
|
83
58
|
next.dms &&
|
|
@@ -89,11 +64,16 @@ const mergeAttachment = (target, next, nodeName) => {
|
|
|
89
64
|
target.rooms ??= {};
|
|
90
65
|
for (const [roomId, policy] of Object.entries(next.rooms ?? {})) {
|
|
91
66
|
const existingPolicy = target.rooms[roomId];
|
|
92
|
-
|
|
67
|
+
const existingHasPolicy = existingPolicy ? hasRoomPolicy(existingPolicy) : false;
|
|
68
|
+
const nextHasPolicy = hasRoomPolicy(policy);
|
|
69
|
+
if (existingHasPolicy &&
|
|
70
|
+
nextHasPolicy &&
|
|
93
71
|
roomPolicyKey(existingPolicy) !== roomPolicyKey(policy)) {
|
|
94
72
|
throw new SpawnfileError("validation_error", `Agent ${nodeName} declares incompatible Moltnet room policy for ${next.network}/${next.memberId ?? "unknown"} room ${roomId}`);
|
|
95
73
|
}
|
|
96
|
-
target.rooms[roomId] =
|
|
74
|
+
target.rooms[roomId] = existingPolicy && existingHasPolicy && !nextHasPolicy
|
|
75
|
+
? { ...existingPolicy }
|
|
76
|
+
: { ...policy };
|
|
97
77
|
}
|
|
98
78
|
if (next.contextRooms) {
|
|
99
79
|
target.contextRooms ??= {};
|
|
@@ -149,7 +129,8 @@ const mergeAgentAttachments = (agentNode, attachments) => {
|
|
|
149
129
|
};
|
|
150
130
|
export const resolvePlanMoltnetAttachments = (plan) => {
|
|
151
131
|
validateGlobalMemberIds(plan);
|
|
152
|
-
const
|
|
132
|
+
const roomMemberships = getRoomMemberships(plan);
|
|
133
|
+
const synthesizedAttachments = synthesizeRepresentativeAttachments(roomMemberships);
|
|
153
134
|
const synthesizedByAgent = new Map();
|
|
154
135
|
for (const attachment of synthesizedAttachments) {
|
|
155
136
|
const representativeContext = (plan.memberships ?? []).find((context) => context.memberId === attachment.memberId);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CompilePlan, ResolvedMoltnetRoomMembership, ResolvedTeamNetworkRoom, ResolvedTeamNode } from "./types.js";
|
|
2
|
+
export declare const listConcreteMoltnetRoomMemberIds: (plan: CompilePlan, teamNode: ResolvedTeamNode, networkId: string, room: ResolvedTeamNetworkRoom, memberships?: ResolvedMoltnetRoomMembership[] | undefined) => string[];
|
|
3
|
+
export declare const resolveMoltnetRoomMemberships: (plan: CompilePlan) => ResolvedMoltnetRoomMembership[];
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
2
|
+
import { resolveTeamRepresentatives } from "./moltnetRepresentativeResolution.js";
|
|
3
|
+
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
4
|
+
const findAgentBySource = (plan, source) => {
|
|
5
|
+
const node = plan.nodes.find((entry) => entry.kind === "agent" && entry.value.source === source);
|
|
6
|
+
if (!node || node.value.kind !== "agent") {
|
|
7
|
+
throw new SpawnfileError("compile_error", `Unable to find agent node at ${source}`);
|
|
8
|
+
}
|
|
9
|
+
return node.value;
|
|
10
|
+
};
|
|
11
|
+
const findTeamBySource = (plan, source) => {
|
|
12
|
+
const node = plan.nodes.find((entry) => entry.kind === "team" && entry.value.source === source);
|
|
13
|
+
if (!node || node.value.kind !== "team") {
|
|
14
|
+
throw new SpawnfileError("compile_error", `Unable to find team node at ${source}`);
|
|
15
|
+
}
|
|
16
|
+
return node.value;
|
|
17
|
+
};
|
|
18
|
+
const clonePolicy = (policy) => ({
|
|
19
|
+
...(policy.read ? { read: policy.read } : {}),
|
|
20
|
+
...(policy.reply ? { reply: policy.reply } : {})
|
|
21
|
+
});
|
|
22
|
+
const findDirectRoomPolicy = (plan, teamNode, agentSource, memberId, networkId, roomId) => {
|
|
23
|
+
const agentNode = findAgentBySource(plan, agentSource);
|
|
24
|
+
for (const attachment of agentNode.surfaces?.moltnet ?? []) {
|
|
25
|
+
if (attachment.network !== networkId) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (attachment.teamSource && attachment.teamSource !== teamNode.source) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (attachment.memberId && attachment.memberId !== memberId) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (!attachment.rooms || !hasOwn(attachment.rooms, roomId)) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
return clonePolicy(attachment.rooms[roomId] ?? {});
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
};
|
|
41
|
+
const compareRoomMemberships = (left, right) => [
|
|
42
|
+
left.declaringTeamSource.localeCompare(right.declaringTeamSource),
|
|
43
|
+
left.networkId.localeCompare(right.networkId),
|
|
44
|
+
left.roomId.localeCompare(right.roomId),
|
|
45
|
+
left.declaredSlot.localeCompare(right.declaredSlot),
|
|
46
|
+
(left.representativePath ?? []).join("/").localeCompare((right.representativePath ?? []).join("/")),
|
|
47
|
+
left.concreteMemberId.localeCompare(right.concreteMemberId),
|
|
48
|
+
left.agentSource.localeCompare(right.agentSource)
|
|
49
|
+
].find((result) => result !== 0) ?? 0;
|
|
50
|
+
export const listConcreteMoltnetRoomMemberIds = (plan, teamNode, networkId, room, memberships = plan.moltnetRoomMemberships) => {
|
|
51
|
+
if (memberships) {
|
|
52
|
+
return [
|
|
53
|
+
...new Set(memberships
|
|
54
|
+
.filter((membership) => membership.declaringTeamSource === teamNode.source
|
|
55
|
+
&& membership.networkId === networkId
|
|
56
|
+
&& membership.roomId === room.id)
|
|
57
|
+
.map((membership) => membership.concreteMemberId))
|
|
58
|
+
].sort();
|
|
59
|
+
}
|
|
60
|
+
const concreteMembers = [];
|
|
61
|
+
for (const declaredSlot of room.members) {
|
|
62
|
+
const member = teamNode.members.find((entry) => entry.id === declaredSlot);
|
|
63
|
+
if (!member) {
|
|
64
|
+
throw new SpawnfileError("validation_error", `Team ${teamNode.name} Moltnet room ${room.id} references unknown member ${declaredSlot}`);
|
|
65
|
+
}
|
|
66
|
+
if (member.kind === "agent") {
|
|
67
|
+
concreteMembers.push(member.id);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const childTeam = findTeamBySource(plan, member.nodeSource);
|
|
71
|
+
const representatives = resolveTeamRepresentatives(plan, childTeam);
|
|
72
|
+
if (representatives.length === 0) {
|
|
73
|
+
throw new SpawnfileError("validation_error", `Team ${childTeam.name} has no concrete representative for Moltnet room ${room.id} on ${teamNode.name}`);
|
|
74
|
+
}
|
|
75
|
+
concreteMembers.push(...representatives.map((representative) => representative.memberId));
|
|
76
|
+
}
|
|
77
|
+
return [...new Set(concreteMembers)].sort();
|
|
78
|
+
};
|
|
79
|
+
export const resolveMoltnetRoomMemberships = (plan) => {
|
|
80
|
+
const memberships = [];
|
|
81
|
+
for (const node of plan.nodes) {
|
|
82
|
+
if (node.value.kind !== "team") {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const teamNode = node.value;
|
|
86
|
+
for (const network of teamNode.networks ?? []) {
|
|
87
|
+
for (const room of network.rooms) {
|
|
88
|
+
for (const declaredSlot of room.members) {
|
|
89
|
+
const declaredMember = teamNode.members.find((member) => member.id === declaredSlot);
|
|
90
|
+
if (!declaredMember) {
|
|
91
|
+
throw new SpawnfileError("validation_error", `Team ${teamNode.name} Moltnet room ${room.id} references unknown member ${declaredSlot}`);
|
|
92
|
+
}
|
|
93
|
+
if (declaredMember.kind === "agent") {
|
|
94
|
+
const agentNode = findAgentBySource(plan, declaredMember.nodeSource);
|
|
95
|
+
const policy = findDirectRoomPolicy(plan, teamNode, agentNode.source, declaredSlot, network.id, room.id);
|
|
96
|
+
memberships.push({
|
|
97
|
+
agentName: agentNode.name,
|
|
98
|
+
agentSource: agentNode.source,
|
|
99
|
+
concreteMemberId: declaredSlot,
|
|
100
|
+
declaredSlot,
|
|
101
|
+
declaringTeamName: teamNode.name,
|
|
102
|
+
declaringTeamSource: teamNode.source,
|
|
103
|
+
directTeamName: teamNode.name,
|
|
104
|
+
directTeamSource: teamNode.source,
|
|
105
|
+
networkId: network.id,
|
|
106
|
+
...(policy ? { policy } : {}),
|
|
107
|
+
roomId: room.id
|
|
108
|
+
});
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const childTeam = findTeamBySource(plan, declaredMember.nodeSource);
|
|
112
|
+
const representatives = resolveTeamRepresentatives(plan, childTeam);
|
|
113
|
+
if (representatives.length === 0) {
|
|
114
|
+
throw new SpawnfileError("validation_error", `Team ${childTeam.name} has no concrete representative for Moltnet room ${room.id} on ${teamNode.name}`);
|
|
115
|
+
}
|
|
116
|
+
for (const representative of representatives) {
|
|
117
|
+
const agentNode = findAgentBySource(plan, representative.agentSource);
|
|
118
|
+
memberships.push({
|
|
119
|
+
agentName: agentNode.name,
|
|
120
|
+
agentSource: agentNode.source,
|
|
121
|
+
concreteMemberId: representative.memberId,
|
|
122
|
+
declaredSlot,
|
|
123
|
+
declaringTeamName: teamNode.name,
|
|
124
|
+
declaringTeamSource: teamNode.source,
|
|
125
|
+
directTeamName: representative.sourceTeamName,
|
|
126
|
+
directTeamSource: representative.sourceTeamSource,
|
|
127
|
+
networkId: network.id,
|
|
128
|
+
representedSlot: declaredSlot,
|
|
129
|
+
representedTeamName: childTeam.name,
|
|
130
|
+
representedTeamSource: childTeam.source,
|
|
131
|
+
representativePath: [declaredSlot, ...representative.path],
|
|
132
|
+
roomId: room.id
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return memberships.sort(compareRoomMemberships);
|
|
140
|
+
};
|
package/dist/compiler/types.d.ts
CHANGED
|
@@ -189,9 +189,27 @@ export interface ResolvedTeamMembershipContext {
|
|
|
189
189
|
teamName: string;
|
|
190
190
|
teamSource: string;
|
|
191
191
|
}
|
|
192
|
+
export interface ResolvedMoltnetRoomMembership {
|
|
193
|
+
agentName: string;
|
|
194
|
+
agentSource: string;
|
|
195
|
+
concreteMemberId: string;
|
|
196
|
+
declaredSlot: string;
|
|
197
|
+
declaringTeamName: string;
|
|
198
|
+
declaringTeamSource: string;
|
|
199
|
+
directTeamName: string;
|
|
200
|
+
directTeamSource: string;
|
|
201
|
+
networkId: string;
|
|
202
|
+
policy?: ResolvedMoltnetRoomPolicy;
|
|
203
|
+
representedSlot?: string;
|
|
204
|
+
representedTeamName?: string;
|
|
205
|
+
representedTeamSource?: string;
|
|
206
|
+
representativePath?: string[];
|
|
207
|
+
roomId: string;
|
|
208
|
+
}
|
|
192
209
|
export interface CompilePlan {
|
|
193
210
|
edges: CompilePlanEdge[];
|
|
194
211
|
memberships?: ResolvedTeamMembershipContext[];
|
|
212
|
+
moltnetRoomMemberships?: ResolvedMoltnetRoomMembership[];
|
|
195
213
|
nodes: CompilePlanNode[];
|
|
196
214
|
root: string;
|
|
197
215
|
runtimes: Record<string, {
|
|
@@ -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 {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spawnfile",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Canonical source compiler for autonomous agents and teams.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
18
|
"blueprints": "./scripts/blueprints.sh",
|
|
19
|
-
"build": "rm -rf dist && tsc --project tsconfig.build.json && node ./scripts/copy-runtime-scaffold-assets.mjs",
|
|
19
|
+
"build": "rm -rf dist && tsc --project tsconfig.build.json && chmod +x dist/cli/index.js && node ./scripts/copy-runtime-scaffold-assets.mjs",
|
|
20
20
|
"clean": "rm -rf coverage dist",
|
|
21
21
|
"coverage": "vitest run --coverage",
|
|
22
22
|
"dev": "tsx src/cli/index.ts",
|