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.
Files changed (102) hide show
  1. package/README.md +80 -396
  2. package/dist/cli/index.js +0 -0
  3. package/dist/cli/modelCommands.d.ts +3 -0
  4. package/dist/cli/modelCommands.js +68 -0
  5. package/dist/cli/runCli.d.ts +23 -2
  6. package/dist/cli/runCli.js +78 -122
  7. package/dist/cli/runtimeCommands.d.ts +3 -0
  8. package/dist/cli/runtimeCommands.js +20 -0
  9. package/dist/cli/surfaceCommands.d.ts +3 -0
  10. package/dist/cli/surfaceCommands.js +98 -0
  11. package/dist/cli/viewCommand.d.ts +3 -0
  12. package/dist/cli/viewCommand.js +87 -0
  13. package/dist/compiler/agentSurfaces.js +51 -5
  14. package/dist/compiler/buildCompilePlan.js +38 -40
  15. package/dist/compiler/buildCompilePlanRuntime.d.ts +14 -0
  16. package/dist/compiler/buildCompilePlanRuntime.js +39 -0
  17. package/dist/compiler/buildCompilePlanTeams.d.ts +5 -0
  18. package/dist/compiler/buildCompilePlanTeams.js +38 -0
  19. package/dist/compiler/compilePlanHelpers.js +4 -1
  20. package/dist/compiler/compileProject.js +62 -13
  21. package/dist/compiler/compileProjectSupport.d.ts +17 -0
  22. package/dist/compiler/compileProjectSupport.js +136 -0
  23. package/dist/compiler/containerArtifacts.d.ts +6 -1
  24. package/dist/compiler/containerArtifacts.js +26 -4
  25. package/dist/compiler/containerArtifactsPlans.js +16 -1
  26. package/dist/compiler/containerArtifactsRender.d.ts +4 -2
  27. package/dist/compiler/containerArtifactsRender.js +21 -126
  28. package/dist/compiler/containerArtifactsTypes.d.ts +7 -0
  29. package/dist/compiler/containerEntrypointRender.d.ts +12 -0
  30. package/dist/compiler/containerEntrypointRender.js +186 -0
  31. package/dist/compiler/index.d.ts +4 -0
  32. package/dist/compiler/index.js +4 -0
  33. package/dist/compiler/interactiveSurfaceScopes.d.ts +2 -0
  34. package/dist/compiler/interactiveSurfaceScopes.js +21 -0
  35. package/dist/compiler/moltnetArtifacts.d.ts +27 -0
  36. package/dist/compiler/moltnetArtifacts.js +208 -0
  37. package/dist/compiler/moltnetBinaries.d.ts +4 -0
  38. package/dist/compiler/moltnetBinaries.js +103 -0
  39. package/dist/compiler/moltnetClientConfig.d.ts +11 -0
  40. package/dist/compiler/moltnetClientConfig.js +89 -0
  41. package/dist/compiler/moltnetRepresentativeResolution.d.ts +16 -0
  42. package/dist/compiler/moltnetRepresentativeResolution.js +86 -0
  43. package/dist/compiler/moltnetResolution.d.ts +3 -0
  44. package/dist/compiler/moltnetResolution.js +182 -0
  45. package/dist/compiler/moltnetRoomMemberships.d.ts +3 -0
  46. package/dist/compiler/moltnetRoomMemberships.js +140 -0
  47. package/dist/compiler/runProject.js +1 -1
  48. package/dist/compiler/surfaceDefinitions.d.ts +55 -0
  49. package/dist/compiler/surfaceDefinitions.js +204 -0
  50. package/dist/compiler/teamContextHelpers.d.ts +18 -0
  51. package/dist/compiler/teamContextHelpers.js +112 -0
  52. package/dist/compiler/teamContextSupport.d.ts +4 -0
  53. package/dist/compiler/teamContextSupport.js +264 -0
  54. package/dist/compiler/teamContextSupport.testHelpers.d.ts +16 -0
  55. package/dist/compiler/teamContextSupport.testHelpers.js +68 -0
  56. package/dist/compiler/teamContextTypes.d.ts +28 -0
  57. package/dist/compiler/teamContextTypes.js +1 -0
  58. package/dist/compiler/teamRoster.d.ts +12 -0
  59. package/dist/compiler/teamRoster.js +48 -0
  60. package/dist/compiler/teamRosterEntries.d.ts +13 -0
  61. package/dist/compiler/teamRosterEntries.js +230 -0
  62. package/dist/compiler/teamRosterTypes.d.ts +45 -0
  63. package/dist/compiler/teamRosterTypes.js +1 -0
  64. package/dist/compiler/types.d.ts +90 -6
  65. package/dist/compiler/updateProjectRuntime.d.ts +9 -0
  66. package/dist/compiler/updateProjectRuntime.js +67 -0
  67. package/dist/compiler/updateProjectSurfaces.d.ts +8 -0
  68. package/dist/compiler/updateProjectSurfaces.js +106 -0
  69. package/dist/compiler/view/buildOrganizationView.d.ts +2 -0
  70. package/dist/compiler/view/buildOrganizationView.js +180 -0
  71. package/dist/compiler/view/index.d.ts +4 -0
  72. package/dist/compiler/view/index.js +4 -0
  73. package/dist/compiler/view/renderNetworks.d.ts +2 -0
  74. package/dist/compiler/view/renderNetworks.js +93 -0
  75. package/dist/compiler/view/renderTree.d.ts +2 -0
  76. package/dist/compiler/view/renderTree.js +59 -0
  77. package/dist/compiler/view/sourcePaths.d.ts +2 -0
  78. package/dist/compiler/view/sourcePaths.js +19 -0
  79. package/dist/compiler/view/types.d.ts +80 -0
  80. package/dist/compiler/view/types.js +1 -0
  81. package/dist/manifest/loadManifest.js +4 -4
  82. package/dist/manifest/renderSpawnfile.js +74 -8
  83. package/dist/manifest/scaffold.js +1 -3
  84. package/dist/manifest/schemas.d.ts +227 -17
  85. package/dist/manifest/schemas.js +62 -20
  86. package/dist/manifest/surfaceSchemas.d.ts +154 -0
  87. package/dist/manifest/surfaceSchemas.js +77 -5
  88. package/dist/runtime/common.js +3 -0
  89. package/dist/runtime/openclaw/adapter.js +38 -5
  90. package/dist/runtime/openclaw/moltnet.d.ts +12 -0
  91. package/dist/runtime/openclaw/moltnet.js +124 -0
  92. package/dist/runtime/openclaw/surfaces.js +3 -0
  93. package/dist/runtime/picoclaw/adapter.js +27 -8
  94. package/dist/runtime/picoclaw/pico.d.ts +2 -0
  95. package/dist/runtime/picoclaw/pico.js +2 -0
  96. package/dist/runtime/picoclaw/surfaces.js +11 -0
  97. package/dist/runtime/tinyclaw/adapter.js +22 -8
  98. package/dist/runtime/tinyclaw/runAuth.js +28 -1
  99. package/dist/runtime/tinyclaw/surfaces.js +8 -0
  100. package/dist/runtime/types.d.ts +11 -0
  101. package/package.json +5 -3
  102. package/runtimes.yaml +4 -4
@@ -48,6 +48,9 @@ declare const discordSurfaceSchema: z.ZodObject<{
48
48
  users: z.ZodOptional<z.ZodArray<z.ZodString>>;
49
49
  }, z.core.$strict>>;
50
50
  bot_token_secret: z.ZodOptional<z.ZodString>;
51
+ identity: z.ZodOptional<z.ZodObject<{
52
+ user_id: z.ZodString;
53
+ }, z.core.$strict>>;
51
54
  }, z.core.$strict>;
52
55
  declare const telegramSurfaceSchema: z.ZodObject<{
53
56
  access: z.ZodOptional<z.ZodObject<{
@@ -60,6 +63,10 @@ declare const telegramSurfaceSchema: z.ZodObject<{
60
63
  users: z.ZodOptional<z.ZodArray<z.ZodString>>;
61
64
  }, z.core.$strict>>;
62
65
  bot_token_secret: z.ZodOptional<z.ZodString>;
66
+ identity: z.ZodOptional<z.ZodObject<{
67
+ user_id: z.ZodOptional<z.ZodString>;
68
+ username: z.ZodOptional<z.ZodString>;
69
+ }, z.core.$strict>>;
63
70
  }, z.core.$strict>;
64
71
  declare const whatsappSurfaceSchema: z.ZodObject<{
65
72
  access: z.ZodOptional<z.ZodObject<{
@@ -71,6 +78,9 @@ declare const whatsappSurfaceSchema: z.ZodObject<{
71
78
  }>>;
72
79
  users: z.ZodOptional<z.ZodArray<z.ZodString>>;
73
80
  }, z.core.$strict>>;
81
+ identity: z.ZodOptional<z.ZodObject<{
82
+ phone: z.ZodString;
83
+ }, z.core.$strict>>;
74
84
  }, z.core.$strict>;
75
85
  declare const slackSurfaceSchema: z.ZodObject<{
76
86
  access: z.ZodOptional<z.ZodObject<{
@@ -84,7 +94,98 @@ declare const slackSurfaceSchema: z.ZodObject<{
84
94
  }, z.core.$strict>>;
85
95
  app_token_secret: z.ZodOptional<z.ZodString>;
86
96
  bot_token_secret: z.ZodOptional<z.ZodString>;
97
+ identity: z.ZodOptional<z.ZodObject<{
98
+ user_id: z.ZodString;
99
+ }, z.core.$strict>>;
100
+ }, z.core.$strict>;
101
+ declare const webhookSurfaceSchema: z.ZodObject<{
102
+ signing_secret: z.ZodOptional<z.ZodString>;
103
+ url: z.ZodString;
104
+ }, z.core.$strict>;
105
+ declare const moltnetReadSchema: z.ZodEnum<{
106
+ all: "all";
107
+ mentions: "mentions";
108
+ thread_only: "thread_only";
109
+ }>;
110
+ declare const moltnetReplySchema: z.ZodEnum<{
111
+ never: "never";
112
+ auto: "auto";
113
+ }>;
114
+ declare const moltnetRoomBehaviorSchema: z.ZodObject<{
115
+ read: z.ZodOptional<z.ZodEnum<{
116
+ all: "all";
117
+ mentions: "mentions";
118
+ thread_only: "thread_only";
119
+ }>>;
120
+ reply: z.ZodOptional<z.ZodEnum<{
121
+ never: "never";
122
+ auto: "auto";
123
+ }>>;
124
+ }, z.core.$strict>;
125
+ declare const moltnetDmSchema: z.ZodObject<{
126
+ enabled: z.ZodBoolean;
127
+ read: z.ZodOptional<z.ZodEnum<{
128
+ all: "all";
129
+ mentions: "mentions";
130
+ thread_only: "thread_only";
131
+ }>>;
132
+ reply: z.ZodOptional<z.ZodEnum<{
133
+ never: "never";
134
+ auto: "auto";
135
+ }>>;
136
+ }, z.core.$strict>;
137
+ declare const moltnetAttachmentSchema: z.ZodObject<{
138
+ dms: z.ZodOptional<z.ZodObject<{
139
+ enabled: z.ZodBoolean;
140
+ read: z.ZodOptional<z.ZodEnum<{
141
+ all: "all";
142
+ mentions: "mentions";
143
+ thread_only: "thread_only";
144
+ }>>;
145
+ reply: z.ZodOptional<z.ZodEnum<{
146
+ never: "never";
147
+ auto: "auto";
148
+ }>>;
149
+ }, z.core.$strict>>;
150
+ network: z.ZodString;
151
+ rooms: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
152
+ read: z.ZodOptional<z.ZodEnum<{
153
+ all: "all";
154
+ mentions: "mentions";
155
+ thread_only: "thread_only";
156
+ }>>;
157
+ reply: z.ZodOptional<z.ZodEnum<{
158
+ never: "never";
159
+ auto: "auto";
160
+ }>>;
161
+ }, z.core.$strict>>>;
87
162
  }, z.core.$strict>;
163
+ declare const moltnetSurfaceSchema: z.ZodArray<z.ZodObject<{
164
+ dms: z.ZodOptional<z.ZodObject<{
165
+ enabled: z.ZodBoolean;
166
+ read: z.ZodOptional<z.ZodEnum<{
167
+ all: "all";
168
+ mentions: "mentions";
169
+ thread_only: "thread_only";
170
+ }>>;
171
+ reply: z.ZodOptional<z.ZodEnum<{
172
+ never: "never";
173
+ auto: "auto";
174
+ }>>;
175
+ }, z.core.$strict>>;
176
+ network: z.ZodString;
177
+ rooms: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
178
+ read: z.ZodOptional<z.ZodEnum<{
179
+ all: "all";
180
+ mentions: "mentions";
181
+ thread_only: "thread_only";
182
+ }>>;
183
+ reply: z.ZodOptional<z.ZodEnum<{
184
+ never: "never";
185
+ auto: "auto";
186
+ }>>;
187
+ }, z.core.$strict>>>;
188
+ }, z.core.$strict>>;
88
189
  export declare const surfacesSchema: z.ZodObject<{
89
190
  discord: z.ZodOptional<z.ZodObject<{
90
191
  access: z.ZodOptional<z.ZodObject<{
@@ -98,7 +199,36 @@ export declare const surfacesSchema: z.ZodObject<{
98
199
  users: z.ZodOptional<z.ZodArray<z.ZodString>>;
99
200
  }, z.core.$strict>>;
100
201
  bot_token_secret: z.ZodOptional<z.ZodString>;
202
+ identity: z.ZodOptional<z.ZodObject<{
203
+ user_id: z.ZodString;
204
+ }, z.core.$strict>>;
101
205
  }, z.core.$strict>>;
206
+ moltnet: z.ZodOptional<z.ZodArray<z.ZodObject<{
207
+ dms: z.ZodOptional<z.ZodObject<{
208
+ enabled: z.ZodBoolean;
209
+ read: z.ZodOptional<z.ZodEnum<{
210
+ all: "all";
211
+ mentions: "mentions";
212
+ thread_only: "thread_only";
213
+ }>>;
214
+ reply: z.ZodOptional<z.ZodEnum<{
215
+ never: "never";
216
+ auto: "auto";
217
+ }>>;
218
+ }, z.core.$strict>>;
219
+ network: z.ZodString;
220
+ rooms: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
221
+ read: z.ZodOptional<z.ZodEnum<{
222
+ all: "all";
223
+ mentions: "mentions";
224
+ thread_only: "thread_only";
225
+ }>>;
226
+ reply: z.ZodOptional<z.ZodEnum<{
227
+ never: "never";
228
+ auto: "auto";
229
+ }>>;
230
+ }, z.core.$strict>>>;
231
+ }, z.core.$strict>>>;
102
232
  slack: z.ZodOptional<z.ZodObject<{
103
233
  access: z.ZodOptional<z.ZodObject<{
104
234
  channels: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -111,6 +241,9 @@ export declare const surfacesSchema: z.ZodObject<{
111
241
  }, z.core.$strict>>;
112
242
  app_token_secret: z.ZodOptional<z.ZodString>;
113
243
  bot_token_secret: z.ZodOptional<z.ZodString>;
244
+ identity: z.ZodOptional<z.ZodObject<{
245
+ user_id: z.ZodString;
246
+ }, z.core.$strict>>;
114
247
  }, z.core.$strict>>;
115
248
  telegram: z.ZodOptional<z.ZodObject<{
116
249
  access: z.ZodOptional<z.ZodObject<{
@@ -123,6 +256,14 @@ export declare const surfacesSchema: z.ZodObject<{
123
256
  users: z.ZodOptional<z.ZodArray<z.ZodString>>;
124
257
  }, z.core.$strict>>;
125
258
  bot_token_secret: z.ZodOptional<z.ZodString>;
259
+ identity: z.ZodOptional<z.ZodObject<{
260
+ user_id: z.ZodOptional<z.ZodString>;
261
+ username: z.ZodOptional<z.ZodString>;
262
+ }, z.core.$strict>>;
263
+ }, z.core.$strict>>;
264
+ webhook: z.ZodOptional<z.ZodObject<{
265
+ signing_secret: z.ZodOptional<z.ZodString>;
266
+ url: z.ZodString;
126
267
  }, z.core.$strict>>;
127
268
  whatsapp: z.ZodOptional<z.ZodObject<{
128
269
  access: z.ZodOptional<z.ZodObject<{
@@ -134,14 +275,27 @@ export declare const surfacesSchema: z.ZodObject<{
134
275
  }>>;
135
276
  users: z.ZodOptional<z.ZodArray<z.ZodString>>;
136
277
  }, z.core.$strict>>;
278
+ identity: z.ZodOptional<z.ZodObject<{
279
+ phone: z.ZodString;
280
+ }, z.core.$strict>>;
137
281
  }, z.core.$strict>>;
138
282
  }, z.core.$strict>;
139
283
  export type DiscordSurfaceAccess = z.infer<typeof discordSurfaceAccessSchema>;
140
284
  export type DiscordSurface = z.infer<typeof discordSurfaceSchema>;
285
+ export type HttpSurfaceAccess = never;
286
+ export type HttpSurfaceAuth = never;
287
+ export type HttpSurface = never;
288
+ export type MoltnetAttachment = z.infer<typeof moltnetAttachmentSchema>;
289
+ export type MoltnetDM = z.infer<typeof moltnetDmSchema>;
290
+ export type MoltnetRead = z.infer<typeof moltnetReadSchema>;
291
+ export type MoltnetReply = z.infer<typeof moltnetReplySchema>;
292
+ export type MoltnetRoomBehavior = z.infer<typeof moltnetRoomBehaviorSchema>;
293
+ export type MoltnetSurface = z.infer<typeof moltnetSurfaceSchema>;
141
294
  export type SlackSurfaceAccess = z.infer<typeof slackSurfaceAccessSchema>;
142
295
  export type SlackSurface = z.infer<typeof slackSurfaceSchema>;
143
296
  export type TelegramSurfaceAccess = z.infer<typeof telegramSurfaceAccessSchema>;
144
297
  export type TelegramSurface = z.infer<typeof telegramSurfaceSchema>;
298
+ export type WebhookSurface = z.infer<typeof webhookSurfaceSchema>;
145
299
  export type WhatsAppSurfaceAccess = z.infer<typeof whatsappSurfaceAccessSchema>;
146
300
  export type WhatsAppSurface = z.infer<typeof whatsappSurfaceSchema>;
147
301
  export type SurfacesBlock = z.infer<typeof surfacesSchema>;
@@ -120,40 +120,112 @@ const slackSurfaceAccessSchema = z
120
120
  });
121
121
  }
122
122
  });
123
+ const userIdIdentitySchema = z
124
+ .object({
125
+ user_id: z.string().min(1)
126
+ })
127
+ .strict();
128
+ const telegramIdentitySchema = z
129
+ .object({
130
+ user_id: z.string().min(1).optional(),
131
+ username: z.string().min(1).optional()
132
+ })
133
+ .strict()
134
+ .superRefine((value, context) => {
135
+ if (!value.user_id && !value.username) {
136
+ context.addIssue({
137
+ code: z.ZodIssueCode.custom,
138
+ message: "telegram identity must declare user_id or username"
139
+ });
140
+ }
141
+ });
142
+ const whatsappIdentitySchema = z
143
+ .object({
144
+ phone: z.string().min(1)
145
+ })
146
+ .strict();
123
147
  const discordSurfaceSchema = z
124
148
  .object({
125
149
  access: discordSurfaceAccessSchema.optional(),
126
- bot_token_secret: z.string().min(1).optional()
150
+ bot_token_secret: z.string().min(1).optional(),
151
+ identity: userIdIdentitySchema.optional()
127
152
  })
128
153
  .strict();
129
154
  const telegramSurfaceSchema = z
130
155
  .object({
131
156
  access: telegramSurfaceAccessSchema.optional(),
132
- bot_token_secret: z.string().min(1).optional()
157
+ bot_token_secret: z.string().min(1).optional(),
158
+ identity: telegramIdentitySchema.optional()
133
159
  })
134
160
  .strict();
135
161
  const whatsappSurfaceSchema = z
136
162
  .object({
137
- access: whatsappSurfaceAccessSchema.optional()
163
+ access: whatsappSurfaceAccessSchema.optional(),
164
+ identity: whatsappIdentitySchema.optional()
138
165
  })
139
166
  .strict();
140
167
  const slackSurfaceSchema = z
141
168
  .object({
142
169
  access: slackSurfaceAccessSchema.optional(),
143
170
  app_token_secret: z.string().min(1).optional(),
144
- bot_token_secret: z.string().min(1).optional()
171
+ bot_token_secret: z.string().min(1).optional(),
172
+ identity: userIdIdentitySchema.optional()
145
173
  })
146
174
  .strict();
175
+ const webhookSurfaceSchema = z
176
+ .object({
177
+ signing_secret: z.string().min(1).optional(),
178
+ url: z.string().url()
179
+ })
180
+ .strict();
181
+ const moltnetReadSchema = z.enum(["all", "mentions", "thread_only"]);
182
+ const moltnetReplySchema = z.enum(["auto", "never"]);
183
+ const moltnetRoomBehaviorSchema = z
184
+ .object({
185
+ read: moltnetReadSchema.optional(),
186
+ reply: moltnetReplySchema.optional()
187
+ })
188
+ .strict();
189
+ const moltnetDmSchema = z
190
+ .object({
191
+ enabled: z.boolean(),
192
+ read: moltnetReadSchema.optional(),
193
+ reply: moltnetReplySchema.optional()
194
+ })
195
+ .strict();
196
+ const moltnetAttachmentSchema = z
197
+ .object({
198
+ dms: moltnetDmSchema.optional(),
199
+ network: z.string().min(1),
200
+ rooms: z.record(z.string().min(1), moltnetRoomBehaviorSchema).optional()
201
+ })
202
+ .strict()
203
+ .superRefine((value, context) => {
204
+ if (!value.rooms && !value.dms) {
205
+ context.addIssue({
206
+ code: z.ZodIssueCode.custom,
207
+ message: "moltnet attachments must declare rooms or dms"
208
+ });
209
+ }
210
+ });
211
+ const moltnetSurfaceSchema = z.array(moltnetAttachmentSchema).min(1);
147
212
  export const surfacesSchema = z
148
213
  .object({
149
214
  discord: discordSurfaceSchema.optional(),
215
+ moltnet: moltnetSurfaceSchema.optional(),
150
216
  slack: slackSurfaceSchema.optional(),
151
217
  telegram: telegramSurfaceSchema.optional(),
218
+ webhook: webhookSurfaceSchema.optional(),
152
219
  whatsapp: whatsappSurfaceSchema.optional()
153
220
  })
154
221
  .strict()
155
222
  .superRefine((value, context) => {
156
- if (!value.discord && !value.telegram && !value.whatsapp && !value.slack) {
223
+ if (!value.discord &&
224
+ !value.moltnet &&
225
+ !value.telegram &&
226
+ !value.whatsapp &&
227
+ !value.slack &&
228
+ !value.webhook) {
157
229
  context.addIssue({
158
230
  code: z.ZodIssueCode.custom,
159
231
  message: "surfaces must declare at least one surface"
@@ -47,6 +47,9 @@ export const createAgentCapabilities = (node, options = {}) => {
47
47
  if (node.surfaces?.discord) {
48
48
  capabilities.push(createCapability("surfaces.discord", "supported"));
49
49
  }
50
+ if (node.surfaces?.moltnet) {
51
+ capabilities.push(createCapability("surfaces.moltnet", "supported"));
52
+ }
50
53
  if (node.surfaces?.telegram) {
51
54
  capabilities.push(createCapability("surfaces.telegram", "supported"));
52
55
  }
@@ -4,6 +4,7 @@ import { SpawnfileError } from "../../shared/index.js";
4
4
  import { prepareOpenClawRuntimeAuth } from "./runAuth.js";
5
5
  import { createOpenClawAgentScaffold } from "./scaffold.js";
6
6
  import { assertSupportedOpenClawSurfaces, buildOpenClawChannelConfig, buildOpenClawSurfaceEnvBindings } from "./surfaces.js";
7
+ import { buildOpenClawMoltnetConfig, buildOpenClawMoltnetEnvBindings, validateOpenClawMoltnetRuntimeOptions } from "./moltnet.js";
7
8
  const buildEnvSecretRef = (envName) => ({
8
9
  id: envName,
9
10
  provider: "default",
@@ -53,7 +54,7 @@ const buildOpenClawConfig = (node) => {
53
54
  auth: {
54
55
  mode: "token"
55
56
  },
56
- bind: "lan",
57
+ bind: "loopback",
57
58
  controlUi: {
58
59
  allowedOrigins: [
59
60
  "http://127.0.0.1:<gateway-port>",
@@ -64,6 +65,14 @@ const buildOpenClawConfig = (node) => {
64
65
  port: "<gateway-port>"
65
66
  }
66
67
  };
68
+ if ((node.surfaces?.moltnet?.length ?? 0) > 0) {
69
+ config.hooks = {
70
+ allowRequestSessionKey: true,
71
+ allowedSessionKeyPrefixes: ["hook:"],
72
+ enabled: true,
73
+ token: "${OPENCLAW_HOOKS_TOKEN}"
74
+ };
75
+ }
67
76
  const channels = buildOpenClawChannelConfig(node.surfaces);
68
77
  if (Object.keys(channels).length > 0) {
69
78
  config.channels = channels;
@@ -73,6 +82,10 @@ const buildOpenClawConfig = (node) => {
73
82
  providers: modelConfig.providers
74
83
  };
75
84
  }
85
+ const moltnet = buildOpenClawMoltnetConfig(node.runtime.options);
86
+ if (moltnet) {
87
+ config.moltnet = moltnet;
88
+ }
76
89
  return `${JSON.stringify(config, null, 2)}\n`;
77
90
  };
78
91
  const createOpenClawStateFiles = () => [
@@ -89,8 +102,20 @@ const createOpenClawStateFiles = () => [
89
102
  ];
90
103
  const createContainerTargets = async (inputs) => inputs.map((input) => {
91
104
  const agent = input.kind === "agent" ? input.value : null;
105
+ const configEnvBindings = [
106
+ ...(agent?.surfaces?.moltnet
107
+ ? [
108
+ {
109
+ envName: "OPENCLAW_HOOKS_TOKEN",
110
+ jsonPath: "hooks.token"
111
+ }
112
+ ]
113
+ : []),
114
+ ...(buildOpenClawSurfaceEnvBindings(agent?.surfaces) ?? []),
115
+ ...(agent ? (buildOpenClawMoltnetEnvBindings(agent.runtime.options) ?? []) : [])
116
+ ];
92
117
  return {
93
- configEnvBindings: buildOpenClawSurfaceEnvBindings(agent?.surfaces),
118
+ ...(configEnvBindings.length > 0 ? { configEnvBindings } : {}),
94
119
  files: input.emittedFiles,
95
120
  id: `${input.kind}-${input.slug}`,
96
121
  sourceIds: [input.id]
@@ -139,6 +164,7 @@ export const openClawAdapter = {
139
164
  workspacePathTemplate: "<instance-root>/home/.openclaw/workspace"
140
165
  },
141
166
  port: 18789,
167
+ portStride: 20,
142
168
  portEnv: "OPENCLAW_GATEWAY_PORT",
143
169
  standaloneBaseImage: "node:24-bookworm-slim",
144
170
  startCommand: [
@@ -147,13 +173,19 @@ export const openClawAdapter = {
147
173
  "gateway",
148
174
  "--allow-unconfigured",
149
175
  "--bind",
150
- "lan",
176
+ "loopback",
151
177
  "--port",
152
178
  "<port>",
153
179
  "--verbose"
154
180
  ],
155
181
  systemDeps: ["bash", "ca-certificates", "curl", "git", "hostname", "openssl", "procps"]
156
182
  },
183
+ systemInstructionSurface: {
184
+ placement: "append_pointer",
185
+ resolvePath() {
186
+ return "workspace/AGENTS.md";
187
+ }
188
+ },
157
189
  async compileAgent(node) {
158
190
  return {
159
191
  capabilities: createAgentCapabilities(node, {
@@ -186,9 +218,10 @@ export const openClawAdapter = {
186
218
  prepareRuntimeAuth: prepareOpenClawRuntimeAuth,
187
219
  scaffoldAgentProject: createOpenClawAgentScaffold,
188
220
  validateRuntimeOptions(options) {
221
+ const diagnostics = validateOpenClawMoltnetRuntimeOptions(options).map((message) => createDiagnostic("error", message));
189
222
  if ("profile" in options && typeof options.profile !== "string") {
190
- return [createDiagnostic("error", "OpenClaw runtime option profile must be a string")];
223
+ diagnostics.push(createDiagnostic("error", "OpenClaw runtime option profile must be a string"));
191
224
  }
192
- return [];
225
+ return diagnostics;
193
226
  }
194
227
  };
@@ -0,0 +1,12 @@
1
+ import type { RuntimeContainerConfigEnvBinding } from "../types.js";
2
+ export interface OpenClawMoltnetRuntimeOptions {
3
+ baseUrl?: string;
4
+ enabled?: boolean;
5
+ networkId?: string;
6
+ token?: string;
7
+ tokenSecret?: string;
8
+ timeoutMs?: number;
9
+ }
10
+ export declare const validateOpenClawMoltnetRuntimeOptions: (options: Record<string, unknown>) => string[];
11
+ export declare const buildOpenClawMoltnetConfig: (options: Record<string, unknown>) => Record<string, unknown> | undefined;
12
+ export declare const buildOpenClawMoltnetEnvBindings: (options: Record<string, unknown>) => RuntimeContainerConfigEnvBinding[] | undefined;
@@ -0,0 +1,124 @@
1
+ const MOLTNET_OPTION_KEYS = new Set([
2
+ "base_url",
3
+ "enabled",
4
+ "network_id",
5
+ "timeout_ms",
6
+ "token",
7
+ "token_secret"
8
+ ]);
9
+ const parseMoltnetOptions = (options) => {
10
+ const raw = options.moltnet;
11
+ if (raw === undefined) {
12
+ return { errors: [] };
13
+ }
14
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
15
+ return {
16
+ errors: [
17
+ "OpenClaw runtime option moltnet must be an object with enabled/base_url/network_id/timeout_ms/token/token_secret"
18
+ ]
19
+ };
20
+ }
21
+ const value = raw;
22
+ const errors = [];
23
+ for (const key of Object.keys(value)) {
24
+ if (!MOLTNET_OPTION_KEYS.has(key)) {
25
+ errors.push(`OpenClaw runtime option moltnet.${key} is unsupported`);
26
+ }
27
+ }
28
+ const parsed = {};
29
+ if ("enabled" in value) {
30
+ if (typeof value.enabled !== "boolean") {
31
+ errors.push("OpenClaw runtime option moltnet.enabled must be a boolean");
32
+ }
33
+ else {
34
+ parsed.enabled = value.enabled;
35
+ }
36
+ }
37
+ if ("base_url" in value) {
38
+ if (typeof value.base_url !== "string" || value.base_url.trim().length === 0) {
39
+ errors.push("OpenClaw runtime option moltnet.base_url must be a non-empty string");
40
+ }
41
+ else {
42
+ try {
43
+ const url = new URL(value.base_url);
44
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
45
+ errors.push("OpenClaw runtime option moltnet.base_url must use http or https");
46
+ }
47
+ else {
48
+ parsed.baseUrl = value.base_url.trim().replace(/\/+$/, "");
49
+ }
50
+ }
51
+ catch {
52
+ errors.push("OpenClaw runtime option moltnet.base_url must be a valid URL");
53
+ }
54
+ }
55
+ }
56
+ if ("network_id" in value) {
57
+ if (typeof value.network_id !== "string" || value.network_id.trim().length === 0) {
58
+ errors.push("OpenClaw runtime option moltnet.network_id must be a non-empty string");
59
+ }
60
+ else {
61
+ parsed.networkId = value.network_id.trim();
62
+ }
63
+ }
64
+ if ("timeout_ms" in value) {
65
+ if (typeof value.timeout_ms !== "number" ||
66
+ !Number.isInteger(value.timeout_ms) ||
67
+ value.timeout_ms <= 0) {
68
+ errors.push("OpenClaw runtime option moltnet.timeout_ms must be a positive integer");
69
+ }
70
+ else {
71
+ parsed.timeoutMs = value.timeout_ms;
72
+ }
73
+ }
74
+ if ("token" in value) {
75
+ if (typeof value.token !== "string" || value.token.trim().length === 0) {
76
+ errors.push("OpenClaw runtime option moltnet.token must be a non-empty string");
77
+ }
78
+ else {
79
+ parsed.token = value.token.trim();
80
+ }
81
+ }
82
+ if ("token_secret" in value) {
83
+ if (typeof value.token_secret !== "string" || value.token_secret.trim().length === 0) {
84
+ errors.push("OpenClaw runtime option moltnet.token_secret must be a non-empty string");
85
+ }
86
+ else {
87
+ parsed.tokenSecret = value.token_secret.trim();
88
+ }
89
+ }
90
+ if (parsed.enabled === true && !parsed.baseUrl) {
91
+ errors.push("OpenClaw runtime option moltnet.base_url is required when moltnet.enabled=true");
92
+ }
93
+ if (parsed.token && parsed.tokenSecret) {
94
+ errors.push("OpenClaw runtime option moltnet must not declare both token and token_secret");
95
+ }
96
+ return errors.length > 0 ? { errors } : { errors, value: parsed };
97
+ };
98
+ export const validateOpenClawMoltnetRuntimeOptions = (options) => parseMoltnetOptions(options).errors;
99
+ export const buildOpenClawMoltnetConfig = (options) => {
100
+ const parsed = parseMoltnetOptions(options);
101
+ if (parsed.errors.length > 0 || !parsed.value) {
102
+ return undefined;
103
+ }
104
+ const config = {
105
+ ...(parsed.value.enabled !== undefined ? { enabled: parsed.value.enabled } : {}),
106
+ ...(parsed.value.baseUrl ? { baseUrl: parsed.value.baseUrl } : {}),
107
+ ...(parsed.value.networkId ? { networkId: parsed.value.networkId } : {}),
108
+ ...(parsed.value.timeoutMs !== undefined ? { timeoutMs: parsed.value.timeoutMs } : {}),
109
+ ...(parsed.value.token ? { token: parsed.value.token } : {})
110
+ };
111
+ return Object.keys(config).length > 0 ? config : undefined;
112
+ };
113
+ export const buildOpenClawMoltnetEnvBindings = (options) => {
114
+ const parsed = parseMoltnetOptions(options);
115
+ if (parsed.errors.length > 0 || !parsed.value?.tokenSecret) {
116
+ return undefined;
117
+ }
118
+ return [
119
+ {
120
+ envName: parsed.value.tokenSecret,
121
+ jsonPath: "moltnet.token"
122
+ }
123
+ ];
124
+ };
@@ -246,6 +246,9 @@ export const buildOpenClawSurfaceEnvBindings = (surfaces) => {
246
246
  return bindings.length > 0 ? bindings : undefined;
247
247
  };
248
248
  export const assertSupportedOpenClawSurfaces = (surfaces) => {
249
+ if (surfaces?.http) {
250
+ throw new SpawnfileError("validation_error", "OpenClaw does not support portable HTTP surfaces in Spawnfile v0.1");
251
+ }
249
252
  const discordAccess = surfaces?.discord?.access;
250
253
  if (discordAccess && discordAccess.mode === "allowlist" && discordAccess.channels.length > 0 && discordAccess.guilds.length !== 1) {
251
254
  throw new SpawnfileError("validation_error", "OpenClaw Discord channel allowlists require exactly one guild id");
@@ -4,6 +4,7 @@ import { createAgentCapabilities, createDiagnostic, createDocumentFiles, createS
4
4
  import { preparePicoClawRuntimeAuth } from "./runAuth.js";
5
5
  import { createPicoClawAgentScaffold } from "./scaffold.js";
6
6
  import { assertSupportedPicoClawSurfaces, buildPicoClawChannelConfig, buildPicoClawSurfaceEnvBindings } from "./surfaces.js";
7
+ import { PICOCLAW_GATEWAY_BASE_PORT, PICOCLAW_INTERNAL_PICO_TOKEN } from "./pico.js";
7
8
  const formatModelName = (node) => {
8
9
  const primary = node.execution?.model?.primary;
9
10
  if (!primary)
@@ -79,6 +80,21 @@ const buildMcpServers = (servers) => {
79
80
  }
80
81
  return result;
81
82
  };
83
+ const buildPicoClawToolsConfig = (node) => {
84
+ const tools = {};
85
+ if (node.surfaces?.moltnet) {
86
+ tools.exec = {
87
+ enabled: true
88
+ };
89
+ }
90
+ if (node.mcpServers.length > 0) {
91
+ tools.mcp = {
92
+ enabled: true,
93
+ servers: buildMcpServers(node.mcpServers)
94
+ };
95
+ }
96
+ return Object.keys(tools).length > 0 ? tools : undefined;
97
+ };
82
98
  const buildPicoClawConfig = (node) => {
83
99
  const modelName = formatModelName(node);
84
100
  const restrictToWorkspace = node.runtime.options.restrict_to_workspace ?? true;
@@ -98,13 +114,9 @@ const buildPicoClawConfig = (node) => {
98
114
  if (Object.keys(channels).length > 0) {
99
115
  config.channels = channels;
100
116
  }
101
- if (node.mcpServers.length > 0) {
102
- config.tools = {
103
- mcp: {
104
- enabled: true,
105
- servers: buildMcpServers(node.mcpServers)
106
- }
107
- };
117
+ const tools = buildPicoClawToolsConfig(node);
118
+ if (tools) {
119
+ config.tools = tools;
108
120
  }
109
121
  return `${JSON.stringify(config, null, 2)}\n`;
110
122
  };
@@ -163,15 +175,22 @@ export const picoClawAdapter = {
163
175
  homePathTemplate: "<instance-root>/picoclaw",
164
176
  workspacePathTemplate: "<instance-root>/picoclaw/workspace"
165
177
  },
166
- port: 18790,
178
+ port: PICOCLAW_GATEWAY_BASE_PORT,
167
179
  portEnv: "PICOCLAW_GATEWAY_PORT",
168
180
  standaloneBaseImage: "debian:bookworm-slim",
169
181
  startCommand: ["picoclaw", "gateway", "--allow-empty"],
170
182
  staticEnv: {
183
+ PICOCLAW_CHANNELS_PICO_TOKEN: PICOCLAW_INTERNAL_PICO_TOKEN,
171
184
  PICOCLAW_GATEWAY_HOST: "0.0.0.0"
172
185
  },
173
186
  systemDeps: ["bash", "ca-certificates", "curl", "nodejs", "npm", "tar"]
174
187
  },
188
+ systemInstructionSurface: {
189
+ placement: "append_pointer",
190
+ resolvePath() {
191
+ return "workspace/AGENTS.md";
192
+ }
193
+ },
175
194
  async compileAgent(node) {
176
195
  return {
177
196
  capabilities: createAgentCapabilities(node),
@@ -0,0 +1,2 @@
1
+ export declare const PICOCLAW_GATEWAY_BASE_PORT = 18990;
2
+ export declare const PICOCLAW_INTERNAL_PICO_TOKEN = "spawnfile-internal-pico";
@@ -0,0 +1,2 @@
1
+ export const PICOCLAW_GATEWAY_BASE_PORT = 18990;
2
+ export const PICOCLAW_INTERNAL_PICO_TOKEN = "spawnfile-internal-pico";