spawnfile 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/cli/lifecycleCommands.d.ts +3 -0
  2. package/dist/cli/lifecycleCommands.js +80 -0
  3. package/dist/cli/runCli.d.ts +2 -1
  4. package/dist/cli/runCli.js +4 -48
  5. package/dist/compiler/buildCompilePlan.js +12 -202
  6. package/dist/compiler/buildCompilePlanRuntime.d.ts +6 -1
  7. package/dist/compiler/buildCompilePlanRuntime.js +9 -0
  8. package/dist/compiler/buildCompilePlanTeams.js +16 -10
  9. package/dist/compiler/buildCompilePlanTraversal.d.ts +16 -0
  10. package/dist/compiler/buildCompilePlanTraversal.js +214 -0
  11. package/dist/compiler/buildCompilePlanTraversalHelpers.d.ts +18 -0
  12. package/dist/compiler/buildCompilePlanTraversalHelpers.js +22 -0
  13. package/dist/compiler/compilePlanHelpers.d.ts +3 -1
  14. package/dist/compiler/compilePlanHelpers.js +37 -1
  15. package/dist/compiler/compileProject.js +14 -0
  16. package/dist/compiler/containerArtifacts.js +18 -3
  17. package/dist/compiler/containerArtifactsPlans.js +32 -0
  18. package/dist/compiler/containerArtifactsRender.js +86 -3
  19. package/dist/compiler/containerArtifactsTypes.d.ts +7 -3
  20. package/dist/compiler/containerEntrypointRender.d.ts +1 -1
  21. package/dist/compiler/containerEntrypointRender.js +34 -24
  22. package/dist/compiler/containerTargetResources.d.ts +4 -0
  23. package/dist/compiler/containerTargetResources.js +54 -0
  24. package/dist/compiler/containerWorkspaceResourceRender.d.ts +3 -0
  25. package/dist/compiler/containerWorkspaceResourceRender.js +128 -0
  26. package/dist/compiler/executionDefaults.js +0 -3
  27. package/dist/compiler/helpers.d.ts +1 -1
  28. package/dist/compiler/index.d.ts +1 -0
  29. package/dist/compiler/index.js +1 -0
  30. package/dist/compiler/moltnetArtifacts.d.ts +11 -5
  31. package/dist/compiler/moltnetArtifacts.js +133 -117
  32. package/dist/compiler/moltnetClientConfig.js +8 -2
  33. package/dist/compiler/moltnetConfigLowering.d.ts +36 -0
  34. package/dist/compiler/moltnetConfigLowering.js +125 -0
  35. package/dist/compiler/moltnetRuntimeConfig.d.ts +2 -0
  36. package/dist/compiler/moltnetRuntimeConfig.js +69 -0
  37. package/dist/compiler/surfaces.d.ts +3 -13
  38. package/dist/compiler/surfaces.js +1 -6
  39. package/dist/compiler/syncProjectAuth.js +14 -0
  40. package/dist/compiler/types.d.ts +16 -1
  41. package/dist/compiler/upProject.d.ts +19 -0
  42. package/dist/compiler/upProject.js +37 -0
  43. package/dist/compiler/view/buildOrganizationView.js +22 -2
  44. package/dist/compiler/view/renderNetworks.js +14 -3
  45. package/dist/compiler/view/renderTree.js +8 -2
  46. package/dist/compiler/view/types.d.ts +18 -3
  47. package/dist/compiler/workspaceResources.d.ts +34 -0
  48. package/dist/compiler/workspaceResources.js +120 -0
  49. package/dist/manifest/executionSchemas.d.ts +106 -0
  50. package/dist/manifest/executionSchemas.js +140 -0
  51. package/dist/manifest/loadManifest.js +15 -27
  52. package/dist/manifest/renderSpawnfile.js +44 -52
  53. package/dist/manifest/renderSpawnfileNetworks.d.ts +2 -0
  54. package/dist/manifest/renderSpawnfileNetworks.js +63 -0
  55. package/dist/manifest/renderSpawnfileWorkspace.d.ts +2 -0
  56. package/dist/manifest/renderSpawnfileWorkspace.js +47 -0
  57. package/dist/manifest/scaffold.js +12 -6
  58. package/dist/manifest/scheduleSchemas.d.ts +15 -0
  59. package/dist/manifest/scheduleSchemas.js +26 -0
  60. package/dist/manifest/schemas.d.ts +626 -368
  61. package/dist/manifest/schemas.js +51 -191
  62. package/dist/manifest/teamNetworkSchemas.d.ts +228 -0
  63. package/dist/manifest/teamNetworkSchemas.js +295 -0
  64. package/dist/manifest/workspaceSchemas.d.ts +96 -0
  65. package/dist/manifest/workspaceSchemas.js +166 -0
  66. package/dist/report/types.d.ts +10 -0
  67. package/dist/runtime/common.d.ts +2 -1
  68. package/dist/runtime/common.js +3 -3
  69. package/dist/runtime/tinyclaw/adapter.js +9 -2
  70. package/dist/runtime/tinyclaw/schedules.d.ts +9 -0
  71. package/dist/runtime/tinyclaw/schedules.js +62 -0
  72. package/dist/runtime/types.d.ts +1 -0
  73. package/package.json +2 -1
@@ -4,7 +4,7 @@ export interface EntrypointOptions {
4
4
  hasMoltnet?: boolean;
5
5
  hasStagedMoltnetBinaries?: boolean;
6
6
  moltnet?: {
7
- bridgePlans: MoltnetArtifacts["bridgePlans"];
7
+ nodePlans: MoltnetArtifacts["nodePlans"];
8
8
  serverPlans: MoltnetArtifacts["serverPlans"];
9
9
  };
10
10
  moltnetPublishedPorts?: number[];
@@ -1,8 +1,7 @@
1
1
  import path from "node:path";
2
+ import { createWorkspaceResourceCommands, createWorkspaceResourceShellFunctions } from "./containerWorkspaceResourceRender.js";
2
3
  const MOLTNET_SERVER_DATA_DIRECTORY = "/var/lib/spawnfile/moltnet/servers";
3
4
  const shellQuote = (value) => `'${value.replace(/'/g, `'\"'\"'`)}'`;
4
- const pathSafeSegment = (value) => value.replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "") || "server";
5
- const createMoltnetServerDataPath = (serverId) => `${MOLTNET_SERVER_DATA_DIRECTORY}/${pathSafeSegment(serverId)}.db`;
6
5
  const createEnvironmentAssignments = (plan) => {
7
6
  const envAssignments = [];
8
7
  if (plan.instancePaths.homePath) {
@@ -114,13 +113,19 @@ export const renderEntrypoint = (runtimePlans, requiredSecrets, options = {}) =>
114
113
  "",
115
114
  "cursor = data",
116
115
  "for part in json_path[:-1]:",
116
+ " if isinstance(cursor, list):",
117
+ " cursor = cursor[int(part)]",
118
+ " continue",
117
119
  " child = cursor.get(part)",
118
- " if not isinstance(child, dict):",
120
+ " if not isinstance(child, (dict, list)):",
119
121
  " child = {}",
120
122
  " cursor[part] = child",
121
123
  " cursor = child",
122
124
  "",
123
- "cursor[json_path[-1]] = value",
125
+ "if isinstance(cursor, list):",
126
+ " cursor[int(json_path[-1])] = value",
127
+ "else:",
128
+ " cursor[json_path[-1]] = value",
124
129
  "",
125
130
  "with open(target_path, 'w', encoding='utf-8') as handle:",
126
131
  " json.dump(data, handle, indent=2)",
@@ -136,7 +141,8 @@ export const renderEntrypoint = (runtimePlans, requiredSecrets, options = {}) =>
136
141
  ' mkdir -p "$home_path/.codex"',
137
142
  ' printf "%s\\n" "${OPENAI_API_KEY:-}" | HOME="$home_path" CODEX_HOME="$home_path/.codex" codex login --with-api-key >/dev/null',
138
143
  "}",
139
- ""
144
+ "",
145
+ ...createWorkspaceResourceShellFunctions()
140
146
  ];
141
147
  lines.push('if [ -n "${OPENCLAW_GATEWAY_TOKEN:-}" ] && [ -z "${OPENCLAW_HOOKS_TOKEN:-}" ]; then', ' export OPENCLAW_HOOKS_TOKEN="hooks-${OPENCLAW_GATEWAY_TOKEN}"', "fi", "");
142
148
  for (const secretName of requiredSecrets) {
@@ -146,40 +152,44 @@ export const renderEntrypoint = (runtimePlans, requiredSecrets, options = {}) =>
146
152
  lines.push("");
147
153
  }
148
154
  const moltnetServerPlans = options.moltnet?.serverPlans ?? [];
149
- const moltnetBridgePlans = options.moltnet?.bridgePlans ?? [];
155
+ const moltnetNodePlans = options.moltnet?.nodePlans ?? [];
150
156
  if (runtimePlans.length === 1 &&
151
157
  moltnetServerPlans.length === 0 &&
152
- moltnetBridgePlans.length === 0) {
158
+ moltnetNodePlans.length === 0) {
153
159
  const plan = runtimePlans[0];
154
160
  const commandTokens = resolveStartCommand(plan);
155
161
  const envAssignments = createEnvironmentAssignments(plan);
156
- lines.push(`mkdir -p ${shellQuote(plan.instancePaths.workspacePath)}`, `require_file ${shellQuote(plan.instancePaths.configPath)}`, ...createEnvFileWrites(plan), ...createConfigEnvWrites(plan), ...createAuthSetupCommands(plan), `${envAssignments.join(" ")} exec ${commandTokens.map(shellQuote).join(" ")}`);
162
+ lines.push(`mkdir -p ${shellQuote(plan.instancePaths.workspacePath)}`, ...createWorkspaceResourceCommands(plan), `require_file ${shellQuote(plan.instancePaths.configPath)}`, ...createEnvFileWrites(plan), ...createConfigEnvWrites(plan), ...createAuthSetupCommands(plan), `${envAssignments.join(" ")} exec ${commandTokens.map(shellQuote).join(" ")}`);
157
163
  return `${lines.join("\n").trimEnd()}\n`;
158
164
  }
159
165
  lines.push("PIDS=()", "", "terminate_children() {", ' for pid in "${PIDS[@]:-}"; do', ' kill "$pid" 2>/dev/null || true', " done", "}", "", "trap terminate_children INT TERM EXIT", "");
160
- if (moltnetServerPlans.length > 0) {
166
+ const managedMoltnetServerPlans = moltnetServerPlans.filter((serverPlan) => serverPlan.mode === "managed");
167
+ if (managedMoltnetServerPlans.length > 0) {
161
168
  lines.push(`mkdir -p ${shellQuote(MOLTNET_SERVER_DATA_DIRECTORY)}`, "");
162
169
  }
163
- for (const serverPlan of moltnetServerPlans) {
164
- lines.push(`MOLTNET_DATA_PATH=${shellQuote(createMoltnetServerDataPath(serverPlan.id))} MOLTNET_LISTEN_ADDR=${shellQuote(`:${serverPlan.port}`)} MOLTNET_NETWORK_ID=${shellQuote(serverPlan.networkId)} MOLTNET_NETWORK_NAME=${shellQuote(serverPlan.name)} /usr/local/bin/moltnet &`, 'PIDS+=("$!")', "");
170
+ for (const serverPlan of managedMoltnetServerPlans) {
171
+ if (!serverPlan.configPath) {
172
+ continue;
173
+ }
174
+ for (const patch of serverPlan.secretPatches) {
175
+ lines.push(`apply_json_env_value ${shellQuote(serverPlan.configPath)} ${shellQuote(patch.envName)} ${shellQuote(patch.jsonPath)}`);
176
+ }
177
+ lines.push(`MOLTNET_CONFIG=${shellQuote(serverPlan.configPath)} /usr/local/bin/moltnet &`, 'PIDS+=("$!")', "");
178
+ }
179
+ for (const serverPlan of managedMoltnetServerPlans) {
180
+ if (!serverPlan.port) {
181
+ continue;
182
+ }
183
+ lines.push(`until curl -sf ${shellQuote(`http://127.0.0.1:${serverPlan.port}/healthz`)} >/dev/null; do sleep 1; done`);
184
+ lines.push("");
165
185
  }
166
186
  for (const plan of runtimePlans) {
167
187
  const commandTokens = resolveStartCommand(plan);
168
188
  const envAssignments = createEnvironmentAssignments(plan);
169
- lines.push(`mkdir -p ${shellQuote(plan.instancePaths.workspacePath)}`, `require_file ${shellQuote(plan.instancePaths.configPath)}`, ...createEnvFileWrites(plan), ...createConfigEnvWrites(plan), ...createAuthSetupCommands(plan), `${envAssignments.join(" ")} ${commandTokens.map(shellQuote).join(" ")} &`, 'PIDS+=("$!")', "", ...createRuntimeReadinessWait(plan));
170
- }
171
- for (const serverPlan of moltnetServerPlans) {
172
- lines.push(`until curl -sf ${shellQuote(`http://127.0.0.1:${serverPlan.port}/healthz`)} >/dev/null; do sleep 1; done`);
173
- for (const room of serverPlan.rooms) {
174
- lines.push(`curl -sf -X POST -H 'Content-Type: application/json' -d ${shellQuote(JSON.stringify({
175
- id: room.id,
176
- members: room.members
177
- }))} ${shellQuote(`http://127.0.0.1:${serverPlan.port}/v1/rooms`)} >/dev/null || true`);
178
- }
179
- lines.push("");
189
+ lines.push(`mkdir -p ${shellQuote(plan.instancePaths.workspacePath)}`, ...createWorkspaceResourceCommands(plan), `require_file ${shellQuote(plan.instancePaths.configPath)}`, ...createEnvFileWrites(plan), ...createConfigEnvWrites(plan), ...createAuthSetupCommands(plan), `${envAssignments.join(" ")} ${commandTokens.map(shellQuote).join(" ")} &`, 'PIDS+=("$!")', "", ...createRuntimeReadinessWait(plan));
180
190
  }
181
- for (const bridgePlan of moltnetBridgePlans) {
182
- lines.push(`/usr/local/bin/moltnet bridge ${shellQuote(bridgePlan.configPath)} &`, 'PIDS+=("$!")', "");
191
+ for (const nodePlan of moltnetNodePlans) {
192
+ lines.push(`/usr/local/bin/moltnet node ${shellQuote(nodePlan.configPath)} &`, 'PIDS+=("$!")', "");
183
193
  }
184
194
  lines.push('if [ "${#PIDS[@]}" -eq 0 ]; then', ' echo "No runtime targets were generated for this compile output" >&2', " exit 1", "fi", "", "status=0", 'for pid in "${PIDS[@]}"; do', ' if ! wait "$pid"; then', " status=1", " fi", "done", "", 'exit "$status"');
185
195
  return `${lines.join("\n").trimEnd()}\n`;
@@ -0,0 +1,4 @@
1
+ import type { ContainerTarget, ContainerTargetInput, RuntimeContainerMeta } from "../runtime/index.js";
2
+ import type { RuntimeTargetPlan } from "./containerArtifactsTypes.js";
3
+ import { type WorkspaceResourcePlan } from "./workspaceResources.js";
4
+ export declare const resolveTargetResources: (target: ContainerTarget, inputs: ContainerTargetInput[], instancePaths: RuntimeTargetPlan["instancePaths"], meta: RuntimeContainerMeta) => WorkspaceResourcePlan[];
@@ -0,0 +1,54 @@
1
+ import path from "node:path";
2
+ import { SpawnfileError } from "../shared/index.js";
3
+ import { mergeWorkspaceResourcePlans } from "./workspaceResources.js";
4
+ const CONFIG_FILE_PLACEHOLDER = "<config-file>";
5
+ const INSTANCE_ROOT_PLACEHOLDER = "<instance-root>";
6
+ const SOURCE_AGENT_PLACEHOLDER = "<agent-name>";
7
+ const replaceSourceWorkspacePathTemplate = (template, input, instancePaths, meta) => {
8
+ const agentName = input.value.kind === "agent" ? input.value.name : input.slug;
9
+ const instanceRoot = instancePaths.instanceRoot ?? path.posix.dirname(instancePaths.workspacePath);
10
+ return template
11
+ .replaceAll(INSTANCE_ROOT_PLACEHOLDER, instanceRoot)
12
+ .replaceAll(CONFIG_FILE_PLACEHOLDER, meta.configFileName)
13
+ .replaceAll(SOURCE_AGENT_PLACEHOLDER, agentName);
14
+ };
15
+ const resolveSourceWorkspacePath = (input, instancePaths, meta) => meta.instancePaths.sourceWorkspacePathTemplate
16
+ ? replaceSourceWorkspacePathTemplate(meta.instancePaths.sourceWorkspacePathTemplate, input, instancePaths, meta)
17
+ : instancePaths.workspacePath;
18
+ const sourceTargetId = (target, input, isMergedTarget) => isMergedTarget ? `${target.id}:${input.id}` : target.id;
19
+ const pathsOverlap = (left, right) => left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
20
+ const dedupeAndAssertResourcePlans = (target, resources) => {
21
+ const byLinkPath = new Map();
22
+ for (const resource of resources) {
23
+ const existing = byLinkPath.get(resource.linkPath);
24
+ if (existing) {
25
+ if (existing.backingPath !== resource.backingPath || existing.id !== resource.id) {
26
+ throw new SpawnfileError("validation_error", `Container target ${target.id} declares conflicting workspace resources at ${resource.linkPath}`);
27
+ }
28
+ continue;
29
+ }
30
+ const overlapping = [...byLinkPath.values()].find((candidate) => pathsOverlap(candidate.linkPath, resource.linkPath));
31
+ if (overlapping) {
32
+ throw new SpawnfileError("validation_error", `Container target ${target.id} declares overlapping workspace resource links ${overlapping.linkPath} and ${resource.linkPath}`);
33
+ }
34
+ byLinkPath.set(resource.linkPath, resource);
35
+ }
36
+ return [...byLinkPath.values()].sort((left, right) => left.linkPath.localeCompare(right.linkPath) || left.id.localeCompare(right.id));
37
+ };
38
+ export const resolveTargetResources = (target, inputs, instancePaths, meta) => {
39
+ const sourceIds = new Set(target.sourceIds ?? []);
40
+ if (sourceIds.size === 0) {
41
+ return [];
42
+ }
43
+ const isMergedTarget = sourceIds.size > 1;
44
+ const resources = inputs.flatMap((input) => {
45
+ if (!sourceIds.has(input.id) || input.value.kind !== "agent") {
46
+ return [];
47
+ }
48
+ return mergeWorkspaceResourcePlans(input.value.workspaceResources ?? [], `${target.id}/${input.id}`, {
49
+ targetId: sourceTargetId(target, input, isMergedTarget),
50
+ workspacePath: resolveSourceWorkspacePath(input, instancePaths, meta)
51
+ });
52
+ });
53
+ return dedupeAndAssertResourcePlans(target, resources);
54
+ };
@@ -0,0 +1,3 @@
1
+ import type { RuntimeTargetPlan } from "./containerArtifactsTypes.js";
2
+ export declare const createWorkspaceResourceCommands: (plan: RuntimeTargetPlan) => string[];
3
+ export declare const createWorkspaceResourceShellFunctions: () => string[];
@@ -0,0 +1,128 @@
1
+ const shellQuote = (value) => `'${value.replace(/'/g, `'\"'\"'`)}'`;
2
+ export const createWorkspaceResourceCommands = (plan) => (plan.resources ?? []).flatMap((resource) => {
3
+ const readonlyFlag = resource.mode === "readonly" ? "readonly" : "mutable";
4
+ if (resource.kind === "volume") {
5
+ return [
6
+ `prepare_volume_resource ${shellQuote(resource.id)} ${shellQuote(resource.linkPath)} ${shellQuote(resource.backingPath)} ${shellQuote(readonlyFlag)}`
7
+ ];
8
+ }
9
+ const selectorKind = resource.branch
10
+ ? "branch"
11
+ : resource.tag
12
+ ? "tag"
13
+ : resource.ref
14
+ ? "ref"
15
+ : "none";
16
+ const selectorValue = resource.branch ?? resource.tag ?? resource.ref ?? "";
17
+ return [
18
+ `prepare_git_resource ${shellQuote(resource.id)} ${shellQuote(resource.linkPath)} ${shellQuote(resource.backingPath)} ${shellQuote(resource.url ?? "")} ${shellQuote(selectorKind)} ${shellQuote(selectorValue)} ${shellQuote(readonlyFlag)}`
19
+ ];
20
+ });
21
+ export const createWorkspaceResourceShellFunctions = () => [
22
+ "directory_is_empty() {",
23
+ ' local target="$1"',
24
+ ' [ -d "$target" ] && [ -z "$(find "$target" -mindepth 1 -maxdepth 1 -print -quit)" ]',
25
+ "}",
26
+ "",
27
+ "prepare_resource_link() {",
28
+ ' local id="$1"',
29
+ ' local link_path="$2"',
30
+ ' local backing_path="$3"',
31
+ ' mkdir -p "$(dirname "$link_path")"',
32
+ ' if [ -L "$link_path" ]; then',
33
+ ' local existing_target',
34
+ ' existing_target="$(readlink "$link_path")"',
35
+ ' if [ "$existing_target" != "$backing_path" ]; then',
36
+ ' echo "Workspace resource $id link points to $existing_target, expected $backing_path" >&2',
37
+ " exit 1",
38
+ " fi",
39
+ " return",
40
+ " fi",
41
+ ' if [ -e "$link_path" ]; then',
42
+ ' echo "Workspace resource $id mount path already exists and is not a symlink: $link_path" >&2',
43
+ " exit 1",
44
+ " fi",
45
+ ' ln -s "$backing_path" "$link_path"',
46
+ "}",
47
+ "",
48
+ "mark_readonly_resource() {",
49
+ ' local target="$1"',
50
+ ' local mode="$2"',
51
+ ' if [ "$mode" = "readonly" ]; then',
52
+ ' chmod -R a-w "$target"',
53
+ " fi",
54
+ "}",
55
+ "",
56
+ "prepare_volume_resource() {",
57
+ ' local id="$1"',
58
+ ' local link_path="$2"',
59
+ ' local backing_path="$3"',
60
+ ' local mode="$4"',
61
+ ' mkdir -p "$backing_path"',
62
+ ' if [ ! -d "$backing_path" ]; then',
63
+ ' echo "Workspace volume resource $id backing path is not a directory: $backing_path" >&2',
64
+ " exit 1",
65
+ " fi",
66
+ ' mark_readonly_resource "$backing_path" "$mode"',
67
+ ' prepare_resource_link "$id" "$link_path" "$backing_path"',
68
+ "}",
69
+ "",
70
+ "prepare_git_resource() {",
71
+ ' local id="$1"',
72
+ ' local link_path="$2"',
73
+ ' local backing_path="$3"',
74
+ ' local remote="$4"',
75
+ ' local selector_kind="$5"',
76
+ ' local selector_value="$6"',
77
+ ' local mode="$7"',
78
+ ' if [ -d "$backing_path/.git" ]; then',
79
+ ' local existing_remote',
80
+ ' existing_remote="$(git -C "$backing_path" config --get remote.origin.url || true)"',
81
+ ' if [ "$existing_remote" != "$remote" ]; then',
82
+ ' echo "Workspace git resource $id has remote $existing_remote, expected $remote" >&2',
83
+ " exit 1",
84
+ " fi",
85
+ ' if [ "$selector_kind" = "branch" ]; then',
86
+ ' local branch',
87
+ ' branch="$(git -C "$backing_path" rev-parse --abbrev-ref HEAD)"',
88
+ ' if [ "$branch" != "$selector_value" ]; then',
89
+ ' echo "Workspace git resource $id is on branch $branch, expected $selector_value" >&2',
90
+ " exit 1",
91
+ " fi",
92
+ ' elif [ "$selector_kind" = "tag" ]; then',
93
+ ' local tag',
94
+ ' tag="$(git -C "$backing_path" describe --tags --exact-match 2>/dev/null || true)"',
95
+ ' if [ "$tag" != "$selector_value" ]; then',
96
+ ' echo "Workspace git resource $id is on tag $tag, expected $selector_value" >&2',
97
+ " exit 1",
98
+ " fi",
99
+ ' elif [ "$selector_kind" = "ref" ]; then',
100
+ ' local current_ref',
101
+ ' current_ref="$(git -C "$backing_path" rev-parse HEAD)"',
102
+ ' if [ "$current_ref" != "$selector_value" ]; then',
103
+ ' echo "Workspace git resource $id is on ref $current_ref, expected $selector_value" >&2',
104
+ " exit 1",
105
+ " fi",
106
+ " fi",
107
+ ' mark_readonly_resource "$backing_path" "$mode"',
108
+ ' prepare_resource_link "$id" "$link_path" "$backing_path"',
109
+ " return",
110
+ " fi",
111
+ ' if [ -e "$backing_path" ] && ! directory_is_empty "$backing_path"; then',
112
+ ' echo "Workspace git resource $id backing path is not empty: $backing_path" >&2',
113
+ " exit 1",
114
+ " fi",
115
+ ' mkdir -p "$(dirname "$backing_path")"',
116
+ ' if [ "$selector_kind" = "branch" ]; then',
117
+ ' git clone --branch "$selector_value" "$remote" "$backing_path"',
118
+ " else",
119
+ ' git clone "$remote" "$backing_path"',
120
+ ' if [ "$selector_kind" = "tag" ] || [ "$selector_kind" = "ref" ]; then',
121
+ ' git -C "$backing_path" checkout "$selector_value"',
122
+ " fi",
123
+ " fi",
124
+ ' mark_readonly_resource "$backing_path" "$mode"',
125
+ ' prepare_resource_link "$id" "$link_path" "$backing_path"',
126
+ "}",
127
+ ""
128
+ ];
@@ -2,8 +2,5 @@ export const applyExecutionDefaults = (execution) => ({
2
2
  model: execution?.model,
3
3
  sandbox: execution?.sandbox ?? {
4
4
  mode: "workspace"
5
- },
6
- workspace: execution?.workspace ?? {
7
- isolation: "isolated"
8
5
  }
9
6
  });
@@ -1,4 +1,4 @@
1
- import { CompilePlanNode } from "./types.js";
1
+ import type { CompilePlanNode } from "./types.js";
2
2
  export declare const createShortHash: (value: string) => string;
3
3
  export declare const slugify: (value: string) => string;
4
4
  export declare const stableStringify: (value: unknown) => string;
@@ -1,6 +1,7 @@
1
1
  export * from "./addProjectNode.js";
2
2
  export * from "./buildCompilePlan.js";
3
3
  export * from "./buildProject.js";
4
+ export * from "./upProject.js";
4
5
  export * from "./compileProject.js";
5
6
  export * from "./initProject.js";
6
7
  export * from "./moltnetRoomMemberships.js";
@@ -1,6 +1,7 @@
1
1
  export * from "./addProjectNode.js";
2
2
  export * from "./buildCompilePlan.js";
3
3
  export * from "./buildProject.js";
4
+ export * from "./upProject.js";
4
5
  export * from "./compileProject.js";
5
6
  export * from "./initProject.js";
6
7
  export * from "./moltnetRoomMemberships.js";
@@ -1,25 +1,31 @@
1
1
  import type { EmittedFile } from "../runtime/index.js";
2
+ import type { TeamNetworkServer } from "../manifest/index.js";
3
+ import { type MoltnetSecretPatch } from "./moltnetConfigLowering.js";
2
4
  import type { CompilePlan } from "./types.js";
3
5
  export interface MoltnetServerPlan {
6
+ baseUrl: string;
7
+ configPath?: string;
4
8
  id: string;
9
+ mode: "external" | "managed";
5
10
  name: string;
6
11
  networkId: string;
7
- port: number;
12
+ port?: number;
8
13
  rooms: Array<{
9
14
  id: string;
10
15
  members: string[];
16
+ name?: string;
11
17
  }>;
18
+ server: TeamNetworkServer;
19
+ secretPatches: MoltnetSecretPatch[];
12
20
  teamSource: string;
13
21
  }
14
- export interface MoltnetBridgePlan {
15
- agentId: string;
22
+ export interface MoltnetNodePlan {
16
23
  configPath: string;
17
24
  networkId: string;
18
- runtime: string;
19
25
  }
20
26
  export interface MoltnetArtifacts {
21
- bridgePlans: MoltnetBridgePlan[];
22
27
  files: EmittedFile[];
28
+ nodePlans: MoltnetNodePlan[];
23
29
  ports: number[];
24
30
  publishedPorts: number[];
25
31
  serverPlans: MoltnetServerPlan[];