clawchef 0.1.4 → 0.1.5
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.
- package/README.md +10 -0
- package/dist/api.d.ts +1 -0
- package/dist/api.js +5 -2
- package/dist/cli.js +8 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.js +50 -1
- package/dist/index.js +0 -2
- package/package.json +1 -1
- package/src/api.ts +5 -2
- package/src/cli.ts +8 -0
- package/src/env.ts +52 -1
- package/src/index.ts +0 -2
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Recipe-driven OpenClaw environment orchestrator.
|
|
|
8
8
|
- Accepts recipe input from local file/dir/archive and HTTP URL/archive.
|
|
9
9
|
- Resolves `${var}` parameters from `--var`, environment, and defaults.
|
|
10
10
|
- Auto-loads environment variables from `.env` in the current working directory.
|
|
11
|
+
- Supports loading env vars from a custom `.env` path/URL via `--env-file`.
|
|
11
12
|
- Requires secrets to be injected via `--var` / `CLAWCHEF_VAR_*` (no inline secrets in recipe).
|
|
12
13
|
- Prepares OpenClaw version (install or reuse).
|
|
13
14
|
- When installed OpenClaw version mismatches recipe version, prompts: ignore / abort / force reinstall (silent mode auto-picks force reinstall).
|
|
@@ -39,6 +40,13 @@ Run recipe from URL:
|
|
|
39
40
|
clawchef cook https://example.com/recipes/sample.yaml --provider remote
|
|
40
41
|
```
|
|
41
42
|
|
|
43
|
+
Run with custom env file (local path or HTTP URL):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
clawchef cook recipes/sample.yaml --env-file ./.env.staging
|
|
47
|
+
clawchef cook recipes/sample.yaml --env-file https://example.com/envs/staging.env
|
|
48
|
+
```
|
|
49
|
+
|
|
42
50
|
Run recipe from GitHub repository root (`recipe.yaml` at repo root):
|
|
43
51
|
|
|
44
52
|
```bash
|
|
@@ -190,6 +198,7 @@ await scaffold("./my-recipe-project", {
|
|
|
190
198
|
- `plugins`: plugin npm specs to preinstall for this run (`string[]`)
|
|
191
199
|
- `provider`: `command | remote | mock`
|
|
192
200
|
- `remote`: remote provider config (same fields as CLI remote flags)
|
|
201
|
+
- `envFile`: custom env file path/URL; when set, default cwd `.env` loading is skipped
|
|
193
202
|
- `dryRun`, `allowMissing`, `verbose`
|
|
194
203
|
- `silent` (default: `true` in Node API)
|
|
195
204
|
- `loadDotEnvFromCwd` (default: `true`)
|
|
@@ -212,6 +221,7 @@ Notes:
|
|
|
212
221
|
If `params.<key>.required: true` and no value is found, run fails.
|
|
213
222
|
|
|
214
223
|
If `.env` exists in the directory where `clawchef` is executed, it is loaded before recipe parsing.
|
|
224
|
+
If `--env-file` (or Node API `envFile`) is provided, only that env source is loaded.
|
|
215
225
|
|
|
216
226
|
## Recipe reference formats
|
|
217
227
|
|
package/dist/api.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface CookOptions {
|
|
|
9
9
|
silent?: boolean;
|
|
10
10
|
provider?: OpenClawProvider;
|
|
11
11
|
remote?: Partial<OpenClawRemoteConfig>;
|
|
12
|
+
envFile?: string;
|
|
12
13
|
loadDotEnvFromCwd?: boolean;
|
|
13
14
|
}
|
|
14
15
|
export declare function cook(recipeRef: string, options?: CookOptions): Promise<void>;
|
package/dist/api.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import YAML from "js-yaml";
|
|
2
2
|
import { ClawChefError } from "./errors.js";
|
|
3
|
-
import { importDotEnvFromCwd } from "./env.js";
|
|
3
|
+
import { importDotEnvFromCwd, importDotEnvFromRef } from "./env.js";
|
|
4
4
|
import { Logger } from "./logger.js";
|
|
5
5
|
import { runRecipe } from "./orchestrator.js";
|
|
6
6
|
import { loadRecipe, loadRecipeText } from "./recipe.js";
|
|
@@ -21,7 +21,10 @@ function normalizeCookOptions(options) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
export async function cook(recipeRef, options = {}) {
|
|
24
|
-
if (options.
|
|
24
|
+
if (options.envFile) {
|
|
25
|
+
await importDotEnvFromRef(options.envFile);
|
|
26
|
+
}
|
|
27
|
+
else if (options.loadDotEnvFromCwd ?? true) {
|
|
25
28
|
importDotEnvFromCwd();
|
|
26
29
|
}
|
|
27
30
|
const runOptions = normalizeCookOptions(options);
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { ClawChefError } from "./errors.js";
|
|
3
|
+
import { importDotEnvFromCwd, importDotEnvFromRef } from "./env.js";
|
|
3
4
|
import { Logger } from "./logger.js";
|
|
4
5
|
import { runRecipe } from "./orchestrator.js";
|
|
5
6
|
import { loadRecipe, loadRecipeText } from "./recipe.js";
|
|
@@ -78,6 +79,7 @@ export function buildCli() {
|
|
|
78
79
|
.option("--verbose", "Verbose logging", false)
|
|
79
80
|
.option("-s, --silent", "Skip reset confirmation prompt", false)
|
|
80
81
|
.option("--keep-openclaw-state", "Preserve existing OpenClaw state (skip factory reset)", false)
|
|
82
|
+
.option("--env-file <path-or-url>", "Load env vars from local file or HTTP URL")
|
|
81
83
|
.option("--provider <provider>", "Execution provider: command | remote | mock")
|
|
82
84
|
.option("--plugin <npm-spec>", "Preinstall plugin package (repeatable)", (v, p) => p.concat([v]), [])
|
|
83
85
|
.option("--remote-base-url <url>", "Remote OpenClaw API base URL")
|
|
@@ -87,6 +89,12 @@ export function buildCli() {
|
|
|
87
89
|
.option("--remote-timeout-ms <ms>", "Remote operation timeout in milliseconds")
|
|
88
90
|
.option("--remote-operation-path <path>", "Remote operation endpoint path")
|
|
89
91
|
.action(async (recipeRef, opts) => {
|
|
92
|
+
if (opts.envFile) {
|
|
93
|
+
await importDotEnvFromRef(String(opts.envFile));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
importDotEnvFromCwd();
|
|
97
|
+
}
|
|
90
98
|
const provider = parseProvider(opts.provider ?? readEnv("CLAWCHEF_PROVIDER") ?? "command");
|
|
91
99
|
const options = {
|
|
92
100
|
vars: parseVarFlags(opts.var),
|
package/dist/env.d.ts
CHANGED
package/dist/env.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
|
-
import { config as loadDotenv } from "dotenv";
|
|
4
|
+
import { config as loadDotenv, parse as parseDotenv } from "dotenv";
|
|
4
5
|
import { ClawChefError } from "./errors.js";
|
|
5
6
|
export function importDotEnvFromCwd() {
|
|
6
7
|
const envPath = path.resolve(process.cwd(), ".env");
|
|
@@ -12,3 +13,51 @@ export function importDotEnvFromCwd() {
|
|
|
12
13
|
throw new ClawChefError(`Failed to load .env from current directory: ${result.error.message}`);
|
|
13
14
|
}
|
|
14
15
|
}
|
|
16
|
+
function isHttpUrl(value) {
|
|
17
|
+
try {
|
|
18
|
+
const url = new URL(value);
|
|
19
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function applyEnv(entries) {
|
|
26
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
27
|
+
if (process.env[key] === undefined) {
|
|
28
|
+
process.env[key] = value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function importDotEnvFromRef(ref) {
|
|
33
|
+
const trimmed = ref.trim();
|
|
34
|
+
if (!trimmed) {
|
|
35
|
+
throw new ClawChefError("--env-file cannot be empty");
|
|
36
|
+
}
|
|
37
|
+
if (isHttpUrl(trimmed)) {
|
|
38
|
+
let response;
|
|
39
|
+
try {
|
|
40
|
+
response = await fetch(trimmed);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
44
|
+
throw new ClawChefError(`Failed to fetch env file URL ${trimmed}: ${message}`);
|
|
45
|
+
}
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
throw new ClawChefError(`Failed to fetch env file URL ${trimmed}: HTTP ${response.status}`);
|
|
48
|
+
}
|
|
49
|
+
const content = await response.text();
|
|
50
|
+
applyEnv(parseDotenv(content));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const envPath = path.resolve(trimmed);
|
|
54
|
+
let content;
|
|
55
|
+
try {
|
|
56
|
+
content = await readFile(envPath, "utf8");
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
60
|
+
throw new ClawChefError(`Failed to load env file ${envPath}: ${message}`);
|
|
61
|
+
}
|
|
62
|
+
applyEnv(parseDotenv(content));
|
|
63
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { buildCli } from "./cli.js";
|
|
3
|
-
import { importDotEnvFromCwd } from "./env.js";
|
|
4
3
|
import { ClawChefError } from "./errors.js";
|
|
5
4
|
async function main() {
|
|
6
|
-
importDotEnvFromCwd();
|
|
7
5
|
const program = buildCli();
|
|
8
6
|
await program.parseAsync(process.argv);
|
|
9
7
|
}
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import YAML from "js-yaml";
|
|
2
2
|
import { ClawChefError } from "./errors.js";
|
|
3
|
-
import { importDotEnvFromCwd } from "./env.js";
|
|
3
|
+
import { importDotEnvFromCwd, importDotEnvFromRef } from "./env.js";
|
|
4
4
|
import { Logger } from "./logger.js";
|
|
5
5
|
import { runRecipe } from "./orchestrator.js";
|
|
6
6
|
import { loadRecipe, loadRecipeText } from "./recipe.js";
|
|
@@ -18,6 +18,7 @@ export interface CookOptions {
|
|
|
18
18
|
silent?: boolean;
|
|
19
19
|
provider?: OpenClawProvider;
|
|
20
20
|
remote?: Partial<OpenClawRemoteConfig>;
|
|
21
|
+
envFile?: string;
|
|
21
22
|
loadDotEnvFromCwd?: boolean;
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -37,7 +38,9 @@ function normalizeCookOptions(options: CookOptions): RunOptions {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export async function cook(recipeRef: string, options: CookOptions = {}): Promise<void> {
|
|
40
|
-
if (options.
|
|
41
|
+
if (options.envFile) {
|
|
42
|
+
await importDotEnvFromRef(options.envFile);
|
|
43
|
+
} else if (options.loadDotEnvFromCwd ?? true) {
|
|
41
44
|
importDotEnvFromCwd();
|
|
42
45
|
}
|
|
43
46
|
|
package/src/cli.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { ClawChefError } from "./errors.js";
|
|
3
|
+
import { importDotEnvFromCwd, importDotEnvFromRef } from "./env.js";
|
|
3
4
|
import { Logger } from "./logger.js";
|
|
4
5
|
import { runRecipe } from "./orchestrator.js";
|
|
5
6
|
import { loadRecipe, loadRecipeText } from "./recipe.js";
|
|
@@ -88,6 +89,7 @@ export function buildCli(): Command {
|
|
|
88
89
|
.option("--verbose", "Verbose logging", false)
|
|
89
90
|
.option("-s, --silent", "Skip reset confirmation prompt", false)
|
|
90
91
|
.option("--keep-openclaw-state", "Preserve existing OpenClaw state (skip factory reset)", false)
|
|
92
|
+
.option("--env-file <path-or-url>", "Load env vars from local file or HTTP URL")
|
|
91
93
|
.option("--provider <provider>", "Execution provider: command | remote | mock")
|
|
92
94
|
.option("--plugin <npm-spec>", "Preinstall plugin package (repeatable)", (v, p: string[]) => p.concat([v]), [])
|
|
93
95
|
.option("--remote-base-url <url>", "Remote OpenClaw API base URL")
|
|
@@ -97,6 +99,12 @@ export function buildCli(): Command {
|
|
|
97
99
|
.option("--remote-timeout-ms <ms>", "Remote operation timeout in milliseconds")
|
|
98
100
|
.option("--remote-operation-path <path>", "Remote operation endpoint path")
|
|
99
101
|
.action(async (recipeRef: string, opts) => {
|
|
102
|
+
if (opts.envFile) {
|
|
103
|
+
await importDotEnvFromRef(String(opts.envFile));
|
|
104
|
+
} else {
|
|
105
|
+
importDotEnvFromCwd();
|
|
106
|
+
}
|
|
107
|
+
|
|
100
108
|
const provider = parseProvider(opts.provider ?? readEnv("CLAWCHEF_PROVIDER") ?? "command");
|
|
101
109
|
const options: RunOptions = {
|
|
102
110
|
vars: parseVarFlags(opts.var),
|
package/src/env.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
|
-
import { config as loadDotenv } from "dotenv";
|
|
4
|
+
import { config as loadDotenv, parse as parseDotenv } from "dotenv";
|
|
4
5
|
import { ClawChefError } from "./errors.js";
|
|
5
6
|
|
|
6
7
|
export function importDotEnvFromCwd(): void {
|
|
@@ -14,3 +15,53 @@ export function importDotEnvFromCwd(): void {
|
|
|
14
15
|
throw new ClawChefError(`Failed to load .env from current directory: ${result.error.message}`);
|
|
15
16
|
}
|
|
16
17
|
}
|
|
18
|
+
|
|
19
|
+
function isHttpUrl(value: string): boolean {
|
|
20
|
+
try {
|
|
21
|
+
const url = new URL(value);
|
|
22
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function applyEnv(entries: Record<string, string>): void {
|
|
29
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
30
|
+
if (process.env[key] === undefined) {
|
|
31
|
+
process.env[key] = value;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function importDotEnvFromRef(ref: string): Promise<void> {
|
|
37
|
+
const trimmed = ref.trim();
|
|
38
|
+
if (!trimmed) {
|
|
39
|
+
throw new ClawChefError("--env-file cannot be empty");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (isHttpUrl(trimmed)) {
|
|
43
|
+
let response: Response;
|
|
44
|
+
try {
|
|
45
|
+
response = await fetch(trimmed);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
48
|
+
throw new ClawChefError(`Failed to fetch env file URL ${trimmed}: ${message}`);
|
|
49
|
+
}
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new ClawChefError(`Failed to fetch env file URL ${trimmed}: HTTP ${response.status}`);
|
|
52
|
+
}
|
|
53
|
+
const content = await response.text();
|
|
54
|
+
applyEnv(parseDotenv(content));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const envPath = path.resolve(trimmed);
|
|
59
|
+
let content: string;
|
|
60
|
+
try {
|
|
61
|
+
content = await readFile(envPath, "utf8");
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
64
|
+
throw new ClawChefError(`Failed to load env file ${envPath}: ${message}`);
|
|
65
|
+
}
|
|
66
|
+
applyEnv(parseDotenv(content));
|
|
67
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { buildCli } from "./cli.js";
|
|
3
|
-
import { importDotEnvFromCwd } from "./env.js";
|
|
4
3
|
import { ClawChefError } from "./errors.js";
|
|
5
4
|
|
|
6
5
|
async function main(): Promise<void> {
|
|
7
|
-
importDotEnvFromCwd();
|
|
8
6
|
const program = buildCli();
|
|
9
7
|
await program.parseAsync(process.argv);
|
|
10
8
|
}
|