clawchef 0.1.7 → 0.1.9

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.
@@ -55,6 +55,18 @@ function resolveWorkspacePath(recipeOrigin, name, configuredPath) {
55
55
  const workspaceName = trimmedName.startsWith("workspace-") ? trimmedName : `workspace-${trimmedName}`;
56
56
  return path.join(homedir(), ".openclaw", workspaceName);
57
57
  }
58
+ function resolveOpenClawRootPath(recipeOrigin, configuredPath) {
59
+ if (configuredPath?.trim()) {
60
+ if (path.isAbsolute(configuredPath)) {
61
+ return configuredPath;
62
+ }
63
+ if (recipeOrigin.kind === "local") {
64
+ return path.resolve(recipeOrigin.recipeDir, configuredPath);
65
+ }
66
+ return path.resolve(configuredPath);
67
+ }
68
+ return path.join(homedir(), ".openclaw");
69
+ }
58
70
  function isHttpUrl(value) {
59
71
  try {
60
72
  const url = new URL(value);
@@ -164,6 +176,72 @@ export async function runRecipe(recipe, recipeOrigin, options, logger) {
164
176
  await provider.installPlugin(recipe.openclaw, pluginSpec, options.dryRun);
165
177
  logger.info(`Plugin preinstalled: ${pluginSpec}`);
166
178
  }
179
+ const root = recipe.openclaw.root;
180
+ if (root && (root.assets?.trim() || (root.files?.length ?? 0) > 0)) {
181
+ if (remoteMode) {
182
+ throw new ClawChefError("openclaw.root assets/files are not supported with --provider remote");
183
+ }
184
+ const openclawRootPath = resolveOpenClawRootPath(recipeOrigin, root.path);
185
+ if (!options.dryRun) {
186
+ await mkdir(openclawRootPath, { recursive: true });
187
+ }
188
+ if (root.assets?.trim()) {
189
+ const resolvedAssets = resolveFileRef(recipeOrigin, root.assets);
190
+ if (resolvedAssets.kind !== "local") {
191
+ throw new ClawChefError(`openclaw.root.assets must resolve to a local directory: ${root.assets}. Direct URL recipes cannot use openclaw.root.assets.`);
192
+ }
193
+ let assetDirStat;
194
+ try {
195
+ assetDirStat = await stat(resolvedAssets.value);
196
+ }
197
+ catch (err) {
198
+ const message = err instanceof Error ? err.message : String(err);
199
+ throw new ClawChefError(`openclaw.root.assets path is not accessible: ${resolvedAssets.value} (${message})`);
200
+ }
201
+ if (!assetDirStat.isDirectory()) {
202
+ throw new ClawChefError(`openclaw.root.assets must be a directory: ${resolvedAssets.value}`);
203
+ }
204
+ const assetFiles = await collectLocalAssetFiles(resolvedAssets.value);
205
+ for (const assetFile of assetFiles) {
206
+ const target = path.resolve(openclawRootPath, assetFile.relativePath);
207
+ if (!options.dryRun) {
208
+ await mkdir(path.dirname(target), { recursive: true });
209
+ await copyFile(assetFile.absolutePath, target);
210
+ }
211
+ logger.info(`OpenClaw root asset copied: ${assetFile.relativePath}`);
212
+ }
213
+ }
214
+ for (const file of root.files ?? []) {
215
+ const target = path.resolve(openclawRootPath, file.path);
216
+ const targetDir = path.dirname(target);
217
+ if (!options.dryRun) {
218
+ await mkdir(targetDir, { recursive: true });
219
+ const alreadyExists = await exists(target);
220
+ if (alreadyExists && file.overwrite === false) {
221
+ logger.warn(`Skipping existing file: ${target}`);
222
+ }
223
+ else if (file.content !== undefined) {
224
+ await writeFile(target, file.content, "utf8");
225
+ }
226
+ else if (file.content_from) {
227
+ const rawContent = await readTextFromRef(recipeOrigin, file.content_from);
228
+ const content = renderTemplateString(rawContent, options.vars, options.allowMissing);
229
+ await writeFile(target, content, "utf8");
230
+ }
231
+ else if (file.source) {
232
+ const resolved = resolveFileRef(recipeOrigin, file.source);
233
+ if (resolved.kind === "local") {
234
+ await copyFile(resolved.value, target);
235
+ }
236
+ else {
237
+ const content = await readBinaryFromRef(recipeOrigin, file.source);
238
+ await writeFile(target, content);
239
+ }
240
+ }
241
+ }
242
+ logger.info(`OpenClaw root file materialized: ${file.path}`);
243
+ }
244
+ }
167
245
  for (const ws of recipe.workspaces ?? []) {
168
246
  const absPath = resolveWorkspacePath(recipeOrigin, ws.name, ws.path);
169
247
  workspacePaths.set(ws.name, absPath);
@@ -215,8 +293,15 @@ export async function runRecipe(recipe, recipeOrigin, options, logger) {
215
293
  logger.info(`Agent created: ${agent.workspace}/${agent.name}`);
216
294
  }
217
295
  for (const channel of recipe.channels ?? []) {
218
- await provider.configureChannel(recipe.openclaw, channel, options.dryRun);
219
- logger.info(`Channel configured: ${channel.channel}${channel.account ? `/${channel.account}` : ""}`);
296
+ const effectiveChannel = channel.agent?.trim() && !channel.account?.trim()
297
+ ? { ...channel, account: channel.agent.trim() }
298
+ : channel;
299
+ await provider.configureChannel(recipe.openclaw, effectiveChannel, options.dryRun);
300
+ logger.info(`Channel configured: ${effectiveChannel.channel}${effectiveChannel.account ? `/${effectiveChannel.account}` : ""}`);
301
+ if (effectiveChannel.agent?.trim()) {
302
+ await provider.bindChannelAgent(recipe.openclaw, effectiveChannel, effectiveChannel.agent, options.dryRun);
303
+ logger.info(`Channel bound to agent: ${effectiveChannel.channel}${effectiveChannel.account ? `/${effectiveChannel.account}` : ""} -> ${effectiveChannel.agent}`);
304
+ }
220
305
  }
221
306
  for (const workspace of recipe.workspaces ?? []) {
222
307
  const wsPath = workspacePaths.get(workspace.name);
@@ -316,17 +401,25 @@ export async function runRecipe(recipe, recipeOrigin, options, logger) {
316
401
  }
317
402
  logger.info(`Preset messages sent: ${conv.workspace}/${conv.agent}`);
318
403
  }
319
- await provider.startGateway(recipe.openclaw, options.dryRun);
320
- logger.info("Gateway started");
404
+ await provider.startGateway(recipe.openclaw, options.gatewayMode, options.dryRun);
405
+ if (options.gatewayMode === "none") {
406
+ logger.info("Gateway start skipped by gateway mode: none");
407
+ }
408
+ else {
409
+ logger.info(`Gateway started (${options.gatewayMode})`);
410
+ }
321
411
  for (const channel of recipe.channels ?? []) {
322
- if (!channel.login) {
412
+ const effectiveChannel = channel.agent?.trim() && !channel.account?.trim()
413
+ ? { ...channel, account: channel.agent.trim() }
414
+ : channel;
415
+ if (!effectiveChannel.login) {
323
416
  continue;
324
417
  }
325
418
  if (!options.dryRun && !input.isTTY) {
326
- throw new ClawChefError(`Channel login for ${channel.channel} requires an interactive terminal session`);
419
+ throw new ClawChefError(`Channel login for ${effectiveChannel.channel} requires an interactive terminal session`);
327
420
  }
328
- await provider.loginChannel(recipe.openclaw, channel, options.dryRun);
329
- logger.info(`Channel login completed: ${channel.channel}${channel.account ? `/${channel.account}` : ""}`);
421
+ await provider.loginChannel(recipe.openclaw, effectiveChannel, options.dryRun);
422
+ logger.info(`Channel login completed: ${effectiveChannel.channel}${effectiveChannel.account ? `/${effectiveChannel.account}` : ""}`);
330
423
  }
331
424
  logger.info("Recipe execution completed");
332
425
  }
package/dist/recipe.js CHANGED
@@ -145,12 +145,26 @@ function filterRecipeByWorkspaceName(recipe, workspaceName) {
145
145
  }
146
146
  function semanticValidate(recipe) {
147
147
  const ws = new Set((recipe.workspaces ?? []).map((w) => w.name));
148
+ const agentNameCounts = new Map();
149
+ const root = recipe.openclaw.root;
150
+ if (root?.path !== undefined && !root.path.trim()) {
151
+ throw new ClawChefError("openclaw.root.path cannot be empty");
152
+ }
153
+ if (root?.assets !== undefined && !root.assets.trim()) {
154
+ throw new ClawChefError("openclaw.root.assets cannot be empty");
155
+ }
156
+ for (const file of root?.files ?? []) {
157
+ if (!file.path.trim()) {
158
+ throw new ClawChefError("openclaw.root.files[] has file with empty path");
159
+ }
160
+ }
148
161
  for (const workspace of recipe.workspaces ?? []) {
149
162
  if (workspace.assets !== undefined && !workspace.assets.trim()) {
150
163
  throw new ClawChefError(`Workspace ${workspace.name} has empty assets path`);
151
164
  }
152
165
  }
153
166
  for (const agent of recipe.agents ?? []) {
167
+ agentNameCounts.set(agent.name, (agentNameCounts.get(agent.name) ?? 0) + 1);
154
168
  if (!ws.has(agent.workspace)) {
155
169
  throw new ClawChefError(`Agent ${agent.name} references missing workspace: ${agent.workspace}`);
156
170
  }
@@ -179,6 +193,18 @@ function semanticValidate(recipe) {
179
193
  (channel.login || channel.login_mode !== undefined || channel.login_account !== undefined)) {
180
194
  throw new ClawChefError("channels[] entry for telegram does not support login/login_mode/login_account. Configure token (or use_env/token_file), then start gateway.");
181
195
  }
196
+ if (channel.agent?.trim()) {
197
+ if (channel.channel !== "telegram") {
198
+ throw new ClawChefError(`channels[] entry for ${channel.channel} does not support agent binding. Use channel: telegram with agent.`);
199
+ }
200
+ const matched = agentNameCounts.get(channel.agent) ?? 0;
201
+ if (matched === 0) {
202
+ throw new ClawChefError(`channels[] entry references missing agent by name: ${channel.agent}`);
203
+ }
204
+ if (matched > 1) {
205
+ throw new ClawChefError(`channels[] entry references duplicate agent name: ${channel.agent}. Agent names must be unique for channel binding.`);
206
+ }
207
+ }
182
208
  const hasAuth = Boolean(channel.use_env) ||
183
209
  Boolean(channel.token?.trim()) ||
184
210
  Boolean(channel.token_file?.trim()) ||
package/dist/schema.d.ts CHANGED
@@ -20,6 +20,61 @@ export declare const recipeSchema: z.ZodObject<{
20
20
  version: z.ZodString;
21
21
  install: z.ZodOptional<z.ZodEnum<["auto", "always", "never"]>>;
22
22
  plugins: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
23
+ root: z.ZodOptional<z.ZodObject<{
24
+ path: z.ZodOptional<z.ZodString>;
25
+ assets: z.ZodOptional<z.ZodString>;
26
+ files: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodObject<{
27
+ path: z.ZodString;
28
+ content: z.ZodOptional<z.ZodString>;
29
+ content_from: z.ZodOptional<z.ZodString>;
30
+ source: z.ZodOptional<z.ZodString>;
31
+ overwrite: z.ZodOptional<z.ZodBoolean>;
32
+ }, "strict", z.ZodTypeAny, {
33
+ path: string;
34
+ content?: string | undefined;
35
+ overwrite?: boolean | undefined;
36
+ content_from?: string | undefined;
37
+ source?: string | undefined;
38
+ }, {
39
+ path: string;
40
+ content?: string | undefined;
41
+ overwrite?: boolean | undefined;
42
+ content_from?: string | undefined;
43
+ source?: string | undefined;
44
+ }>, {
45
+ path: string;
46
+ content?: string | undefined;
47
+ overwrite?: boolean | undefined;
48
+ content_from?: string | undefined;
49
+ source?: string | undefined;
50
+ }, {
51
+ path: string;
52
+ content?: string | undefined;
53
+ overwrite?: boolean | undefined;
54
+ content_from?: string | undefined;
55
+ source?: string | undefined;
56
+ }>, "many">>;
57
+ }, "strict", z.ZodTypeAny, {
58
+ path?: string | undefined;
59
+ files?: {
60
+ path: string;
61
+ content?: string | undefined;
62
+ overwrite?: boolean | undefined;
63
+ content_from?: string | undefined;
64
+ source?: string | undefined;
65
+ }[] | undefined;
66
+ assets?: string | undefined;
67
+ }, {
68
+ path?: string | undefined;
69
+ files?: {
70
+ path: string;
71
+ content?: string | undefined;
72
+ overwrite?: boolean | undefined;
73
+ content_from?: string | undefined;
74
+ source?: string | undefined;
75
+ }[] | undefined;
76
+ assets?: string | undefined;
77
+ }>>;
23
78
  bootstrap: z.ZodOptional<z.ZodObject<{
24
79
  non_interactive: z.ZodOptional<z.ZodBoolean>;
25
80
  accept_risk: z.ZodOptional<z.ZodBoolean>;
@@ -48,9 +103,9 @@ export declare const recipeSchema: z.ZodObject<{
48
103
  token_provider?: string | undefined;
49
104
  token_profile_id?: string | undefined;
50
105
  llm_api_key?: string | undefined;
106
+ mode?: "remote" | "local" | undefined;
51
107
  non_interactive?: boolean | undefined;
52
108
  accept_risk?: boolean | undefined;
53
- mode?: "remote" | "local" | undefined;
54
109
  flow?: "quickstart" | "advanced" | "manual" | undefined;
55
110
  auth_choice?: string | undefined;
56
111
  reset?: boolean | undefined;
@@ -68,9 +123,9 @@ export declare const recipeSchema: z.ZodObject<{
68
123
  token_provider?: string | undefined;
69
124
  token_profile_id?: string | undefined;
70
125
  llm_api_key?: string | undefined;
126
+ mode?: "remote" | "local" | undefined;
71
127
  non_interactive?: boolean | undefined;
72
128
  accept_risk?: boolean | undefined;
73
- mode?: "remote" | "local" | undefined;
74
129
  flow?: "quickstart" | "advanced" | "manual" | undefined;
75
130
  auth_choice?: string | undefined;
76
131
  reset?: boolean | undefined;
@@ -88,7 +143,9 @@ export declare const recipeSchema: z.ZodObject<{
88
143
  install_plugin: z.ZodOptional<z.ZodString>;
89
144
  factory_reset: z.ZodOptional<z.ZodString>;
90
145
  start_gateway: z.ZodOptional<z.ZodString>;
146
+ run_gateway: z.ZodOptional<z.ZodString>;
91
147
  enable_plugin: z.ZodOptional<z.ZodString>;
148
+ bind_channel_agent: z.ZodOptional<z.ZodString>;
92
149
  login_channel: z.ZodOptional<z.ZodString>;
93
150
  create_workspace: z.ZodOptional<z.ZodString>;
94
151
  create_agent: z.ZodOptional<z.ZodString>;
@@ -102,7 +159,9 @@ export declare const recipeSchema: z.ZodObject<{
102
159
  install_plugin?: string | undefined;
103
160
  factory_reset?: string | undefined;
104
161
  start_gateway?: string | undefined;
162
+ run_gateway?: string | undefined;
105
163
  enable_plugin?: string | undefined;
164
+ bind_channel_agent?: string | undefined;
106
165
  login_channel?: string | undefined;
107
166
  create_agent?: string | undefined;
108
167
  install_skill?: string | undefined;
@@ -116,7 +175,9 @@ export declare const recipeSchema: z.ZodObject<{
116
175
  install_plugin?: string | undefined;
117
176
  factory_reset?: string | undefined;
118
177
  start_gateway?: string | undefined;
178
+ run_gateway?: string | undefined;
119
179
  enable_plugin?: string | undefined;
180
+ bind_channel_agent?: string | undefined;
120
181
  login_channel?: string | undefined;
121
182
  create_agent?: string | undefined;
122
183
  install_skill?: string | undefined;
@@ -129,6 +190,17 @@ export declare const recipeSchema: z.ZodObject<{
129
190
  bin?: string | undefined;
130
191
  install?: "auto" | "always" | "never" | undefined;
131
192
  plugins?: string[] | undefined;
193
+ root?: {
194
+ path?: string | undefined;
195
+ files?: {
196
+ path: string;
197
+ content?: string | undefined;
198
+ overwrite?: boolean | undefined;
199
+ content_from?: string | undefined;
200
+ source?: string | undefined;
201
+ }[] | undefined;
202
+ assets?: string | undefined;
203
+ } | undefined;
132
204
  bootstrap?: {
133
205
  workspace?: string | undefined;
134
206
  cloudflare_ai_gateway_account_id?: string | undefined;
@@ -137,9 +209,9 @@ export declare const recipeSchema: z.ZodObject<{
137
209
  token_provider?: string | undefined;
138
210
  token_profile_id?: string | undefined;
139
211
  llm_api_key?: string | undefined;
212
+ mode?: "remote" | "local" | undefined;
140
213
  non_interactive?: boolean | undefined;
141
214
  accept_risk?: boolean | undefined;
142
- mode?: "remote" | "local" | undefined;
143
215
  flow?: "quickstart" | "advanced" | "manual" | undefined;
144
216
  auth_choice?: string | undefined;
145
217
  reset?: boolean | undefined;
@@ -157,7 +229,9 @@ export declare const recipeSchema: z.ZodObject<{
157
229
  install_plugin?: string | undefined;
158
230
  factory_reset?: string | undefined;
159
231
  start_gateway?: string | undefined;
232
+ run_gateway?: string | undefined;
160
233
  enable_plugin?: string | undefined;
234
+ bind_channel_agent?: string | undefined;
161
235
  login_channel?: string | undefined;
162
236
  create_agent?: string | undefined;
163
237
  install_skill?: string | undefined;
@@ -170,6 +244,17 @@ export declare const recipeSchema: z.ZodObject<{
170
244
  bin?: string | undefined;
171
245
  install?: "auto" | "always" | "never" | undefined;
172
246
  plugins?: string[] | undefined;
247
+ root?: {
248
+ path?: string | undefined;
249
+ files?: {
250
+ path: string;
251
+ content?: string | undefined;
252
+ overwrite?: boolean | undefined;
253
+ content_from?: string | undefined;
254
+ source?: string | undefined;
255
+ }[] | undefined;
256
+ assets?: string | undefined;
257
+ } | undefined;
173
258
  bootstrap?: {
174
259
  workspace?: string | undefined;
175
260
  cloudflare_ai_gateway_account_id?: string | undefined;
@@ -178,9 +263,9 @@ export declare const recipeSchema: z.ZodObject<{
178
263
  token_provider?: string | undefined;
179
264
  token_profile_id?: string | undefined;
180
265
  llm_api_key?: string | undefined;
266
+ mode?: "remote" | "local" | undefined;
181
267
  non_interactive?: boolean | undefined;
182
268
  accept_risk?: boolean | undefined;
183
- mode?: "remote" | "local" | undefined;
184
269
  flow?: "quickstart" | "advanced" | "manual" | undefined;
185
270
  auth_choice?: string | undefined;
186
271
  reset?: boolean | undefined;
@@ -198,7 +283,9 @@ export declare const recipeSchema: z.ZodObject<{
198
283
  install_plugin?: string | undefined;
199
284
  factory_reset?: string | undefined;
200
285
  start_gateway?: string | undefined;
286
+ run_gateway?: string | undefined;
201
287
  enable_plugin?: string | undefined;
288
+ bind_channel_agent?: string | undefined;
202
289
  login_channel?: string | undefined;
203
290
  create_agent?: string | undefined;
204
291
  install_skill?: string | undefined;
@@ -268,6 +355,7 @@ export declare const recipeSchema: z.ZodObject<{
268
355
  channels: z.ZodOptional<z.ZodArray<z.ZodObject<{
269
356
  channel: z.ZodString;
270
357
  account: z.ZodOptional<z.ZodString>;
358
+ agent: z.ZodOptional<z.ZodString>;
271
359
  login: z.ZodOptional<z.ZodBoolean>;
272
360
  login_mode: z.ZodOptional<z.ZodEnum<["interactive"]>>;
273
361
  login_account: z.ZodOptional<z.ZodString>;
@@ -287,6 +375,7 @@ export declare const recipeSchema: z.ZodObject<{
287
375
  channel: string;
288
376
  token?: string | undefined;
289
377
  account?: string | undefined;
378
+ agent?: string | undefined;
290
379
  login?: boolean | undefined;
291
380
  login_mode?: "interactive" | undefined;
292
381
  login_account?: string | undefined;
@@ -305,6 +394,7 @@ export declare const recipeSchema: z.ZodObject<{
305
394
  channel: string;
306
395
  token?: string | undefined;
307
396
  account?: string | undefined;
397
+ agent?: string | undefined;
308
398
  login?: boolean | undefined;
309
399
  login_mode?: "interactive" | undefined;
310
400
  login_account?: string | undefined;
@@ -408,6 +498,17 @@ export declare const recipeSchema: z.ZodObject<{
408
498
  bin?: string | undefined;
409
499
  install?: "auto" | "always" | "never" | undefined;
410
500
  plugins?: string[] | undefined;
501
+ root?: {
502
+ path?: string | undefined;
503
+ files?: {
504
+ path: string;
505
+ content?: string | undefined;
506
+ overwrite?: boolean | undefined;
507
+ content_from?: string | undefined;
508
+ source?: string | undefined;
509
+ }[] | undefined;
510
+ assets?: string | undefined;
511
+ } | undefined;
411
512
  bootstrap?: {
412
513
  workspace?: string | undefined;
413
514
  cloudflare_ai_gateway_account_id?: string | undefined;
@@ -416,9 +517,9 @@ export declare const recipeSchema: z.ZodObject<{
416
517
  token_provider?: string | undefined;
417
518
  token_profile_id?: string | undefined;
418
519
  llm_api_key?: string | undefined;
520
+ mode?: "remote" | "local" | undefined;
419
521
  non_interactive?: boolean | undefined;
420
522
  accept_risk?: boolean | undefined;
421
- mode?: "remote" | "local" | undefined;
422
523
  flow?: "quickstart" | "advanced" | "manual" | undefined;
423
524
  auth_choice?: string | undefined;
424
525
  reset?: boolean | undefined;
@@ -436,7 +537,9 @@ export declare const recipeSchema: z.ZodObject<{
436
537
  install_plugin?: string | undefined;
437
538
  factory_reset?: string | undefined;
438
539
  start_gateway?: string | undefined;
540
+ run_gateway?: string | undefined;
439
541
  enable_plugin?: string | undefined;
542
+ bind_channel_agent?: string | undefined;
440
543
  login_channel?: string | undefined;
441
544
  create_agent?: string | undefined;
442
545
  install_skill?: string | undefined;
@@ -468,6 +571,7 @@ export declare const recipeSchema: z.ZodObject<{
468
571
  channel: string;
469
572
  token?: string | undefined;
470
573
  account?: string | undefined;
574
+ agent?: string | undefined;
471
575
  login?: boolean | undefined;
472
576
  login_mode?: "interactive" | undefined;
473
577
  login_account?: string | undefined;
@@ -509,6 +613,17 @@ export declare const recipeSchema: z.ZodObject<{
509
613
  bin?: string | undefined;
510
614
  install?: "auto" | "always" | "never" | undefined;
511
615
  plugins?: string[] | undefined;
616
+ root?: {
617
+ path?: string | undefined;
618
+ files?: {
619
+ path: string;
620
+ content?: string | undefined;
621
+ overwrite?: boolean | undefined;
622
+ content_from?: string | undefined;
623
+ source?: string | undefined;
624
+ }[] | undefined;
625
+ assets?: string | undefined;
626
+ } | undefined;
512
627
  bootstrap?: {
513
628
  workspace?: string | undefined;
514
629
  cloudflare_ai_gateway_account_id?: string | undefined;
@@ -517,9 +632,9 @@ export declare const recipeSchema: z.ZodObject<{
517
632
  token_provider?: string | undefined;
518
633
  token_profile_id?: string | undefined;
519
634
  llm_api_key?: string | undefined;
635
+ mode?: "remote" | "local" | undefined;
520
636
  non_interactive?: boolean | undefined;
521
637
  accept_risk?: boolean | undefined;
522
- mode?: "remote" | "local" | undefined;
523
638
  flow?: "quickstart" | "advanced" | "manual" | undefined;
524
639
  auth_choice?: string | undefined;
525
640
  reset?: boolean | undefined;
@@ -537,7 +652,9 @@ export declare const recipeSchema: z.ZodObject<{
537
652
  install_plugin?: string | undefined;
538
653
  factory_reset?: string | undefined;
539
654
  start_gateway?: string | undefined;
655
+ run_gateway?: string | undefined;
540
656
  enable_plugin?: string | undefined;
657
+ bind_channel_agent?: string | undefined;
541
658
  login_channel?: string | undefined;
542
659
  create_agent?: string | undefined;
543
660
  install_skill?: string | undefined;
@@ -569,6 +686,7 @@ export declare const recipeSchema: z.ZodObject<{
569
686
  channel: string;
570
687
  token?: string | undefined;
571
688
  account?: string | undefined;
689
+ agent?: string | undefined;
572
690
  login?: boolean | undefined;
573
691
  login_mode?: "interactive" | undefined;
574
692
  login_account?: string | undefined;
package/dist/schema.js CHANGED
@@ -12,7 +12,9 @@ const openClawCommandsSchema = z
12
12
  install_plugin: z.string().optional(),
13
13
  factory_reset: z.string().optional(),
14
14
  start_gateway: z.string().optional(),
15
+ run_gateway: z.string().optional(),
15
16
  enable_plugin: z.string().optional(),
17
+ bind_channel_agent: z.string().optional(),
16
18
  login_channel: z.string().optional(),
17
19
  create_workspace: z.string().optional(),
18
20
  create_agent: z.string().optional(),
@@ -44,12 +46,32 @@ const openClawBootstrapSchema = z
44
46
  token_profile_id: z.string().optional(),
45
47
  })
46
48
  .strict();
49
+ const rootFileSchema = z
50
+ .object({
51
+ path: z.string().min(1),
52
+ content: z.string().optional(),
53
+ content_from: z.string().min(1).optional(),
54
+ source: z.string().optional(),
55
+ overwrite: z.boolean().optional(),
56
+ })
57
+ .strict()
58
+ .refine((v) => [v.content, v.content_from, v.source].filter((item) => item !== undefined).length === 1, {
59
+ message: "openclaw.root.files[] requires exactly one of content, content_from, or source",
60
+ });
61
+ const openClawRootSchema = z
62
+ .object({
63
+ path: z.string().min(1).optional(),
64
+ assets: z.string().min(1).optional(),
65
+ files: z.array(rootFileSchema).optional(),
66
+ })
67
+ .strict();
47
68
  const openClawSchema = z
48
69
  .object({
49
70
  bin: z.string().optional(),
50
71
  version: z.string(),
51
72
  install: z.enum(["auto", "always", "never"]).optional(),
52
73
  plugins: z.array(z.string().min(1)).optional(),
74
+ root: openClawRootSchema.optional(),
53
75
  bootstrap: openClawBootstrapSchema.optional(),
54
76
  commands: openClawCommandsSchema.optional(),
55
77
  })
@@ -79,6 +101,7 @@ const channelSchema = z
79
101
  .object({
80
102
  channel: z.string().min(1),
81
103
  account: z.string().min(1).optional(),
104
+ agent: z.string().min(1).optional(),
82
105
  login: z.boolean().optional(),
83
106
  login_mode: z.enum(["interactive"]).optional(),
84
107
  login_account: z.string().min(1).optional(),
package/dist/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export type InstallPolicy = "auto" | "always" | "never";
2
2
  export type OpenClawProvider = "command" | "mock" | "remote";
3
3
  export type RunScope = "full" | "files" | "workspace";
4
+ export type GatewayMode = "service" | "run" | "none";
4
5
  export interface OpenClawRemoteConfig {
5
6
  base_url: string;
6
7
  api_key?: string;
@@ -21,7 +22,9 @@ export interface OpenClawCommandOverrides {
21
22
  install_plugin?: string;
22
23
  factory_reset?: string;
23
24
  start_gateway?: string;
25
+ run_gateway?: string;
24
26
  enable_plugin?: string;
27
+ bind_channel_agent?: string;
25
28
  login_channel?: string;
26
29
  create_workspace?: string;
27
30
  create_agent?: string;
@@ -55,9 +58,15 @@ export interface OpenClawSection {
55
58
  version: string;
56
59
  install?: InstallPolicy;
57
60
  plugins?: string[];
61
+ root?: OpenClawRootDef;
58
62
  bootstrap?: OpenClawBootstrap;
59
63
  commands?: OpenClawCommandOverrides;
60
64
  }
65
+ export interface OpenClawRootDef {
66
+ path?: string;
67
+ assets?: string;
68
+ files?: WorkspaceFileDef[];
69
+ }
61
70
  export interface WorkspaceDef {
62
71
  name: string;
63
72
  path?: string;
@@ -67,6 +76,7 @@ export interface WorkspaceDef {
67
76
  export interface ChannelDef {
68
77
  channel: string;
69
78
  account?: string;
79
+ agent?: string;
70
80
  login?: boolean;
71
81
  login_mode?: "interactive";
72
82
  login_account?: string;
@@ -127,6 +137,7 @@ export interface RunOptions {
127
137
  plugins: string[];
128
138
  scope: RunScope;
129
139
  workspaceName?: string;
140
+ gatewayMode: GatewayMode;
130
141
  dryRun: boolean;
131
142
  allowMissing: boolean;
132
143
  verbose: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawchef",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Recipe-driven OpenClaw environment orchestrator",
5
5
  "homepage": "https://renorzr.github.io/clawchef",
6
6
  "repository": {
@@ -12,6 +12,13 @@ params:
12
12
  openclaw:
13
13
  version: "${openclaw_version}"
14
14
  install: "auto"
15
+ root:
16
+ files:
17
+ - path: "AGENTS.md"
18
+ overwrite: true
19
+ content: |
20
+ # OpenClaw Root
21
+ Project: ${project_name}
15
22
 
16
23
  workspaces:
17
24
  - name: "${project_name}"
package/src/api.ts CHANGED
@@ -6,7 +6,7 @@ import { runRecipe } from "./orchestrator.js";
6
6
  import { loadRecipe, loadRecipeText } from "./recipe.js";
7
7
  import { scaffoldProject } from "./scaffold.js";
8
8
  import { recipeSchema } from "./schema.js";
9
- import type { OpenClawProvider, OpenClawRemoteConfig, RunOptions, RunScope } from "./types.js";
9
+ import type { GatewayMode, OpenClawProvider, OpenClawRemoteConfig, RunOptions, RunScope } from "./types.js";
10
10
  import type { ScaffoldOptions, ScaffoldResult } from "./scaffold.js";
11
11
 
12
12
  export interface CookOptions {
@@ -18,6 +18,7 @@ export interface CookOptions {
18
18
  silent?: boolean;
19
19
  scope?: RunScope;
20
20
  workspaceName?: string;
21
+ gatewayMode?: GatewayMode;
21
22
  provider?: OpenClawProvider;
22
23
  remote?: Partial<OpenClawRemoteConfig>;
23
24
  envFile?: string;
@@ -39,6 +40,7 @@ function normalizeCookOptions(options: CookOptions): RunOptions {
39
40
  plugins,
40
41
  scope,
41
42
  workspaceName,
43
+ gatewayMode: options.gatewayMode ?? "service",
42
44
  dryRun: Boolean(options.dryRun),
43
45
  allowMissing: Boolean(options.allowMissing),
44
46
  verbose: Boolean(options.verbose),