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
@@ -0,0 +1,37 @@
1
+ import { ClawChefError } from "./errors.js";
2
+ import type { ConversationExpectDef } from "./types.js";
3
+
4
+ export function validateReply(reply: string, expect?: ConversationExpectDef): void {
5
+ if (!expect) {
6
+ return;
7
+ }
8
+
9
+ for (const text of expect.contains ?? []) {
10
+ if (!reply.includes(text)) {
11
+ throw new ClawChefError(`Output assertion failed: must contain -> ${text}`);
12
+ }
13
+ }
14
+
15
+ for (const text of expect.not_contains ?? []) {
16
+ if (reply.includes(text)) {
17
+ throw new ClawChefError(`Output assertion failed: must not contain -> ${text}`);
18
+ }
19
+ }
20
+
21
+ for (const pattern of expect.regex ?? []) {
22
+ let re: RegExp;
23
+ try {
24
+ re = new RegExp(pattern, "m");
25
+ } catch (err) {
26
+ const msg = err instanceof Error ? err.message : String(err);
27
+ throw new ClawChefError(`Output assertion failed: invalid regex ${pattern} (${msg})`);
28
+ }
29
+ if (!re.test(reply)) {
30
+ throw new ClawChefError(`Output assertion failed: regex mismatch -> ${pattern}`);
31
+ }
32
+ }
33
+
34
+ if (expect.equals !== undefined && reply !== expect.equals) {
35
+ throw new ClawChefError("Output assertion failed: equals mismatch");
36
+ }
37
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,123 @@
1
+ import { Command } from "commander";
2
+ import { ClawChefError } from "./errors.js";
3
+ import { Logger } from "./logger.js";
4
+ import { runRecipe } from "./orchestrator.js";
5
+ import { loadRecipe, loadRecipeText } from "./recipe.js";
6
+ import { recipeSchema } from "./schema.js";
7
+ import type { RunOptions } from "./types.js";
8
+ import YAML from "js-yaml";
9
+
10
+ function parseVarFlags(values: string[]): Record<string, string> {
11
+ const out: Record<string, string> = {};
12
+ for (const item of values) {
13
+ const idx = item.indexOf("=");
14
+ if (idx <= 0 || idx === item.length - 1) {
15
+ throw new ClawChefError(`Invalid --var format: ${item}. Expected key=value`);
16
+ }
17
+ const k = item.slice(0, idx).trim();
18
+ const v = item.slice(idx + 1).trim();
19
+ out[k] = v;
20
+ }
21
+ return out;
22
+ }
23
+
24
+ function readEnv(name: string): string | undefined {
25
+ const value = process.env[name];
26
+ if (value === undefined) {
27
+ return undefined;
28
+ }
29
+ const trimmed = value.trim();
30
+ return trimmed.length > 0 ? trimmed : undefined;
31
+ }
32
+
33
+ function parseProvider(value: string): "command" | "mock" | "remote" {
34
+ if (value === "command" || value === "mock" || value === "remote") {
35
+ return value;
36
+ }
37
+ throw new ClawChefError(`Invalid --provider value: ${value}. Expected command, remote, or mock`);
38
+ }
39
+
40
+ function parseOptionalInt(value: string | undefined, fieldName: string): number | undefined {
41
+ if (value === undefined) {
42
+ return undefined;
43
+ }
44
+ const parsed = Number(value);
45
+ if (!Number.isInteger(parsed) || parsed <= 0) {
46
+ throw new ClawChefError(`${fieldName} must be a positive integer`);
47
+ }
48
+ return parsed;
49
+ }
50
+
51
+ export function buildCli(): Command {
52
+ const program = new Command();
53
+
54
+ program
55
+ .name("clawchef")
56
+ .description("Run OpenClaw environment recipes")
57
+ .version("0.1.0");
58
+
59
+ program
60
+ .command("cook")
61
+ .argument("<recipe>", "Recipe path/URL/dir/archive[:file]")
62
+ .option("--var <key=value>", "Template variable", (v, p: string[]) => p.concat([v]), [])
63
+ .option("--dry-run", "Print actions without executing", false)
64
+ .option("--allow-missing", "Allow unresolved template variables", false)
65
+ .option("--verbose", "Verbose logging", false)
66
+ .option("-s, --silent", "Skip reset confirmation prompt", false)
67
+ .option("--provider <provider>", "Execution provider: command | remote | mock")
68
+ .option("--remote-base-url <url>", "Remote OpenClaw API base URL")
69
+ .option("--remote-api-key <key>", "Remote OpenClaw API key")
70
+ .option("--remote-api-header <header>", "Remote auth header name")
71
+ .option("--remote-api-scheme <scheme>", "Remote auth scheme (default: Bearer)")
72
+ .option("--remote-timeout-ms <ms>", "Remote operation timeout in milliseconds")
73
+ .option("--remote-operation-path <path>", "Remote operation endpoint path")
74
+ .action(async (recipeRef: string, opts) => {
75
+ const provider = parseProvider(opts.provider ?? readEnv("CLAWCHEF_PROVIDER") ?? "command");
76
+ const options: RunOptions = {
77
+ vars: parseVarFlags(opts.var),
78
+ dryRun: Boolean(opts.dryRun),
79
+ allowMissing: Boolean(opts.allowMissing),
80
+ verbose: Boolean(opts.verbose),
81
+ silent: Boolean(opts.silent),
82
+ provider,
83
+ remote: {
84
+ base_url: opts.remoteBaseUrl ?? readEnv("CLAWCHEF_REMOTE_BASE_URL"),
85
+ api_key: opts.remoteApiKey ?? readEnv("CLAWCHEF_REMOTE_API_KEY"),
86
+ api_header: opts.remoteApiHeader ?? readEnv("CLAWCHEF_REMOTE_API_HEADER"),
87
+ api_scheme: opts.remoteApiScheme ?? readEnv("CLAWCHEF_REMOTE_API_SCHEME"),
88
+ timeout_ms: parseOptionalInt(opts.remoteTimeoutMs ?? readEnv("CLAWCHEF_REMOTE_TIMEOUT_MS"), "remote-timeout-ms"),
89
+ operation_path: opts.remoteOperationPath ?? readEnv("CLAWCHEF_REMOTE_OPERATION_PATH"),
90
+ },
91
+ };
92
+ const logger = new Logger(options.verbose);
93
+ const loaded = await loadRecipe(recipeRef, options);
94
+ try {
95
+ await runRecipe(loaded.recipe, loaded.origin, options, logger);
96
+ } finally {
97
+ if (loaded.cleanup) {
98
+ await loaded.cleanup();
99
+ }
100
+ }
101
+ });
102
+
103
+ program
104
+ .command("validate")
105
+ .argument("<recipe>", "Recipe path/URL/dir/archive[:file]")
106
+ .action(async (recipeRef: string) => {
107
+ const loaded = await loadRecipeText(recipeRef);
108
+ try {
109
+ const raw = YAML.load(loaded.source);
110
+ const result = recipeSchema.safeParse(raw);
111
+ if (!result.success) {
112
+ throw new ClawChefError(`Validation failed: ${result.error.message}`);
113
+ }
114
+ process.stdout.write("Recipe structure is valid\n");
115
+ } finally {
116
+ if (loaded.cleanup) {
117
+ await loaded.cleanup();
118
+ }
119
+ }
120
+ });
121
+
122
+ return program;
123
+ }
package/src/env.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { config as loadDotenv } from "dotenv";
4
+ import { ClawChefError } from "./errors.js";
5
+
6
+ export function importDotEnvFromCwd(): void {
7
+ const envPath = path.resolve(process.cwd(), ".env");
8
+ if (!existsSync(envPath)) {
9
+ return;
10
+ }
11
+
12
+ const result = loadDotenv({ path: envPath, override: false });
13
+ if (result.error) {
14
+ throw new ClawChefError(`Failed to load .env from current directory: ${result.error.message}`);
15
+ }
16
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,6 @@
1
+ export class ClawChefError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ this.name = "ClawChefError";
5
+ }
6
+ }
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import { buildCli } from "./cli.js";
3
+ import { importDotEnvFromCwd } from "./env.js";
4
+ import { ClawChefError } from "./errors.js";
5
+
6
+ async function main(): Promise<void> {
7
+ importDotEnvFromCwd();
8
+ const program = buildCli();
9
+ await program.parseAsync(process.argv);
10
+ }
11
+
12
+ main().catch((err: unknown) => {
13
+ if (err instanceof ClawChefError) {
14
+ process.stderr.write(`[ERROR] ${err.message}\n`);
15
+ process.exit(1);
16
+ }
17
+ const msg = err instanceof Error ? err.stack ?? err.message : String(err);
18
+ process.stderr.write(`[FATAL] ${msg}\n`);
19
+ process.exit(1);
20
+ });
package/src/logger.ts ADDED
@@ -0,0 +1,17 @@
1
+ export class Logger {
2
+ constructor(private readonly verboseEnabled: boolean) {}
3
+
4
+ info(message: string): void {
5
+ process.stdout.write(`[INFO] ${message}\n`);
6
+ }
7
+
8
+ warn(message: string): void {
9
+ process.stdout.write(`[WARN] ${message}\n`);
10
+ }
11
+
12
+ debug(message: string): void {
13
+ if (this.verboseEnabled) {
14
+ process.stdout.write(`[DEBUG] ${message}\n`);
15
+ }
16
+ }
17
+ }