@better-openclaw/core 1.0.24 → 1.0.26
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/addon-stack.cjs +725 -0
- package/dist/addon-stack.cjs.map +1 -0
- package/dist/addon-stack.d.cts +23 -0
- package/dist/addon-stack.d.cts.map +1 -0
- package/dist/addon-stack.d.mts +23 -0
- package/dist/addon-stack.d.mts.map +1 -0
- package/dist/addon-stack.mjs +723 -0
- package/dist/addon-stack.mjs.map +1 -0
- package/dist/addon-stack.test.cjs +461 -0
- package/dist/addon-stack.test.cjs.map +1 -0
- package/dist/addon-stack.test.d.cts +1 -0
- package/dist/addon-stack.test.d.mts +1 -0
- package/dist/addon-stack.test.mjs +461 -0
- package/dist/addon-stack.test.mjs.map +1 -0
- package/dist/bare-metal-partition.test.cjs +20 -20
- package/dist/bare-metal-partition.test.cjs.map +1 -1
- package/dist/bare-metal-partition.test.mjs +2 -2
- package/dist/compose-validation.test.cjs +1 -1
- package/dist/composer.cjs +5 -1
- package/dist/composer.cjs.map +1 -1
- package/dist/composer.d.cts +24 -1
- package/dist/composer.d.cts.map +1 -1
- package/dist/composer.d.mts +24 -1
- package/dist/composer.d.mts.map +1 -1
- package/dist/composer.mjs +1 -1
- package/dist/composer.mjs.map +1 -1
- package/dist/composer.snapshot.test.cjs +20 -20
- package/dist/composer.snapshot.test.cjs.map +1 -1
- package/dist/composer.snapshot.test.mjs +2 -2
- package/dist/composer.test.cjs +54 -54
- package/dist/composer.test.cjs.map +1 -1
- package/dist/composer.test.mjs +2 -2
- package/dist/deployers/strip-host-ports.cjs +1 -1
- package/dist/deployers/strip-host-ports.test.cjs +26 -26
- package/dist/deployers/strip-host-ports.test.cjs.map +1 -1
- package/dist/deployers/strip-host-ports.test.mjs +1 -1
- package/dist/generate.cjs +3 -3
- package/dist/generate.mjs +3 -3
- package/dist/generate.test.cjs +56 -56
- package/dist/generate.test.cjs.map +1 -1
- package/dist/generate.test.mjs +1 -1
- package/dist/generators/bare-metal-install.test.cjs +18 -18
- package/dist/generators/bare-metal-install.test.cjs.map +1 -1
- package/dist/generators/bare-metal-install.test.mjs +1 -1
- package/dist/generators/caddy.test.cjs +13 -13
- package/dist/generators/caddy.test.cjs.map +1 -1
- package/dist/generators/caddy.test.mjs +1 -1
- package/dist/generators/clone-repos.test.cjs +27 -27
- package/dist/generators/clone-repos.test.cjs.map +1 -1
- package/dist/generators/clone-repos.test.mjs +1 -1
- package/dist/generators/env.cjs +1 -1
- package/dist/generators/env.test.cjs +17 -17
- package/dist/generators/env.test.cjs.map +1 -1
- package/dist/generators/env.test.mjs +1 -1
- package/dist/generators/health-check.test.cjs +39 -39
- package/dist/generators/health-check.test.cjs.map +1 -1
- package/dist/generators/health-check.test.mjs +1 -1
- package/dist/generators/postgres-init.cjs +5 -0
- package/dist/generators/postgres-init.cjs.map +1 -1
- package/dist/generators/postgres-init.d.cts.map +1 -1
- package/dist/generators/postgres-init.d.mts.map +1 -1
- package/dist/generators/postgres-init.mjs +5 -0
- package/dist/generators/postgres-init.mjs.map +1 -1
- package/dist/generators/scripts.test.cjs +39 -39
- package/dist/generators/scripts.test.cjs.map +1 -1
- package/dist/generators/scripts.test.mjs +1 -1
- package/dist/generators/skills.cjs +1 -1
- package/dist/generators/skills.d.cts.map +1 -1
- package/dist/generators/skills.d.mts.map +1 -1
- package/dist/generators/skills.mjs +141 -0
- package/dist/generators/skills.mjs.map +1 -1
- package/dist/generators/traefik.test.cjs +32 -32
- package/dist/generators/traefik.test.cjs.map +1 -1
- package/dist/generators/traefik.test.mjs +1 -1
- package/dist/index.cjs +21 -5
- package/dist/index.d.cts +5 -4
- package/dist/index.d.mts +5 -4
- package/dist/index.mjs +7 -6
- package/dist/migrations.test.cjs +16 -16
- package/dist/migrations.test.cjs.map +1 -1
- package/dist/migrations.test.mjs +1 -1
- package/dist/presets/presets.test.cjs +1 -1
- package/dist/presets/registry.test.cjs +14 -14
- package/dist/presets/registry.test.cjs.map +1 -1
- package/dist/presets/registry.test.mjs +1 -1
- package/dist/resolver.test.cjs +95 -95
- package/dist/resolver.test.cjs.map +1 -1
- package/dist/resolver.test.mjs +1 -1
- package/dist/{schema-eX44HhRp.d.mts → schema-BQnZrcw8.d.cts} +300 -2
- package/dist/schema-BQnZrcw8.d.cts.map +1 -0
- package/dist/{schema-tn5RK8CM.d.cts → schema-SBpL0bdI.d.mts} +300 -2
- package/dist/schema-SBpL0bdI.d.mts.map +1 -0
- package/dist/schema.cjs +148 -2
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +2 -2
- package/dist/schema.d.mts +2 -2
- package/dist/schema.mjs +139 -2
- package/dist/schema.mjs.map +1 -1
- package/dist/schema.test.cjs +86 -86
- package/dist/schema.test.cjs.map +1 -1
- package/dist/schema.test.mjs +1 -1
- package/dist/services/definitions/browserless.cjs +4 -1
- package/dist/services/definitions/browserless.cjs.map +1 -1
- package/dist/services/definitions/browserless.mjs +4 -1
- package/dist/services/definitions/browserless.mjs.map +1 -1
- package/dist/services/definitions/burnlink.cjs +142 -0
- package/dist/services/definitions/burnlink.cjs.map +1 -0
- package/dist/services/definitions/burnlink.d.cts +7 -0
- package/dist/services/definitions/burnlink.d.cts.map +1 -0
- package/dist/services/definitions/burnlink.d.mts +7 -0
- package/dist/services/definitions/burnlink.d.mts.map +1 -0
- package/dist/services/definitions/burnlink.mjs +141 -0
- package/dist/services/definitions/burnlink.mjs.map +1 -0
- package/dist/services/definitions/convex.cjs +43 -1
- package/dist/services/definitions/convex.cjs.map +1 -1
- package/dist/services/definitions/convex.mjs +43 -1
- package/dist/services/definitions/convex.mjs.map +1 -1
- package/dist/services/definitions/grafana.cjs +11 -1
- package/dist/services/definitions/grafana.cjs.map +1 -1
- package/dist/services/definitions/grafana.mjs +11 -1
- package/dist/services/definitions/grafana.mjs.map +1 -1
- package/dist/services/definitions/hindsight.cjs +130 -0
- package/dist/services/definitions/hindsight.cjs.map +1 -0
- package/dist/services/definitions/hindsight.d.cts +7 -0
- package/dist/services/definitions/hindsight.d.cts.map +1 -0
- package/dist/services/definitions/hindsight.d.mts +7 -0
- package/dist/services/definitions/hindsight.d.mts.map +1 -0
- package/dist/services/definitions/hindsight.mjs +129 -0
- package/dist/services/definitions/hindsight.mjs.map +1 -0
- package/dist/services/definitions/index.cjs +9 -0
- package/dist/services/definitions/index.cjs.map +1 -1
- package/dist/services/definitions/index.d.cts +4 -1
- package/dist/services/definitions/index.d.cts.map +1 -1
- package/dist/services/definitions/index.d.mts +4 -1
- package/dist/services/definitions/index.d.mts.map +1 -1
- package/dist/services/definitions/index.mjs +7 -1
- package/dist/services/definitions/index.mjs.map +1 -1
- package/dist/services/definitions/meilisearch.cjs +11 -1
- package/dist/services/definitions/meilisearch.cjs.map +1 -1
- package/dist/services/definitions/meilisearch.mjs +11 -1
- package/dist/services/definitions/meilisearch.mjs.map +1 -1
- package/dist/services/definitions/minio.cjs +3 -1
- package/dist/services/definitions/minio.cjs.map +1 -1
- package/dist/services/definitions/minio.mjs +3 -1
- package/dist/services/definitions/minio.mjs.map +1 -1
- package/dist/services/definitions/n8n.cjs +11 -1
- package/dist/services/definitions/n8n.cjs.map +1 -1
- package/dist/services/definitions/n8n.mjs +11 -1
- package/dist/services/definitions/n8n.mjs.map +1 -1
- package/dist/services/definitions/ollama.cjs +3 -1
- package/dist/services/definitions/ollama.cjs.map +1 -1
- package/dist/services/definitions/ollama.mjs +3 -1
- package/dist/services/definitions/ollama.mjs.map +1 -1
- package/dist/services/definitions/opensandbox.cjs +149 -0
- package/dist/services/definitions/opensandbox.cjs.map +1 -0
- package/dist/services/definitions/opensandbox.d.cts +7 -0
- package/dist/services/definitions/opensandbox.d.cts.map +1 -0
- package/dist/services/definitions/opensandbox.d.mts +7 -0
- package/dist/services/definitions/opensandbox.d.mts.map +1 -0
- package/dist/services/definitions/opensandbox.mjs +148 -0
- package/dist/services/definitions/opensandbox.mjs.map +1 -0
- package/dist/services/definitions/qdrant.cjs +3 -1
- package/dist/services/definitions/qdrant.cjs.map +1 -1
- package/dist/services/definitions/qdrant.mjs +3 -1
- package/dist/services/definitions/qdrant.mjs.map +1 -1
- package/dist/services/definitions/searxng.cjs +8 -1
- package/dist/services/definitions/searxng.cjs.map +1 -1
- package/dist/services/definitions/searxng.mjs +8 -1
- package/dist/services/definitions/searxng.mjs.map +1 -1
- package/dist/services/definitions/uptime-kuma.cjs +8 -1
- package/dist/services/definitions/uptime-kuma.cjs.map +1 -1
- package/dist/services/definitions/uptime-kuma.mjs +8 -1
- package/dist/services/definitions/uptime-kuma.mjs.map +1 -1
- package/dist/services/registry.test.cjs +36 -36
- package/dist/services/registry.test.cjs.map +1 -1
- package/dist/services/registry.test.mjs +1 -1
- package/dist/{skills-BlzpHmpH.cjs → skills-BSF7iNa4.cjs} +142 -1
- package/dist/{skills-BlzpHmpH.cjs.map → skills-BSF7iNa4.cjs.map} +1 -1
- package/dist/{vi.2VT5v0um-C_jmO7m2.mjs → test.CTcmp4Su-ClCHJ3FA.mjs} +6793 -6403
- package/dist/test.CTcmp4Su-ClCHJ3FA.mjs.map +1 -0
- package/dist/{vi.2VT5v0um-iVBt6Fyq.cjs → test.CTcmp4Su-DlzTarwH.cjs} +6793 -6403
- package/dist/test.CTcmp4Su-DlzTarwH.cjs.map +1 -0
- package/dist/track-analytics.test.cjs +28 -28
- package/dist/track-analytics.test.cjs.map +1 -1
- package/dist/track-analytics.test.mjs +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +10 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +10 -2
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/validator.cjs +1 -1
- package/dist/validator.test.cjs +15 -15
- package/dist/validator.test.cjs.map +1 -1
- package/dist/validator.test.mjs +2 -2
- package/dist/version-manager.test.cjs +37 -37
- package/dist/version-manager.test.cjs.map +1 -1
- package/dist/version-manager.test.mjs +1 -1
- package/package.json +4 -4
- package/src/__snapshots__/composer.snapshot.test.ts.snap +5 -0
- package/src/addon-stack.test.ts +648 -0
- package/src/addon-stack.ts +1046 -0
- package/src/composer.ts +4 -4
- package/src/generators/postgres-init.ts +2 -0
- package/src/generators/skills.ts +142 -0
- package/src/index.ts +20 -2
- package/src/schema.ts +190 -0
- package/src/services/definitions/browserless.ts +3 -0
- package/src/services/definitions/burnlink.ts +142 -0
- package/src/services/definitions/convex.ts +31 -0
- package/src/services/definitions/grafana.ts +9 -0
- package/src/services/definitions/hindsight.ts +131 -0
- package/src/services/definitions/index.ts +10 -0
- package/src/services/definitions/meilisearch.ts +9 -0
- package/src/services/definitions/minio.ts +2 -0
- package/src/services/definitions/n8n.ts +9 -0
- package/src/services/definitions/ollama.ts +2 -0
- package/src/services/definitions/opensandbox.ts +156 -0
- package/src/services/definitions/qdrant.ts +2 -0
- package/src/services/definitions/searxng.ts +3 -0
- package/src/services/definitions/uptime-kuma.ts +3 -0
- package/src/types.ts +18 -0
- package/dist/schema-eX44HhRp.d.mts.map +0 -1
- package/dist/schema-tn5RK8CM.d.cts.map +0 -1
- package/dist/vi.2VT5v0um-C_jmO7m2.mjs.map +0 -1
- package/dist/vi.2VT5v0um-iVBt6Fyq.cjs.map +0 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
const
|
|
2
|
-
const require_bare_metal_partition = require("./bare-metal-partition.cjs");
|
|
1
|
+
const require_test_CTcmp4Su = require("./test.CTcmp4Su-DlzTarwH.cjs");
|
|
3
2
|
const require_resolver = require("./resolver.cjs");
|
|
3
|
+
const require_bare_metal_partition = require("./bare-metal-partition.cjs");
|
|
4
4
|
//#region src/bare-metal-partition.test.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
require_test_CTcmp4Su.describe("bare-metal partition", () => {
|
|
6
|
+
require_test_CTcmp4Su.it("platformToNativePlatform maps platform to native", () => {
|
|
7
|
+
require_test_CTcmp4Su.globalExpect(require_bare_metal_partition.platformToNativePlatform("linux/amd64")).toBe("linux");
|
|
8
|
+
require_test_CTcmp4Su.globalExpect(require_bare_metal_partition.platformToNativePlatform("linux/arm64")).toBe("linux");
|
|
9
|
+
require_test_CTcmp4Su.globalExpect(require_bare_metal_partition.platformToNativePlatform("windows/amd64")).toBe("windows");
|
|
10
|
+
require_test_CTcmp4Su.globalExpect(require_bare_metal_partition.platformToNativePlatform("macos/arm64")).toBe("macos");
|
|
11
11
|
});
|
|
12
|
-
|
|
12
|
+
require_test_CTcmp4Su.it("partitions redis as native on Linux when redis has native recipe", () => {
|
|
13
13
|
const result = require_bare_metal_partition.partitionBareMetal(require_resolver.resolve({
|
|
14
14
|
services: ["redis"],
|
|
15
15
|
skillPacks: [],
|
|
@@ -18,11 +18,11 @@ require_vi_2VT5v0um.describe("bare-metal partition", () => {
|
|
|
18
18
|
platform: "linux/amd64",
|
|
19
19
|
monitoring: false
|
|
20
20
|
}), "linux/amd64");
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
require_test_CTcmp4Su.globalExpect(result.nativeIds.has("redis")).toBe(true);
|
|
22
|
+
require_test_CTcmp4Su.globalExpect(result.nativeServices.find((s) => s.definition.id === "redis")).toBeDefined();
|
|
23
|
+
require_test_CTcmp4Su.globalExpect(result.dockerOnlyServices.length).toBeGreaterThanOrEqual(0);
|
|
24
24
|
});
|
|
25
|
-
|
|
25
|
+
require_test_CTcmp4Su.it("partitions n8n as Docker-only (no native recipe)", () => {
|
|
26
26
|
const result = require_bare_metal_partition.partitionBareMetal(require_resolver.resolve({
|
|
27
27
|
services: ["n8n"],
|
|
28
28
|
skillPacks: [],
|
|
@@ -31,12 +31,12 @@ require_vi_2VT5v0um.describe("bare-metal partition", () => {
|
|
|
31
31
|
platform: "linux/amd64",
|
|
32
32
|
monitoring: false
|
|
33
33
|
}), "linux/amd64");
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
require_test_CTcmp4Su.globalExpect(result.nativeIds.has("n8n")).toBe(false);
|
|
35
|
+
require_test_CTcmp4Su.globalExpect(result.nativeServices.length).toBe(0);
|
|
36
|
+
require_test_CTcmp4Su.globalExpect(result.dockerOnlyServices.length).toBeGreaterThanOrEqual(1);
|
|
37
|
+
require_test_CTcmp4Su.globalExpect(result.dockerOnlyServices.some((s) => s.definition.id === "n8n")).toBe(true);
|
|
38
38
|
});
|
|
39
|
-
|
|
39
|
+
require_test_CTcmp4Su.it("resolvedWithOnlyServices filters to given service list", () => {
|
|
40
40
|
const resolved = require_resolver.resolve({
|
|
41
41
|
services: ["redis", "n8n"],
|
|
42
42
|
skillPacks: [],
|
|
@@ -46,8 +46,8 @@ require_vi_2VT5v0um.describe("bare-metal partition", () => {
|
|
|
46
46
|
monitoring: false
|
|
47
47
|
});
|
|
48
48
|
const filtered = require_bare_metal_partition.resolvedWithOnlyServices(resolved, require_bare_metal_partition.partitionBareMetal(resolved, "linux/amd64").dockerOnlyServices);
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
require_test_CTcmp4Su.globalExpect(filtered.services.every((s) => s.definition.id !== "redis")).toBe(true);
|
|
50
|
+
require_test_CTcmp4Su.globalExpect(filtered.services.some((s) => s.definition.id === "n8n")).toBe(true);
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
53
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bare-metal-partition.test.cjs","names":["describe","platformToNativePlatform","partitionBareMetal","resolve","resolvedWithOnlyServices"],"sources":["../src/bare-metal-partition.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport {\n\tpartitionBareMetal,\n\tplatformToNativePlatform,\n\tresolvedWithOnlyServices,\n} from \"./bare-metal-partition.js\";\nimport { resolve } from \"./resolver.js\";\n\ndescribe(\"bare-metal partition\", () => {\n\tit(\"platformToNativePlatform maps platform to native\", () => {\n\t\texpect(platformToNativePlatform(\"linux/amd64\")).toBe(\"linux\");\n\t\texpect(platformToNativePlatform(\"linux/arm64\")).toBe(\"linux\");\n\t\texpect(platformToNativePlatform(\"windows/amd64\")).toBe(\"windows\");\n\t\texpect(platformToNativePlatform(\"macos/arm64\")).toBe(\"macos\");\n\t});\n\n\tit(\"partitions redis as native on Linux when redis has native recipe\", () => {\n\t\tconst resolved = resolve({\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tmonitoring: false,\n\t\t});\n\t\tconst result = partitionBareMetal(resolved, \"linux/amd64\");\n\t\texpect(result.nativeIds.has(\"redis\")).toBe(true);\n\t\tconst redisNative = result.nativeServices.find((s) => s.definition.id === \"redis\");\n\t\texpect(redisNative).toBeDefined();\n\t\t// Mandatory services (convex, mission-control, tailscale) are Docker-only\n\t\texpect(result.dockerOnlyServices.length).toBeGreaterThanOrEqual(0);\n\t});\n\n\tit(\"partitions n8n as Docker-only (no native recipe)\", () => {\n\t\tconst resolved = resolve({\n\t\t\tservices: [\"n8n\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tmonitoring: false,\n\t\t});\n\t\tconst result = partitionBareMetal(resolved, \"linux/amd64\");\n\t\texpect(result.nativeIds.has(\"n8n\")).toBe(false);\n\t\texpect(result.nativeServices.length).toBe(0);\n\t\texpect(result.dockerOnlyServices.length).toBeGreaterThanOrEqual(1);\n\t\texpect(result.dockerOnlyServices.some((s) => s.definition.id === \"n8n\")).toBe(true);\n\t});\n\n\tit(\"resolvedWithOnlyServices filters to given service list\", () => {\n\t\tconst resolved = resolve({\n\t\t\tservices: [\"redis\", \"n8n\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tmonitoring: false,\n\t\t});\n\t\tconst partition = partitionBareMetal(resolved, \"linux/amd64\");\n\t\tconst filtered = resolvedWithOnlyServices(resolved, partition.dockerOnlyServices);\n\t\texpect(filtered.services.every((s) => s.definition.id !== \"redis\")).toBe(true);\n\t\texpect(filtered.services.some((s) => s.definition.id === \"n8n\")).toBe(true);\n\t});\n});\n"],"mappings":";;;;AAQAA,
|
|
1
|
+
{"version":3,"file":"bare-metal-partition.test.cjs","names":["describe","platformToNativePlatform","partitionBareMetal","resolve","resolvedWithOnlyServices"],"sources":["../src/bare-metal-partition.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport {\n\tpartitionBareMetal,\n\tplatformToNativePlatform,\n\tresolvedWithOnlyServices,\n} from \"./bare-metal-partition.js\";\nimport { resolve } from \"./resolver.js\";\n\ndescribe(\"bare-metal partition\", () => {\n\tit(\"platformToNativePlatform maps platform to native\", () => {\n\t\texpect(platformToNativePlatform(\"linux/amd64\")).toBe(\"linux\");\n\t\texpect(platformToNativePlatform(\"linux/arm64\")).toBe(\"linux\");\n\t\texpect(platformToNativePlatform(\"windows/amd64\")).toBe(\"windows\");\n\t\texpect(platformToNativePlatform(\"macos/arm64\")).toBe(\"macos\");\n\t});\n\n\tit(\"partitions redis as native on Linux when redis has native recipe\", () => {\n\t\tconst resolved = resolve({\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tmonitoring: false,\n\t\t});\n\t\tconst result = partitionBareMetal(resolved, \"linux/amd64\");\n\t\texpect(result.nativeIds.has(\"redis\")).toBe(true);\n\t\tconst redisNative = result.nativeServices.find((s) => s.definition.id === \"redis\");\n\t\texpect(redisNative).toBeDefined();\n\t\t// Mandatory services (convex, mission-control, tailscale) are Docker-only\n\t\texpect(result.dockerOnlyServices.length).toBeGreaterThanOrEqual(0);\n\t});\n\n\tit(\"partitions n8n as Docker-only (no native recipe)\", () => {\n\t\tconst resolved = resolve({\n\t\t\tservices: [\"n8n\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tmonitoring: false,\n\t\t});\n\t\tconst result = partitionBareMetal(resolved, \"linux/amd64\");\n\t\texpect(result.nativeIds.has(\"n8n\")).toBe(false);\n\t\texpect(result.nativeServices.length).toBe(0);\n\t\texpect(result.dockerOnlyServices.length).toBeGreaterThanOrEqual(1);\n\t\texpect(result.dockerOnlyServices.some((s) => s.definition.id === \"n8n\")).toBe(true);\n\t});\n\n\tit(\"resolvedWithOnlyServices filters to given service list\", () => {\n\t\tconst resolved = resolve({\n\t\t\tservices: [\"redis\", \"n8n\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tmonitoring: false,\n\t\t});\n\t\tconst partition = partitionBareMetal(resolved, \"linux/amd64\");\n\t\tconst filtered = resolvedWithOnlyServices(resolved, partition.dockerOnlyServices);\n\t\texpect(filtered.services.every((s) => s.definition.id !== \"redis\")).toBe(true);\n\t\texpect(filtered.services.some((s) => s.definition.id === \"n8n\")).toBe(true);\n\t});\n});\n"],"mappings":";;;;AAQAA,sBAAAA,SAAS,8BAA8B;AACtC,uBAAA,GAAG,0DAA0D;AAC5D,wBAAA,aAAOC,6BAAAA,yBAAyB,cAAc,CAAC,CAAC,KAAK,QAAQ;AAC7D,wBAAA,aAAOA,6BAAAA,yBAAyB,cAAc,CAAC,CAAC,KAAK,QAAQ;AAC7D,wBAAA,aAAOA,6BAAAA,yBAAyB,gBAAgB,CAAC,CAAC,KAAK,UAAU;AACjE,wBAAA,aAAOA,6BAAAA,yBAAyB,cAAc,CAAC,CAAC,KAAK,QAAQ;GAC5D;AAEF,uBAAA,GAAG,0EAA0E;EAS5E,MAAM,SAASC,6BAAAA,mBAREC,iBAAAA,QAAQ;GACxB,UAAU,CAAC,QAAQ;GACnB,YAAY,EAAE;GACd,OAAO;GACP,KAAK;GACL,UAAU;GACV,YAAY;GACZ,CAAC,EAC0C,cAAc;AAC1D,wBAAA,aAAO,OAAO,UAAU,IAAI,QAAQ,CAAC,CAAC,KAAK,KAAK;AAEhD,wBAAA,aADoB,OAAO,eAAe,MAAM,MAAM,EAAE,WAAW,OAAO,QAAQ,CAC/D,CAAC,aAAa;AAEjC,wBAAA,aAAO,OAAO,mBAAmB,OAAO,CAAC,uBAAuB,EAAE;GACjE;AAEF,uBAAA,GAAG,0DAA0D;EAS5D,MAAM,SAASD,6BAAAA,mBAREC,iBAAAA,QAAQ;GACxB,UAAU,CAAC,MAAM;GACjB,YAAY,EAAE;GACd,OAAO;GACP,KAAK;GACL,UAAU;GACV,YAAY;GACZ,CAAC,EAC0C,cAAc;AAC1D,wBAAA,aAAO,OAAO,UAAU,IAAI,MAAM,CAAC,CAAC,KAAK,MAAM;AAC/C,wBAAA,aAAO,OAAO,eAAe,OAAO,CAAC,KAAK,EAAE;AAC5C,wBAAA,aAAO,OAAO,mBAAmB,OAAO,CAAC,uBAAuB,EAAE;AAClE,wBAAA,aAAO,OAAO,mBAAmB,MAAM,MAAM,EAAE,WAAW,OAAO,MAAM,CAAC,CAAC,KAAK,KAAK;GAClF;AAEF,uBAAA,GAAG,gEAAgE;EAClE,MAAM,WAAWA,iBAAAA,QAAQ;GACxB,UAAU,CAAC,SAAS,MAAM;GAC1B,YAAY,EAAE;GACd,OAAO;GACP,KAAK;GACL,UAAU;GACV,YAAY;GACZ,CAAC;EAEF,MAAM,WAAWC,6BAAAA,yBAAyB,UADxBF,6BAAAA,mBAAmB,UAAU,cAAc,CACC,mBAAmB;AACjF,wBAAA,aAAO,SAAS,SAAS,OAAO,MAAM,EAAE,WAAW,OAAO,QAAQ,CAAC,CAAC,KAAK,KAAK;AAC9E,wBAAA,aAAO,SAAS,SAAS,MAAM,MAAM,EAAE,WAAW,OAAO,MAAM,CAAC,CAAC,KAAK,KAAK;GAC1E;EACD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as describe, r as it, t as globalExpect } from "./
|
|
2
|
-
import { partitionBareMetal, platformToNativePlatform, resolvedWithOnlyServices } from "./bare-metal-partition.mjs";
|
|
1
|
+
import { n as describe, r as it, t as globalExpect } from "./test.CTcmp4Su-ClCHJ3FA.mjs";
|
|
3
2
|
import { resolve } from "./resolver.mjs";
|
|
3
|
+
import { partitionBareMetal, platformToNativePlatform, resolvedWithOnlyServices } from "./bare-metal-partition.mjs";
|
|
4
4
|
//#region src/bare-metal-partition.test.ts
|
|
5
5
|
describe("bare-metal partition", () => {
|
|
6
6
|
it("platformToNativePlatform maps platform to native", () => {
|
package/dist/composer.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("./skills-
|
|
2
|
+
require("./skills-BSF7iNa4.cjs");
|
|
3
3
|
const require_generators_postgres_init = require("./generators/postgres-init.cjs");
|
|
4
4
|
let yaml = require("yaml");
|
|
5
5
|
//#region src/composer.ts
|
|
@@ -481,7 +481,11 @@ function composeMultiFile(resolved, options) {
|
|
|
481
481
|
};
|
|
482
482
|
}
|
|
483
483
|
//#endregion
|
|
484
|
+
exports.YAML_OPTIONS = YAML_OPTIONS;
|
|
485
|
+
exports.buildCompanionService = buildCompanionService;
|
|
486
|
+
exports.buildPostgresSetup = buildPostgresSetup;
|
|
484
487
|
exports.compose = compose;
|
|
485
488
|
exports.composeMultiFile = composeMultiFile;
|
|
489
|
+
exports.quotedStr = quotedStr;
|
|
486
490
|
|
|
487
491
|
//# sourceMappingURL=composer.cjs.map
|
package/dist/composer.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composer.cjs","names":["Scalar","getDbRequirements"],"sources":["../src/composer.ts"],"sourcesContent":["import { Scalar, stringify } from \"yaml\";\nimport { getDbRequirements } from \"./generators/postgres-init.js\";\nimport type {\n\tComposeOptions,\n\tOpenclawImageVariant,\n\tResolverOutput,\n\tServiceCategory,\n} from \"./types.js\";\n\n/** Maps image variant to the Docker image string. */\nconst IMAGE_VARIANTS: Record<OpenclawImageVariant, string> = {\n\tofficial: \"ghcr.io/openclaw/openclaw\",\n\tcoolify: \"coollabsio/openclaw\",\n\talpine: \"alpine/openclaw\",\n};\n\n/** Returns the OpenClaw image string with version tag for a given variant. */\nfunction getOpenclawImage(variant: OpenclawImageVariant, version: string): string {\n\tconst base = IMAGE_VARIANTS[variant];\n\t// Coolify and alpine images use :latest by default\n\tconst tag = variant === \"official\" ? version : \"latest\";\n\treturn `${base}:${tag}`;\n}\n\n/** Creates a YAML scalar that is always quoted — avoids YAML 1.1 bare `no` → false. */\nfunction quotedStr(value: string): Scalar {\n\tconst s = new Scalar(value);\n\ts.type = Scalar.QUOTE_DOUBLE;\n\treturn s;\n}\n\n// ── Public Types ────────────────────────────────────────────────────────────\n\nexport interface ComposeResult {\n\tfiles: Record<string, string>; // filename -> YAML content\n\tmainFile: string; // \"docker-compose.yml\"\n\tprofiles: string[]; // list of profiles used\n}\n\n// ── Category → Profile Mapping ──────────────────────────────────────────────\n\nconst CATEGORY_PROFILE_MAP: Partial<Record<ServiceCategory, { file: string; profile: string }>> = {\n\tai: { file: \"docker-compose.ai.yml\", profile: \"ai\" },\n\t\"ai-platform\": { file: \"docker-compose.ai.yml\", profile: \"ai\" },\n\tmedia: { file: \"docker-compose.media.yml\", profile: \"media\" },\n\tmonitoring: { file: \"docker-compose.monitoring.yml\", profile: \"monitoring\" },\n\tanalytics: { file: \"docker-compose.monitoring.yml\", profile: \"monitoring\" },\n\t\"dev-tools\": { file: \"docker-compose.tools.yml\", profile: \"tools\" },\n\t\"coding-agent\": { file: \"docker-compose.tools.yml\", profile: \"tools\" },\n\t\"social-media\": { file: \"docker-compose.social.yml\", profile: \"social\" },\n\tknowledge: { file: \"docker-compose.knowledge.yml\", profile: \"knowledge\" },\n\tcommunication: { file: \"docker-compose.communication.yml\", profile: \"communication\" },\n\t\"saas-boilerplate\": { file: \"docker-compose.saas.yml\", profile: \"saas\" },\n};\n\nconst YAML_OPTIONS = { lineWidth: 120, nullStr: \"\" };\n\n// ── Shared Gateway Builder ──────────────────────────────────────────────────\n\ninterface GatewayBuildResult {\n\tgatewayService: Record<string, unknown>;\n\tcliService: Record<string, unknown>;\n\tallVolumes: Set<string>;\n}\n\n/**\n * Builds the OpenClaw gateway and CLI service entries.\n * Matches the real OpenClaw docker-compose.yml structure:\n * - Bridge port 18790 for ACP/WebSocket\n * - Bind-mount volumes (not named volumes)\n * - Claude web-provider session env vars\n * - Gateway bind mode (--bind lan)\n * - CLI companion service with stdin/tty\n */\nfunction buildGatewayServices(\n\tresolved: ResolverOutput,\n\toptions: ComposeOptions,\n\tdependsOn?: Record<string, { condition: string }>,\n): GatewayBuildResult {\n\tconst allVolumes = new Set<string>();\n\n\t// Gateway environment\n\tconst gatewayEnv: Record<string, string> = {\n\t\tHOME: \"/home/node\",\n\t\tTERM: \"xterm-256color\",\n\t\tOPENCLAW_GATEWAY_TOKEN: \"${OPENCLAW_GATEWAY_TOKEN}\",\n\t\tOPENCLAW_ALLOW_INSECURE_PRIVATE_WS: \"${OPENCLAW_ALLOW_INSECURE_PRIVATE_WS:-}\",\n\t\t// Claude web-provider session vars (optional, user fills in .env)\n\t\tCLAUDE_AI_SESSION_KEY: \"${CLAUDE_AI_SESSION_KEY:-}\",\n\t\tCLAUDE_WEB_SESSION_KEY: \"${CLAUDE_WEB_SESSION_KEY:-}\",\n\t\tCLAUDE_WEB_COOKIE: \"${CLAUDE_WEB_COOKIE:-}\",\n\t};\n\n\t// Add AI provider API keys to gateway environment\n\tconst providerKeys = [\n\t\t\"OPENAI_API_KEY\",\n\t\t\"ANTHROPIC_API_KEY\",\n\t\t\"GOOGLE_API_KEY\",\n\t\t\"XAI_API_KEY\",\n\t\t\"DEEPSEEK_API_KEY\",\n\t\t\"GROQ_API_KEY\",\n\t\t\"OPENROUTER_API_KEY\",\n\t\t\"MISTRAL_API_KEY\",\n\t\t\"TOGETHER_API_KEY\",\n\t\t\"OLLAMA_API_KEY\",\n\t];\n\tfor (const key of providerKeys) {\n\t\tgatewayEnv[key] = `\\${${key}}`;\n\t}\n\n\t// Gateway volumes (bind-mount style matching real docker-setup.sh)\n\tconst gatewayVolumes: string[] = [\n\t\t\"${OPENCLAW_CONFIG_DIR:-./openclaw/config}:/home/node/.openclaw\",\n\t\t\"${OPENCLAW_WORKSPACE_DIR:-./openclaw/workspace}:/home/node/.openclaw/workspace\",\n\t];\n\n\t// Collect env vars and volume mounts from companion services\n\tfor (const { definition: def } of resolved.services) {\n\t\tfor (const env of def.openclawEnvVars) {\n\t\t\tgatewayEnv[env.key] = env.secret ? `\\${${env.key}}` : env.defaultValue;\n\t\t}\n\t\tif (def.openclawVolumeMounts) {\n\t\t\tfor (const vol of def.openclawVolumeMounts) {\n\t\t\t\tconst isBindMount =\n\t\t\t\t\tvol.name.startsWith(\"./\") || vol.name.startsWith(\"/\") || vol.name.startsWith(\"~\");\n\t\t\t\tif (!isBindMount) {\n\t\t\t\t\tallVolumes.add(vol.name);\n\t\t\t\t}\n\t\t\t\tgatewayVolumes.push(`${vol.name}:${vol.containerPath}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Gateway service\n\tconst defaultImage = getOpenclawImage(\n\t\toptions.openclawImage ?? \"official\",\n\t\toptions.openclawVersion,\n\t);\n\tconst gateway: Record<string, unknown> = {\n\t\timage: `\\${OPENCLAW_IMAGE:-${defaultImage}}`,\n\t\tenvironment: gatewayEnv,\n\t\tvolumes: gatewayVolumes,\n\t\tports: [\"${OPENCLAW_GATEWAY_PORT:-18789}:18789\", \"${OPENCLAW_BRIDGE_PORT:-18790}:18790\"],\n\t\tnetworks: [\"openclaw-network\"],\n\t\tinit: true,\n\t\trestart: \"unless-stopped\",\n\t\tcommand: [\n\t\t\t\"node\",\n\t\t\t\"dist/index.js\",\n\t\t\t\"gateway\",\n\t\t\t\"--bind\",\n\t\t\t\"${OPENCLAW_GATEWAY_BIND:-lan}\",\n\t\t\t\"--port\",\n\t\t\t\"18789\",\n\t\t\t\"--allow-unconfigured\",\n\t\t],\n\t\thealthcheck: {\n\t\t\ttest: [\n\t\t\t\t\"CMD\",\n\t\t\t\t\"node\",\n\t\t\t\t\"-e\",\n\t\t\t\t\"fetch('http://127.0.0.1:18789/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\",\n\t\t\t],\n\t\t\tinterval: \"30s\",\n\t\t\ttimeout: \"5s\",\n\t\t\tretries: 5,\n\t\t\tstart_period: \"20s\",\n\t\t},\n\t};\n\n\t// Traefik labels for the gateway\n\tconst gwTraefikLabels = options.traefikLabels?.get(\"openclaw-gateway\");\n\tif (gwTraefikLabels) {\n\t\tgateway.labels = gwTraefikLabels;\n\t}\n\n\tif (options.bareMetalNativeHost) {\n\t\tgateway.extra_hosts = [\"host.docker.internal:host-gateway\"];\n\t}\n\n\tif (dependsOn && Object.keys(dependsOn).length > 0) {\n\t\tgateway.depends_on = dependsOn;\n\t}\n\n\t// CLI companion service (matching real OpenClaw docker-compose.yml)\n\t// Build CLI environment with same keys as gateway for consistency\n\tconst cliEnv: Record<string, string> = {\n\t\tHOME: \"/home/node\",\n\t\tTERM: \"xterm-256color\",\n\t\tOPENCLAW_GATEWAY_TOKEN: \"${OPENCLAW_GATEWAY_TOKEN}\",\n\t\tOPENCLAW_ALLOW_INSECURE_PRIVATE_WS: \"${OPENCLAW_ALLOW_INSECURE_PRIVATE_WS:-}\",\n\t\tBROWSER: \"echo\",\n\t\tCLAUDE_AI_SESSION_KEY: \"${CLAUDE_AI_SESSION_KEY:-}\",\n\t\tCLAUDE_WEB_SESSION_KEY: \"${CLAUDE_WEB_SESSION_KEY:-}\",\n\t\tCLAUDE_WEB_COOKIE: \"${CLAUDE_WEB_COOKIE:-}\",\n\t};\n\n\t// Add same AI provider API keys to CLI for direct AI interactions\n\tfor (const key of providerKeys) {\n\t\tcliEnv[key] = `\\${${key}}`;\n\t}\n\n\tconst cliService: Record<string, unknown> = {\n\t\timage: `\\${OPENCLAW_IMAGE:-${defaultImage}}`,\n\t\tnetwork_mode: \"service:openclaw-gateway\",\n\t\tcap_drop: [\"NET_RAW\", \"NET_ADMIN\"],\n\t\tsecurity_opt: [\"no-new-privileges:true\"],\n\t\tenvironment: cliEnv,\n\t\tvolumes: [\n\t\t\t\"${OPENCLAW_CONFIG_DIR:-./openclaw/config}:/home/node/.openclaw\",\n\t\t\t\"${OPENCLAW_WORKSPACE_DIR:-./openclaw/workspace}:/home/node/.openclaw/workspace\",\n\t\t],\n\t\tstdin_open: true,\n\t\ttty: true,\n\t\tinit: true,\n\t\tentrypoint: [\"node\", \"dist/index.js\"],\n\t\tdepends_on: [\"openclaw-gateway\"],\n\t};\n\n\treturn { gatewayService: gateway, cliService: cliService, allVolumes };\n}\n\n// ── Shared Companion Service Builder ────────────────────────────────────────\n\nfunction buildCompanionService(\n\tdef: ResolverOutput[\"services\"][number][\"definition\"],\n\tresolved: ResolverOutput,\n\toptions: ComposeOptions,\n\tallVolumes: Set<string>,\n): { entry: Record<string, unknown>; volumeNames: string[] } {\n\tconst svc: Record<string, unknown> = {};\n\tconst volumeNames: string[] = [];\n\n\t// Git-based services use build: context; image-based services use image:\n\tif (def.gitSource && def.buildContext) {\n\t\tconst subdir = def.gitSource.subdirectory || \".\";\n\t\tconst ctxPath = def.buildContext.context || \".\";\n\t\tconst contextFull = subdir === \".\" ? `./repos/${def.id}/${ctxPath}` : `./repos/${def.id}/${subdir}/${ctxPath}`;\n\t\tconst buildBlock: Record<string, unknown> = { context: contextFull };\n\t\tif (def.buildContext.dockerfile) {\n\t\t\tbuildBlock.dockerfile = def.buildContext.dockerfile;\n\t\t}\n\t\tif (def.buildContext.args && Object.keys(def.buildContext.args).length > 0) {\n\t\t\tbuildBlock.args = def.buildContext.args;\n\t\t}\n\t\tif (def.buildContext.target) {\n\t\t\tbuildBlock.target = def.buildContext.target;\n\t\t}\n\t\tsvc.build = buildBlock;\n\t} else {\n\t\tsvc.image = `${def.image}:${def.imageTag}`;\n\t}\n\n\tif (def.environment.length > 0) {\n\t\tconst env: Record<string, string> = {};\n\t\tfor (const e of def.environment) {\n\t\t\tenv[e.key] = e.secret ? `\\${${e.key}}` : e.defaultValue;\n\t\t}\n\t\tsvc.environment = env;\n\t}\n\n\tconst exposedPorts = def.ports.filter((p) => p.exposed);\n\tif (exposedPorts.length > 0) {\n\t\tconst prefix = def.id.toUpperCase().replace(/-/g, \"_\");\n\t\tsvc.ports = exposedPorts.map((p, i) => {\n\t\t\tconst suffix = exposedPorts.length > 1 ? `_${i}` : \"\";\n\t\t\tlet defaultPort = p.host;\n\n\t\t\t// Override proxy ports if custom ports are specified\n\t\t\tif (\n\t\t\t\t(def.id === \"caddy\" || def.id === \"traefik\") &&\n\t\t\t\toptions.proxyHttpPort !== undefined &&\n\t\t\t\tp.container === 80\n\t\t\t) {\n\t\t\t\tdefaultPort = options.proxyHttpPort;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t(def.id === \"caddy\" || def.id === \"traefik\") &&\n\t\t\t\toptions.proxyHttpsPort !== undefined &&\n\t\t\t\tp.container === 443\n\t\t\t) {\n\t\t\t\tdefaultPort = options.proxyHttpsPort;\n\t\t\t}\n\n\t\t\t// Apply global port overrides if specified\n\t\t\tif (options.portOverrides?.[def.id]?.[p.host] !== undefined) {\n\t\t\t\tdefaultPort = options.portOverrides?.[def.id]?.[p.host] || p.host;\n\t\t\t}\n\n\t\t\t// Use _EXTERNAL_PORT to avoid colliding with openclawEnvVars _PORT keys.\n\t\t\t// openclawEnvVars define the container port (e.g. GRAFANA_PORT=3000 for\n\t\t\t// internal Docker networking), while these are host port mappings (e.g. 3150).\n\t\t\t// Without this, GRAFANA_PORT=3000 from .env would override the default\n\t\t\t// 3150 in ${GRAFANA_PORT:-3150}:3000, mapping host port 3000 instead.\n\t\t\treturn `\\${${prefix}_EXTERNAL_PORT${suffix}:-${defaultPort}}:${p.container}`;\n\t\t});\n\t}\n\n\tif (def.volumes.length > 0) {\n\t\tsvc.volumes = def.volumes.map((v) => {\n\t\t\tconst isBindMount =\n\t\t\t\tv.name.startsWith(\"./\") || v.name.startsWith(\"/\") || v.name.startsWith(\"~\");\n\n\t\t\tif (!isBindMount) {\n\t\t\t\tallVolumes.add(v.name);\n\t\t\t\tvolumeNames.push(v.name);\n\t\t\t}\n\t\t\treturn `${v.name}:${v.containerPath}`;\n\t\t});\n\t}\n\n\t// PostgreSQL: mount the init script and pass per-service DB passwords\n\tif (def.id === \"postgresql\") {\n\t\tif (!svc.volumes) svc.volumes = [];\n\t\t(svc.volumes as string[]).push(\n\t\t\t\"./postgres/init-databases.sh:/docker-entrypoint-initdb.d/init-databases.sh:ro\",\n\t\t);\n\n\t\t// Pass per-service database password env vars so the init script can use them\n\t\tconst dbReqs = getDbRequirements(resolved);\n\t\tif (dbReqs.length > 0) {\n\t\t\tconst env = (svc.environment ?? {}) as Record<string, string>;\n\t\t\tfor (const req of dbReqs) {\n\t\t\t\tenv[req.passwordEnvVar] = `\\${${req.passwordEnvVar}}`;\n\t\t\t}\n\t\t\tsvc.environment = env;\n\t\t}\n\t}\n\n\tif (def.healthcheck) {\n\t\tconst hc: Record<string, unknown> = {\n\t\t\ttest: [\"CMD-SHELL\", def.healthcheck.test],\n\t\t\tinterval: def.healthcheck.interval,\n\t\t\ttimeout: def.healthcheck.timeout,\n\t\t\tretries: def.healthcheck.retries,\n\t\t};\n\t\tif (def.healthcheck.startPeriod) {\n\t\t\thc.start_period = def.healthcheck.startPeriod;\n\t\t}\n\t\tsvc.healthcheck = hc;\n\t}\n\n\tsvc.restart = def.restartPolicy;\n\tsvc.networks = def.networks;\n\n\tif (def.command) svc.command = def.command;\n\tif (def.entrypoint) svc.entrypoint = def.entrypoint;\n\n\t// Labels: merge static definition labels with dynamic Traefik labels\n\tconst mergedLabels: Record<string, string> = {};\n\tif (def.labels) Object.assign(mergedLabels, def.labels);\n\tconst traefikLabels = options.traefikLabels?.get(def.id);\n\tif (traefikLabels) Object.assign(mergedLabels, traefikLabels);\n\tif (Object.keys(mergedLabels).length > 0) svc.labels = mergedLabels;\n\n\t// Traefik: bind-mount static config and Docker socket\n\tif (def.id === \"traefik\" && options.traefikLabels) {\n\t\tif (!svc.volumes) svc.volumes = [];\n\t\t(svc.volumes as string[]).push(\n\t\t\t\"./traefik/traefik.yml:/etc/traefik/traefik.yml:ro\",\n\t\t\t\"/var/run/docker.sock:/var/run/docker.sock:ro\",\n\t\t);\n\t}\n\n\tlet deploy: Record<string, unknown> | undefined;\n\tif (def.deploy) {\n\t\tdeploy = JSON.parse(JSON.stringify(def.deploy)) as Record<string, unknown>;\n\t}\n\tif (options.gpu && def.gpuRequired) {\n\t\tconst base = deploy ?? {};\n\t\tconst resources = (base.resources ?? {}) as Record<string, unknown>;\n\t\tdeploy = {\n\t\t\t...base,\n\t\t\tresources: {\n\t\t\t\t...resources,\n\t\t\t\treservations: {\n\t\t\t\t\t...((resources.reservations as Record<string, unknown>) ?? {}),\n\t\t\t\t\tdevices: [{ driver: \"nvidia\", count: \"all\", capabilities: [\"gpu\"] }],\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\t// Memory limits from estimatedMemoryMB\n\tif (def.minMemoryMB && options.hardened) {\n\t\tconst base = deploy ?? {};\n\t\tconst resources = (base.resources ?? {}) as Record<string, unknown>;\n\t\tconst limits = (resources.limits as Record<string, unknown>) ?? {};\n\t\tdeploy = {\n\t\t\t...base,\n\t\t\tresources: {\n\t\t\t\t...resources,\n\t\t\t\tlimits: {\n\t\t\t\t\t...limits,\n\t\t\t\t\tmemory: `${def.minMemoryMB * 2}m`, // 2x min as safe limit\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\tif (deploy) svc.deploy = deploy;\n\n\t// Security hardening (when enabled)\n\tif (options.hardened) {\n\t\tsvc.cap_drop = [\"ALL\"];\n\t\tsvc.security_opt = [\"no-new-privileges:true\"];\n\n\t\t// Services that need specific capabilities\n\t\tconst capAddMap: Record<string, string[]> = {\n\t\t\tcaddy: [\"NET_BIND_SERVICE\"],\n\t\t\ttraefik: [\"NET_BIND_SERVICE\"],\n\t\t\tcrowdsec: [\"NET_BIND_SERVICE\", \"DAC_READ_SEARCH\"],\n\t\t};\n\t\tif (capAddMap[def.id]) {\n\t\t\tsvc.cap_add = capAddMap[def.id];\n\t\t}\n\t}\n\n\t// Merge both dependsOn and requires to ensure proper Docker startup ordering\n\tconst depIds = [...new Set([...def.dependsOn, ...def.requires])].filter((id) =>\n\t\tresolved.services.some((s) => s.definition.id === id),\n\t);\n\tif (depIds.length > 0) {\n\t\tconst dependsOn: Record<string, { condition: string }> = {};\n\t\tfor (const depId of depIds) {\n\t\t\tconst dep = resolved.services.find((s) => s.definition.id === depId);\n\t\t\tdependsOn[depId] = {\n\t\t\t\tcondition: dep?.definition.healthcheck ? \"service_healthy\" : \"service_started\",\n\t\t\t};\n\t\t}\n\t\tsvc.depends_on = dependsOn;\n\t}\n\n\treturn { entry: svc, volumeNames };\n}\n\n// ── PostgreSQL Setup Init Container ─────────────────────────────────────────\n\n/**\n * Builds a one-shot init container that creates databases and users for\n * services that need their own PostgreSQL database. Runs AFTER PostgreSQL\n * is healthy, on every `docker compose up`, and is idempotent.\n *\n * Uses standard PG* environment variables (PGHOST, PGUSER, PGDATABASE,\n * PGPASSWORD) so psql/createuser/createdb automatically connect without\n * needing explicit -h/-U/-d flags — simpler and avoids YAML escaping issues.\n *\n * Returns null when no setup is needed (no PostgreSQL or no DB requirements).\n */\nfunction buildPostgresSetup(resolved: ResolverOutput): Record<string, unknown> | null {\n\tconst hasPostgres = resolved.services.some((s) => s.definition.id === \"postgresql\");\n\tif (!hasPostgres) return null;\n\n\tconst dbReqs = getDbRequirements(resolved);\n\tif (dbReqs.length === 0) return null;\n\n\t// Build a shell script with one command per line.\n\t// Uses $$ to escape $ from Docker Compose's variable substitution —\n\t// Docker Compose converts $$ → $ before passing to the container.\n\t// NO set -e: we handle errors explicitly so one failed service doesn't block others.\n\tconst scriptLines = [\"echo '=== PostgreSQL database setup ==='\", \"FAILED=0\"];\n\n\tfor (const req of dbReqs) {\n\t\t// Each service's setup is wrapped so a failure doesn't block the others.\n\t\t// Uses psql -v ON_ERROR_STOP=0 so SQL errors don't abort psql.\n\t\tscriptLines.push(\n\t\t\t`echo \"Setting up database '${req.dbName}' with user '${req.dbUser}'...\"`,\n\t\t\t// Create user if not exists (pure SQL, no createuser binary quirks)\n\t\t\t`psql -c \"SELECT 1 FROM pg_roles WHERE rolname='${req.dbUser}'\" | grep -q 1 || psql -c \"CREATE ROLE ${req.dbUser} WITH LOGIN PASSWORD '$$${req.passwordEnvVar}'\"`,\n\t\t\t// Always sync the password to match current env\n\t\t\t`psql -c \"ALTER ROLE ${req.dbUser} WITH LOGIN PASSWORD '$$${req.passwordEnvVar}'\"`,\n\t\t\t// Create database if not exists\n\t\t\t`psql -tc \"SELECT 1 FROM pg_database WHERE datname='${req.dbName}'\" | grep -q 1 || psql -c \"CREATE DATABASE ${req.dbName} OWNER ${req.dbUser}\"`,\n\t\t\t// Grant privileges (idempotent)\n\t\t\t`psql -c \"GRANT ALL PRIVILEGES ON DATABASE ${req.dbName} TO ${req.dbUser}\" || FAILED=1`,\n\t\t\t`echo \" Done: ${req.dbName}\"`,\n\t\t);\n\t}\n\n\tscriptLines.push(\"echo '=== All databases ready ==='\", \"exit $$FAILED\");\n\n\t// Standard PG* env vars: psql/createuser/createdb use these automatically\n\tconst env: Record<string, string> = {\n\t\tPGHOST: \"postgresql\",\n\t\tPGUSER: \"${POSTGRES_USER:-openclaw}\",\n\t\tPGDATABASE: \"${POSTGRES_DB:-openclaw}\",\n\t\tPGPASSWORD: \"${POSTGRES_PASSWORD}\",\n\t};\n\tfor (const req of dbReqs) {\n\t\tenv[req.passwordEnvVar] = `\\${${req.passwordEnvVar}}`;\n\t}\n\n\treturn {\n\t\timage: \"postgres:17-alpine\",\n\t\tdepends_on: {\n\t\t\tpostgresql: { condition: \"service_healthy\" },\n\t\t},\n\t\tenvironment: env,\n\t\t// command MUST be a single-element array so the entire script is passed\n\t\t// as ONE argument to `sh -c`. A plain string gets shlex-split by Docker\n\t\t// Compose into multiple args, breaking multi-line scripts.\n\t\tentrypoint: [\"/bin/sh\", \"-c\"],\n\t\tcommand: [scriptLines.join(\"\\n\")],\n\t\trestart: quotedStr(\"no\"),\n\t\tnetworks: [\"openclaw-network\"],\n\t};\n}\n\n// ── Single-File Compose ─────────────────────────────────────────────────────\n\n/**\n * Generates a single Docker Compose YAML string with ALL services.\n * Backward-compatible signature.\n */\nexport function compose(resolved: ResolverOutput, options: ComposeOptions): string {\n\tconst isDirectInstall = options.openclawInstallMethod === \"direct\";\n\n\t// Build depends_on for ALL companion services\n\tconst gatewayDependsOn: Record<string, { condition: string }> = {};\n\tfor (const { definition: def } of resolved.services) {\n\t\tgatewayDependsOn[def.id] = {\n\t\t\tcondition: def.healthcheck ? \"service_healthy\" : \"service_started\",\n\t\t};\n\t}\n\n\tconst services: Record<string, Record<string, unknown>> = {};\n\tlet allVolumes = new Set<string>();\n\n\tif (!isDirectInstall) {\n\t\tconst {\n\t\t\tgatewayService,\n\t\t\tcliService,\n\t\t\tallVolumes: gwVolumes,\n\t\t} = buildGatewayServices(resolved, options, gatewayDependsOn);\n\t\tallVolumes = gwVolumes;\n\t\tservices[\"openclaw-gateway\"] = gatewayService;\n\t\t// CLI service added after companions\n\t\t// Determine which services need DB setup so we can redirect their depends_on\n\t\tconst dbReqs = getDbRequirements(resolved);\n\t\tconst dbServiceIds = new Set(dbReqs.map((r) => r.serviceId));\n\n\t\tfor (const { definition: def } of resolved.services) {\n\t\t\tconst { entry } = buildCompanionService(def, resolved, options, allVolumes);\n\t\t\tif (dbServiceIds.has(def.id) && entry.depends_on) {\n\t\t\t\tconst deps = entry.depends_on as Record<string, { condition: string }>;\n\t\t\t\tif (deps.postgresql) {\n\t\t\t\t\tdelete deps.postgresql;\n\t\t\t\t\tdeps[\"postgres-setup\"] = { condition: \"service_completed_successfully\" };\n\t\t\t\t}\n\t\t\t}\n\t\t\tservices[def.id] = entry;\n\t\t}\n\n\t\tconst pgSetup = buildPostgresSetup(resolved);\n\t\tif (pgSetup) {\n\t\t\tservices[\"postgres-setup\"] = pgSetup;\n\t\t}\n\n\t\tservices[\"openclaw-cli\"] = cliService;\n\t} else {\n\t\t// Direct install: no gateway/CLI containers, just companion services\n\t\tconst dbReqs = getDbRequirements(resolved);\n\t\tconst dbServiceIds = new Set(dbReqs.map((r) => r.serviceId));\n\n\t\tfor (const { definition: def } of resolved.services) {\n\t\t\tconst { entry } = buildCompanionService(def, resolved, options, allVolumes);\n\t\t\tif (dbServiceIds.has(def.id) && entry.depends_on) {\n\t\t\t\tconst deps = entry.depends_on as Record<string, { condition: string }>;\n\t\t\t\tif (deps.postgresql) {\n\t\t\t\t\tdelete deps.postgresql;\n\t\t\t\t\tdeps[\"postgres-setup\"] = { condition: \"service_completed_successfully\" };\n\t\t\t\t}\n\t\t\t}\n\t\t\tservices[def.id] = entry;\n\t\t}\n\n\t\tconst pgSetup = buildPostgresSetup(resolved);\n\t\tif (pgSetup) {\n\t\t\tservices[\"postgres-setup\"] = pgSetup;\n\t\t}\n\t}\n\n\tconst volumes: Record<string, null> = {};\n\tfor (const v of [...allVolumes].sort()) {\n\t\tvolumes[v] = null;\n\t}\n\n\tconst networks = { \"openclaw-network\": { driver: \"bridge\" } };\n\n\treturn stringify({ services, volumes, networks }, YAML_OPTIONS);\n}\n\n// ── Multi-File Compose ──────────────────────────────────────────────────────\n\ninterface ServiceInfo {\n\tid: string;\n\tcategory: ServiceCategory;\n\tentry: Record<string, unknown>;\n\tvolumeNames: string[];\n}\n\n/**\n * Generates multiple Docker Compose files, splitting services into profile-based\n * override files by category.\n */\nexport function composeMultiFile(resolved: ResolverOutput, options: ComposeOptions): ComposeResult {\n\tconst isDirectInstall = options.openclawInstallMethod === \"direct\";\n\tconst allVolumes = new Set<string>();\n\n\t// Build all companion service entries & classify by category\n\tconst serviceInfos: ServiceInfo[] = [];\n\tconst dbReqs = getDbRequirements(resolved);\n\tconst dbServiceIds = new Set(dbReqs.map((r) => r.serviceId));\n\n\tfor (const { definition: def } of resolved.services) {\n\t\tconst { entry, volumeNames } = buildCompanionService(def, resolved, options, allVolumes);\n\t\t// Redirect DB-dependent services to depend on postgres-setup\n\t\tif (dbServiceIds.has(def.id) && entry.depends_on) {\n\t\t\tconst deps = entry.depends_on as Record<string, { condition: string }>;\n\t\t\tif (deps.postgresql) {\n\t\t\t\tdelete deps.postgresql;\n\t\t\t\tdeps[\"postgres-setup\"] = { condition: \"service_completed_successfully\" };\n\t\t\t}\n\t\t}\n\t\tserviceInfos.push({ id: def.id, category: def.category, entry, volumeNames });\n\t}\n\n\t// Partition services into base vs. profile files\n\tconst baseServiceIds = new Set<string>();\n\tconst profileFileMap: Record<string, { profile: string; services: ServiceInfo[] }> = {};\n\n\tfor (const info of serviceInfos) {\n\t\tconst mapping = CATEGORY_PROFILE_MAP[info.category];\n\t\tif (mapping) {\n\t\t\tif (!profileFileMap[mapping.file]) {\n\t\t\t\tprofileFileMap[mapping.file] = { profile: mapping.profile, services: [] };\n\t\t\t}\n\t\t\tprofileFileMap[mapping.file]!.services.push(info);\n\t\t} else {\n\t\t\tbaseServiceIds.add(info.id);\n\t\t}\n\t}\n\n\tconst baseServices: Record<string, Record<string, unknown>> = {};\n\n\tif (!isDirectInstall) {\n\t\t// Gateway depends_on (only base services)\n\t\tconst gatewayDependsOn: Record<string, { condition: string }> = {};\n\t\tfor (const { definition: def } of resolved.services) {\n\t\t\tif (baseServiceIds.has(def.id)) {\n\t\t\t\tgatewayDependsOn[def.id] = {\n\t\t\t\t\tcondition: def.healthcheck ? \"service_healthy\" : \"service_started\",\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tconst {\n\t\t\tgatewayService,\n\t\t\tcliService,\n\t\t\tallVolumes: gwVolumes,\n\t\t} = buildGatewayServices(resolved, options, gatewayDependsOn);\n\n\t\t// Merge gateway volumes into allVolumes\n\t\tfor (const v of gwVolumes) allVolumes.add(v);\n\n\t\tbaseServices[\"openclaw-gateway\"] = gatewayService;\n\n\t\tfor (const info of serviceInfos) {\n\t\t\tif (baseServiceIds.has(info.id)) {\n\t\t\t\tbaseServices[info.id] = info.entry;\n\t\t\t}\n\t\t}\n\n\t\t// Add postgres-setup init container if needed\n\t\tconst pgSetup = buildPostgresSetup(resolved);\n\t\tif (pgSetup) {\n\t\t\tbaseServices[\"postgres-setup\"] = pgSetup;\n\t\t}\n\n\t\tbaseServices[\"openclaw-cli\"] = cliService;\n\t} else {\n\t\t// Direct install: no gateway/CLI containers\n\t\tfor (const info of serviceInfos) {\n\t\t\tif (baseServiceIds.has(info.id)) {\n\t\t\t\tbaseServices[info.id] = info.entry;\n\t\t\t}\n\t\t}\n\n\t\tconst pgSetup = buildPostgresSetup(resolved);\n\t\tif (pgSetup) {\n\t\t\tbaseServices[\"postgres-setup\"] = pgSetup;\n\t\t}\n\t}\n\n\tconst sortedAllVolumes: Record<string, null> = {};\n\tfor (const v of [...allVolumes].sort()) {\n\t\tsortedAllVolumes[v] = null;\n\t}\n\n\tconst networks = { \"openclaw-network\": { driver: \"bridge\" } };\n\n\tconst files: Record<string, string> = {};\n\tfiles[\"docker-compose.yml\"] = stringify(\n\t\t{ services: baseServices, volumes: sortedAllVolumes, networks },\n\t\tYAML_OPTIONS,\n\t);\n\n\t// Profile override files\n\tconst usedProfiles = new Set<string>();\n\n\tfor (const [fileName, { profile, services }] of Object.entries(profileFileMap)) {\n\t\tusedProfiles.add(profile);\n\n\t\tconst profileServices: Record<string, Record<string, unknown>> = {};\n\t\tconst profileVolumes = new Set<string>();\n\n\t\tfor (const info of services) {\n\t\t\tprofileServices[info.id] = { ...info.entry, profiles: [profile] };\n\t\t\tfor (const vName of info.volumeNames) {\n\t\t\t\tprofileVolumes.add(vName);\n\t\t\t}\n\t\t}\n\n\t\tconst fileContent: Record<string, unknown> = { services: profileServices };\n\n\t\tif (profileVolumes.size > 0) {\n\t\t\tconst sortedProfileVolumes: Record<string, null> = {};\n\t\t\tfor (const v of [...profileVolumes].sort()) {\n\t\t\t\tsortedProfileVolumes[v] = null;\n\t\t\t}\n\t\t\tfileContent.volumes = sortedProfileVolumes;\n\t\t}\n\n\t\tfiles[fileName] = stringify(fileContent, YAML_OPTIONS);\n\t}\n\n\treturn {\n\t\tfiles,\n\t\tmainFile: \"docker-compose.yml\",\n\t\tprofiles: [...usedProfiles].sort(),\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"composer.cjs","names":["Scalar","getDbRequirements"],"sources":["../src/composer.ts"],"sourcesContent":["import { Scalar, stringify } from \"yaml\";\nimport { getDbRequirements } from \"./generators/postgres-init.js\";\nimport type {\n\tComposeOptions,\n\tOpenclawImageVariant,\n\tResolverOutput,\n\tServiceCategory,\n} from \"./types.js\";\n\n/** Maps image variant to the Docker image string. */\nconst IMAGE_VARIANTS: Record<OpenclawImageVariant, string> = {\n\tofficial: \"ghcr.io/openclaw/openclaw\",\n\tcoolify: \"coollabsio/openclaw\",\n\talpine: \"alpine/openclaw\",\n};\n\n/** Returns the OpenClaw image string with version tag for a given variant. */\nfunction getOpenclawImage(variant: OpenclawImageVariant, version: string): string {\n\tconst base = IMAGE_VARIANTS[variant];\n\t// Coolify and alpine images use :latest by default\n\tconst tag = variant === \"official\" ? version : \"latest\";\n\treturn `${base}:${tag}`;\n}\n\n/** Creates a YAML scalar that is always quoted — avoids YAML 1.1 bare `no` → false. */\nexport function quotedStr(value: string): Scalar {\n\tconst s = new Scalar(value);\n\ts.type = Scalar.QUOTE_DOUBLE;\n\treturn s;\n}\n\n// ── Public Types ────────────────────────────────────────────────────────────\n\nexport interface ComposeResult {\n\tfiles: Record<string, string>; // filename -> YAML content\n\tmainFile: string; // \"docker-compose.yml\"\n\tprofiles: string[]; // list of profiles used\n}\n\n// ── Category → Profile Mapping ──────────────────────────────────────────────\n\nconst CATEGORY_PROFILE_MAP: Partial<Record<ServiceCategory, { file: string; profile: string }>> = {\n\tai: { file: \"docker-compose.ai.yml\", profile: \"ai\" },\n\t\"ai-platform\": { file: \"docker-compose.ai.yml\", profile: \"ai\" },\n\tmedia: { file: \"docker-compose.media.yml\", profile: \"media\" },\n\tmonitoring: { file: \"docker-compose.monitoring.yml\", profile: \"monitoring\" },\n\tanalytics: { file: \"docker-compose.monitoring.yml\", profile: \"monitoring\" },\n\t\"dev-tools\": { file: \"docker-compose.tools.yml\", profile: \"tools\" },\n\t\"coding-agent\": { file: \"docker-compose.tools.yml\", profile: \"tools\" },\n\t\"social-media\": { file: \"docker-compose.social.yml\", profile: \"social\" },\n\tknowledge: { file: \"docker-compose.knowledge.yml\", profile: \"knowledge\" },\n\tcommunication: { file: \"docker-compose.communication.yml\", profile: \"communication\" },\n\t\"saas-boilerplate\": { file: \"docker-compose.saas.yml\", profile: \"saas\" },\n};\n\nexport const YAML_OPTIONS = { lineWidth: 120, nullStr: \"\" };\n\n// ── Shared Gateway Builder ──────────────────────────────────────────────────\n\ninterface GatewayBuildResult {\n\tgatewayService: Record<string, unknown>;\n\tcliService: Record<string, unknown>;\n\tallVolumes: Set<string>;\n}\n\n/**\n * Builds the OpenClaw gateway and CLI service entries.\n * Matches the real OpenClaw docker-compose.yml structure:\n * - Bridge port 18790 for ACP/WebSocket\n * - Bind-mount volumes (not named volumes)\n * - Claude web-provider session env vars\n * - Gateway bind mode (--bind lan)\n * - CLI companion service with stdin/tty\n */\nfunction buildGatewayServices(\n\tresolved: ResolverOutput,\n\toptions: ComposeOptions,\n\tdependsOn?: Record<string, { condition: string }>,\n): GatewayBuildResult {\n\tconst allVolumes = new Set<string>();\n\n\t// Gateway environment\n\tconst gatewayEnv: Record<string, string> = {\n\t\tHOME: \"/home/node\",\n\t\tTERM: \"xterm-256color\",\n\t\tOPENCLAW_GATEWAY_TOKEN: \"${OPENCLAW_GATEWAY_TOKEN}\",\n\t\tOPENCLAW_ALLOW_INSECURE_PRIVATE_WS: \"${OPENCLAW_ALLOW_INSECURE_PRIVATE_WS:-}\",\n\t\t// Claude web-provider session vars (optional, user fills in .env)\n\t\tCLAUDE_AI_SESSION_KEY: \"${CLAUDE_AI_SESSION_KEY:-}\",\n\t\tCLAUDE_WEB_SESSION_KEY: \"${CLAUDE_WEB_SESSION_KEY:-}\",\n\t\tCLAUDE_WEB_COOKIE: \"${CLAUDE_WEB_COOKIE:-}\",\n\t};\n\n\t// Add AI provider API keys to gateway environment\n\tconst providerKeys = [\n\t\t\"OPENAI_API_KEY\",\n\t\t\"ANTHROPIC_API_KEY\",\n\t\t\"GOOGLE_API_KEY\",\n\t\t\"XAI_API_KEY\",\n\t\t\"DEEPSEEK_API_KEY\",\n\t\t\"GROQ_API_KEY\",\n\t\t\"OPENROUTER_API_KEY\",\n\t\t\"MISTRAL_API_KEY\",\n\t\t\"TOGETHER_API_KEY\",\n\t\t\"OLLAMA_API_KEY\",\n\t];\n\tfor (const key of providerKeys) {\n\t\tgatewayEnv[key] = `\\${${key}}`;\n\t}\n\n\t// Gateway volumes (bind-mount style matching real docker-setup.sh)\n\tconst gatewayVolumes: string[] = [\n\t\t\"${OPENCLAW_CONFIG_DIR:-./openclaw/config}:/home/node/.openclaw\",\n\t\t\"${OPENCLAW_WORKSPACE_DIR:-./openclaw/workspace}:/home/node/.openclaw/workspace\",\n\t];\n\n\t// Collect env vars and volume mounts from companion services\n\tfor (const { definition: def } of resolved.services) {\n\t\tfor (const env of def.openclawEnvVars) {\n\t\t\tgatewayEnv[env.key] = env.secret ? `\\${${env.key}}` : env.defaultValue;\n\t\t}\n\t\tif (def.openclawVolumeMounts) {\n\t\t\tfor (const vol of def.openclawVolumeMounts) {\n\t\t\t\tconst isBindMount =\n\t\t\t\t\tvol.name.startsWith(\"./\") || vol.name.startsWith(\"/\") || vol.name.startsWith(\"~\");\n\t\t\t\tif (!isBindMount) {\n\t\t\t\t\tallVolumes.add(vol.name);\n\t\t\t\t}\n\t\t\t\tgatewayVolumes.push(`${vol.name}:${vol.containerPath}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Gateway service\n\tconst defaultImage = getOpenclawImage(\n\t\toptions.openclawImage ?? \"official\",\n\t\toptions.openclawVersion,\n\t);\n\tconst gateway: Record<string, unknown> = {\n\t\timage: `\\${OPENCLAW_IMAGE:-${defaultImage}}`,\n\t\tenvironment: gatewayEnv,\n\t\tvolumes: gatewayVolumes,\n\t\tports: [\"${OPENCLAW_GATEWAY_PORT:-18789}:18789\", \"${OPENCLAW_BRIDGE_PORT:-18790}:18790\"],\n\t\tnetworks: [\"openclaw-network\"],\n\t\tinit: true,\n\t\trestart: \"unless-stopped\",\n\t\tcommand: [\n\t\t\t\"node\",\n\t\t\t\"dist/index.js\",\n\t\t\t\"gateway\",\n\t\t\t\"--bind\",\n\t\t\t\"${OPENCLAW_GATEWAY_BIND:-lan}\",\n\t\t\t\"--port\",\n\t\t\t\"18789\",\n\t\t\t\"--allow-unconfigured\",\n\t\t],\n\t\thealthcheck: {\n\t\t\ttest: [\n\t\t\t\t\"CMD\",\n\t\t\t\t\"node\",\n\t\t\t\t\"-e\",\n\t\t\t\t\"fetch('http://127.0.0.1:18789/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\",\n\t\t\t],\n\t\t\tinterval: \"30s\",\n\t\t\ttimeout: \"5s\",\n\t\t\tretries: 5,\n\t\t\tstart_period: \"20s\",\n\t\t},\n\t};\n\n\t// Traefik labels for the gateway\n\tconst gwTraefikLabels = options.traefikLabels?.get(\"openclaw-gateway\");\n\tif (gwTraefikLabels) {\n\t\tgateway.labels = gwTraefikLabels;\n\t}\n\n\tif (options.bareMetalNativeHost) {\n\t\tgateway.extra_hosts = [\"host.docker.internal:host-gateway\"];\n\t}\n\n\tif (dependsOn && Object.keys(dependsOn).length > 0) {\n\t\tgateway.depends_on = dependsOn;\n\t}\n\n\t// CLI companion service (matching real OpenClaw docker-compose.yml)\n\t// Build CLI environment with same keys as gateway for consistency\n\tconst cliEnv: Record<string, string> = {\n\t\tHOME: \"/home/node\",\n\t\tTERM: \"xterm-256color\",\n\t\tOPENCLAW_GATEWAY_TOKEN: \"${OPENCLAW_GATEWAY_TOKEN}\",\n\t\tOPENCLAW_ALLOW_INSECURE_PRIVATE_WS: \"${OPENCLAW_ALLOW_INSECURE_PRIVATE_WS:-}\",\n\t\tBROWSER: \"echo\",\n\t\tCLAUDE_AI_SESSION_KEY: \"${CLAUDE_AI_SESSION_KEY:-}\",\n\t\tCLAUDE_WEB_SESSION_KEY: \"${CLAUDE_WEB_SESSION_KEY:-}\",\n\t\tCLAUDE_WEB_COOKIE: \"${CLAUDE_WEB_COOKIE:-}\",\n\t};\n\n\t// Add same AI provider API keys to CLI for direct AI interactions\n\tfor (const key of providerKeys) {\n\t\tcliEnv[key] = `\\${${key}}`;\n\t}\n\n\tconst cliService: Record<string, unknown> = {\n\t\timage: `\\${OPENCLAW_IMAGE:-${defaultImage}}`,\n\t\tnetwork_mode: \"service:openclaw-gateway\",\n\t\tcap_drop: [\"NET_RAW\", \"NET_ADMIN\"],\n\t\tsecurity_opt: [\"no-new-privileges:true\"],\n\t\tenvironment: cliEnv,\n\t\tvolumes: [\n\t\t\t\"${OPENCLAW_CONFIG_DIR:-./openclaw/config}:/home/node/.openclaw\",\n\t\t\t\"${OPENCLAW_WORKSPACE_DIR:-./openclaw/workspace}:/home/node/.openclaw/workspace\",\n\t\t],\n\t\tstdin_open: true,\n\t\ttty: true,\n\t\tinit: true,\n\t\tentrypoint: [\"node\", \"dist/index.js\"],\n\t\tdepends_on: [\"openclaw-gateway\"],\n\t};\n\n\treturn { gatewayService: gateway, cliService: cliService, allVolumes };\n}\n\n// ── Shared Companion Service Builder ────────────────────────────────────────\n\nexport function buildCompanionService(\n\tdef: ResolverOutput[\"services\"][number][\"definition\"],\n\tresolved: ResolverOutput,\n\toptions: ComposeOptions,\n\tallVolumes: Set<string>,\n): { entry: Record<string, unknown>; volumeNames: string[] } {\n\tconst svc: Record<string, unknown> = {};\n\tconst volumeNames: string[] = [];\n\n\t// Git-based services use build: context; image-based services use image:\n\tif (def.gitSource && def.buildContext) {\n\t\tconst subdir = def.gitSource.subdirectory || \".\";\n\t\tconst ctxPath = def.buildContext.context || \".\";\n\t\tconst contextFull = subdir === \".\" ? `./repos/${def.id}/${ctxPath}` : `./repos/${def.id}/${subdir}/${ctxPath}`;\n\t\tconst buildBlock: Record<string, unknown> = { context: contextFull };\n\t\tif (def.buildContext.dockerfile) {\n\t\t\tbuildBlock.dockerfile = def.buildContext.dockerfile;\n\t\t}\n\t\tif (def.buildContext.args && Object.keys(def.buildContext.args).length > 0) {\n\t\t\tbuildBlock.args = def.buildContext.args;\n\t\t}\n\t\tif (def.buildContext.target) {\n\t\t\tbuildBlock.target = def.buildContext.target;\n\t\t}\n\t\tsvc.build = buildBlock;\n\t} else {\n\t\tsvc.image = `${def.image}:${def.imageTag}`;\n\t}\n\n\tif (def.environment.length > 0) {\n\t\tconst env: Record<string, string> = {};\n\t\tfor (const e of def.environment) {\n\t\t\tenv[e.key] = e.secret ? `\\${${e.key}}` : e.defaultValue;\n\t\t}\n\t\tsvc.environment = env;\n\t}\n\n\tconst exposedPorts = def.ports.filter((p) => p.exposed);\n\tif (exposedPorts.length > 0) {\n\t\tconst prefix = def.id.toUpperCase().replace(/-/g, \"_\");\n\t\tsvc.ports = exposedPorts.map((p, i) => {\n\t\t\tconst suffix = exposedPorts.length > 1 ? `_${i}` : \"\";\n\t\t\tlet defaultPort = p.host;\n\n\t\t\t// Override proxy ports if custom ports are specified\n\t\t\tif (\n\t\t\t\t(def.id === \"caddy\" || def.id === \"traefik\") &&\n\t\t\t\toptions.proxyHttpPort !== undefined &&\n\t\t\t\tp.container === 80\n\t\t\t) {\n\t\t\t\tdefaultPort = options.proxyHttpPort;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t(def.id === \"caddy\" || def.id === \"traefik\") &&\n\t\t\t\toptions.proxyHttpsPort !== undefined &&\n\t\t\t\tp.container === 443\n\t\t\t) {\n\t\t\t\tdefaultPort = options.proxyHttpsPort;\n\t\t\t}\n\n\t\t\t// Apply global port overrides if specified\n\t\t\tif (options.portOverrides?.[def.id]?.[p.host] !== undefined) {\n\t\t\t\tdefaultPort = options.portOverrides?.[def.id]?.[p.host] || p.host;\n\t\t\t}\n\n\t\t\t// Use _EXTERNAL_PORT to avoid colliding with openclawEnvVars _PORT keys.\n\t\t\t// openclawEnvVars define the container port (e.g. GRAFANA_PORT=3000 for\n\t\t\t// internal Docker networking), while these are host port mappings (e.g. 3150).\n\t\t\t// Without this, GRAFANA_PORT=3000 from .env would override the default\n\t\t\t// 3150 in ${GRAFANA_PORT:-3150}:3000, mapping host port 3000 instead.\n\t\t\treturn `\\${${prefix}_EXTERNAL_PORT${suffix}:-${defaultPort}}:${p.container}`;\n\t\t});\n\t}\n\n\tif (def.volumes.length > 0) {\n\t\tsvc.volumes = def.volumes.map((v) => {\n\t\t\tconst isBindMount =\n\t\t\t\tv.name.startsWith(\"./\") || v.name.startsWith(\"/\") || v.name.startsWith(\"~\");\n\n\t\t\tif (!isBindMount) {\n\t\t\t\tallVolumes.add(v.name);\n\t\t\t\tvolumeNames.push(v.name);\n\t\t\t}\n\t\t\treturn `${v.name}:${v.containerPath}`;\n\t\t});\n\t}\n\n\t// PostgreSQL: mount the init script and pass per-service DB passwords\n\tif (def.id === \"postgresql\") {\n\t\tif (!svc.volumes) svc.volumes = [];\n\t\t(svc.volumes as string[]).push(\n\t\t\t\"./postgres/init-databases.sh:/docker-entrypoint-initdb.d/init-databases.sh:ro\",\n\t\t);\n\n\t\t// Pass per-service database password env vars so the init script can use them\n\t\tconst dbReqs = getDbRequirements(resolved);\n\t\tif (dbReqs.length > 0) {\n\t\t\tconst env = (svc.environment ?? {}) as Record<string, string>;\n\t\t\tfor (const req of dbReqs) {\n\t\t\t\tenv[req.passwordEnvVar] = `\\${${req.passwordEnvVar}}`;\n\t\t\t}\n\t\t\tsvc.environment = env;\n\t\t}\n\t}\n\n\tif (def.healthcheck) {\n\t\tconst hc: Record<string, unknown> = {\n\t\t\ttest: [\"CMD-SHELL\", def.healthcheck.test],\n\t\t\tinterval: def.healthcheck.interval,\n\t\t\ttimeout: def.healthcheck.timeout,\n\t\t\tretries: def.healthcheck.retries,\n\t\t};\n\t\tif (def.healthcheck.startPeriod) {\n\t\t\thc.start_period = def.healthcheck.startPeriod;\n\t\t}\n\t\tsvc.healthcheck = hc;\n\t}\n\n\tsvc.restart = def.restartPolicy;\n\tsvc.networks = def.networks;\n\n\tif (def.command) svc.command = def.command;\n\tif (def.entrypoint) svc.entrypoint = def.entrypoint;\n\n\t// Labels: merge static definition labels with dynamic Traefik labels\n\tconst mergedLabels: Record<string, string> = {};\n\tif (def.labels) Object.assign(mergedLabels, def.labels);\n\tconst traefikLabels = options.traefikLabels?.get(def.id);\n\tif (traefikLabels) Object.assign(mergedLabels, traefikLabels);\n\tif (Object.keys(mergedLabels).length > 0) svc.labels = mergedLabels;\n\n\t// Traefik: bind-mount static config and Docker socket\n\tif (def.id === \"traefik\" && options.traefikLabels) {\n\t\tif (!svc.volumes) svc.volumes = [];\n\t\t(svc.volumes as string[]).push(\n\t\t\t\"./traefik/traefik.yml:/etc/traefik/traefik.yml:ro\",\n\t\t\t\"/var/run/docker.sock:/var/run/docker.sock:ro\",\n\t\t);\n\t}\n\n\tlet deploy: Record<string, unknown> | undefined;\n\tif (def.deploy) {\n\t\tdeploy = JSON.parse(JSON.stringify(def.deploy)) as Record<string, unknown>;\n\t}\n\tif (options.gpu && def.gpuRequired) {\n\t\tconst base = deploy ?? {};\n\t\tconst resources = (base.resources ?? {}) as Record<string, unknown>;\n\t\tdeploy = {\n\t\t\t...base,\n\t\t\tresources: {\n\t\t\t\t...resources,\n\t\t\t\treservations: {\n\t\t\t\t\t...((resources.reservations as Record<string, unknown>) ?? {}),\n\t\t\t\t\tdevices: [{ driver: \"nvidia\", count: \"all\", capabilities: [\"gpu\"] }],\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\t// Memory limits from estimatedMemoryMB\n\tif (def.minMemoryMB && options.hardened) {\n\t\tconst base = deploy ?? {};\n\t\tconst resources = (base.resources ?? {}) as Record<string, unknown>;\n\t\tconst limits = (resources.limits as Record<string, unknown>) ?? {};\n\t\tdeploy = {\n\t\t\t...base,\n\t\t\tresources: {\n\t\t\t\t...resources,\n\t\t\t\tlimits: {\n\t\t\t\t\t...limits,\n\t\t\t\t\tmemory: `${def.minMemoryMB * 2}m`, // 2x min as safe limit\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\tif (deploy) svc.deploy = deploy;\n\n\t// Security hardening (when enabled)\n\tif (options.hardened) {\n\t\tsvc.cap_drop = [\"ALL\"];\n\t\tsvc.security_opt = [\"no-new-privileges:true\"];\n\n\t\t// Services that need specific capabilities\n\t\tconst capAddMap: Record<string, string[]> = {\n\t\t\tcaddy: [\"NET_BIND_SERVICE\"],\n\t\t\ttraefik: [\"NET_BIND_SERVICE\"],\n\t\t\tcrowdsec: [\"NET_BIND_SERVICE\", \"DAC_READ_SEARCH\"],\n\t\t};\n\t\tif (capAddMap[def.id]) {\n\t\t\tsvc.cap_add = capAddMap[def.id];\n\t\t}\n\t}\n\n\t// Merge both dependsOn and requires to ensure proper Docker startup ordering\n\tconst depIds = [...new Set([...def.dependsOn, ...def.requires])].filter((id) =>\n\t\tresolved.services.some((s) => s.definition.id === id),\n\t);\n\tif (depIds.length > 0) {\n\t\tconst dependsOn: Record<string, { condition: string }> = {};\n\t\tfor (const depId of depIds) {\n\t\t\tconst dep = resolved.services.find((s) => s.definition.id === depId);\n\t\t\tdependsOn[depId] = {\n\t\t\t\tcondition: dep?.definition.healthcheck ? \"service_healthy\" : \"service_started\",\n\t\t\t};\n\t\t}\n\t\tsvc.depends_on = dependsOn;\n\t}\n\n\treturn { entry: svc, volumeNames };\n}\n\n// ── PostgreSQL Setup Init Container ─────────────────────────────────────────\n\n/**\n * Builds a one-shot init container that creates databases and users for\n * services that need their own PostgreSQL database. Runs AFTER PostgreSQL\n * is healthy, on every `docker compose up`, and is idempotent.\n *\n * Uses standard PG* environment variables (PGHOST, PGUSER, PGDATABASE,\n * PGPASSWORD) so psql/createuser/createdb automatically connect without\n * needing explicit -h/-U/-d flags — simpler and avoids YAML escaping issues.\n *\n * Returns null when no setup is needed (no PostgreSQL or no DB requirements).\n */\nexport function buildPostgresSetup(resolved: ResolverOutput): Record<string, unknown> | null {\n\tconst hasPostgres = resolved.services.some((s) => s.definition.id === \"postgresql\");\n\tif (!hasPostgres) return null;\n\n\tconst dbReqs = getDbRequirements(resolved);\n\tif (dbReqs.length === 0) return null;\n\n\t// Build a shell script with one command per line.\n\t// Uses $$ to escape $ from Docker Compose's variable substitution —\n\t// Docker Compose converts $$ → $ before passing to the container.\n\t// NO set -e: we handle errors explicitly so one failed service doesn't block others.\n\tconst scriptLines = [\"echo '=== PostgreSQL database setup ==='\", \"FAILED=0\"];\n\n\tfor (const req of dbReqs) {\n\t\t// Each service's setup is wrapped so a failure doesn't block the others.\n\t\t// Uses psql -v ON_ERROR_STOP=0 so SQL errors don't abort psql.\n\t\tscriptLines.push(\n\t\t\t`echo \"Setting up database '${req.dbName}' with user '${req.dbUser}'...\"`,\n\t\t\t// Create user if not exists (pure SQL, no createuser binary quirks)\n\t\t\t`psql -c \"SELECT 1 FROM pg_roles WHERE rolname='${req.dbUser}'\" | grep -q 1 || psql -c \"CREATE ROLE ${req.dbUser} WITH LOGIN PASSWORD '$$${req.passwordEnvVar}'\"`,\n\t\t\t// Always sync the password to match current env\n\t\t\t`psql -c \"ALTER ROLE ${req.dbUser} WITH LOGIN PASSWORD '$$${req.passwordEnvVar}'\"`,\n\t\t\t// Create database if not exists\n\t\t\t`psql -tc \"SELECT 1 FROM pg_database WHERE datname='${req.dbName}'\" | grep -q 1 || psql -c \"CREATE DATABASE ${req.dbName} OWNER ${req.dbUser}\"`,\n\t\t\t// Grant privileges (idempotent)\n\t\t\t`psql -c \"GRANT ALL PRIVILEGES ON DATABASE ${req.dbName} TO ${req.dbUser}\" || FAILED=1`,\n\t\t\t`echo \" Done: ${req.dbName}\"`,\n\t\t);\n\t}\n\n\tscriptLines.push(\"echo '=== All databases ready ==='\", \"exit $$FAILED\");\n\n\t// Standard PG* env vars: psql/createuser/createdb use these automatically\n\tconst env: Record<string, string> = {\n\t\tPGHOST: \"postgresql\",\n\t\tPGUSER: \"${POSTGRES_USER:-openclaw}\",\n\t\tPGDATABASE: \"${POSTGRES_DB:-openclaw}\",\n\t\tPGPASSWORD: \"${POSTGRES_PASSWORD}\",\n\t};\n\tfor (const req of dbReqs) {\n\t\tenv[req.passwordEnvVar] = `\\${${req.passwordEnvVar}}`;\n\t}\n\n\treturn {\n\t\timage: \"postgres:17-alpine\",\n\t\tdepends_on: {\n\t\t\tpostgresql: { condition: \"service_healthy\" },\n\t\t},\n\t\tenvironment: env,\n\t\t// command MUST be a single-element array so the entire script is passed\n\t\t// as ONE argument to `sh -c`. A plain string gets shlex-split by Docker\n\t\t// Compose into multiple args, breaking multi-line scripts.\n\t\tentrypoint: [\"/bin/sh\", \"-c\"],\n\t\tcommand: [scriptLines.join(\"\\n\")],\n\t\trestart: quotedStr(\"no\"),\n\t\tnetworks: [\"openclaw-network\"],\n\t};\n}\n\n// ── Single-File Compose ─────────────────────────────────────────────────────\n\n/**\n * Generates a single Docker Compose YAML string with ALL services.\n * Backward-compatible signature.\n */\nexport function compose(resolved: ResolverOutput, options: ComposeOptions): string {\n\tconst isDirectInstall = options.openclawInstallMethod === \"direct\";\n\n\t// Build depends_on for ALL companion services\n\tconst gatewayDependsOn: Record<string, { condition: string }> = {};\n\tfor (const { definition: def } of resolved.services) {\n\t\tgatewayDependsOn[def.id] = {\n\t\t\tcondition: def.healthcheck ? \"service_healthy\" : \"service_started\",\n\t\t};\n\t}\n\n\tconst services: Record<string, Record<string, unknown>> = {};\n\tlet allVolumes = new Set<string>();\n\n\tif (!isDirectInstall) {\n\t\tconst {\n\t\t\tgatewayService,\n\t\t\tcliService,\n\t\t\tallVolumes: gwVolumes,\n\t\t} = buildGatewayServices(resolved, options, gatewayDependsOn);\n\t\tallVolumes = gwVolumes;\n\t\tservices[\"openclaw-gateway\"] = gatewayService;\n\t\t// CLI service added after companions\n\t\t// Determine which services need DB setup so we can redirect their depends_on\n\t\tconst dbReqs = getDbRequirements(resolved);\n\t\tconst dbServiceIds = new Set(dbReqs.map((r) => r.serviceId));\n\n\t\tfor (const { definition: def } of resolved.services) {\n\t\t\tconst { entry } = buildCompanionService(def, resolved, options, allVolumes);\n\t\t\tif (dbServiceIds.has(def.id) && entry.depends_on) {\n\t\t\t\tconst deps = entry.depends_on as Record<string, { condition: string }>;\n\t\t\t\tif (deps.postgresql) {\n\t\t\t\t\tdelete deps.postgresql;\n\t\t\t\t\tdeps[\"postgres-setup\"] = { condition: \"service_completed_successfully\" };\n\t\t\t\t}\n\t\t\t}\n\t\t\tservices[def.id] = entry;\n\t\t}\n\n\t\tconst pgSetup = buildPostgresSetup(resolved);\n\t\tif (pgSetup) {\n\t\t\tservices[\"postgres-setup\"] = pgSetup;\n\t\t}\n\n\t\tservices[\"openclaw-cli\"] = cliService;\n\t} else {\n\t\t// Direct install: no gateway/CLI containers, just companion services\n\t\tconst dbReqs = getDbRequirements(resolved);\n\t\tconst dbServiceIds = new Set(dbReqs.map((r) => r.serviceId));\n\n\t\tfor (const { definition: def } of resolved.services) {\n\t\t\tconst { entry } = buildCompanionService(def, resolved, options, allVolumes);\n\t\t\tif (dbServiceIds.has(def.id) && entry.depends_on) {\n\t\t\t\tconst deps = entry.depends_on as Record<string, { condition: string }>;\n\t\t\t\tif (deps.postgresql) {\n\t\t\t\t\tdelete deps.postgresql;\n\t\t\t\t\tdeps[\"postgres-setup\"] = { condition: \"service_completed_successfully\" };\n\t\t\t\t}\n\t\t\t}\n\t\t\tservices[def.id] = entry;\n\t\t}\n\n\t\tconst pgSetup = buildPostgresSetup(resolved);\n\t\tif (pgSetup) {\n\t\t\tservices[\"postgres-setup\"] = pgSetup;\n\t\t}\n\t}\n\n\tconst volumes: Record<string, null> = {};\n\tfor (const v of [...allVolumes].sort()) {\n\t\tvolumes[v] = null;\n\t}\n\n\tconst networks = { \"openclaw-network\": { driver: \"bridge\" } };\n\n\treturn stringify({ services, volumes, networks }, YAML_OPTIONS);\n}\n\n// ── Multi-File Compose ──────────────────────────────────────────────────────\n\ninterface ServiceInfo {\n\tid: string;\n\tcategory: ServiceCategory;\n\tentry: Record<string, unknown>;\n\tvolumeNames: string[];\n}\n\n/**\n * Generates multiple Docker Compose files, splitting services into profile-based\n * override files by category.\n */\nexport function composeMultiFile(resolved: ResolverOutput, options: ComposeOptions): ComposeResult {\n\tconst isDirectInstall = options.openclawInstallMethod === \"direct\";\n\tconst allVolumes = new Set<string>();\n\n\t// Build all companion service entries & classify by category\n\tconst serviceInfos: ServiceInfo[] = [];\n\tconst dbReqs = getDbRequirements(resolved);\n\tconst dbServiceIds = new Set(dbReqs.map((r) => r.serviceId));\n\n\tfor (const { definition: def } of resolved.services) {\n\t\tconst { entry, volumeNames } = buildCompanionService(def, resolved, options, allVolumes);\n\t\t// Redirect DB-dependent services to depend on postgres-setup\n\t\tif (dbServiceIds.has(def.id) && entry.depends_on) {\n\t\t\tconst deps = entry.depends_on as Record<string, { condition: string }>;\n\t\t\tif (deps.postgresql) {\n\t\t\t\tdelete deps.postgresql;\n\t\t\t\tdeps[\"postgres-setup\"] = { condition: \"service_completed_successfully\" };\n\t\t\t}\n\t\t}\n\t\tserviceInfos.push({ id: def.id, category: def.category, entry, volumeNames });\n\t}\n\n\t// Partition services into base vs. profile files\n\tconst baseServiceIds = new Set<string>();\n\tconst profileFileMap: Record<string, { profile: string; services: ServiceInfo[] }> = {};\n\n\tfor (const info of serviceInfos) {\n\t\tconst mapping = CATEGORY_PROFILE_MAP[info.category];\n\t\tif (mapping) {\n\t\t\tif (!profileFileMap[mapping.file]) {\n\t\t\t\tprofileFileMap[mapping.file] = { profile: mapping.profile, services: [] };\n\t\t\t}\n\t\t\tprofileFileMap[mapping.file]!.services.push(info);\n\t\t} else {\n\t\t\tbaseServiceIds.add(info.id);\n\t\t}\n\t}\n\n\tconst baseServices: Record<string, Record<string, unknown>> = {};\n\n\tif (!isDirectInstall) {\n\t\t// Gateway depends_on (only base services)\n\t\tconst gatewayDependsOn: Record<string, { condition: string }> = {};\n\t\tfor (const { definition: def } of resolved.services) {\n\t\t\tif (baseServiceIds.has(def.id)) {\n\t\t\t\tgatewayDependsOn[def.id] = {\n\t\t\t\t\tcondition: def.healthcheck ? \"service_healthy\" : \"service_started\",\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tconst {\n\t\t\tgatewayService,\n\t\t\tcliService,\n\t\t\tallVolumes: gwVolumes,\n\t\t} = buildGatewayServices(resolved, options, gatewayDependsOn);\n\n\t\t// Merge gateway volumes into allVolumes\n\t\tfor (const v of gwVolumes) allVolumes.add(v);\n\n\t\tbaseServices[\"openclaw-gateway\"] = gatewayService;\n\n\t\tfor (const info of serviceInfos) {\n\t\t\tif (baseServiceIds.has(info.id)) {\n\t\t\t\tbaseServices[info.id] = info.entry;\n\t\t\t}\n\t\t}\n\n\t\t// Add postgres-setup init container if needed\n\t\tconst pgSetup = buildPostgresSetup(resolved);\n\t\tif (pgSetup) {\n\t\t\tbaseServices[\"postgres-setup\"] = pgSetup;\n\t\t}\n\n\t\tbaseServices[\"openclaw-cli\"] = cliService;\n\t} else {\n\t\t// Direct install: no gateway/CLI containers\n\t\tfor (const info of serviceInfos) {\n\t\t\tif (baseServiceIds.has(info.id)) {\n\t\t\t\tbaseServices[info.id] = info.entry;\n\t\t\t}\n\t\t}\n\n\t\tconst pgSetup = buildPostgresSetup(resolved);\n\t\tif (pgSetup) {\n\t\t\tbaseServices[\"postgres-setup\"] = pgSetup;\n\t\t}\n\t}\n\n\tconst sortedAllVolumes: Record<string, null> = {};\n\tfor (const v of [...allVolumes].sort()) {\n\t\tsortedAllVolumes[v] = null;\n\t}\n\n\tconst networks = { \"openclaw-network\": { driver: \"bridge\" } };\n\n\tconst files: Record<string, string> = {};\n\tfiles[\"docker-compose.yml\"] = stringify(\n\t\t{ services: baseServices, volumes: sortedAllVolumes, networks },\n\t\tYAML_OPTIONS,\n\t);\n\n\t// Profile override files\n\tconst usedProfiles = new Set<string>();\n\n\tfor (const [fileName, { profile, services }] of Object.entries(profileFileMap)) {\n\t\tusedProfiles.add(profile);\n\n\t\tconst profileServices: Record<string, Record<string, unknown>> = {};\n\t\tconst profileVolumes = new Set<string>();\n\n\t\tfor (const info of services) {\n\t\t\tprofileServices[info.id] = { ...info.entry, profiles: [profile] };\n\t\t\tfor (const vName of info.volumeNames) {\n\t\t\t\tprofileVolumes.add(vName);\n\t\t\t}\n\t\t}\n\n\t\tconst fileContent: Record<string, unknown> = { services: profileServices };\n\n\t\tif (profileVolumes.size > 0) {\n\t\t\tconst sortedProfileVolumes: Record<string, null> = {};\n\t\t\tfor (const v of [...profileVolumes].sort()) {\n\t\t\t\tsortedProfileVolumes[v] = null;\n\t\t\t}\n\t\t\tfileContent.volumes = sortedProfileVolumes;\n\t\t}\n\n\t\tfiles[fileName] = stringify(fileContent, YAML_OPTIONS);\n\t}\n\n\treturn {\n\t\tfiles,\n\t\tmainFile: \"docker-compose.yml\",\n\t\tprofiles: [...usedProfiles].sort(),\n\t};\n}\n"],"mappings":"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"}
|
package/dist/composer.d.cts
CHANGED
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
import { ComposeOptions, ResolverOutput } from "./types.cjs";
|
|
2
|
+
import { Scalar } from "yaml";
|
|
2
3
|
|
|
3
4
|
//#region src/composer.d.ts
|
|
5
|
+
/** Creates a YAML scalar that is always quoted — avoids YAML 1.1 bare `no` → false. */
|
|
6
|
+
declare function quotedStr(value: string): Scalar;
|
|
4
7
|
interface ComposeResult {
|
|
5
8
|
files: Record<string, string>;
|
|
6
9
|
mainFile: string;
|
|
7
10
|
profiles: string[];
|
|
8
11
|
}
|
|
12
|
+
declare const YAML_OPTIONS: {
|
|
13
|
+
lineWidth: number;
|
|
14
|
+
nullStr: string;
|
|
15
|
+
};
|
|
16
|
+
declare function buildCompanionService(def: ResolverOutput["services"][number]["definition"], resolved: ResolverOutput, options: ComposeOptions, allVolumes: Set<string>): {
|
|
17
|
+
entry: Record<string, unknown>;
|
|
18
|
+
volumeNames: string[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Builds a one-shot init container that creates databases and users for
|
|
22
|
+
* services that need their own PostgreSQL database. Runs AFTER PostgreSQL
|
|
23
|
+
* is healthy, on every `docker compose up`, and is idempotent.
|
|
24
|
+
*
|
|
25
|
+
* Uses standard PG* environment variables (PGHOST, PGUSER, PGDATABASE,
|
|
26
|
+
* PGPASSWORD) so psql/createuser/createdb automatically connect without
|
|
27
|
+
* needing explicit -h/-U/-d flags — simpler and avoids YAML escaping issues.
|
|
28
|
+
*
|
|
29
|
+
* Returns null when no setup is needed (no PostgreSQL or no DB requirements).
|
|
30
|
+
*/
|
|
31
|
+
declare function buildPostgresSetup(resolved: ResolverOutput): Record<string, unknown> | null;
|
|
9
32
|
/**
|
|
10
33
|
* Generates a single Docker Compose YAML string with ALL services.
|
|
11
34
|
* Backward-compatible signature.
|
|
@@ -17,5 +40,5 @@ declare function compose(resolved: ResolverOutput, options: ComposeOptions): str
|
|
|
17
40
|
*/
|
|
18
41
|
declare function composeMultiFile(resolved: ResolverOutput, options: ComposeOptions): ComposeResult;
|
|
19
42
|
//#endregion
|
|
20
|
-
export { ComposeResult, compose, composeMultiFile };
|
|
43
|
+
export { ComposeResult, YAML_OPTIONS, buildCompanionService, buildPostgresSetup, compose, composeMultiFile, quotedStr };
|
|
21
44
|
//# sourceMappingURL=composer.d.cts.map
|
package/dist/composer.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composer.d.cts","names":[],"sources":["../src/composer.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"composer.d.cts","names":[],"sources":["../src/composer.ts"],"mappings":";;;;;iBAyBgB,SAAA,CAAU,KAAA,WAAgB,MAAA;AAAA,UAQzB,aAAA;EAChB,KAAA,EAAO,MAAA;EACP,QAAA;EACA,QAAA;AAAA;AAAA,cAmBY,YAAA;EAA8C,SAAA;EAAA,OAAA;AAAA;AAAA,iBAyK3C,qBAAA,CACf,GAAA,EAAK,cAAA,oCACL,QAAA,EAAU,cAAA,EACV,OAAA,EAAS,cAAA,EACT,UAAA,EAAY,GAAA;EACR,KAAA,EAAO,MAAA;EAAyB,WAAA;AAAA;;;AA9KrC;;;;;AAyKA;;;;iBA+NgB,kBAAA,CAAmB,QAAA,EAAU,cAAA,GAAiB,MAAA;;;;;iBAiE9C,OAAA,CAAQ,QAAA,EAAU,cAAA,EAAgB,OAAA,EAAS,cAAA;;;;;iBA2F3C,gBAAA,CAAiB,QAAA,EAAU,cAAA,EAAgB,OAAA,EAAS,cAAA,GAAiB,aAAA"}
|
package/dist/composer.d.mts
CHANGED
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
import { ComposeOptions, ResolverOutput } from "./types.mjs";
|
|
2
|
+
import { Scalar } from "yaml";
|
|
2
3
|
|
|
3
4
|
//#region src/composer.d.ts
|
|
5
|
+
/** Creates a YAML scalar that is always quoted — avoids YAML 1.1 bare `no` → false. */
|
|
6
|
+
declare function quotedStr(value: string): Scalar;
|
|
4
7
|
interface ComposeResult {
|
|
5
8
|
files: Record<string, string>;
|
|
6
9
|
mainFile: string;
|
|
7
10
|
profiles: string[];
|
|
8
11
|
}
|
|
12
|
+
declare const YAML_OPTIONS: {
|
|
13
|
+
lineWidth: number;
|
|
14
|
+
nullStr: string;
|
|
15
|
+
};
|
|
16
|
+
declare function buildCompanionService(def: ResolverOutput["services"][number]["definition"], resolved: ResolverOutput, options: ComposeOptions, allVolumes: Set<string>): {
|
|
17
|
+
entry: Record<string, unknown>;
|
|
18
|
+
volumeNames: string[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Builds a one-shot init container that creates databases and users for
|
|
22
|
+
* services that need their own PostgreSQL database. Runs AFTER PostgreSQL
|
|
23
|
+
* is healthy, on every `docker compose up`, and is idempotent.
|
|
24
|
+
*
|
|
25
|
+
* Uses standard PG* environment variables (PGHOST, PGUSER, PGDATABASE,
|
|
26
|
+
* PGPASSWORD) so psql/createuser/createdb automatically connect without
|
|
27
|
+
* needing explicit -h/-U/-d flags — simpler and avoids YAML escaping issues.
|
|
28
|
+
*
|
|
29
|
+
* Returns null when no setup is needed (no PostgreSQL or no DB requirements).
|
|
30
|
+
*/
|
|
31
|
+
declare function buildPostgresSetup(resolved: ResolverOutput): Record<string, unknown> | null;
|
|
9
32
|
/**
|
|
10
33
|
* Generates a single Docker Compose YAML string with ALL services.
|
|
11
34
|
* Backward-compatible signature.
|
|
@@ -17,5 +40,5 @@ declare function compose(resolved: ResolverOutput, options: ComposeOptions): str
|
|
|
17
40
|
*/
|
|
18
41
|
declare function composeMultiFile(resolved: ResolverOutput, options: ComposeOptions): ComposeResult;
|
|
19
42
|
//#endregion
|
|
20
|
-
export { ComposeResult, compose, composeMultiFile };
|
|
43
|
+
export { ComposeResult, YAML_OPTIONS, buildCompanionService, buildPostgresSetup, compose, composeMultiFile, quotedStr };
|
|
21
44
|
//# sourceMappingURL=composer.d.mts.map
|
package/dist/composer.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composer.d.mts","names":[],"sources":["../src/composer.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"composer.d.mts","names":[],"sources":["../src/composer.ts"],"mappings":";;;;;iBAyBgB,SAAA,CAAU,KAAA,WAAgB,MAAA;AAAA,UAQzB,aAAA;EAChB,KAAA,EAAO,MAAA;EACP,QAAA;EACA,QAAA;AAAA;AAAA,cAmBY,YAAA;EAA8C,SAAA;EAAA,OAAA;AAAA;AAAA,iBAyK3C,qBAAA,CACf,GAAA,EAAK,cAAA,oCACL,QAAA,EAAU,cAAA,EACV,OAAA,EAAS,cAAA,EACT,UAAA,EAAY,GAAA;EACR,KAAA,EAAO,MAAA;EAAyB,WAAA;AAAA;;;;AA9KrC;;;;;AAyKA;;;iBA+NgB,kBAAA,CAAmB,QAAA,EAAU,cAAA,GAAiB,MAAA;;;;;iBAiE9C,OAAA,CAAQ,QAAA,EAAU,cAAA,EAAgB,OAAA,EAAS,cAAA;;;;;iBA2F3C,gBAAA,CAAiB,QAAA,EAAU,cAAA,EAAgB,OAAA,EAAS,cAAA,GAAiB,aAAA"}
|
package/dist/composer.mjs
CHANGED
|
@@ -479,6 +479,6 @@ function composeMultiFile(resolved, options) {
|
|
|
479
479
|
};
|
|
480
480
|
}
|
|
481
481
|
//#endregion
|
|
482
|
-
export { compose, composeMultiFile };
|
|
482
|
+
export { YAML_OPTIONS, buildCompanionService, buildPostgresSetup, compose, composeMultiFile, quotedStr };
|
|
483
483
|
|
|
484
484
|
//# sourceMappingURL=composer.mjs.map
|