spawnfile 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +79 -396
  2. package/dist/cli/modelCommands.d.ts +3 -0
  3. package/dist/cli/modelCommands.js +68 -0
  4. package/dist/cli/runCli.d.ts +6 -1
  5. package/dist/cli/runCli.js +12 -67
  6. package/dist/cli/runtimeCommands.d.ts +3 -0
  7. package/dist/cli/runtimeCommands.js +20 -0
  8. package/dist/cli/surfaceCommands.d.ts +3 -0
  9. package/dist/cli/surfaceCommands.js +98 -0
  10. package/dist/compiler/agentSurfaces.js +51 -5
  11. package/dist/compiler/buildCompilePlan.js +36 -40
  12. package/dist/compiler/buildCompilePlanRuntime.d.ts +14 -0
  13. package/dist/compiler/buildCompilePlanRuntime.js +39 -0
  14. package/dist/compiler/buildCompilePlanTeams.d.ts +5 -0
  15. package/dist/compiler/buildCompilePlanTeams.js +38 -0
  16. package/dist/compiler/compilePlanHelpers.js +4 -1
  17. package/dist/compiler/compileProject.js +62 -13
  18. package/dist/compiler/compileProjectSupport.d.ts +17 -0
  19. package/dist/compiler/compileProjectSupport.js +136 -0
  20. package/dist/compiler/containerArtifacts.d.ts +6 -1
  21. package/dist/compiler/containerArtifacts.js +26 -4
  22. package/dist/compiler/containerArtifactsPlans.js +16 -1
  23. package/dist/compiler/containerArtifactsRender.d.ts +4 -2
  24. package/dist/compiler/containerArtifactsRender.js +21 -126
  25. package/dist/compiler/containerArtifactsTypes.d.ts +7 -0
  26. package/dist/compiler/containerEntrypointRender.d.ts +12 -0
  27. package/dist/compiler/containerEntrypointRender.js +186 -0
  28. package/dist/compiler/index.d.ts +2 -0
  29. package/dist/compiler/index.js +2 -0
  30. package/dist/compiler/interactiveSurfaceScopes.d.ts +2 -0
  31. package/dist/compiler/interactiveSurfaceScopes.js +21 -0
  32. package/dist/compiler/moltnetArtifacts.d.ts +27 -0
  33. package/dist/compiler/moltnetArtifacts.js +204 -0
  34. package/dist/compiler/moltnetBinaries.d.ts +4 -0
  35. package/dist/compiler/moltnetBinaries.js +103 -0
  36. package/dist/compiler/moltnetClientConfig.d.ts +11 -0
  37. package/dist/compiler/moltnetClientConfig.js +89 -0
  38. package/dist/compiler/moltnetRepresentativeResolution.d.ts +16 -0
  39. package/dist/compiler/moltnetRepresentativeResolution.js +86 -0
  40. package/dist/compiler/moltnetResolution.d.ts +3 -0
  41. package/dist/compiler/moltnetResolution.js +201 -0
  42. package/dist/compiler/runProject.js +1 -1
  43. package/dist/compiler/surfaceDefinitions.d.ts +55 -0
  44. package/dist/compiler/surfaceDefinitions.js +204 -0
  45. package/dist/compiler/teamContextHelpers.d.ts +18 -0
  46. package/dist/compiler/teamContextHelpers.js +112 -0
  47. package/dist/compiler/teamContextSupport.d.ts +4 -0
  48. package/dist/compiler/teamContextSupport.js +264 -0
  49. package/dist/compiler/teamContextSupport.testHelpers.d.ts +16 -0
  50. package/dist/compiler/teamContextSupport.testHelpers.js +68 -0
  51. package/dist/compiler/teamContextTypes.d.ts +28 -0
  52. package/dist/compiler/teamContextTypes.js +1 -0
  53. package/dist/compiler/teamRoster.d.ts +12 -0
  54. package/dist/compiler/teamRoster.js +48 -0
  55. package/dist/compiler/teamRosterEntries.d.ts +13 -0
  56. package/dist/compiler/teamRosterEntries.js +230 -0
  57. package/dist/compiler/teamRosterTypes.d.ts +45 -0
  58. package/dist/compiler/teamRosterTypes.js +1 -0
  59. package/dist/compiler/types.d.ts +72 -6
  60. package/dist/compiler/updateProjectRuntime.d.ts +9 -0
  61. package/dist/compiler/updateProjectRuntime.js +67 -0
  62. package/dist/compiler/updateProjectSurfaces.d.ts +8 -0
  63. package/dist/compiler/updateProjectSurfaces.js +106 -0
  64. package/dist/manifest/loadManifest.js +4 -4
  65. package/dist/manifest/renderSpawnfile.js +74 -8
  66. package/dist/manifest/scaffold.js +1 -3
  67. package/dist/manifest/schemas.d.ts +227 -17
  68. package/dist/manifest/schemas.js +62 -20
  69. package/dist/manifest/surfaceSchemas.d.ts +154 -0
  70. package/dist/manifest/surfaceSchemas.js +77 -5
  71. package/dist/runtime/common.js +3 -0
  72. package/dist/runtime/openclaw/adapter.js +38 -5
  73. package/dist/runtime/openclaw/moltnet.d.ts +12 -0
  74. package/dist/runtime/openclaw/moltnet.js +124 -0
  75. package/dist/runtime/openclaw/surfaces.js +3 -0
  76. package/dist/runtime/picoclaw/adapter.js +27 -8
  77. package/dist/runtime/picoclaw/pico.d.ts +2 -0
  78. package/dist/runtime/picoclaw/pico.js +2 -0
  79. package/dist/runtime/picoclaw/surfaces.js +11 -0
  80. package/dist/runtime/tinyclaw/adapter.js +22 -8
  81. package/dist/runtime/tinyclaw/runAuth.js +28 -1
  82. package/dist/runtime/tinyclaw/surfaces.js +8 -0
  83. package/dist/runtime/types.d.ts +11 -0
  84. package/package.json +4 -2
  85. package/runtimes.yaml +4 -4
@@ -220,6 +220,7 @@ const policySchema = z
220
220
  .strict();
221
221
  const commonManifestSchema = z
222
222
  .object({
223
+ description: z.string().optional(),
223
224
  docs: docsSchema.optional(),
224
225
  env: z.record(z.string(), z.string()).optional(),
225
226
  execution: executionSchema.optional(),
@@ -251,47 +252,67 @@ const sharedSurfaceSchema = z
251
252
  skills: z.array(skillReferenceSchema).optional()
252
253
  })
253
254
  .strict();
254
- const structureSchema = z
255
+ const memberSchema = z
255
256
  .object({
256
- external: z.array(z.string().min(1)).optional(),
257
- leader: z.string().min(1).optional(),
258
- mode: z.enum(["hierarchical", "swarm"])
257
+ id: z.string().min(1),
258
+ ref: z.string()
259
+ })
260
+ .strict();
261
+ const teamNetworkRoomSchema = z
262
+ .object({
263
+ id: z.string().min(1),
264
+ members: z.array(z.string().min(1)).min(1)
265
+ })
266
+ .strict();
267
+ const teamNetworkSchema = z
268
+ .object({
269
+ expose: z.boolean().optional(),
270
+ id: z.string().min(1),
271
+ name: z.string().min(1).optional(),
272
+ provider: z.literal("moltnet"),
273
+ rooms: z.array(teamNetworkRoomSchema).min(1)
259
274
  })
260
275
  .strict()
261
276
  .superRefine((value, context) => {
262
- if (value.mode === "hierarchical" && !value.leader) {
277
+ const roomIds = value.rooms.map((room) => room.id);
278
+ if (new Set(roomIds).size !== roomIds.length) {
263
279
  context.addIssue({
264
280
  code: z.ZodIssueCode.custom,
265
- message: "hierarchical teams must declare a leader"
266
- });
267
- }
268
- if (value.mode === "swarm" && value.leader) {
269
- context.addIssue({
270
- code: z.ZodIssueCode.custom,
271
- message: "swarm teams must not declare a leader"
281
+ message: `network ${value.id} declares duplicate room ids`
272
282
  });
273
283
  }
274
284
  });
275
- const memberSchema = z
276
- .object({
277
- id: z.string().min(1),
278
- ref: z.string()
279
- })
280
- .strict();
281
285
  const agentManifestSchema = commonManifestSchema
282
286
  .extend({
287
+ expose: z.boolean().optional(),
283
288
  kind: z.literal("agent"),
284
289
  subagents: z.array(subagentSchema).optional()
285
290
  })
286
291
  .strict();
287
292
  const teamManifestSchema = commonManifestSchema
288
293
  .extend({
294
+ external: z.array(z.string().min(1)).optional(),
289
295
  kind: z.literal("team"),
296
+ lead: z.string().min(1).optional(),
290
297
  members: z.array(memberSchema),
291
- shared: sharedSurfaceSchema.optional(),
292
- structure: structureSchema
298
+ mode: z.enum(["hierarchical", "swarm"]),
299
+ networks: z.array(teamNetworkSchema).optional(),
300
+ shared: sharedSurfaceSchema.optional()
293
301
  })
294
302
  .superRefine((value, context) => {
303
+ const memberIds = new Set(value.members.map((member) => member.id));
304
+ if (value.mode === "hierarchical" && !value.lead) {
305
+ context.addIssue({
306
+ code: z.ZodIssueCode.custom,
307
+ message: "hierarchical teams must declare lead"
308
+ });
309
+ }
310
+ if (value.mode === "swarm" && value.lead) {
311
+ context.addIssue({
312
+ code: z.ZodIssueCode.custom,
313
+ message: "swarm teams must not declare lead"
314
+ });
315
+ }
295
316
  if (value.execution !== undefined) {
296
317
  context.addIssue({
297
318
  code: z.ZodIssueCode.custom,
@@ -304,6 +325,27 @@ const teamManifestSchema = commonManifestSchema
304
325
  message: "team manifests must not declare surfaces"
305
326
  });
306
327
  }
328
+ if (value.networks) {
329
+ const networkIds = value.networks.map((network) => network.id);
330
+ if (new Set(networkIds).size !== networkIds.length) {
331
+ context.addIssue({
332
+ code: z.ZodIssueCode.custom,
333
+ message: "team manifests must not declare duplicate network ids"
334
+ });
335
+ }
336
+ for (const network of value.networks) {
337
+ for (const room of network.rooms) {
338
+ for (const memberId of room.members) {
339
+ if (!memberIds.has(memberId)) {
340
+ context.addIssue({
341
+ code: z.ZodIssueCode.custom,
342
+ message: `network ${network.id} room ${room.id} references unknown member ${memberId}`
343
+ });
344
+ }
345
+ }
346
+ }
347
+ }
348
+ }
307
349
  })
308
350
  .strict();
309
351
  export const manifestSchema = z.discriminatedUnion("kind", [
@@ -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;