clawchef 0.1.0

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 (62) hide show
  1. package/README.md +405 -0
  2. package/dist/api.d.ts +14 -0
  3. package/dist/api.js +49 -0
  4. package/dist/assertions.d.ts +2 -0
  5. package/dist/assertions.js +32 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +115 -0
  8. package/dist/env.d.ts +1 -0
  9. package/dist/env.js +14 -0
  10. package/dist/errors.d.ts +3 -0
  11. package/dist/errors.js +6 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +18 -0
  14. package/dist/logger.d.ts +7 -0
  15. package/dist/logger.js +17 -0
  16. package/dist/openclaw/command-provider.d.ts +15 -0
  17. package/dist/openclaw/command-provider.js +489 -0
  18. package/dist/openclaw/factory.d.ts +3 -0
  19. package/dist/openclaw/factory.js +13 -0
  20. package/dist/openclaw/mock-provider.d.ts +15 -0
  21. package/dist/openclaw/mock-provider.js +65 -0
  22. package/dist/openclaw/provider.d.ts +20 -0
  23. package/dist/openclaw/provider.js +1 -0
  24. package/dist/openclaw/remote-provider.d.ts +19 -0
  25. package/dist/openclaw/remote-provider.js +158 -0
  26. package/dist/orchestrator.d.ts +4 -0
  27. package/dist/orchestrator.js +243 -0
  28. package/dist/recipe.d.ts +20 -0
  29. package/dist/recipe.js +522 -0
  30. package/dist/schema.d.ts +626 -0
  31. package/dist/schema.js +143 -0
  32. package/dist/template.d.ts +2 -0
  33. package/dist/template.js +30 -0
  34. package/dist/types.d.ts +136 -0
  35. package/dist/types.js +1 -0
  36. package/package.json +41 -0
  37. package/recipes/content-from-sample.yaml +20 -0
  38. package/recipes/openclaw-from-zero.yaml +45 -0
  39. package/recipes/openclaw-local.yaml +65 -0
  40. package/recipes/openclaw-remote-http.yaml +38 -0
  41. package/recipes/openclaw-telegram-mock.yaml +22 -0
  42. package/recipes/openclaw-telegram.yaml +19 -0
  43. package/recipes/sample.yaml +49 -0
  44. package/recipes/snippets/readme-template.md +3 -0
  45. package/src/api.ts +65 -0
  46. package/src/assertions.ts +37 -0
  47. package/src/cli.ts +123 -0
  48. package/src/env.ts +16 -0
  49. package/src/errors.ts +6 -0
  50. package/src/index.ts +20 -0
  51. package/src/logger.ts +17 -0
  52. package/src/openclaw/command-provider.ts +594 -0
  53. package/src/openclaw/factory.ts +16 -0
  54. package/src/openclaw/mock-provider.ts +104 -0
  55. package/src/openclaw/provider.ts +44 -0
  56. package/src/openclaw/remote-provider.ts +264 -0
  57. package/src/orchestrator.ts +271 -0
  58. package/src/recipe.ts +621 -0
  59. package/src/schema.ts +157 -0
  60. package/src/template.ts +41 -0
  61. package/src/types.ts +150 -0
  62. package/tsconfig.json +16 -0
package/dist/schema.js ADDED
@@ -0,0 +1,143 @@
1
+ import { z } from "zod";
2
+ const paramDefSchema = z.object({
3
+ default: z.string().optional(),
4
+ required: z.boolean().optional(),
5
+ description: z.string().optional(),
6
+ });
7
+ const openClawCommandsSchema = z
8
+ .object({
9
+ use_version: z.string().optional(),
10
+ install_version: z.string().optional(),
11
+ uninstall_version: z.string().optional(),
12
+ factory_reset: z.string().optional(),
13
+ start_gateway: z.string().optional(),
14
+ enable_plugin: z.string().optional(),
15
+ login_channel: z.string().optional(),
16
+ create_workspace: z.string().optional(),
17
+ create_agent: z.string().optional(),
18
+ install_skill: z.string().optional(),
19
+ send_message: z.string().optional(),
20
+ run_agent: z.string().optional(),
21
+ })
22
+ .strict();
23
+ const openClawBootstrapSchema = z
24
+ .object({
25
+ non_interactive: z.boolean().optional(),
26
+ accept_risk: z.boolean().optional(),
27
+ mode: z.enum(["local", "remote"]).optional(),
28
+ flow: z.enum(["quickstart", "advanced", "manual"]).optional(),
29
+ auth_choice: z.string().optional(),
30
+ workspace: z.string().optional(),
31
+ reset: z.boolean().optional(),
32
+ skip_channels: z.boolean().optional(),
33
+ skip_skills: z.boolean().optional(),
34
+ skip_health: z.boolean().optional(),
35
+ skip_ui: z.boolean().optional(),
36
+ skip_daemon: z.boolean().optional(),
37
+ install_daemon: z.boolean().optional(),
38
+ openai_api_key: z.string().optional(),
39
+ anthropic_api_key: z.string().optional(),
40
+ openrouter_api_key: z.string().optional(),
41
+ xai_api_key: z.string().optional(),
42
+ gemini_api_key: z.string().optional(),
43
+ ai_gateway_api_key: z.string().optional(),
44
+ cloudflare_ai_gateway_api_key: z.string().optional(),
45
+ cloudflare_ai_gateway_account_id: z.string().optional(),
46
+ cloudflare_ai_gateway_gateway_id: z.string().optional(),
47
+ token: z.string().optional(),
48
+ token_provider: z.string().optional(),
49
+ token_profile_id: z.string().optional(),
50
+ })
51
+ .strict();
52
+ const openClawSchema = z
53
+ .object({
54
+ bin: z.string().optional(),
55
+ version: z.string(),
56
+ install: z.enum(["auto", "always", "never"]).optional(),
57
+ bootstrap: openClawBootstrapSchema.optional(),
58
+ commands: openClawCommandsSchema.optional(),
59
+ })
60
+ .strict();
61
+ const workspaceSchema = z
62
+ .object({
63
+ name: z.string().min(1),
64
+ path: z.string().min(1).optional(),
65
+ })
66
+ .strict();
67
+ const channelSchema = z
68
+ .object({
69
+ channel: z.string().min(1),
70
+ account: z.string().min(1).optional(),
71
+ login: z.boolean().optional(),
72
+ login_mode: z.enum(["interactive"]).optional(),
73
+ login_account: z.string().min(1).optional(),
74
+ name: z.string().min(1).optional(),
75
+ token: z.string().min(1).optional(),
76
+ token_file: z.string().min(1).optional(),
77
+ use_env: z.boolean().optional(),
78
+ bot_token: z.string().min(1).optional(),
79
+ access_token: z.string().min(1).optional(),
80
+ app_token: z.string().min(1).optional(),
81
+ webhook_url: z.string().min(1).optional(),
82
+ webhook_path: z.string().min(1).optional(),
83
+ signal_number: z.string().min(1).optional(),
84
+ password: z.string().min(1).optional(),
85
+ extra_flags: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
86
+ })
87
+ .strict();
88
+ const agentSchema = z
89
+ .object({
90
+ workspace: z.string().min(1),
91
+ name: z.string().min(1),
92
+ model: z.string().optional(),
93
+ skills: z.array(z.string().min(1)).optional(),
94
+ })
95
+ .strict();
96
+ const fileSchema = z
97
+ .object({
98
+ workspace: z.string().min(1),
99
+ path: z.string().min(1),
100
+ content: z.string().optional(),
101
+ content_from: z.string().min(1).optional(),
102
+ source: z.string().optional(),
103
+ overwrite: z.boolean().optional(),
104
+ })
105
+ .strict()
106
+ .refine((v) => [v.content, v.content_from, v.source].filter((item) => item !== undefined).length === 1, {
107
+ message: "files[] requires exactly one of content, content_from, or source",
108
+ });
109
+ const conversationExpectSchema = z
110
+ .object({
111
+ contains: z.array(z.string()).optional(),
112
+ not_contains: z.array(z.string()).optional(),
113
+ regex: z.array(z.string()).optional(),
114
+ equals: z.string().optional(),
115
+ })
116
+ .strict();
117
+ const messageSchema = z
118
+ .object({
119
+ content: z.string(),
120
+ expect: conversationExpectSchema.optional(),
121
+ })
122
+ .strict();
123
+ const conversationSchema = z
124
+ .object({
125
+ workspace: z.string().min(1),
126
+ agent: z.string().min(1),
127
+ messages: z.array(messageSchema).min(1),
128
+ run: z.boolean().optional(),
129
+ })
130
+ .strict();
131
+ export const recipeSchema = z
132
+ .object({
133
+ version: z.string().min(1),
134
+ name: z.string().min(1),
135
+ params: z.record(z.string(), paramDefSchema).optional(),
136
+ openclaw: openClawSchema,
137
+ workspaces: z.array(workspaceSchema).optional(),
138
+ channels: z.array(channelSchema).optional(),
139
+ agents: z.array(agentSchema).optional(),
140
+ files: z.array(fileSchema).optional(),
141
+ conversations: z.array(conversationSchema).optional(),
142
+ })
143
+ .strict();
@@ -0,0 +1,2 @@
1
+ export declare function resolveTemplate(value: string, vars: Record<string, string>, allowMissing: boolean): string;
2
+ export declare function deepResolveTemplates<T>(input: T, vars: Record<string, string>, allowMissing: boolean): T;
@@ -0,0 +1,30 @@
1
+ import { ClawChefError } from "./errors.js";
2
+ const TOKEN_RE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
3
+ export function resolveTemplate(value, vars, allowMissing) {
4
+ return value.replace(TOKEN_RE, (_, key) => {
5
+ if (Object.prototype.hasOwnProperty.call(vars, key)) {
6
+ return vars[key];
7
+ }
8
+ if (allowMissing) {
9
+ return `\${${key}}`;
10
+ }
11
+ throw new ClawChefError(`Missing parameter: ${key}`);
12
+ });
13
+ }
14
+ export function deepResolveTemplates(input, vars, allowMissing) {
15
+ if (typeof input === "string") {
16
+ return resolveTemplate(input, vars, allowMissing);
17
+ }
18
+ if (Array.isArray(input)) {
19
+ return input.map((v) => deepResolveTemplates(v, vars, allowMissing));
20
+ }
21
+ if (input && typeof input === "object") {
22
+ const obj = input;
23
+ const out = {};
24
+ for (const [key, value] of Object.entries(obj)) {
25
+ out[key] = deepResolveTemplates(value, vars, allowMissing);
26
+ }
27
+ return out;
28
+ }
29
+ return input;
30
+ }
@@ -0,0 +1,136 @@
1
+ export type InstallPolicy = "auto" | "always" | "never";
2
+ export type OpenClawProvider = "command" | "mock" | "remote";
3
+ export interface OpenClawRemoteConfig {
4
+ base_url: string;
5
+ api_key?: string;
6
+ api_header?: string;
7
+ api_scheme?: string;
8
+ timeout_ms?: number;
9
+ operation_path?: string;
10
+ }
11
+ export interface ParamDef {
12
+ default?: string;
13
+ required?: boolean;
14
+ description?: string;
15
+ }
16
+ export interface OpenClawCommandOverrides {
17
+ use_version?: string;
18
+ install_version?: string;
19
+ uninstall_version?: string;
20
+ factory_reset?: string;
21
+ start_gateway?: string;
22
+ enable_plugin?: string;
23
+ login_channel?: string;
24
+ create_workspace?: string;
25
+ create_agent?: string;
26
+ install_skill?: string;
27
+ send_message?: string;
28
+ run_agent?: string;
29
+ }
30
+ export interface OpenClawBootstrap {
31
+ non_interactive?: boolean;
32
+ accept_risk?: boolean;
33
+ mode?: "local" | "remote";
34
+ flow?: "quickstart" | "advanced" | "manual";
35
+ auth_choice?: string;
36
+ workspace?: string;
37
+ reset?: boolean;
38
+ skip_channels?: boolean;
39
+ skip_skills?: boolean;
40
+ skip_health?: boolean;
41
+ skip_ui?: boolean;
42
+ skip_daemon?: boolean;
43
+ install_daemon?: boolean;
44
+ openai_api_key?: string;
45
+ anthropic_api_key?: string;
46
+ openrouter_api_key?: string;
47
+ xai_api_key?: string;
48
+ gemini_api_key?: string;
49
+ ai_gateway_api_key?: string;
50
+ cloudflare_ai_gateway_api_key?: string;
51
+ cloudflare_ai_gateway_account_id?: string;
52
+ cloudflare_ai_gateway_gateway_id?: string;
53
+ token?: string;
54
+ token_provider?: string;
55
+ token_profile_id?: string;
56
+ }
57
+ export interface OpenClawSection {
58
+ bin?: string;
59
+ version: string;
60
+ install?: InstallPolicy;
61
+ bootstrap?: OpenClawBootstrap;
62
+ commands?: OpenClawCommandOverrides;
63
+ }
64
+ export interface WorkspaceDef {
65
+ name: string;
66
+ path?: string;
67
+ }
68
+ export interface ChannelDef {
69
+ channel: string;
70
+ account?: string;
71
+ login?: boolean;
72
+ login_mode?: "interactive";
73
+ login_account?: string;
74
+ name?: string;
75
+ token?: string;
76
+ token_file?: string;
77
+ use_env?: boolean;
78
+ bot_token?: string;
79
+ access_token?: string;
80
+ app_token?: string;
81
+ webhook_url?: string;
82
+ webhook_path?: string;
83
+ signal_number?: string;
84
+ password?: string;
85
+ extra_flags?: Record<string, string | number | boolean>;
86
+ }
87
+ export interface AgentDef {
88
+ workspace: string;
89
+ name: string;
90
+ model?: string;
91
+ skills?: string[];
92
+ }
93
+ export interface FileDef {
94
+ workspace: string;
95
+ path: string;
96
+ content?: string;
97
+ content_from?: string;
98
+ source?: string;
99
+ overwrite?: boolean;
100
+ }
101
+ export interface MessageDef {
102
+ content: string;
103
+ expect?: ConversationExpectDef;
104
+ }
105
+ export interface ConversationExpectDef {
106
+ contains?: string[];
107
+ not_contains?: string[];
108
+ regex?: string[];
109
+ equals?: string;
110
+ }
111
+ export interface ConversationDef {
112
+ workspace: string;
113
+ agent: string;
114
+ messages: MessageDef[];
115
+ run?: boolean;
116
+ }
117
+ export interface Recipe {
118
+ version: string;
119
+ name: string;
120
+ params?: Record<string, ParamDef>;
121
+ openclaw: OpenClawSection;
122
+ workspaces?: WorkspaceDef[];
123
+ channels?: ChannelDef[];
124
+ agents?: AgentDef[];
125
+ files?: FileDef[];
126
+ conversations?: ConversationDef[];
127
+ }
128
+ export interface RunOptions {
129
+ vars: Record<string, string>;
130
+ dryRun: boolean;
131
+ allowMissing: boolean;
132
+ verbose: boolean;
133
+ silent: boolean;
134
+ provider: OpenClawProvider;
135
+ remote: Partial<OpenClawRemoteConfig>;
136
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "clawchef",
3
+ "version": "0.1.0",
4
+ "description": "Recipe-driven OpenClaw environment orchestrator",
5
+ "type": "module",
6
+ "main": "dist/api.js",
7
+ "types": "dist/api.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/api.d.ts",
11
+ "import": "./dist/api.js"
12
+ },
13
+ "./api": {
14
+ "types": "./dist/api.d.ts",
15
+ "import": "./dist/api.js"
16
+ }
17
+ },
18
+ "bin": {
19
+ "clawchef": "dist/index.js"
20
+ },
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.json",
23
+ "dev": "tsx src/index.ts",
24
+ "typecheck": "tsc --noEmit -p tsconfig.json"
25
+ },
26
+ "engines": {
27
+ "node": ">=20"
28
+ },
29
+ "dependencies": {
30
+ "commander": "^12.1.0",
31
+ "dotenv": "^16.4.7",
32
+ "js-yaml": "^4.1.0",
33
+ "zod": "^3.23.8"
34
+ },
35
+ "devDependencies": {
36
+ "@types/js-yaml": "^4.0.9",
37
+ "@types/node": "^22.13.13",
38
+ "tsx": "^4.19.3",
39
+ "typescript": "^5.8.2"
40
+ }
41
+ }
@@ -0,0 +1,20 @@
1
+ version: "1"
2
+ name: "content-from-sample"
3
+
4
+ openclaw:
5
+ version: "1.3.2"
6
+ install: "auto"
7
+
8
+ workspaces:
9
+ - name: "content-from-demo"
10
+
11
+ agents:
12
+ - workspace: "content-from-demo"
13
+ name: "planner"
14
+ model: "gpt-5.3-codex"
15
+
16
+ files:
17
+ - workspace: "content-from-demo"
18
+ path: "README.md"
19
+ overwrite: true
20
+ content_from: "./snippets/readme-template.md"
@@ -0,0 +1,45 @@
1
+ version: "1"
2
+ name: "openclaw-from-zero"
3
+
4
+ params:
5
+ project_name:
6
+ default: "from-zero-demo"
7
+ openclaw_version:
8
+ default: "2026.2.9"
9
+ openai_api_key:
10
+ required: true
11
+
12
+ openclaw:
13
+ bin: "openclaw"
14
+ version: "${openclaw_version}"
15
+ install: "never"
16
+ bootstrap:
17
+ auth_choice: "openai-api-key"
18
+ openai_api_key: "${openai_api_key}"
19
+ mode: "local"
20
+ flow: "quickstart"
21
+ accept_risk: true
22
+ non_interactive: true
23
+ skip_channels: true
24
+ skip_skills: true
25
+ skip_health: true
26
+ skip_ui: true
27
+ skip_daemon: true
28
+
29
+ workspaces:
30
+ - name: "${project_name}"
31
+
32
+ agents:
33
+ - workspace: "${project_name}"
34
+ name: "chef-openai"
35
+ model: "openai/gpt-5.3-codex"
36
+
37
+ conversations:
38
+ - workspace: "${project_name}"
39
+ agent: "chef-openai"
40
+ run: true
41
+ messages:
42
+ - content: "Say hello in one sentence."
43
+ expect:
44
+ contains:
45
+ - "hello"
@@ -0,0 +1,65 @@
1
+ version: "1"
2
+ name: "openclaw-local-demo"
3
+
4
+ params:
5
+ project_name:
6
+ default: "openclaw-local-demo"
7
+ openclaw_version:
8
+ default: "2026.2.9"
9
+ openai_api_key:
10
+ required: true
11
+ telegram_bot_token:
12
+ required: true
13
+ user_name:
14
+ required: true
15
+ agent_name:
16
+ required: true
17
+
18
+ openclaw:
19
+ bin: "openclaw"
20
+ version: "${openclaw_version}"
21
+ install: "never"
22
+ bootstrap:
23
+ auth_choice: "openai-api-key"
24
+ openai_api_key: "${openai_api_key}"
25
+ mode: "local"
26
+ flow: "quickstart"
27
+ accept_risk: true
28
+ non_interactive: true
29
+ skip_channels: true
30
+ skip_skills: true
31
+ skip_health: true
32
+ skip_ui: true
33
+ skip_daemon: true
34
+
35
+ workspaces:
36
+ - name: "${project_name}"
37
+
38
+ agents:
39
+ - workspace: "${project_name}"
40
+ name: "chef-openai"
41
+ model: "openai/gpt-5.3-codex"
42
+
43
+ files:
44
+ - workspace: "${project_name}"
45
+ path: "README.md"
46
+ overwrite: true
47
+ content: |
48
+ # ${project_name}
49
+ Generated by clawchef.
50
+
51
+ channels:
52
+ - channel: "telegram"
53
+ token: "${telegram_bot_token}"
54
+ account: "default"
55
+ login: true
56
+
57
+ conversations:
58
+ - workspace: "${project_name}"
59
+ agent: "chef-openai"
60
+ run: true
61
+ messages:
62
+ - content: "I'm ${user_name}, you're ${agent_name}."
63
+ expect:
64
+ contains:
65
+ - "${user_name}"
@@ -0,0 +1,38 @@
1
+ version: "1"
2
+ name: "openclaw-remote-http-demo"
3
+
4
+ params:
5
+ project_name:
6
+ default: "remote-http-demo"
7
+ openclaw_version:
8
+ default: "2026.2.9"
9
+
10
+ openclaw:
11
+ version: "${openclaw_version}"
12
+ install: "never"
13
+
14
+ workspaces:
15
+ - name: "${project_name}"
16
+
17
+ agents:
18
+ - workspace: "${project_name}"
19
+ name: "chef-openai"
20
+ model: "openai/gpt-5.3-codex"
21
+
22
+ files:
23
+ - workspace: "${project_name}"
24
+ path: "README.md"
25
+ overwrite: true
26
+ content: |
27
+ # ${project_name}
28
+ Managed by clawchef remote HTTP provider.
29
+
30
+ conversations:
31
+ - workspace: "${project_name}"
32
+ agent: "chef-openai"
33
+ run: true
34
+ messages:
35
+ - content: "Say hello in one sentence."
36
+ expect:
37
+ contains:
38
+ - "hello"
@@ -0,0 +1,22 @@
1
+ version: "1"
2
+ name: "openclaw-telegram-mock-channel"
3
+
4
+ params:
5
+ openclaw_version:
6
+ default: "2026.2.9"
7
+ telegram_mock_api_key:
8
+ required: true
9
+
10
+ openclaw:
11
+ bin: "openclaw"
12
+ version: "${openclaw_version}"
13
+ install: "never"
14
+
15
+ channels:
16
+ - channel: "telegram-mock"
17
+ account: "testbot"
18
+ token: "${telegram_mock_api_key}"
19
+ extra_flags:
20
+ mock_bind: "127.0.0.1:18790"
21
+ mock_api_key: "${telegram_mock_api_key}"
22
+ mode: "webhook"
@@ -0,0 +1,19 @@
1
+ version: "1"
2
+ name: "openclaw-telegram-channel"
3
+
4
+ params:
5
+ openclaw_version:
6
+ default: "2026.2.9"
7
+ telegram_bot_token:
8
+ required: true
9
+
10
+ openclaw:
11
+ bin: "openclaw"
12
+ version: "${openclaw_version}"
13
+ install: "never"
14
+
15
+ channels:
16
+ - channel: "telegram"
17
+ token: "${telegram_bot_token}"
18
+ account: "default"
19
+ login: true
@@ -0,0 +1,49 @@
1
+ version: "1"
2
+ name: "demo-run"
3
+
4
+ params:
5
+ project_name:
6
+ default: "demo-app"
7
+ openclaw_version:
8
+ default: "1.3.2"
9
+ telegram_bot_token:
10
+ default: "mock-telegram-token"
11
+
12
+ openclaw:
13
+ version: "${openclaw_version}"
14
+ install: "auto"
15
+
16
+ workspaces:
17
+ - name: "${project_name}"
18
+
19
+ channels:
20
+ - channel: "telegram"
21
+ token: "${telegram_bot_token}"
22
+ account: "default"
23
+
24
+ agents:
25
+ - workspace: "${project_name}"
26
+ name: "planner"
27
+ model: "gpt-5.3-codex"
28
+ skills: ["repo-explore", "code-review"]
29
+
30
+ files:
31
+ - workspace: "${project_name}"
32
+ path: "README.md"
33
+ overwrite: true
34
+ content: |
35
+ # ${project_name}
36
+ Generated by clawchef.
37
+
38
+ conversations:
39
+ - workspace: "${project_name}"
40
+ agent: "planner"
41
+ run: true
42
+ messages:
43
+ - content: "You are a strict software planning assistant."
44
+ - content: "Please provide a 3-step plan."
45
+ expect:
46
+ contains:
47
+ - "mock-reply"
48
+ regex:
49
+ - "[Pp]lan"
@@ -0,0 +1,3 @@
1
+ # content-from-demo
2
+
3
+ This content is loaded from a referenced file.
package/src/api.ts ADDED
@@ -0,0 +1,65 @@
1
+ import YAML from "js-yaml";
2
+ import { ClawChefError } from "./errors.js";
3
+ import { importDotEnvFromCwd } from "./env.js";
4
+ import { Logger } from "./logger.js";
5
+ import { runRecipe } from "./orchestrator.js";
6
+ import { loadRecipe, loadRecipeText } from "./recipe.js";
7
+ import { recipeSchema } from "./schema.js";
8
+ import type { OpenClawProvider, OpenClawRemoteConfig, RunOptions } from "./types.js";
9
+
10
+ export interface CookOptions {
11
+ vars?: Record<string, string>;
12
+ dryRun?: boolean;
13
+ allowMissing?: boolean;
14
+ verbose?: boolean;
15
+ silent?: boolean;
16
+ provider?: OpenClawProvider;
17
+ remote?: Partial<OpenClawRemoteConfig>;
18
+ loadDotEnvFromCwd?: boolean;
19
+ }
20
+
21
+ function normalizeCookOptions(options: CookOptions): RunOptions {
22
+ return {
23
+ vars: options.vars ?? {},
24
+ dryRun: Boolean(options.dryRun),
25
+ allowMissing: Boolean(options.allowMissing),
26
+ verbose: Boolean(options.verbose),
27
+ silent: options.silent ?? true,
28
+ provider: options.provider ?? "command",
29
+ remote: options.remote ?? {},
30
+ };
31
+ }
32
+
33
+ export async function cook(recipeRef: string, options: CookOptions = {}): Promise<void> {
34
+ if (options.loadDotEnvFromCwd ?? true) {
35
+ importDotEnvFromCwd();
36
+ }
37
+
38
+ const runOptions = normalizeCookOptions(options);
39
+ const logger = new Logger(runOptions.verbose);
40
+ const loaded = await loadRecipe(recipeRef, runOptions);
41
+ try {
42
+ await runRecipe(loaded.recipe, loaded.origin, runOptions, logger);
43
+ } finally {
44
+ if (loaded.cleanup) {
45
+ await loaded.cleanup();
46
+ }
47
+ }
48
+ }
49
+
50
+ export async function validate(recipeRef: string): Promise<void> {
51
+ const loaded = await loadRecipeText(recipeRef);
52
+ try {
53
+ const raw = YAML.load(loaded.source);
54
+ const result = recipeSchema.safeParse(raw);
55
+ if (!result.success) {
56
+ throw new ClawChefError(`Validation failed: ${result.error.message}`);
57
+ }
58
+ } finally {
59
+ if (loaded.cleanup) {
60
+ await loaded.cleanup();
61
+ }
62
+ }
63
+ }
64
+
65
+ export type { OpenClawProvider, OpenClawRemoteConfig };