@better-openclaw/core 1.0.9 → 1.0.11
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/dist/bare-metal-partition.test.mjs +1 -1
- package/dist/composer.snapshot.test.mjs +1 -1
- package/dist/composer.test.mjs +1 -1
- package/dist/generate.d.mts.map +1 -1
- package/dist/generate.mjs +7 -1
- package/dist/generate.mjs.map +1 -1
- package/dist/generate.test.mjs +1 -1
- package/dist/generators/bare-metal-install.test.mjs +1 -1
- package/dist/generators/caddy.test.d.mts +1 -0
- package/dist/generators/caddy.test.mjs +45 -0
- package/dist/generators/caddy.test.mjs.map +1 -0
- package/dist/generators/env.test.d.mts +1 -0
- package/dist/generators/env.test.mjs +60 -0
- package/dist/generators/env.test.mjs.map +1 -0
- package/dist/generators/health-check.d.mts +18 -0
- package/dist/generators/health-check.d.mts.map +1 -0
- package/dist/generators/health-check.mjs +705 -0
- package/dist/generators/health-check.mjs.map +1 -0
- package/dist/generators/health-check.test.d.mts +1 -0
- package/dist/generators/health-check.test.mjs +85 -0
- package/dist/generators/health-check.test.mjs.map +1 -0
- package/dist/generators/scripts.test.d.mts +1 -0
- package/dist/generators/scripts.test.mjs +52 -0
- package/dist/generators/scripts.test.mjs.map +1 -0
- package/dist/generators/traefik.test.mjs +1 -1
- package/dist/generators/traefik.test.mjs.map +1 -1
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +3 -1
- package/dist/migrations.d.mts.map +1 -1
- package/dist/migrations.mjs.map +1 -1
- package/dist/migrations.test.mjs +1 -1
- package/dist/migrations.test.mjs.map +1 -1
- package/dist/presets/registry.test.mjs +1 -1
- package/dist/resolver.test.mjs +1 -1
- package/dist/schema.test.mjs +1 -1
- package/dist/services/definitions/convex.mjs.map +1 -1
- package/dist/services/definitions/desktop-environment.mjs.map +1 -1
- package/dist/services/definitions/index.d.mts +2 -2
- package/dist/services/definitions/index.mjs +3 -3
- package/dist/services/definitions/index.mjs.map +1 -1
- package/dist/services/definitions/mission-control.mjs.map +1 -1
- package/dist/services/definitions/stream-gateway.mjs.map +1 -1
- package/dist/services/registry.test.mjs +1 -1
- package/dist/skills/registry.d.mts.map +1 -1
- package/dist/skills/registry.mjs +498 -6
- package/dist/skills/registry.mjs.map +1 -1
- package/dist/skills/skill-manifest.d.mts +20 -0
- package/dist/skills/skill-manifest.d.mts.map +1 -0
- package/dist/skills/skill-manifest.mjs +28 -0
- package/dist/skills/skill-manifest.mjs.map +1 -0
- package/dist/validator.test.mjs +1 -1
- package/dist/version-manager.test.mjs +1 -1
- package/dist/version-manager.test.mjs.map +1 -1
- package/dist/{vi.2VT5v0um-Qk6MgAnK.mjs → vi.2VT5v0um-YSByewHe.mjs} +5 -5
- package/dist/{vi.2VT5v0um-Qk6MgAnK.mjs.map → vi.2VT5v0um-YSByewHe.mjs.map} +1 -1
- package/package.json +1 -1
- package/src/generate.ts +15 -3
- package/src/generators/caddy.test.ts +56 -0
- package/src/generators/env.test.ts +73 -0
- package/src/generators/health-check.test.ts +118 -0
- package/src/generators/health-check.ts +774 -0
- package/src/generators/scripts.test.ts +60 -0
- package/src/generators/traefik.test.ts +9 -17
- package/src/index.ts +12 -6
- package/src/migrations.test.ts +1 -1
- package/src/migrations.ts +1 -4
- package/src/services/definitions/convex.ts +2 -4
- package/src/services/definitions/desktop-environment.ts +1 -9
- package/src/services/definitions/index.ts +6 -6
- package/src/services/definitions/mission-control.ts +2 -4
- package/src/services/definitions/stream-gateway.ts +1 -2
- package/src/skills/registry.ts +336 -6
- package/src/skills/skill-manifest.ts +52 -0
- package/src/version-manager.test.ts +15 -4
- package/tsdown.config.ts +6 -6
package/package.json
CHANGED
package/src/generate.ts
CHANGED
|
@@ -4,11 +4,12 @@ import {
|
|
|
4
4
|
resolvedWithOnlyServices,
|
|
5
5
|
} from "./bare-metal-partition.js";
|
|
6
6
|
import { composeMultiFile } from "./composer.js";
|
|
7
|
+
import { StackConfigError, ValidationError } from "./errors.js";
|
|
7
8
|
import { generateBareMetalInstall } from "./generators/bare-metal-install.js";
|
|
8
9
|
import { generateCaddyfile } from "./generators/caddy.js";
|
|
9
10
|
import { generateEnvFiles } from "./generators/env.js";
|
|
10
|
-
import { generateTraefikConfig } from "./generators/traefik.js";
|
|
11
11
|
import { generateGrafanaConfig, generateGrafanaDashboard } from "./generators/grafana.js";
|
|
12
|
+
import { generateHealthCheck } from "./generators/health-check.js";
|
|
12
13
|
import { generateN8nWorkflows } from "./generators/n8n-workflows.js";
|
|
13
14
|
import { generateNativeInstallScripts } from "./generators/native-services.js";
|
|
14
15
|
import { generatePostgresInit } from "./generators/postgres-init.js";
|
|
@@ -16,6 +17,7 @@ import { generatePrometheusConfig } from "./generators/prometheus.js";
|
|
|
16
17
|
import { generateReadme } from "./generators/readme.js";
|
|
17
18
|
import { generateScripts } from "./generators/scripts.js";
|
|
18
19
|
import { generateSkillFiles } from "./generators/skills.js";
|
|
20
|
+
import { generateTraefikConfig } from "./generators/traefik.js";
|
|
19
21
|
import { migrateConfig } from "./migrations.js";
|
|
20
22
|
import { resolve } from "./resolver.js";
|
|
21
23
|
import type {
|
|
@@ -25,7 +27,6 @@ import type {
|
|
|
25
27
|
Platform,
|
|
26
28
|
ResolverInput,
|
|
27
29
|
} from "./types.js";
|
|
28
|
-
import { StackConfigError, ValidationError } from "./errors.js";
|
|
29
30
|
import { validate } from "./validator.js";
|
|
30
31
|
|
|
31
32
|
/** Resolver/compose only support linux image platforms; normalize for bare-metal (windows/macos). */
|
|
@@ -103,7 +104,9 @@ export function generate(rawInput: GenerationInput): GenerationResult {
|
|
|
103
104
|
generateSecrets: input.generateSecrets,
|
|
104
105
|
});
|
|
105
106
|
if (!validation.valid) {
|
|
106
|
-
throw new ValidationError(
|
|
107
|
+
throw new ValidationError(
|
|
108
|
+
`Validation failed: ${validation.errors.map((e) => e.message).join("; ")}`,
|
|
109
|
+
);
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
// 4. Generate all files
|
|
@@ -154,6 +157,15 @@ export function generate(rawInput: GenerationInput): GenerationResult {
|
|
|
154
157
|
files[path] = content;
|
|
155
158
|
}
|
|
156
159
|
|
|
160
|
+
// Health check scripts (dynamic, stack-specific)
|
|
161
|
+
const healthCheckFiles = generateHealthCheck(resolved, {
|
|
162
|
+
projectName: input.projectName,
|
|
163
|
+
deploymentType: input.deploymentType,
|
|
164
|
+
});
|
|
165
|
+
for (const [path, content] of Object.entries(healthCheckFiles)) {
|
|
166
|
+
files[path] = content;
|
|
167
|
+
}
|
|
168
|
+
|
|
157
169
|
// n8n workflows
|
|
158
170
|
const n8nWorkflows = generateN8nWorkflows(resolved);
|
|
159
171
|
for (const [path, content] of Object.entries(n8nWorkflows)) {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { generate } from "../generate.js";
|
|
3
|
+
|
|
4
|
+
describe("generateCaddyfile (via generate)", () => {
|
|
5
|
+
const baseInput = {
|
|
6
|
+
projectName: "caddy-test",
|
|
7
|
+
services: ["redis", "n8n"],
|
|
8
|
+
skillPacks: [] as string[],
|
|
9
|
+
proxy: "caddy" as const,
|
|
10
|
+
domain: "example.com",
|
|
11
|
+
gpu: false,
|
|
12
|
+
platform: "linux/amd64" as const,
|
|
13
|
+
deployment: "local" as const,
|
|
14
|
+
generateSecrets: true,
|
|
15
|
+
openclawVersion: "latest",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
it("generates Caddyfile when proxy is caddy", () => {
|
|
19
|
+
const result = generate(baseInput);
|
|
20
|
+
|
|
21
|
+
expect(result.files).toHaveProperty("caddy/Caddyfile");
|
|
22
|
+
expect(result.files["caddy/Caddyfile"]!.length).toBeGreaterThan(0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("does not generate Caddyfile when proxy is none", () => {
|
|
26
|
+
const result = generate({
|
|
27
|
+
...baseInput,
|
|
28
|
+
proxy: "none",
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
expect(result.files).not.toHaveProperty("caddy/Caddyfile");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("Caddyfile contains the domain", () => {
|
|
35
|
+
const result = generate(baseInput);
|
|
36
|
+
const caddyfile = result.files["caddy/Caddyfile"]!;
|
|
37
|
+
|
|
38
|
+
expect(caddyfile).toContain("example.com");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("Caddyfile includes reverse proxy directives for services with exposed ports", () => {
|
|
42
|
+
const result = generate(baseInput);
|
|
43
|
+
const caddyfile = result.files["caddy/Caddyfile"]!;
|
|
44
|
+
|
|
45
|
+
// Should reference n8n since it has exposed ports
|
|
46
|
+
expect(caddyfile).toContain("n8n");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("Caddyfile includes TLS configuration", () => {
|
|
50
|
+
const result = generate(baseInput);
|
|
51
|
+
const caddyfile = result.files["caddy/Caddyfile"]!;
|
|
52
|
+
|
|
53
|
+
// Caddy auto-enables TLS, so should reference HTTPS or TLS
|
|
54
|
+
expect(caddyfile.length).toBeGreaterThan(50);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { generate } from "../generate.js";
|
|
3
|
+
|
|
4
|
+
describe("generateEnvFiles (via generate)", () => {
|
|
5
|
+
const baseInput = {
|
|
6
|
+
projectName: "env-test",
|
|
7
|
+
services: ["redis"],
|
|
8
|
+
skillPacks: [] as string[],
|
|
9
|
+
proxy: "none" as const,
|
|
10
|
+
gpu: false,
|
|
11
|
+
platform: "linux/amd64" as const,
|
|
12
|
+
deployment: "local" as const,
|
|
13
|
+
generateSecrets: true,
|
|
14
|
+
openclawVersion: "latest",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
it("generates .env and .env.example files", () => {
|
|
18
|
+
const result = generate(baseInput);
|
|
19
|
+
|
|
20
|
+
expect(result.files).toHaveProperty(".env");
|
|
21
|
+
expect(result.files).toHaveProperty(".env.example");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it(".env.example has empty values for secrets", () => {
|
|
25
|
+
const result = generate(baseInput);
|
|
26
|
+
const envExample = result.files[".env.example"]!;
|
|
27
|
+
|
|
28
|
+
// .env.example should have placeholder empty values for secrets
|
|
29
|
+
expect(envExample).toContain("REDIS_PASSWORD=");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it(".env has populated secret values when generateSecrets is true", () => {
|
|
33
|
+
const result = generate(baseInput);
|
|
34
|
+
const env = result.files[".env"]!;
|
|
35
|
+
|
|
36
|
+
// Find REDIS_PASSWORD line and check it has a value
|
|
37
|
+
const redisLine = env.split("\n").find((l) => l.startsWith("REDIS_PASSWORD="));
|
|
38
|
+
expect(redisLine).toBeDefined();
|
|
39
|
+
// Value should not be empty
|
|
40
|
+
const value = redisLine!.split("=")[1];
|
|
41
|
+
expect(value!.length).toBeGreaterThan(0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("env files contain service-specific variables", () => {
|
|
45
|
+
const result = generate({
|
|
46
|
+
...baseInput,
|
|
47
|
+
services: ["redis", "postgresql", "n8n"],
|
|
48
|
+
});
|
|
49
|
+
const env = result.files[".env"]!;
|
|
50
|
+
|
|
51
|
+
expect(env).toContain("REDIS_PASSWORD");
|
|
52
|
+
expect(env).toContain("POSTGRES_PASSWORD");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("env files group variables by service with comments", () => {
|
|
56
|
+
const result = generate(baseInput);
|
|
57
|
+
const envExample = result.files[".env.example"]!;
|
|
58
|
+
|
|
59
|
+
// Should have comment sections
|
|
60
|
+
expect(envExample).toContain("#");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it(".env references domain when proxy is set", () => {
|
|
64
|
+
const result = generate({
|
|
65
|
+
...baseInput,
|
|
66
|
+
proxy: "caddy",
|
|
67
|
+
domain: "example.com",
|
|
68
|
+
});
|
|
69
|
+
const env = result.files[".env"]!;
|
|
70
|
+
|
|
71
|
+
expect(env).toContain("example.com");
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { generate } from "../generate.js";
|
|
3
|
+
|
|
4
|
+
describe("generateHealthCheck (via generate)", () => {
|
|
5
|
+
const baseInput = {
|
|
6
|
+
projectName: "health-test",
|
|
7
|
+
services: ["redis", "postgresql"],
|
|
8
|
+
skillPacks: [] as string[],
|
|
9
|
+
proxy: "none" as const,
|
|
10
|
+
gpu: false,
|
|
11
|
+
platform: "linux/amd64" as const,
|
|
12
|
+
deployment: "local" as const,
|
|
13
|
+
generateSecrets: true,
|
|
14
|
+
openclawVersion: "latest",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
it("generates health-check.sh and health-check.ps1", () => {
|
|
18
|
+
const result = generate(baseInput);
|
|
19
|
+
|
|
20
|
+
expect(result.files).toHaveProperty("scripts/health-check.sh");
|
|
21
|
+
expect(result.files).toHaveProperty("scripts/health-check.ps1");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("health-check.sh contains project name", () => {
|
|
25
|
+
const result = generate(baseInput);
|
|
26
|
+
const sh = result.files["scripts/health-check.sh"]!;
|
|
27
|
+
|
|
28
|
+
expect(sh).toContain("health-test");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("health-check.sh contains service-specific checks for each resolved service", () => {
|
|
32
|
+
const result = generate(baseInput);
|
|
33
|
+
const sh = result.files["scripts/health-check.sh"]!;
|
|
34
|
+
|
|
35
|
+
expect(sh).toContain("redis");
|
|
36
|
+
expect(sh).toContain("postgresql");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("health-check.sh includes port checks for exposed ports", () => {
|
|
40
|
+
const result = generate(baseInput);
|
|
41
|
+
const sh = result.files["scripts/health-check.sh"]!;
|
|
42
|
+
|
|
43
|
+
// Redis exposes port 6379, PostgreSQL exposes 5432
|
|
44
|
+
expect(sh).toContain("6379");
|
|
45
|
+
expect(sh).toContain("5432");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("health-check.sh has usage/help docs", () => {
|
|
49
|
+
const result = generate(baseInput);
|
|
50
|
+
const sh = result.files["scripts/health-check.sh"]!;
|
|
51
|
+
|
|
52
|
+
expect(sh).toContain("--quick");
|
|
53
|
+
expect(sh).toContain("--json");
|
|
54
|
+
expect(sh).toContain("--verbose");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("health-check.sh starts with shebang", () => {
|
|
58
|
+
const result = generate(baseInput);
|
|
59
|
+
const sh = result.files["scripts/health-check.sh"]!;
|
|
60
|
+
|
|
61
|
+
expect(sh.startsWith("#!/usr/bin/env bash")).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("health-check.ps1 contains project name", () => {
|
|
65
|
+
const result = generate(baseInput);
|
|
66
|
+
const ps1 = result.files["scripts/health-check.ps1"]!;
|
|
67
|
+
|
|
68
|
+
expect(ps1).toContain("health-test");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("health-check.ps1 contains service checks", () => {
|
|
72
|
+
const result = generate(baseInput);
|
|
73
|
+
const ps1 = result.files["scripts/health-check.ps1"]!;
|
|
74
|
+
|
|
75
|
+
expect(ps1).toContain("redis");
|
|
76
|
+
expect(ps1).toContain("postgresql");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("health-check.ps1 includes PowerShell-style parameters", () => {
|
|
80
|
+
const result = generate(baseInput);
|
|
81
|
+
const ps1 = result.files["scripts/health-check.ps1"]!;
|
|
82
|
+
|
|
83
|
+
expect(ps1).toContain("[switch]$Quick");
|
|
84
|
+
expect(ps1).toContain("[switch]$Json");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("health check scripts include all resolved services including dependencies", () => {
|
|
88
|
+
const result = generate({
|
|
89
|
+
...baseInput,
|
|
90
|
+
services: ["postiz"], // postiz depends on redis and postgresql
|
|
91
|
+
});
|
|
92
|
+
const sh = result.files["scripts/health-check.sh"]!;
|
|
93
|
+
|
|
94
|
+
expect(sh).toContain("postiz");
|
|
95
|
+
// Dependencies should be auto-resolved and included
|
|
96
|
+
expect(sh).toContain("redis");
|
|
97
|
+
expect(sh).toContain("postgresql");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("health check script includes health check commands when service defines one", () => {
|
|
101
|
+
const result = generate(baseInput);
|
|
102
|
+
const sh = result.files["scripts/health-check.sh"]!;
|
|
103
|
+
|
|
104
|
+
// Redis has a healthcheck command (redis-cli ping)
|
|
105
|
+
expect(sh).toContain("check_health_cmd");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("health check scripts contain the phase structure", () => {
|
|
109
|
+
const result = generate(baseInput);
|
|
110
|
+
const sh = result.files["scripts/health-check.sh"]!;
|
|
111
|
+
|
|
112
|
+
expect(sh).toContain("Phase 1");
|
|
113
|
+
expect(sh).toContain("phase_environment");
|
|
114
|
+
expect(sh).toContain("check_container");
|
|
115
|
+
expect(sh).toContain("check_port");
|
|
116
|
+
expect(sh).toContain("phase_logs");
|
|
117
|
+
});
|
|
118
|
+
});
|