@better-openclaw/core 1.0.24 → 1.0.25
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 +673 -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 +671 -0
- package/dist/addon-stack.mjs.map +1 -0
- package/dist/addon-stack.test.cjs +349 -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 +349 -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/composer.cjs +4 -0
- 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 +53 -53
- package/dist/composer.test.cjs.map +1 -1
- package/dist/composer.test.mjs +2 -2
- 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 +2 -2
- package/dist/generate.mjs +3 -3
- package/dist/generate.test.cjs +55 -55
- 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.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/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/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 +20 -4
- 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/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-CKBRu-Rt.d.cts} +295 -2
- package/dist/schema-CKBRu-Rt.d.cts.map +1 -0
- package/dist/{schema-tn5RK8CM.d.cts → schema-Dn-_Jpb6.d.mts} +295 -2
- package/dist/schema-Dn-_Jpb6.d.mts.map +1 -0
- package/dist/schema.cjs +138 -1
- 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 +130 -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/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/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/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/{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.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 +490 -0
- package/src/addon-stack.ts +998 -0
- package/src/composer.ts +4 -4
- package/src/index.ts +20 -2
- package/src/schema.ts +183 -0
- package/src/services/definitions/browserless.ts +3 -0
- package/src/services/definitions/convex.ts +31 -0
- package/src/services/definitions/grafana.ts +9 -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/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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composer.snapshot.test.cjs","names":["composeMultiFile","resolve","describe"],"sources":["../src/composer.snapshot.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { composeMultiFile } from \"./composer.js\";\nimport { resolve } from \"./resolver.js\";\n\nfunction generateForPreset(services: string[], skillPacks: string[] = []) {\n\tconst resolved = resolve({ services, skillPacks });\n\treturn composeMultiFile(resolved, {\n\t\tprojectName: \"test-stack\",\n\t\tproxy: \"none\",\n\t\tgpu: false,\n\t\tplatform: \"linux/amd64\",\n\t\tdeployment: \"local\",\n\t\topenclawVersion: \"latest\",\n\t});\n}\n\ndescribe(\"compose snapshot tests\", () => {\n\tit(\"minimal preset (redis only)\", () => {\n\t\tconst result = generateForPreset([\"redis\"]);\n\t\texpect(result.files[\"docker-compose.yml\"]).toMatchSnapshot();\n\t});\n\n\tit(\"creator preset (ffmpeg + remotion + minio + redis)\", () => {\n\t\tconst result = generateForPreset([\"ffmpeg\", \"remotion\", \"minio\", \"redis\"], [\"video-creator\"]);\n\t\texpect(result.files[\"docker-compose.yml\"]).toMatchSnapshot();\n\t\tif (result.files[\"docker-compose.media.yml\"]) {\n\t\t\texpect(result.files[\"docker-compose.media.yml\"]).toMatchSnapshot();\n\t\t}\n\t});\n\n\tit(\"researcher preset (qdrant + searxng + browserless + redis)\", () => {\n\t\tconst result = generateForPreset(\n\t\t\t[\"qdrant\", \"searxng\", \"browserless\", \"redis\"],\n\t\t\t[\"research-agent\"],\n\t\t);\n\t\texpect(result.files[\"docker-compose.yml\"]).toMatchSnapshot();\n\t});\n\n\tit(\"devops preset (n8n + postgresql + redis + monitoring)\", () => {\n\t\tconst result = generateForPreset(\n\t\t\t[\"n8n\", \"postgresql\", \"redis\", \"uptime-kuma\", \"grafana\", \"prometheus\"],\n\t\t\t[\"dev-ops\"],\n\t\t);\n\t\texpect(result.files[\"docker-compose.yml\"]).toMatchSnapshot();\n\t\tif (result.files[\"docker-compose.monitoring.yml\"]) {\n\t\t\texpect(result.files[\"docker-compose.monitoring.yml\"]).toMatchSnapshot();\n\t\t}\n\t});\n\n\tit(\"full preset (many services)\", () => {\n\t\tconst result = generateForPreset([\n\t\t\t\"redis\",\n\t\t\t\"postgresql\",\n\t\t\t\"qdrant\",\n\t\t\t\"n8n\",\n\t\t\t\"ffmpeg\",\n\t\t\t\"remotion\",\n\t\t\t\"minio\",\n\t\t\t\"caddy\",\n\t\t\t\"browserless\",\n\t\t\t\"searxng\",\n\t\t\t\"meilisearch\",\n\t\t\t\"uptime-kuma\",\n\t\t\t\"grafana\",\n\t\t\t\"prometheus\",\n\t\t\t\"ollama\",\n\t\t\t\"whisper\",\n\t\t\t\"gotify\",\n\t\t]);\n\t\texpect(result.files[\"docker-compose.yml\"]).toMatchSnapshot();\n\t\texpect(result.profiles.length).toBeGreaterThan(0);\n\t\t// Verify multiple compose files were created\n\t\texpect(Object.keys(result.files).length).toBeGreaterThan(1);\n\t});\n\n\tit(\"multi-file output has correct profile assignments\", () => {\n\t\tconst result = generateForPreset([\"redis\", \"ollama\", \"open-webui\", \"grafana\", \"prometheus\"]);\n\t\t// AI services should be in the ai profile file\n\t\tif (result.files[\"docker-compose.ai.yml\"]) {\n\t\t\texpect(result.files[\"docker-compose.ai.yml\"]).toContain(\"profiles:\");\n\t\t}\n\t\t// Monitoring should be in the monitoring profile file\n\t\tif (result.files[\"docker-compose.monitoring.yml\"]) {\n\t\t\texpect(result.files[\"docker-compose.monitoring.yml\"]).toContain(\"profiles:\");\n\t\t}\n\t});\n});\n"],"mappings":";;;;AAIA,SAAS,kBAAkB,UAAoB,aAAuB,EAAE,EAAE;AAEzE,QAAOA,iBAAAA,iBADUC,iBAAAA,QAAQ;EAAE;EAAU;EAAY,CAAC,EAChB;EACjC,aAAa;EACb,OAAO;EACP,KAAK;EACL,UAAU;EACV,YAAY;EACZ,iBAAiB;EACjB,CAAC;;AAGHC,
|
|
1
|
+
{"version":3,"file":"composer.snapshot.test.cjs","names":["composeMultiFile","resolve","describe"],"sources":["../src/composer.snapshot.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { composeMultiFile } from \"./composer.js\";\nimport { resolve } from \"./resolver.js\";\n\nfunction generateForPreset(services: string[], skillPacks: string[] = []) {\n\tconst resolved = resolve({ services, skillPacks });\n\treturn composeMultiFile(resolved, {\n\t\tprojectName: \"test-stack\",\n\t\tproxy: \"none\",\n\t\tgpu: false,\n\t\tplatform: \"linux/amd64\",\n\t\tdeployment: \"local\",\n\t\topenclawVersion: \"latest\",\n\t});\n}\n\ndescribe(\"compose snapshot tests\", () => {\n\tit(\"minimal preset (redis only)\", () => {\n\t\tconst result = generateForPreset([\"redis\"]);\n\t\texpect(result.files[\"docker-compose.yml\"]).toMatchSnapshot();\n\t});\n\n\tit(\"creator preset (ffmpeg + remotion + minio + redis)\", () => {\n\t\tconst result = generateForPreset([\"ffmpeg\", \"remotion\", \"minio\", \"redis\"], [\"video-creator\"]);\n\t\texpect(result.files[\"docker-compose.yml\"]).toMatchSnapshot();\n\t\tif (result.files[\"docker-compose.media.yml\"]) {\n\t\t\texpect(result.files[\"docker-compose.media.yml\"]).toMatchSnapshot();\n\t\t}\n\t});\n\n\tit(\"researcher preset (qdrant + searxng + browserless + redis)\", () => {\n\t\tconst result = generateForPreset(\n\t\t\t[\"qdrant\", \"searxng\", \"browserless\", \"redis\"],\n\t\t\t[\"research-agent\"],\n\t\t);\n\t\texpect(result.files[\"docker-compose.yml\"]).toMatchSnapshot();\n\t});\n\n\tit(\"devops preset (n8n + postgresql + redis + monitoring)\", () => {\n\t\tconst result = generateForPreset(\n\t\t\t[\"n8n\", \"postgresql\", \"redis\", \"uptime-kuma\", \"grafana\", \"prometheus\"],\n\t\t\t[\"dev-ops\"],\n\t\t);\n\t\texpect(result.files[\"docker-compose.yml\"]).toMatchSnapshot();\n\t\tif (result.files[\"docker-compose.monitoring.yml\"]) {\n\t\t\texpect(result.files[\"docker-compose.monitoring.yml\"]).toMatchSnapshot();\n\t\t}\n\t});\n\n\tit(\"full preset (many services)\", () => {\n\t\tconst result = generateForPreset([\n\t\t\t\"redis\",\n\t\t\t\"postgresql\",\n\t\t\t\"qdrant\",\n\t\t\t\"n8n\",\n\t\t\t\"ffmpeg\",\n\t\t\t\"remotion\",\n\t\t\t\"minio\",\n\t\t\t\"caddy\",\n\t\t\t\"browserless\",\n\t\t\t\"searxng\",\n\t\t\t\"meilisearch\",\n\t\t\t\"uptime-kuma\",\n\t\t\t\"grafana\",\n\t\t\t\"prometheus\",\n\t\t\t\"ollama\",\n\t\t\t\"whisper\",\n\t\t\t\"gotify\",\n\t\t]);\n\t\texpect(result.files[\"docker-compose.yml\"]).toMatchSnapshot();\n\t\texpect(result.profiles.length).toBeGreaterThan(0);\n\t\t// Verify multiple compose files were created\n\t\texpect(Object.keys(result.files).length).toBeGreaterThan(1);\n\t});\n\n\tit(\"multi-file output has correct profile assignments\", () => {\n\t\tconst result = generateForPreset([\"redis\", \"ollama\", \"open-webui\", \"grafana\", \"prometheus\"]);\n\t\t// AI services should be in the ai profile file\n\t\tif (result.files[\"docker-compose.ai.yml\"]) {\n\t\t\texpect(result.files[\"docker-compose.ai.yml\"]).toContain(\"profiles:\");\n\t\t}\n\t\t// Monitoring should be in the monitoring profile file\n\t\tif (result.files[\"docker-compose.monitoring.yml\"]) {\n\t\t\texpect(result.files[\"docker-compose.monitoring.yml\"]).toContain(\"profiles:\");\n\t\t}\n\t});\n});\n"],"mappings":";;;;AAIA,SAAS,kBAAkB,UAAoB,aAAuB,EAAE,EAAE;AAEzE,QAAOA,iBAAAA,iBADUC,iBAAAA,QAAQ;EAAE;EAAU;EAAY,CAAC,EAChB;EACjC,aAAa;EACb,OAAO;EACP,KAAK;EACL,UAAU;EACV,YAAY;EACZ,iBAAiB;EACjB,CAAC;;AAGHC,sBAAAA,SAAS,gCAAgC;AACxC,uBAAA,GAAG,qCAAqC;AAEvC,wBAAA,aADe,kBAAkB,CAAC,QAAQ,CAAC,CAC7B,MAAM,sBAAsB,CAAC,iBAAiB;GAC3D;AAEF,uBAAA,GAAG,4DAA4D;EAC9D,MAAM,SAAS,kBAAkB;GAAC;GAAU;GAAY;GAAS;GAAQ,EAAE,CAAC,gBAAgB,CAAC;AAC7F,wBAAA,aAAO,OAAO,MAAM,sBAAsB,CAAC,iBAAiB;AAC5D,MAAI,OAAO,MAAM,4BAChB,uBAAA,aAAO,OAAO,MAAM,4BAA4B,CAAC,iBAAiB;GAElE;AAEF,uBAAA,GAAG,oEAAoE;AAKtE,wBAAA,aAJe,kBACd;GAAC;GAAU;GAAW;GAAe;GAAQ,EAC7C,CAAC,iBAAiB,CAClB,CACa,MAAM,sBAAsB,CAAC,iBAAiB;GAC3D;AAEF,uBAAA,GAAG,+DAA+D;EACjE,MAAM,SAAS,kBACd;GAAC;GAAO;GAAc;GAAS;GAAe;GAAW;GAAa,EACtE,CAAC,UAAU,CACX;AACD,wBAAA,aAAO,OAAO,MAAM,sBAAsB,CAAC,iBAAiB;AAC5D,MAAI,OAAO,MAAM,iCAChB,uBAAA,aAAO,OAAO,MAAM,iCAAiC,CAAC,iBAAiB;GAEvE;AAEF,uBAAA,GAAG,qCAAqC;EACvC,MAAM,SAAS,kBAAkB;GAChC;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,CAAC;AACF,wBAAA,aAAO,OAAO,MAAM,sBAAsB,CAAC,iBAAiB;AAC5D,wBAAA,aAAO,OAAO,SAAS,OAAO,CAAC,gBAAgB,EAAE;AAEjD,wBAAA,aAAO,OAAO,KAAK,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE;GAC1D;AAEF,uBAAA,GAAG,2DAA2D;EAC7D,MAAM,SAAS,kBAAkB;GAAC;GAAS;GAAU;GAAc;GAAW;GAAa,CAAC;AAE5F,MAAI,OAAO,MAAM,yBAChB,uBAAA,aAAO,OAAO,MAAM,yBAAyB,CAAC,UAAU,YAAY;AAGrE,MAAI,OAAO,MAAM,iCAChB,uBAAA,aAAO,OAAO,MAAM,iCAAiC,CAAC,UAAU,YAAY;GAE5E;EACD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as describe, r as it, t as globalExpect } from "./
|
|
2
|
-
import { resolve } from "./resolver.mjs";
|
|
1
|
+
import { n as describe, r as it, t as globalExpect } from "./test.CTcmp4Su-ClCHJ3FA.mjs";
|
|
3
2
|
import { composeMultiFile } from "./composer.mjs";
|
|
3
|
+
import { resolve } from "./resolver.mjs";
|
|
4
4
|
//#region src/composer.snapshot.test.ts
|
|
5
5
|
function generateForPreset(services, skillPacks = []) {
|
|
6
6
|
return composeMultiFile(resolve({
|
package/dist/composer.test.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require("./skills-BlzpHmpH.cjs");
|
|
2
|
-
const
|
|
3
|
-
const require_resolver = require("./resolver.cjs");
|
|
2
|
+
const require_test_CTcmp4Su = require("./test.CTcmp4Su-DlzTarwH.cjs");
|
|
4
3
|
const require_composer = require("./composer.cjs");
|
|
4
|
+
const require_resolver = require("./resolver.cjs");
|
|
5
5
|
let yaml = require("yaml");
|
|
6
6
|
//#region src/composer.test.ts
|
|
7
7
|
const defaultOptions = {
|
|
@@ -12,58 +12,58 @@ const defaultOptions = {
|
|
|
12
12
|
deployment: "local",
|
|
13
13
|
openclawVersion: "latest"
|
|
14
14
|
};
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
require_test_CTcmp4Su.describe("compose", () => {
|
|
16
|
+
require_test_CTcmp4Su.it("generates minimal stack with just OpenClaw gateway when no companions", () => {
|
|
17
17
|
const parsed = (0, yaml.parse)(require_composer.compose(require_resolver.resolve({
|
|
18
18
|
services: [],
|
|
19
19
|
skillPacks: []
|
|
20
20
|
}), defaultOptions));
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
require_test_CTcmp4Su.globalExpect(parsed.services).toHaveProperty("openclaw-gateway");
|
|
22
|
+
require_test_CTcmp4Su.globalExpect(parsed.services["openclaw-gateway"].image).toContain("ghcr.io/openclaw/openclaw");
|
|
23
|
+
require_test_CTcmp4Su.globalExpect(parsed.services["openclaw-gateway"].environment.HOME).toBe("/home/node");
|
|
24
|
+
require_test_CTcmp4Su.globalExpect(parsed.services["openclaw-gateway"].environment.TERM).toBe("xterm-256color");
|
|
25
25
|
const gwVolumes = parsed.services["openclaw-gateway"].volumes;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
require_test_CTcmp4Su.globalExpect(gwVolumes.some((v) => v.includes(".openclaw"))).toBe(true);
|
|
27
|
+
require_test_CTcmp4Su.globalExpect(parsed.networks).toHaveProperty("openclaw-network");
|
|
28
|
+
require_test_CTcmp4Su.globalExpect(parsed.networks["openclaw-network"].driver).toBe("bridge");
|
|
29
|
+
require_test_CTcmp4Su.globalExpect(parsed.services).toHaveProperty("openclaw-cli");
|
|
30
|
+
require_test_CTcmp4Su.globalExpect(parsed.services).toHaveProperty("convex");
|
|
31
|
+
require_test_CTcmp4Su.globalExpect(parsed.services).toHaveProperty("mission-control");
|
|
32
|
+
require_test_CTcmp4Su.globalExpect(parsed.services["openclaw-gateway"].restart).toBe("unless-stopped");
|
|
33
33
|
});
|
|
34
|
-
|
|
34
|
+
require_test_CTcmp4Su.it("generates Redis companion with proper gateway depends_on", () => {
|
|
35
35
|
const parsed = (0, yaml.parse)(require_composer.compose(require_resolver.resolve({
|
|
36
36
|
services: ["redis"],
|
|
37
37
|
skillPacks: []
|
|
38
38
|
}), defaultOptions));
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
39
|
+
require_test_CTcmp4Su.globalExpect(parsed.services).toHaveProperty("openclaw-gateway");
|
|
40
|
+
require_test_CTcmp4Su.globalExpect(parsed.services).toHaveProperty("redis");
|
|
41
|
+
require_test_CTcmp4Su.globalExpect(parsed.services.redis.image).toBe("redis:8-alpine");
|
|
42
|
+
require_test_CTcmp4Su.globalExpect(parsed.services["openclaw-gateway"].depends_on).toHaveProperty("redis");
|
|
43
|
+
require_test_CTcmp4Su.globalExpect(parsed.services["openclaw-gateway"].depends_on.redis.condition).toBe("service_healthy");
|
|
44
|
+
require_test_CTcmp4Su.globalExpect(parsed.services.redis.healthcheck).toBeDefined();
|
|
45
|
+
require_test_CTcmp4Su.globalExpect(parsed.services.redis.healthcheck.test).toContain("redis-cli ping");
|
|
46
|
+
require_test_CTcmp4Su.globalExpect(parsed.services.redis.restart).toBe("unless-stopped");
|
|
47
|
+
require_test_CTcmp4Su.globalExpect(parsed.services.redis.networks).toContain("openclaw-network");
|
|
48
|
+
require_test_CTcmp4Su.globalExpect(parsed.services["openclaw-gateway"].environment).toHaveProperty("REDIS_HOST");
|
|
49
|
+
require_test_CTcmp4Su.globalExpect(parsed.services["openclaw-gateway"].environment.REDIS_HOST).toBe("redis");
|
|
50
50
|
});
|
|
51
|
-
|
|
51
|
+
require_test_CTcmp4Su.it("generates parseable YAML for a multi-service stack", () => {
|
|
52
52
|
const yaml$1 = require_composer.compose(require_resolver.resolve({
|
|
53
53
|
services: ["redis", "n8n"],
|
|
54
54
|
skillPacks: []
|
|
55
55
|
}), defaultOptions);
|
|
56
|
-
|
|
56
|
+
require_test_CTcmp4Su.globalExpect(() => (0, yaml.parse)(yaml$1)).not.toThrow();
|
|
57
57
|
const parsed = (0, yaml.parse)(yaml$1);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
require_test_CTcmp4Su.globalExpect(parsed).toHaveProperty("services");
|
|
59
|
+
require_test_CTcmp4Su.globalExpect(parsed).toHaveProperty("volumes");
|
|
60
|
+
require_test_CTcmp4Su.globalExpect(parsed).toHaveProperty("networks");
|
|
61
|
+
require_test_CTcmp4Su.globalExpect(parsed.services).toHaveProperty("openclaw-gateway");
|
|
62
|
+
require_test_CTcmp4Su.globalExpect(parsed.services).toHaveProperty("redis");
|
|
63
|
+
require_test_CTcmp4Su.globalExpect(parsed.services).toHaveProperty("n8n");
|
|
64
|
+
require_test_CTcmp4Su.globalExpect(parsed.services).toHaveProperty("postgresql");
|
|
65
65
|
});
|
|
66
|
-
|
|
66
|
+
require_test_CTcmp4Su.it("includes all service volumes in top-level volumes section", () => {
|
|
67
67
|
const resolved = require_resolver.resolve({
|
|
68
68
|
services: ["redis", "n8n"],
|
|
69
69
|
skillPacks: []
|
|
@@ -71,11 +71,11 @@ require_vi_2VT5v0um.describe("compose", () => {
|
|
|
71
71
|
const parsed = (0, yaml.parse)(require_composer.compose(resolved, defaultOptions));
|
|
72
72
|
const serviceVolumeNames = /* @__PURE__ */ new Set();
|
|
73
73
|
for (const rs of resolved.services) for (const vol of rs.definition.volumes) serviceVolumeNames.add(vol.name);
|
|
74
|
-
for (const volName of serviceVolumeNames)
|
|
74
|
+
for (const volName of serviceVolumeNames) require_test_CTcmp4Su.globalExpect(parsed.volumes).toHaveProperty(volName);
|
|
75
75
|
const gwVols = parsed.services["openclaw-gateway"].volumes;
|
|
76
|
-
|
|
76
|
+
require_test_CTcmp4Su.globalExpect(gwVols.some((v) => v.includes(".openclaw"))).toBe(true);
|
|
77
77
|
});
|
|
78
|
-
|
|
78
|
+
require_test_CTcmp4Su.it("includes GPU passthrough when gpu=true and service requires it", () => {
|
|
79
79
|
const resolved = require_resolver.resolve({
|
|
80
80
|
services: ["ollama"],
|
|
81
81
|
skillPacks: []
|
|
@@ -93,14 +93,14 @@ require_vi_2VT5v0um.describe("compose", () => {
|
|
|
93
93
|
...defaultOptions,
|
|
94
94
|
gpu: true
|
|
95
95
|
})).services.ollama;
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
require_test_CTcmp4Su.globalExpect(ollamaService.deploy).toBeDefined();
|
|
97
|
+
require_test_CTcmp4Su.globalExpect(ollamaService.deploy.resources.reservations.devices).toEqual([{
|
|
98
98
|
driver: "nvidia",
|
|
99
99
|
count: "all",
|
|
100
100
|
capabilities: ["gpu"]
|
|
101
101
|
}]);
|
|
102
102
|
});
|
|
103
|
-
|
|
103
|
+
require_test_CTcmp4Su.it("does not include GPU passthrough when gpu=false", () => {
|
|
104
104
|
const resolved = require_resolver.resolve({
|
|
105
105
|
services: ["ollama"],
|
|
106
106
|
skillPacks: []
|
|
@@ -118,9 +118,9 @@ require_vi_2VT5v0um.describe("compose", () => {
|
|
|
118
118
|
...defaultOptions,
|
|
119
119
|
gpu: false
|
|
120
120
|
})).services.ollama;
|
|
121
|
-
|
|
121
|
+
require_test_CTcmp4Su.globalExpect(ollamaService.deploy?.resources?.reservations?.devices).toBeUndefined();
|
|
122
122
|
});
|
|
123
|
-
|
|
123
|
+
require_test_CTcmp4Su.it("does not map internal-only ports to host", () => {
|
|
124
124
|
const resolved = require_resolver.resolve({
|
|
125
125
|
services: ["redis"],
|
|
126
126
|
skillPacks: []
|
|
@@ -140,25 +140,25 @@ require_vi_2VT5v0um.describe("compose", () => {
|
|
|
140
140
|
}
|
|
141
141
|
}))
|
|
142
142
|
}, defaultOptions)).services.redis.ports;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
require_test_CTcmp4Su.globalExpect(redisPorts).toHaveLength(1);
|
|
144
|
+
require_test_CTcmp4Su.globalExpect(redisPorts[0]).toContain("6379");
|
|
145
|
+
require_test_CTcmp4Su.globalExpect(redisPorts[0]).not.toContain("16379");
|
|
146
146
|
});
|
|
147
|
-
|
|
147
|
+
require_test_CTcmp4Su.it("uses service_started condition for services without healthcheck", () => {
|
|
148
148
|
const resolved = require_resolver.resolve({
|
|
149
149
|
services: ["minio"],
|
|
150
150
|
skillPacks: []
|
|
151
151
|
});
|
|
152
152
|
const gatewayDeps = (0, yaml.parse)(require_composer.compose(resolved, defaultOptions)).services["openclaw-gateway"].depends_on;
|
|
153
|
-
for (const [id, dep] of Object.entries(gatewayDeps)) if (resolved.services.find((s) => s.definition.id === id)?.definition.healthcheck)
|
|
154
|
-
else
|
|
153
|
+
for (const [id, dep] of Object.entries(gatewayDeps)) if (resolved.services.find((s) => s.definition.id === id)?.definition.healthcheck) require_test_CTcmp4Su.globalExpect(dep.condition).toBe("service_healthy");
|
|
154
|
+
else require_test_CTcmp4Su.globalExpect(dep.condition).toBe("service_started");
|
|
155
155
|
});
|
|
156
|
-
|
|
156
|
+
require_test_CTcmp4Su.it("gateway is always the first service in the output", () => {
|
|
157
157
|
const parsed = (0, yaml.parse)(require_composer.compose(require_resolver.resolve({
|
|
158
158
|
services: ["redis", "ollama"],
|
|
159
159
|
skillPacks: []
|
|
160
160
|
}), defaultOptions));
|
|
161
|
-
|
|
161
|
+
require_test_CTcmp4Su.globalExpect(Object.keys(parsed.services)[0]).toBe("openclaw-gateway");
|
|
162
162
|
});
|
|
163
163
|
});
|
|
164
164
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composer.test.cjs","names":["describe","compose","resolve","yaml"],"sources":["../src/composer.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { parse } from \"yaml\";\nimport { compose } from \"./composer.js\";\nimport { resolve } from \"./resolver.js\";\nimport type { ComposeOptions, ResolverOutput } from \"./types.js\";\n\nconst defaultOptions: ComposeOptions = {\n\tprojectName: \"test-project\",\n\tproxy: \"none\",\n\tgpu: false,\n\tplatform: \"linux/amd64\",\n\tdeployment: \"local\",\n\topenclawVersion: \"latest\",\n};\n\ndescribe(\"compose\", () => {\n\tit(\"generates minimal stack with just OpenClaw gateway when no companions\", () => {\n\t\tconst resolved = resolve({ services: [], skillPacks: [] });\n\t\tconst yaml = compose(resolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// Should have the gateway service\n\t\texpect(parsed.services).toHaveProperty(\"openclaw-gateway\");\n\t\texpect(parsed.services[\"openclaw-gateway\"].image).toContain(\"ghcr.io/openclaw/openclaw\");\n\n\t\t// Gateway should have core environment\n\t\texpect(parsed.services[\"openclaw-gateway\"].environment.HOME).toBe(\"/home/node\");\n\t\texpect(parsed.services[\"openclaw-gateway\"].environment.TERM).toBe(\"xterm-256color\");\n\n\t\t// Gateway uses bind-mount volumes (not named volumes in top-level volumes section)\n\t\tconst gwVolumes = parsed.services[\"openclaw-gateway\"].volumes as string[];\n\t\texpect(gwVolumes.some((v: string) => v.includes(\".openclaw\"))).toBe(true);\n\n\t\t// Should have network\n\t\texpect(parsed.networks).toHaveProperty(\"openclaw-network\");\n\t\texpect(parsed.networks[\"openclaw-network\"].driver).toBe(\"bridge\");\n\n\t\t// Should have CLI companion service\n\t\texpect(parsed.services).toHaveProperty(\"openclaw-cli\");\n\n\t\t// Gateway should have depends_on for mandatory services (convex, mission-control, tailscale)\n\t\t// Even with no user-selected services, mandatory services are always present\n\t\texpect(parsed.services).toHaveProperty(\"convex\");\n\t\texpect(parsed.services).toHaveProperty(\"mission-control\");\n\n\t\t// Gateway should have restart policy\n\t\texpect(parsed.services[\"openclaw-gateway\"].restart).toBe(\"unless-stopped\");\n\t});\n\n\tit(\"generates Redis companion with proper gateway depends_on\", () => {\n\t\tconst resolved = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\tconst yaml = compose(resolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// Should have gateway and redis services\n\t\texpect(parsed.services).toHaveProperty(\"openclaw-gateway\");\n\t\texpect(parsed.services).toHaveProperty(\"redis\");\n\n\t\t// Redis service should have correct image\n\t\texpect(parsed.services.redis.image).toBe(\"redis:8-alpine\");\n\n\t\t// Gateway depends_on should include redis with service_healthy (redis has healthcheck)\n\t\texpect(parsed.services[\"openclaw-gateway\"].depends_on).toHaveProperty(\"redis\");\n\t\texpect(parsed.services[\"openclaw-gateway\"].depends_on.redis.condition).toBe(\"service_healthy\");\n\n\t\t// Redis should have a healthcheck\n\t\texpect(parsed.services.redis.healthcheck).toBeDefined();\n\t\texpect(parsed.services.redis.healthcheck.test).toContain(\"redis-cli ping\");\n\n\t\t// Redis should have restart policy\n\t\texpect(parsed.services.redis.restart).toBe(\"unless-stopped\");\n\n\t\t// Redis should be on openclaw-network\n\t\texpect(parsed.services.redis.networks).toContain(\"openclaw-network\");\n\n\t\t// Gateway environment should include redis-related openclawEnvVars\n\t\texpect(parsed.services[\"openclaw-gateway\"].environment).toHaveProperty(\"REDIS_HOST\");\n\t\texpect(parsed.services[\"openclaw-gateway\"].environment.REDIS_HOST).toBe(\"redis\");\n\t});\n\n\tit(\"generates parseable YAML for a multi-service stack\", () => {\n\t\tconst resolved = resolve({ services: [\"redis\", \"n8n\"], skillPacks: [] });\n\t\tconst yaml = compose(resolved, defaultOptions);\n\n\t\t// Should not throw when parsing\n\t\texpect(() => parse(yaml)).not.toThrow();\n\n\t\tconst parsed = parse(yaml);\n\t\texpect(parsed).toHaveProperty(\"services\");\n\t\texpect(parsed).toHaveProperty(\"volumes\");\n\t\texpect(parsed).toHaveProperty(\"networks\");\n\n\t\t// Should have gateway + redis + n8n + postgresql (n8n requires postgresql)\n\t\texpect(parsed.services).toHaveProperty(\"openclaw-gateway\");\n\t\texpect(parsed.services).toHaveProperty(\"redis\");\n\t\texpect(parsed.services).toHaveProperty(\"n8n\");\n\t\texpect(parsed.services).toHaveProperty(\"postgresql\");\n\t});\n\n\tit(\"includes all service volumes in top-level volumes section\", () => {\n\t\tconst resolved = resolve({ services: [\"redis\", \"n8n\"], skillPacks: [] });\n\t\tconst yaml = compose(resolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// Collect all volume names from all resolved services\n\t\tconst serviceVolumeNames = new Set<string>();\n\t\tfor (const rs of resolved.services) {\n\t\t\tfor (const vol of rs.definition.volumes) {\n\t\t\t\tserviceVolumeNames.add(vol.name);\n\t\t\t}\n\t\t}\n\n\t\t// All service volumes must appear in top-level volumes\n\t\tfor (const volName of serviceVolumeNames) {\n\t\t\texpect(parsed.volumes).toHaveProperty(volName);\n\t\t}\n\n\t\t// Gateway uses bind-mount volumes (not in top-level volumes section)\n\t\tconst gwVols = parsed.services[\"openclaw-gateway\"].volumes as string[];\n\t\texpect(gwVols.some((v: string) => v.includes(\".openclaw\"))).toBe(true);\n\t});\n\n\tit(\"includes GPU passthrough when gpu=true and service requires it\", () => {\n\t\tconst resolved = resolve({ services: [\"ollama\"], skillPacks: [] });\n\n\t\t// Modify resolved output to simulate a GPU-required service\n\t\tconst gpuResolved: ResolverOutput = {\n\t\t\t...resolved,\n\t\t\tservices: resolved.services.map((s) => ({\n\t\t\t\t...s,\n\t\t\t\tdefinition: { ...s.definition, gpuRequired: true },\n\t\t\t})),\n\t\t};\n\n\t\tconst yaml = compose(gpuResolved, { ...defaultOptions, gpu: true });\n\t\tconst parsed = parse(yaml);\n\n\t\t// Ollama service should have GPU device reservation\n\t\tconst ollamaService = parsed.services.ollama;\n\t\texpect(ollamaService.deploy).toBeDefined();\n\t\texpect(ollamaService.deploy.resources.reservations.devices).toEqual([\n\t\t\t{\n\t\t\t\tdriver: \"nvidia\",\n\t\t\t\tcount: \"all\",\n\t\t\t\tcapabilities: [\"gpu\"],\n\t\t\t},\n\t\t]);\n\t});\n\n\tit(\"does not include GPU passthrough when gpu=false\", () => {\n\t\tconst resolved = resolve({ services: [\"ollama\"], skillPacks: [] });\n\n\t\t// Even if service requires GPU, gpu option is false\n\t\tconst gpuResolved: ResolverOutput = {\n\t\t\t...resolved,\n\t\t\tservices: resolved.services.map((s) => ({\n\t\t\t\t...s,\n\t\t\t\tdefinition: { ...s.definition, gpuRequired: true },\n\t\t\t})),\n\t\t};\n\n\t\tconst yaml = compose(gpuResolved, { ...defaultOptions, gpu: false });\n\t\tconst parsed = parse(yaml);\n\n\t\t// Should NOT have deploy with GPU devices\n\t\tconst ollamaService = parsed.services.ollama;\n\t\texpect(ollamaService.deploy?.resources?.reservations?.devices).toBeUndefined();\n\t});\n\n\tit(\"does not map internal-only ports to host\", () => {\n\t\tconst resolved = resolve({ services: [\"redis\"], skillPacks: [] });\n\n\t\t// Modify redis to have an internal-only port\n\t\tconst modifiedResolved: ResolverOutput = {\n\t\t\t...resolved,\n\t\t\tservices: resolved.services.map((s) => ({\n\t\t\t\t...s,\n\t\t\t\tdefinition: {\n\t\t\t\t\t...s.definition,\n\t\t\t\t\tports: [\n\t\t\t\t\t\t...s.definition.ports,\n\t\t\t\t\t\t{ host: 16379, container: 16379, description: \"Redis cluster bus\", exposed: false },\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t})),\n\t\t};\n\n\t\tconst yaml = compose(modifiedResolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// Should only have the exposed port, not the internal one\n\t\tconst redisPorts = parsed.services.redis.ports;\n\t\texpect(redisPorts).toHaveLength(1);\n\t\texpect(redisPorts[0]).toContain(\"6379\");\n\t\texpect(redisPorts[0]).not.toContain(\"16379\");\n\t});\n\n\tit(\"uses service_started condition for services without healthcheck\", () => {\n\t\tconst resolved = resolve({ services: [\"minio\"], skillPacks: [] });\n\n\t\tconst yaml = compose(resolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// Minio may or may not have a healthcheck – the gateway depends_on\n\t\t// condition should reflect the definition accurately\n\t\tconst gatewayDeps = parsed.services[\"openclaw-gateway\"].depends_on;\n\t\tfor (const [id, dep] of Object.entries(gatewayDeps)) {\n\t\t\tconst svc = resolved.services.find((s) => s.definition.id === id);\n\t\t\tif (svc?.definition.healthcheck) {\n\t\t\t\texpect((dep as { condition: string }).condition).toBe(\"service_healthy\");\n\t\t\t} else {\n\t\t\t\texpect((dep as { condition: string }).condition).toBe(\"service_started\");\n\t\t\t}\n\t\t}\n\t});\n\n\tit(\"gateway is always the first service in the output\", () => {\n\t\tconst resolved = resolve({ services: [\"redis\", \"ollama\"], skillPacks: [] });\n\t\tconst yaml = compose(resolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// First key in services should be openclaw-gateway\n\t\tconst serviceKeys = Object.keys(parsed.services);\n\t\texpect(serviceKeys[0]).toBe(\"openclaw-gateway\");\n\t});\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"composer.test.cjs","names":["describe","compose","resolve","yaml"],"sources":["../src/composer.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { parse } from \"yaml\";\nimport { compose } from \"./composer.js\";\nimport { resolve } from \"./resolver.js\";\nimport type { ComposeOptions, ResolverOutput } from \"./types.js\";\n\nconst defaultOptions: ComposeOptions = {\n\tprojectName: \"test-project\",\n\tproxy: \"none\",\n\tgpu: false,\n\tplatform: \"linux/amd64\",\n\tdeployment: \"local\",\n\topenclawVersion: \"latest\",\n};\n\ndescribe(\"compose\", () => {\n\tit(\"generates minimal stack with just OpenClaw gateway when no companions\", () => {\n\t\tconst resolved = resolve({ services: [], skillPacks: [] });\n\t\tconst yaml = compose(resolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// Should have the gateway service\n\t\texpect(parsed.services).toHaveProperty(\"openclaw-gateway\");\n\t\texpect(parsed.services[\"openclaw-gateway\"].image).toContain(\"ghcr.io/openclaw/openclaw\");\n\n\t\t// Gateway should have core environment\n\t\texpect(parsed.services[\"openclaw-gateway\"].environment.HOME).toBe(\"/home/node\");\n\t\texpect(parsed.services[\"openclaw-gateway\"].environment.TERM).toBe(\"xterm-256color\");\n\n\t\t// Gateway uses bind-mount volumes (not named volumes in top-level volumes section)\n\t\tconst gwVolumes = parsed.services[\"openclaw-gateway\"].volumes as string[];\n\t\texpect(gwVolumes.some((v: string) => v.includes(\".openclaw\"))).toBe(true);\n\n\t\t// Should have network\n\t\texpect(parsed.networks).toHaveProperty(\"openclaw-network\");\n\t\texpect(parsed.networks[\"openclaw-network\"].driver).toBe(\"bridge\");\n\n\t\t// Should have CLI companion service\n\t\texpect(parsed.services).toHaveProperty(\"openclaw-cli\");\n\n\t\t// Gateway should have depends_on for mandatory services (convex, mission-control, tailscale)\n\t\t// Even with no user-selected services, mandatory services are always present\n\t\texpect(parsed.services).toHaveProperty(\"convex\");\n\t\texpect(parsed.services).toHaveProperty(\"mission-control\");\n\n\t\t// Gateway should have restart policy\n\t\texpect(parsed.services[\"openclaw-gateway\"].restart).toBe(\"unless-stopped\");\n\t});\n\n\tit(\"generates Redis companion with proper gateway depends_on\", () => {\n\t\tconst resolved = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\tconst yaml = compose(resolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// Should have gateway and redis services\n\t\texpect(parsed.services).toHaveProperty(\"openclaw-gateway\");\n\t\texpect(parsed.services).toHaveProperty(\"redis\");\n\n\t\t// Redis service should have correct image\n\t\texpect(parsed.services.redis.image).toBe(\"redis:8-alpine\");\n\n\t\t// Gateway depends_on should include redis with service_healthy (redis has healthcheck)\n\t\texpect(parsed.services[\"openclaw-gateway\"].depends_on).toHaveProperty(\"redis\");\n\t\texpect(parsed.services[\"openclaw-gateway\"].depends_on.redis.condition).toBe(\"service_healthy\");\n\n\t\t// Redis should have a healthcheck\n\t\texpect(parsed.services.redis.healthcheck).toBeDefined();\n\t\texpect(parsed.services.redis.healthcheck.test).toContain(\"redis-cli ping\");\n\n\t\t// Redis should have restart policy\n\t\texpect(parsed.services.redis.restart).toBe(\"unless-stopped\");\n\n\t\t// Redis should be on openclaw-network\n\t\texpect(parsed.services.redis.networks).toContain(\"openclaw-network\");\n\n\t\t// Gateway environment should include redis-related openclawEnvVars\n\t\texpect(parsed.services[\"openclaw-gateway\"].environment).toHaveProperty(\"REDIS_HOST\");\n\t\texpect(parsed.services[\"openclaw-gateway\"].environment.REDIS_HOST).toBe(\"redis\");\n\t});\n\n\tit(\"generates parseable YAML for a multi-service stack\", () => {\n\t\tconst resolved = resolve({ services: [\"redis\", \"n8n\"], skillPacks: [] });\n\t\tconst yaml = compose(resolved, defaultOptions);\n\n\t\t// Should not throw when parsing\n\t\texpect(() => parse(yaml)).not.toThrow();\n\n\t\tconst parsed = parse(yaml);\n\t\texpect(parsed).toHaveProperty(\"services\");\n\t\texpect(parsed).toHaveProperty(\"volumes\");\n\t\texpect(parsed).toHaveProperty(\"networks\");\n\n\t\t// Should have gateway + redis + n8n + postgresql (n8n requires postgresql)\n\t\texpect(parsed.services).toHaveProperty(\"openclaw-gateway\");\n\t\texpect(parsed.services).toHaveProperty(\"redis\");\n\t\texpect(parsed.services).toHaveProperty(\"n8n\");\n\t\texpect(parsed.services).toHaveProperty(\"postgresql\");\n\t});\n\n\tit(\"includes all service volumes in top-level volumes section\", () => {\n\t\tconst resolved = resolve({ services: [\"redis\", \"n8n\"], skillPacks: [] });\n\t\tconst yaml = compose(resolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// Collect all volume names from all resolved services\n\t\tconst serviceVolumeNames = new Set<string>();\n\t\tfor (const rs of resolved.services) {\n\t\t\tfor (const vol of rs.definition.volumes) {\n\t\t\t\tserviceVolumeNames.add(vol.name);\n\t\t\t}\n\t\t}\n\n\t\t// All service volumes must appear in top-level volumes\n\t\tfor (const volName of serviceVolumeNames) {\n\t\t\texpect(parsed.volumes).toHaveProperty(volName);\n\t\t}\n\n\t\t// Gateway uses bind-mount volumes (not in top-level volumes section)\n\t\tconst gwVols = parsed.services[\"openclaw-gateway\"].volumes as string[];\n\t\texpect(gwVols.some((v: string) => v.includes(\".openclaw\"))).toBe(true);\n\t});\n\n\tit(\"includes GPU passthrough when gpu=true and service requires it\", () => {\n\t\tconst resolved = resolve({ services: [\"ollama\"], skillPacks: [] });\n\n\t\t// Modify resolved output to simulate a GPU-required service\n\t\tconst gpuResolved: ResolverOutput = {\n\t\t\t...resolved,\n\t\t\tservices: resolved.services.map((s) => ({\n\t\t\t\t...s,\n\t\t\t\tdefinition: { ...s.definition, gpuRequired: true },\n\t\t\t})),\n\t\t};\n\n\t\tconst yaml = compose(gpuResolved, { ...defaultOptions, gpu: true });\n\t\tconst parsed = parse(yaml);\n\n\t\t// Ollama service should have GPU device reservation\n\t\tconst ollamaService = parsed.services.ollama;\n\t\texpect(ollamaService.deploy).toBeDefined();\n\t\texpect(ollamaService.deploy.resources.reservations.devices).toEqual([\n\t\t\t{\n\t\t\t\tdriver: \"nvidia\",\n\t\t\t\tcount: \"all\",\n\t\t\t\tcapabilities: [\"gpu\"],\n\t\t\t},\n\t\t]);\n\t});\n\n\tit(\"does not include GPU passthrough when gpu=false\", () => {\n\t\tconst resolved = resolve({ services: [\"ollama\"], skillPacks: [] });\n\n\t\t// Even if service requires GPU, gpu option is false\n\t\tconst gpuResolved: ResolverOutput = {\n\t\t\t...resolved,\n\t\t\tservices: resolved.services.map((s) => ({\n\t\t\t\t...s,\n\t\t\t\tdefinition: { ...s.definition, gpuRequired: true },\n\t\t\t})),\n\t\t};\n\n\t\tconst yaml = compose(gpuResolved, { ...defaultOptions, gpu: false });\n\t\tconst parsed = parse(yaml);\n\n\t\t// Should NOT have deploy with GPU devices\n\t\tconst ollamaService = parsed.services.ollama;\n\t\texpect(ollamaService.deploy?.resources?.reservations?.devices).toBeUndefined();\n\t});\n\n\tit(\"does not map internal-only ports to host\", () => {\n\t\tconst resolved = resolve({ services: [\"redis\"], skillPacks: [] });\n\n\t\t// Modify redis to have an internal-only port\n\t\tconst modifiedResolved: ResolverOutput = {\n\t\t\t...resolved,\n\t\t\tservices: resolved.services.map((s) => ({\n\t\t\t\t...s,\n\t\t\t\tdefinition: {\n\t\t\t\t\t...s.definition,\n\t\t\t\t\tports: [\n\t\t\t\t\t\t...s.definition.ports,\n\t\t\t\t\t\t{ host: 16379, container: 16379, description: \"Redis cluster bus\", exposed: false },\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t})),\n\t\t};\n\n\t\tconst yaml = compose(modifiedResolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// Should only have the exposed port, not the internal one\n\t\tconst redisPorts = parsed.services.redis.ports;\n\t\texpect(redisPorts).toHaveLength(1);\n\t\texpect(redisPorts[0]).toContain(\"6379\");\n\t\texpect(redisPorts[0]).not.toContain(\"16379\");\n\t});\n\n\tit(\"uses service_started condition for services without healthcheck\", () => {\n\t\tconst resolved = resolve({ services: [\"minio\"], skillPacks: [] });\n\n\t\tconst yaml = compose(resolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// Minio may or may not have a healthcheck – the gateway depends_on\n\t\t// condition should reflect the definition accurately\n\t\tconst gatewayDeps = parsed.services[\"openclaw-gateway\"].depends_on;\n\t\tfor (const [id, dep] of Object.entries(gatewayDeps)) {\n\t\t\tconst svc = resolved.services.find((s) => s.definition.id === id);\n\t\t\tif (svc?.definition.healthcheck) {\n\t\t\t\texpect((dep as { condition: string }).condition).toBe(\"service_healthy\");\n\t\t\t} else {\n\t\t\t\texpect((dep as { condition: string }).condition).toBe(\"service_started\");\n\t\t\t}\n\t\t}\n\t});\n\n\tit(\"gateway is always the first service in the output\", () => {\n\t\tconst resolved = resolve({ services: [\"redis\", \"ollama\"], skillPacks: [] });\n\t\tconst yaml = compose(resolved, defaultOptions);\n\t\tconst parsed = parse(yaml);\n\n\t\t// First key in services should be openclaw-gateway\n\t\tconst serviceKeys = Object.keys(parsed.services);\n\t\texpect(serviceKeys[0]).toBe(\"openclaw-gateway\");\n\t});\n});\n"],"mappings":"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"}
|
package/dist/composer.test.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as describe, r as it, t as globalExpect } from "./
|
|
2
|
-
import { resolve } from "./resolver.mjs";
|
|
1
|
+
import { n as describe, r as it, t as globalExpect } from "./test.CTcmp4Su-ClCHJ3FA.mjs";
|
|
3
2
|
import { compose } from "./composer.mjs";
|
|
3
|
+
import { resolve } from "./resolver.mjs";
|
|
4
4
|
import { parse } from "yaml";
|
|
5
5
|
//#region src/composer.test.ts
|
|
6
6
|
const defaultOptions = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_test_CTcmp4Su = require("../test.CTcmp4Su-DlzTarwH.cjs");
|
|
2
2
|
const require_deployers_strip_host_ports = require("./strip-host-ports.cjs");
|
|
3
3
|
//#region src/deployers/strip-host-ports.test.ts
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
require_test_CTcmp4Su.describe("stripHostPorts", () => {
|
|
5
|
+
require_test_CTcmp4Su.it("strips host port from short syntax (host:container)", () => {
|
|
6
6
|
const result = require_deployers_strip_host_ports.stripHostPorts(`
|
|
7
7
|
services:
|
|
8
8
|
web:
|
|
@@ -11,11 +11,11 @@ services:
|
|
|
11
11
|
- "8080:80"
|
|
12
12
|
- "443:443"
|
|
13
13
|
`);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
require_test_CTcmp4Su.globalExpect(result).toContain("\"80\"");
|
|
15
|
+
require_test_CTcmp4Su.globalExpect(result).toContain("\"443\"");
|
|
16
|
+
require_test_CTcmp4Su.globalExpect(result).not.toContain("8080");
|
|
17
17
|
});
|
|
18
|
-
|
|
18
|
+
require_test_CTcmp4Su.it("strips host IP and port from extended short syntax", () => {
|
|
19
19
|
const result = require_deployers_strip_host_ports.stripHostPorts(`
|
|
20
20
|
services:
|
|
21
21
|
web:
|
|
@@ -23,11 +23,11 @@ services:
|
|
|
23
23
|
ports:
|
|
24
24
|
- "0.0.0.0:8080:80"
|
|
25
25
|
`);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
require_test_CTcmp4Su.globalExpect(result).toContain("\"80\"");
|
|
27
|
+
require_test_CTcmp4Su.globalExpect(result).not.toContain("8080");
|
|
28
|
+
require_test_CTcmp4Su.globalExpect(result).not.toContain("0.0.0.0");
|
|
29
29
|
});
|
|
30
|
-
|
|
30
|
+
require_test_CTcmp4Su.it("preserves protocol suffix", () => {
|
|
31
31
|
const result = require_deployers_strip_host_ports.stripHostPorts(`
|
|
32
32
|
services:
|
|
33
33
|
web:
|
|
@@ -36,13 +36,13 @@ services:
|
|
|
36
36
|
- "8080:80/tcp"
|
|
37
37
|
- "5353:53/udp"
|
|
38
38
|
`);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
require_test_CTcmp4Su.globalExpect(result).toContain("80/tcp");
|
|
40
|
+
require_test_CTcmp4Su.globalExpect(result).toContain("53/udp");
|
|
41
|
+
require_test_CTcmp4Su.globalExpect(result).not.toContain("8080");
|
|
42
|
+
require_test_CTcmp4Su.globalExpect(result).not.toContain("5353");
|
|
43
43
|
});
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
require_test_CTcmp4Su.it("keeps container-only ports unchanged", () => {
|
|
45
|
+
require_test_CTcmp4Su.globalExpect(require_deployers_strip_host_ports.stripHostPorts(`
|
|
46
46
|
services:
|
|
47
47
|
web:
|
|
48
48
|
image: nginx
|
|
@@ -50,7 +50,7 @@ services:
|
|
|
50
50
|
- "80"
|
|
51
51
|
`)).toContain("\"80\"");
|
|
52
52
|
});
|
|
53
|
-
|
|
53
|
+
require_test_CTcmp4Su.it("handles multiple services", () => {
|
|
54
54
|
const result = require_deployers_strip_host_ports.stripHostPorts(`
|
|
55
55
|
services:
|
|
56
56
|
web:
|
|
@@ -66,17 +66,17 @@ services:
|
|
|
66
66
|
ports:
|
|
67
67
|
- "8888:8080"
|
|
68
68
|
`);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
require_test_CTcmp4Su.globalExpect(result).toContain("\"80\"");
|
|
70
|
+
require_test_CTcmp4Su.globalExpect(result).toContain("\"6379\"");
|
|
71
|
+
require_test_CTcmp4Su.globalExpect(result).toContain("\"8080\"");
|
|
72
|
+
require_test_CTcmp4Su.globalExpect(result).not.toContain("8888");
|
|
73
73
|
});
|
|
74
|
-
|
|
74
|
+
require_test_CTcmp4Su.it("returns original YAML if no services section", () => {
|
|
75
75
|
const yaml = `version: "3"`;
|
|
76
|
-
|
|
76
|
+
require_test_CTcmp4Su.globalExpect(require_deployers_strip_host_ports.stripHostPorts(yaml)).toBe(yaml);
|
|
77
77
|
});
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
require_test_CTcmp4Su.it("handles services with no ports", () => {
|
|
79
|
+
require_test_CTcmp4Su.globalExpect(require_deployers_strip_host_ports.stripHostPorts(`
|
|
80
80
|
services:
|
|
81
81
|
web:
|
|
82
82
|
image: nginx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strip-host-ports.test.cjs","names":["describe","stripHostPorts"],"sources":["../../src/deployers/strip-host-ports.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { stripHostPorts } from \"./strip-host-ports.js\";\n\ndescribe(\"stripHostPorts\", () => {\n\tit(\"strips host port from short syntax (host:container)\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n ports:\n - \"8080:80\"\n - \"443:443\"\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain('\"80\"');\n\t\texpect(result).toContain('\"443\"');\n\t\texpect(result).not.toContain(\"8080\");\n\t});\n\n\tit(\"strips host IP and port from extended short syntax\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n ports:\n - \"0.0.0.0:8080:80\"\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain('\"80\"');\n\t\texpect(result).not.toContain(\"8080\");\n\t\texpect(result).not.toContain(\"0.0.0.0\");\n\t});\n\n\tit(\"preserves protocol suffix\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n ports:\n - \"8080:80/tcp\"\n - \"5353:53/udp\"\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain(\"80/tcp\");\n\t\texpect(result).toContain(\"53/udp\");\n\t\texpect(result).not.toContain(\"8080\");\n\t\texpect(result).not.toContain(\"5353\");\n\t});\n\n\tit(\"keeps container-only ports unchanged\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n ports:\n - \"80\"\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain('\"80\"');\n\t});\n\n\tit(\"handles multiple services\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n ports:\n - \"8080:80\"\n redis:\n image: redis\n ports:\n - \"6379:6379\"\n searxng:\n image: searxng/searxng\n ports:\n - \"8888:8080\"\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain('\"80\"');\n\t\texpect(result).toContain('\"6379\"');\n\t\texpect(result).toContain('\"8080\"');\n\t\texpect(result).not.toContain(\"8888\");\n\t});\n\n\tit(\"returns original YAML if no services section\", () => {\n\t\tconst yaml = `version: \"3\"`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toBe(yaml);\n\t});\n\n\tit(\"handles services with no ports\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain(\"nginx\");\n\t});\n});\n"],"mappings":";;;AAGAA,
|
|
1
|
+
{"version":3,"file":"strip-host-ports.test.cjs","names":["describe","stripHostPorts"],"sources":["../../src/deployers/strip-host-ports.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { stripHostPorts } from \"./strip-host-ports.js\";\n\ndescribe(\"stripHostPorts\", () => {\n\tit(\"strips host port from short syntax (host:container)\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n ports:\n - \"8080:80\"\n - \"443:443\"\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain('\"80\"');\n\t\texpect(result).toContain('\"443\"');\n\t\texpect(result).not.toContain(\"8080\");\n\t});\n\n\tit(\"strips host IP and port from extended short syntax\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n ports:\n - \"0.0.0.0:8080:80\"\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain('\"80\"');\n\t\texpect(result).not.toContain(\"8080\");\n\t\texpect(result).not.toContain(\"0.0.0.0\");\n\t});\n\n\tit(\"preserves protocol suffix\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n ports:\n - \"8080:80/tcp\"\n - \"5353:53/udp\"\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain(\"80/tcp\");\n\t\texpect(result).toContain(\"53/udp\");\n\t\texpect(result).not.toContain(\"8080\");\n\t\texpect(result).not.toContain(\"5353\");\n\t});\n\n\tit(\"keeps container-only ports unchanged\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n ports:\n - \"80\"\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain('\"80\"');\n\t});\n\n\tit(\"handles multiple services\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n ports:\n - \"8080:80\"\n redis:\n image: redis\n ports:\n - \"6379:6379\"\n searxng:\n image: searxng/searxng\n ports:\n - \"8888:8080\"\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain('\"80\"');\n\t\texpect(result).toContain('\"6379\"');\n\t\texpect(result).toContain('\"8080\"');\n\t\texpect(result).not.toContain(\"8888\");\n\t});\n\n\tit(\"returns original YAML if no services section\", () => {\n\t\tconst yaml = `version: \"3\"`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toBe(yaml);\n\t});\n\n\tit(\"handles services with no ports\", () => {\n\t\tconst yaml = `\nservices:\n web:\n image: nginx\n`;\n\t\tconst result = stripHostPorts(yaml);\n\t\texpect(result).toContain(\"nginx\");\n\t});\n});\n"],"mappings":";;;AAGAA,sBAAAA,SAAS,wBAAwB;AAChC,uBAAA,GAAG,6DAA6D;EAS/D,MAAM,SAASC,mCAAAA,eARF;;;;;;;EAQsB;AACnC,wBAAA,aAAO,OAAO,CAAC,UAAU,SAAO;AAChC,wBAAA,aAAO,OAAO,CAAC,UAAU,UAAQ;AACjC,wBAAA,aAAO,OAAO,CAAC,IAAI,UAAU,OAAO;GACnC;AAEF,uBAAA,GAAG,4DAA4D;EAQ9D,MAAM,SAASA,mCAAAA,eAPF;;;;;;EAOsB;AACnC,wBAAA,aAAO,OAAO,CAAC,UAAU,SAAO;AAChC,wBAAA,aAAO,OAAO,CAAC,IAAI,UAAU,OAAO;AACpC,wBAAA,aAAO,OAAO,CAAC,IAAI,UAAU,UAAU;GACtC;AAEF,uBAAA,GAAG,mCAAmC;EASrC,MAAM,SAASA,mCAAAA,eARF;;;;;;;EAQsB;AACnC,wBAAA,aAAO,OAAO,CAAC,UAAU,SAAS;AAClC,wBAAA,aAAO,OAAO,CAAC,UAAU,SAAS;AAClC,wBAAA,aAAO,OAAO,CAAC,IAAI,UAAU,OAAO;AACpC,wBAAA,aAAO,OAAO,CAAC,IAAI,UAAU,OAAO;GACnC;AAEF,uBAAA,GAAG,8CAA8C;AAShD,wBAAA,aADeA,mCAAAA,eAPF;;;;;;EAOsB,CACrB,CAAC,UAAU,SAAO;GAC/B;AAEF,uBAAA,GAAG,mCAAmC;EAgBrC,MAAM,SAASA,mCAAAA,eAfF;;;;;;;;;;;;;;EAesB;AACnC,wBAAA,aAAO,OAAO,CAAC,UAAU,SAAO;AAChC,wBAAA,aAAO,OAAO,CAAC,UAAU,WAAS;AAClC,wBAAA,aAAO,OAAO,CAAC,UAAU,WAAS;AAClC,wBAAA,aAAO,OAAO,CAAC,IAAI,UAAU,OAAO;GACnC;AAEF,uBAAA,GAAG,sDAAsD;EACxD,MAAM,OAAO;AAEb,wBAAA,aADeA,mCAAAA,eAAe,KAAK,CACrB,CAAC,KAAK,KAAK;GACxB;AAEF,uBAAA,GAAG,wCAAwC;AAO1C,wBAAA,aADeA,mCAAAA,eALF;;;;EAKsB,CACrB,CAAC,UAAU,QAAQ;GAChC;EACD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as describe, r as it, t as globalExpect } from "../
|
|
1
|
+
import { n as describe, r as it, t as globalExpect } from "../test.CTcmp4Su-ClCHJ3FA.mjs";
|
|
2
2
|
import { stripHostPorts } from "./strip-host-ports.mjs";
|
|
3
3
|
//#region src/deployers/strip-host-ports.test.ts
|
|
4
4
|
describe("stripHostPorts", () => {
|
package/dist/generate.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_skills = require("./skills-BlzpHmpH.cjs");
|
|
3
|
-
const require_bare_metal_partition = require("./bare-metal-partition.cjs");
|
|
4
|
-
const require_resolver = require("./resolver.cjs");
|
|
5
3
|
const require_generators_postgres_init = require("./generators/postgres-init.cjs");
|
|
6
4
|
const require_composer = require("./composer.cjs");
|
|
5
|
+
const require_resolver = require("./resolver.cjs");
|
|
6
|
+
const require_bare_metal_partition = require("./bare-metal-partition.cjs");
|
|
7
7
|
const require_errors = require("./errors.cjs");
|
|
8
8
|
const require_generators_bare_metal_install = require("./generators/bare-metal-install.cjs");
|
|
9
9
|
const require_generators_caddy = require("./generators/caddy.cjs");
|
package/dist/generate.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { partitionBareMetal, platformToNativePlatform, resolvedWithOnlyServices } from "./bare-metal-partition.mjs";
|
|
2
|
-
import { resolve } from "./resolver.mjs";
|
|
3
1
|
import { generatePostgresInit } from "./generators/postgres-init.mjs";
|
|
4
2
|
import { composeMultiFile } from "./composer.mjs";
|
|
3
|
+
import { generateSkillFiles } from "./generators/skills.mjs";
|
|
4
|
+
import { resolve } from "./resolver.mjs";
|
|
5
|
+
import { partitionBareMetal, platformToNativePlatform, resolvedWithOnlyServices } from "./bare-metal-partition.mjs";
|
|
5
6
|
import { StackConfigError, ValidationError } from "./errors.mjs";
|
|
6
7
|
import { generateBareMetalInstall } from "./generators/bare-metal-install.mjs";
|
|
7
8
|
import { generateCaddyfile } from "./generators/caddy.mjs";
|
|
@@ -18,7 +19,6 @@ import { generateOpenClawConfig } from "./generators/openclaw-json.mjs";
|
|
|
18
19
|
import { generatePrometheusConfig } from "./generators/prometheus.mjs";
|
|
19
20
|
import { generateReadme } from "./generators/readme.mjs";
|
|
20
21
|
import { generateScripts } from "./generators/scripts.mjs";
|
|
21
|
-
import { generateSkillFiles } from "./generators/skills.mjs";
|
|
22
22
|
import { generateStackManifest } from "./generators/stack-manifest.mjs";
|
|
23
23
|
import { generateTraefikConfig } from "./generators/traefik.mjs";
|
|
24
24
|
import { migrateConfig } from "./migrations.mjs";
|