@better-openclaw/core 1.0.26 → 1.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -7
- package/dist/addon-stack.cjs +41 -28
- package/dist/addon-stack.cjs.map +1 -1
- package/dist/addon-stack.d.cts +1 -1
- package/dist/addon-stack.d.cts.map +1 -1
- package/dist/addon-stack.d.mts +1 -1
- package/dist/addon-stack.d.mts.map +1 -1
- package/dist/addon-stack.mjs +40 -27
- package/dist/addon-stack.mjs.map +1 -1
- package/dist/addon-stack.test.cjs +14 -14
- package/dist/addon-stack.test.cjs.map +1 -1
- package/dist/addon-stack.test.mjs +13 -13
- package/dist/addon-stack.test.mjs.map +1 -1
- package/dist/bare-metal-partition.d.cts +1 -1
- package/dist/bare-metal-partition.d.mts +1 -1
- package/dist/bare-metal-partition.test.cjs +1 -1
- package/dist/bare-metal-partition.test.mjs +1 -1
- package/dist/callback-sink-CdMKYayY.d.mts +15 -0
- package/dist/callback-sink-CdMKYayY.d.mts.map +1 -0
- package/dist/callback-sink-iNThWRnQ.d.cts +15 -0
- package/dist/callback-sink-iNThWRnQ.d.cts.map +1 -0
- package/dist/claude-code-fw-CrbAlzTM.d.mts +7 -0
- package/dist/claude-code-fw-CrbAlzTM.d.mts.map +1 -0
- package/dist/claude-code-fw-dKYH2prH.d.cts +7 -0
- package/dist/claude-code-fw-dKYH2prH.d.cts.map +1 -0
- package/dist/codex-fw-Ct8q4pzo.d.mts +7 -0
- package/dist/codex-fw-Ct8q4pzo.d.mts.map +1 -0
- package/dist/codex-fw-l7Ku8ZI8.d.cts +7 -0
- package/dist/codex-fw-l7Ku8ZI8.d.cts.map +1 -0
- package/dist/compose-validation.test.cjs +1 -1
- package/dist/composer.cjs +89 -129
- package/dist/composer.cjs.map +1 -1
- package/dist/composer.d.cts +3 -3
- package/dist/composer.d.cts.map +1 -1
- package/dist/composer.d.mts +3 -3
- package/dist/composer.d.mts.map +1 -1
- package/dist/composer.mjs +88 -128
- package/dist/composer.mjs.map +1 -1
- package/dist/composer.snapshot.test.cjs +1 -1
- package/dist/composer.snapshot.test.mjs +1 -1
- package/dist/composer.test.cjs +53 -2
- package/dist/composer.test.cjs.map +1 -1
- package/dist/composer.test.mjs +52 -1
- package/dist/composer.test.mjs.map +1 -1
- package/dist/console-sink-BHQpRyCt.d.cts +13 -0
- package/dist/console-sink-BHQpRyCt.d.cts.map +1 -0
- package/dist/console-sink-bouG_M1K.d.mts +13 -0
- package/dist/console-sink-bouG_M1K.d.mts.map +1 -0
- package/dist/{coolify-BVGGcMrT.d.mts → coolify-D5E0z01M.d.mts} +2 -2
- package/dist/{coolify-BVGGcMrT.d.mts.map → coolify-D5E0z01M.d.mts.map} +1 -1
- package/dist/{coolify-vlb1G9V2.d.cts → coolify-DjHecHCr.d.cts} +2 -2
- package/dist/{coolify-vlb1G9V2.d.cts.map → coolify-DjHecHCr.d.cts.map} +1 -1
- package/dist/copaw-BQyjioz7.d.cts +7 -0
- package/dist/copaw-BQyjioz7.d.cts.map +1 -0
- package/dist/copaw-C_3N7eQu.d.mts +7 -0
- package/dist/copaw-C_3N7eQu.d.mts.map +1 -0
- package/dist/deployers/coolify.cjs +25 -0
- package/dist/deployers/coolify.cjs.map +1 -1
- package/dist/deployers/coolify.d.cts +1 -1
- package/dist/deployers/coolify.d.mts +1 -1
- package/dist/deployers/coolify.mjs +25 -0
- package/dist/deployers/coolify.mjs.map +1 -1
- package/dist/deployers/dokploy.cjs +25 -0
- package/dist/deployers/dokploy.cjs.map +1 -1
- package/dist/deployers/dokploy.d.cts +1 -1
- package/dist/deployers/dokploy.d.mts +1 -1
- package/dist/deployers/dokploy.mjs +25 -0
- package/dist/deployers/dokploy.mjs.map +1 -1
- package/dist/deployers/index.d.cts +3 -3
- package/dist/deployers/index.d.mts +3 -3
- package/dist/deployers/strip-host-ports.cjs +1 -1
- package/dist/deployers/strip-host-ports.test.cjs +1 -1
- package/dist/deployers/strip-host-ports.test.mjs +1 -1
- package/dist/deployers/types.d.cts +1 -1
- package/dist/deployers/types.d.mts +1 -1
- package/dist/{dokploy-8cbrxUun.d.cts → dokploy-BnVDjWCV.d.cts} +2 -2
- package/dist/{dokploy-8cbrxUun.d.cts.map → dokploy-BnVDjWCV.d.cts.map} +1 -1
- package/dist/{dokploy-BTflLhTM.d.mts → dokploy-BqLdCfyR.d.mts} +2 -2
- package/dist/{dokploy-BTflLhTM.d.mts.map → dokploy-BqLdCfyR.d.mts.map} +1 -1
- package/dist/frameworks/claude-code-fw.cjs +79 -0
- package/dist/frameworks/claude-code-fw.cjs.map +1 -0
- package/dist/frameworks/claude-code-fw.d.cts +2 -0
- package/dist/frameworks/claude-code-fw.d.mts +2 -0
- package/dist/frameworks/claude-code-fw.mjs +78 -0
- package/dist/frameworks/claude-code-fw.mjs.map +1 -0
- package/dist/frameworks/codex-fw.cjs +67 -0
- package/dist/frameworks/codex-fw.cjs.map +1 -0
- package/dist/frameworks/codex-fw.d.cts +2 -0
- package/dist/frameworks/codex-fw.d.mts +2 -0
- package/dist/frameworks/codex-fw.mjs +66 -0
- package/dist/frameworks/codex-fw.mjs.map +1 -0
- package/dist/frameworks/copaw.cjs +118 -0
- package/dist/frameworks/copaw.cjs.map +1 -0
- package/dist/frameworks/copaw.d.cts +2 -0
- package/dist/frameworks/copaw.d.mts +2 -0
- package/dist/frameworks/copaw.mjs +117 -0
- package/dist/frameworks/copaw.mjs.map +1 -0
- package/dist/frameworks/index.cjs +35 -0
- package/dist/frameworks/index.cjs.map +1 -0
- package/dist/frameworks/index.d.cts +11 -0
- package/dist/frameworks/index.d.mts +11 -0
- package/dist/frameworks/index.mjs +22 -0
- package/dist/frameworks/index.mjs.map +1 -0
- package/dist/frameworks/memu.cjs +118 -0
- package/dist/frameworks/memu.cjs.map +1 -0
- package/dist/frameworks/memu.d.cts +2 -0
- package/dist/frameworks/memu.d.mts +2 -0
- package/dist/frameworks/memu.mjs +117 -0
- package/dist/frameworks/memu.mjs.map +1 -0
- package/dist/frameworks/nanobot.cjs +126 -0
- package/dist/frameworks/nanobot.cjs.map +1 -0
- package/dist/frameworks/nanobot.d.cts +2 -0
- package/dist/frameworks/nanobot.d.mts +2 -0
- package/dist/frameworks/nanobot.mjs +125 -0
- package/dist/frameworks/nanobot.mjs.map +1 -0
- package/dist/frameworks/nanoclaw.cjs +99 -0
- package/dist/frameworks/nanoclaw.cjs.map +1 -0
- package/dist/frameworks/nanoclaw.d.cts +2 -0
- package/dist/frameworks/nanoclaw.d.mts +2 -0
- package/dist/frameworks/nanoclaw.mjs +98 -0
- package/dist/frameworks/nanoclaw.mjs.map +1 -0
- package/dist/frameworks/openclaw.cjs +253 -0
- package/dist/frameworks/openclaw.cjs.map +1 -0
- package/dist/frameworks/openclaw.d.cts +2 -0
- package/dist/frameworks/openclaw.d.mts +2 -0
- package/dist/frameworks/openclaw.mjs +251 -0
- package/dist/frameworks/openclaw.mjs.map +1 -0
- package/dist/frameworks/registry.cjs +31 -0
- package/dist/frameworks/registry.cjs.map +1 -0
- package/dist/frameworks/registry.d.cts +2 -0
- package/dist/frameworks/registry.d.mts +2 -0
- package/dist/frameworks/registry.mjs +26 -0
- package/dist/frameworks/registry.mjs.map +1 -0
- package/dist/frameworks/registry.test.cjs +110 -0
- package/dist/frameworks/registry.test.cjs.map +1 -0
- package/dist/frameworks/registry.test.d.cts +1 -0
- package/dist/frameworks/registry.test.d.mts +1 -0
- package/dist/frameworks/registry.test.mjs +111 -0
- package/dist/frameworks/registry.test.mjs.map +1 -0
- package/dist/frameworks/types.cjs +0 -0
- package/dist/frameworks/types.d.cts +2 -0
- package/dist/frameworks/types.d.mts +2 -0
- package/dist/frameworks/types.mjs +1 -0
- package/dist/frameworks/zeroclaw.cjs +123 -0
- package/dist/frameworks/zeroclaw.cjs.map +1 -0
- package/dist/frameworks/zeroclaw.d.cts +2 -0
- package/dist/frameworks/zeroclaw.d.mts +2 -0
- package/dist/frameworks/zeroclaw.mjs +122 -0
- package/dist/frameworks/zeroclaw.mjs.map +1 -0
- package/dist/generate.cjs +64 -13
- package/dist/generate.cjs.map +1 -1
- package/dist/generate.d.cts +5 -2
- package/dist/generate.d.cts.map +1 -1
- package/dist/generate.d.mts +5 -2
- package/dist/generate.d.mts.map +1 -1
- package/dist/generate.mjs +63 -12
- package/dist/generate.mjs.map +1 -1
- package/dist/generate.test.cjs +42 -6
- package/dist/generate.test.cjs.map +1 -1
- package/dist/generate.test.mjs +41 -5
- package/dist/generate.test.mjs.map +1 -1
- package/dist/generators/bare-metal-install.d.cts +1 -1
- package/dist/generators/bare-metal-install.d.mts +1 -1
- package/dist/generators/bare-metal-install.test.cjs +1 -1
- package/dist/generators/bare-metal-install.test.mjs +1 -1
- package/dist/generators/caddy.cjs +9 -3
- package/dist/generators/caddy.cjs.map +1 -1
- package/dist/generators/caddy.d.cts +1 -1
- package/dist/generators/caddy.d.mts +1 -1
- package/dist/generators/caddy.mjs +9 -3
- package/dist/generators/caddy.mjs.map +1 -1
- package/dist/generators/caddy.test.cjs +30 -2
- package/dist/generators/caddy.test.cjs.map +1 -1
- package/dist/generators/caddy.test.mjs +30 -2
- package/dist/generators/caddy.test.mjs.map +1 -1
- package/dist/generators/clone-repos.cjs.map +1 -1
- package/dist/generators/clone-repos.d.cts +1 -1
- package/dist/generators/clone-repos.d.mts +1 -1
- package/dist/generators/clone-repos.mjs.map +1 -1
- package/dist/generators/clone-repos.test.cjs +1 -1
- package/dist/generators/clone-repos.test.cjs.map +1 -1
- package/dist/generators/clone-repos.test.mjs +1 -1
- package/dist/generators/clone-repos.test.mjs.map +1 -1
- package/dist/generators/env.cjs +28 -103
- package/dist/generators/env.cjs.map +1 -1
- package/dist/generators/env.d.cts +4 -1
- package/dist/generators/env.d.cts.map +1 -1
- package/dist/generators/env.d.mts +4 -1
- package/dist/generators/env.d.mts.map +1 -1
- package/dist/generators/env.mjs +27 -102
- package/dist/generators/env.mjs.map +1 -1
- package/dist/generators/env.test.cjs +3 -3
- package/dist/generators/env.test.cjs.map +1 -1
- package/dist/generators/env.test.mjs +3 -3
- package/dist/generators/env.test.mjs.map +1 -1
- package/dist/generators/get-shit-done.d.cts +1 -1
- package/dist/generators/get-shit-done.d.mts +1 -1
- package/dist/generators/health-check.cjs +6 -6
- package/dist/generators/health-check.cjs.map +1 -1
- package/dist/generators/health-check.d.cts +1 -1
- package/dist/generators/health-check.d.mts +1 -1
- package/dist/generators/health-check.mjs +6 -6
- package/dist/generators/health-check.mjs.map +1 -1
- package/dist/generators/health-check.test.cjs +1 -1
- package/dist/generators/health-check.test.mjs +1 -1
- package/dist/generators/n8n-workflows.d.cts +1 -1
- package/dist/generators/n8n-workflows.d.mts +1 -1
- package/dist/generators/native-services.d.cts +1 -1
- package/dist/generators/native-services.d.mts +1 -1
- package/dist/generators/openclaw-json.cjs +60 -4
- package/dist/generators/openclaw-json.cjs.map +1 -1
- package/dist/generators/openclaw-json.d.cts +1 -5
- package/dist/generators/openclaw-json.d.cts.map +1 -1
- package/dist/generators/openclaw-json.d.mts +1 -5
- package/dist/generators/openclaw-json.d.mts.map +1 -1
- package/dist/generators/openclaw-json.mjs +60 -4
- package/dist/generators/openclaw-json.mjs.map +1 -1
- package/dist/generators/postgres-init.cjs +20 -0
- package/dist/generators/postgres-init.cjs.map +1 -1
- package/dist/generators/postgres-init.d.cts +1 -1
- package/dist/generators/postgres-init.d.cts.map +1 -1
- package/dist/generators/postgres-init.d.mts +1 -1
- package/dist/generators/postgres-init.d.mts.map +1 -1
- package/dist/generators/postgres-init.mjs +20 -0
- package/dist/generators/postgres-init.mjs.map +1 -1
- package/dist/generators/prometheus.d.cts +1 -1
- package/dist/generators/prometheus.d.mts +1 -1
- package/dist/generators/readme.cjs +50 -7
- package/dist/generators/readme.cjs.map +1 -1
- package/dist/generators/readme.d.cts +3 -1
- package/dist/generators/readme.d.cts.map +1 -1
- package/dist/generators/readme.d.mts +3 -1
- package/dist/generators/readme.d.mts.map +1 -1
- package/dist/generators/readme.mjs +50 -7
- package/dist/generators/readme.mjs.map +1 -1
- package/dist/generators/scripts.test.cjs +2 -2
- package/dist/generators/scripts.test.cjs.map +1 -1
- package/dist/generators/scripts.test.mjs +2 -2
- package/dist/generators/scripts.test.mjs.map +1 -1
- package/dist/generators/skills.cjs +1 -1
- package/dist/generators/skills.d.cts +1 -1
- package/dist/generators/skills.d.cts.map +1 -1
- package/dist/generators/skills.d.mts +1 -1
- package/dist/generators/skills.d.mts.map +1 -1
- package/dist/generators/skills.mjs +310 -0
- package/dist/generators/skills.mjs.map +1 -1
- package/dist/generators/stack-manifest.d.cts +1 -1
- package/dist/generators/stack-manifest.d.mts +1 -1
- package/dist/generators/traefik.d.cts +1 -1
- package/dist/generators/traefik.d.mts +1 -1
- package/dist/generators/traefik.test.cjs +12 -12
- package/dist/generators/traefik.test.cjs.map +1 -1
- package/dist/generators/traefik.test.mjs +12 -12
- package/dist/generators/traefik.test.mjs.map +1 -1
- package/dist/index.cjs +20 -2
- package/dist/index.d.cts +14 -7
- package/dist/index.d.mts +14 -7
- package/dist/index.mjs +10 -3
- package/dist/logger/__tests__/logger.test.cjs +308 -0
- package/dist/logger/__tests__/logger.test.cjs.map +1 -0
- package/dist/logger/__tests__/logger.test.d.cts +1 -0
- package/dist/logger/__tests__/logger.test.d.mts +1 -0
- package/dist/logger/__tests__/logger.test.mjs +308 -0
- package/dist/logger/__tests__/logger.test.mjs.map +1 -0
- package/dist/logger/index.cjs +10 -0
- package/dist/logger/index.d.cts +6 -0
- package/dist/logger/index.d.mts +6 -0
- package/dist/logger/index.mjs +5 -0
- package/dist/logger/logger.cjs +270 -0
- package/dist/logger/logger.cjs.map +1 -0
- package/dist/logger/logger.d.cts +2 -0
- package/dist/logger/logger.d.mts +2 -0
- package/dist/logger/logger.mjs +267 -0
- package/dist/logger/logger.mjs.map +1 -0
- package/dist/logger/sinks/callback-sink.cjs +18 -0
- package/dist/logger/sinks/callback-sink.cjs.map +1 -0
- package/dist/logger/sinks/callback-sink.d.cts +2 -0
- package/dist/logger/sinks/callback-sink.d.mts +2 -0
- package/dist/logger/sinks/callback-sink.mjs +17 -0
- package/dist/logger/sinks/callback-sink.mjs.map +1 -0
- package/dist/logger/sinks/console-sink.cjs +39 -0
- package/dist/logger/sinks/console-sink.cjs.map +1 -0
- package/dist/logger/sinks/console-sink.d.cts +2 -0
- package/dist/logger/sinks/console-sink.d.mts +2 -0
- package/dist/logger/sinks/console-sink.mjs +38 -0
- package/dist/logger/sinks/console-sink.mjs.map +1 -0
- package/dist/logger/sinks/file-sink.cjs +72 -0
- package/dist/logger/sinks/file-sink.cjs.map +1 -0
- package/dist/logger/sinks/file-sink.d.cts +36 -0
- package/dist/logger/sinks/file-sink.d.cts.map +1 -0
- package/dist/logger/sinks/file-sink.d.mts +36 -0
- package/dist/logger/sinks/file-sink.d.mts.map +1 -0
- package/dist/logger/sinks/file-sink.mjs +70 -0
- package/dist/logger/sinks/file-sink.mjs.map +1 -0
- package/dist/logger/types.cjs +0 -0
- package/dist/logger/types.d.cts +2 -0
- package/dist/logger/types.d.mts +2 -0
- package/dist/logger/types.mjs +1 -0
- package/dist/logger-CZ0Qnyiu.d.mts +66 -0
- package/dist/logger-CZ0Qnyiu.d.mts.map +1 -0
- package/dist/logger-DDfwai-A.d.cts +66 -0
- package/dist/logger-DDfwai-A.d.cts.map +1 -0
- package/dist/memu-BfRXtwmL.d.cts +7 -0
- package/dist/memu-BfRXtwmL.d.cts.map +1 -0
- package/dist/memu-C-fXbWr-.d.mts +7 -0
- package/dist/memu-C-fXbWr-.d.mts.map +1 -0
- package/dist/migrations.cjs +17 -9
- package/dist/migrations.cjs.map +1 -1
- package/dist/migrations.d.cts +1 -1
- package/dist/migrations.d.cts.map +1 -1
- package/dist/migrations.d.mts +1 -1
- package/dist/migrations.d.mts.map +1 -1
- package/dist/migrations.mjs +17 -9
- package/dist/migrations.mjs.map +1 -1
- package/dist/migrations.test.cjs +4 -4
- package/dist/migrations.test.mjs +4 -4
- package/dist/nanobot-2c0bS4lu.d.cts +7 -0
- package/dist/nanobot-2c0bS4lu.d.cts.map +1 -0
- package/dist/nanobot-BUO6pVQc.d.mts +7 -0
- package/dist/nanobot-BUO6pVQc.d.mts.map +1 -0
- package/dist/nanoclaw-CgHubEig.d.mts +7 -0
- package/dist/nanoclaw-CgHubEig.d.mts.map +1 -0
- package/dist/nanoclaw-Dvz0ef6J.d.cts +7 -0
- package/dist/nanoclaw-Dvz0ef6J.d.cts.map +1 -0
- package/dist/openclaw-D8lVecjC.d.mts +7 -0
- package/dist/openclaw-D8lVecjC.d.mts.map +1 -0
- package/dist/openclaw-DThj8i3N.d.cts +7 -0
- package/dist/openclaw-DThj8i3N.d.cts.map +1 -0
- package/dist/port-scanner.d.cts +1 -1
- package/dist/port-scanner.d.mts +1 -1
- package/dist/presets/presets.test.cjs +5 -5
- package/dist/presets/presets.test.cjs.map +1 -1
- package/dist/presets/presets.test.mjs +4 -4
- package/dist/presets/presets.test.mjs.map +1 -1
- package/dist/presets/registry.d.cts +1 -1
- package/dist/presets/registry.d.mts +1 -1
- package/dist/presets/registry.test.cjs +7 -7
- package/dist/presets/registry.test.cjs.map +1 -1
- package/dist/presets/registry.test.mjs +7 -7
- package/dist/presets/registry.test.mjs.map +1 -1
- package/dist/registry-DHX7vnVo.d.cts +16 -0
- package/dist/registry-DHX7vnVo.d.cts.map +1 -0
- package/dist/registry-DTAILnga.d.mts +16 -0
- package/dist/registry-DTAILnga.d.mts.map +1 -0
- package/dist/resolver.cjs +13 -3
- package/dist/resolver.cjs.map +1 -1
- package/dist/resolver.d.cts +1 -1
- package/dist/resolver.d.cts.map +1 -1
- package/dist/resolver.d.mts +1 -1
- package/dist/resolver.d.mts.map +1 -1
- package/dist/resolver.mjs +13 -3
- package/dist/resolver.mjs.map +1 -1
- package/dist/resolver.test.cjs +35 -3
- package/dist/resolver.test.cjs.map +1 -1
- package/dist/resolver.test.mjs +35 -3
- package/dist/resolver.test.mjs.map +1 -1
- package/dist/{schema-BQnZrcw8.d.cts → schema-BSl9wiFe.d.mts} +134 -2
- package/dist/schema-BSl9wiFe.d.mts.map +1 -0
- package/dist/{schema-SBpL0bdI.d.mts → schema-Bd9l2r7p.d.cts} +134 -2
- package/dist/schema-Bd9l2r7p.d.cts.map +1 -0
- package/dist/schema.cjs +28 -4
- 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 +27 -4
- package/dist/schema.mjs.map +1 -1
- package/dist/schema.test.cjs +3 -3
- package/dist/schema.test.cjs.map +1 -1
- package/dist/schema.test.mjs +3 -3
- package/dist/schema.test.mjs.map +1 -1
- package/dist/services/definitions/adguard-home.d.cts +1 -1
- package/dist/services/definitions/adguard-home.d.mts +1 -1
- package/dist/services/definitions/agent-browser.cjs +161 -0
- package/dist/services/definitions/agent-browser.cjs.map +1 -0
- package/dist/services/definitions/agent-browser.d.cts +7 -0
- package/dist/services/definitions/agent-browser.d.cts.map +1 -0
- package/dist/services/definitions/agent-browser.d.mts +7 -0
- package/dist/services/definitions/agent-browser.d.mts.map +1 -0
- package/dist/services/definitions/agent-browser.mjs +160 -0
- package/dist/services/definitions/agent-browser.mjs.map +1 -0
- package/dist/services/definitions/airbyte.d.cts +1 -1
- package/dist/services/definitions/airbyte.d.mts +1 -1
- package/dist/services/definitions/airflow.d.cts +1 -1
- package/dist/services/definitions/airflow.d.mts +1 -1
- package/dist/services/definitions/anything-llm.d.cts +1 -1
- package/dist/services/definitions/anything-llm.d.mts +1 -1
- package/dist/services/definitions/appflowy.d.cts +1 -1
- package/dist/services/definitions/appflowy.d.mts +1 -1
- package/dist/services/definitions/apptension-saas.cjs.map +1 -1
- package/dist/services/definitions/apptension-saas.d.cts +1 -1
- package/dist/services/definitions/apptension-saas.d.mts +1 -1
- package/dist/services/definitions/apptension-saas.mjs.map +1 -1
- package/dist/services/definitions/appwrite.d.cts +1 -1
- package/dist/services/definitions/appwrite.d.mts +1 -1
- package/dist/services/definitions/audiobookshelf.d.cts +1 -1
- package/dist/services/definitions/audiobookshelf.d.mts +1 -1
- package/dist/services/definitions/authelia.d.cts +1 -1
- package/dist/services/definitions/authelia.d.mts +1 -1
- package/dist/services/definitions/authentik.d.cts +1 -1
- package/dist/services/definitions/authentik.d.mts +1 -1
- package/dist/services/definitions/axolotl.d.cts +1 -1
- package/dist/services/definitions/axolotl.d.mts +1 -1
- package/dist/services/definitions/baserow.d.cts +1 -1
- package/dist/services/definitions/baserow.d.mts +1 -1
- package/dist/services/definitions/beszel.d.cts +1 -1
- package/dist/services/definitions/beszel.d.mts +1 -1
- package/dist/services/definitions/boxyhq-saas.d.cts +1 -1
- package/dist/services/definitions/boxyhq-saas.d.mts +1 -1
- package/dist/services/definitions/browserless.d.cts +1 -1
- package/dist/services/definitions/browserless.d.mts +1 -1
- package/dist/services/definitions/burnlink.d.cts +1 -1
- package/dist/services/definitions/burnlink.d.mts +1 -1
- package/dist/services/definitions/caddy.d.cts +1 -1
- package/dist/services/definitions/caddy.d.mts +1 -1
- package/dist/services/definitions/cal-com.d.cts +1 -1
- package/dist/services/definitions/cal-com.d.mts +1 -1
- package/dist/services/definitions/calibre-web.d.cts +1 -1
- package/dist/services/definitions/calibre-web.d.mts +1 -1
- package/dist/services/definitions/chatwoot-worker.d.cts +1 -1
- package/dist/services/definitions/chatwoot-worker.d.mts +1 -1
- package/dist/services/definitions/chatwoot.d.cts +1 -1
- package/dist/services/definitions/chatwoot.d.mts +1 -1
- package/dist/services/definitions/chromadb.cjs +4 -1
- package/dist/services/definitions/chromadb.cjs.map +1 -1
- package/dist/services/definitions/chromadb.d.cts +1 -1
- package/dist/services/definitions/chromadb.d.mts +1 -1
- package/dist/services/definitions/chromadb.mjs +4 -1
- package/dist/services/definitions/chromadb.mjs.map +1 -1
- package/dist/services/definitions/claude-code.cjs +1 -1
- package/dist/services/definitions/claude-code.cjs.map +1 -1
- package/dist/services/definitions/claude-code.d.cts +1 -1
- package/dist/services/definitions/claude-code.d.mts +1 -1
- package/dist/services/definitions/claude-code.mjs +1 -1
- package/dist/services/definitions/claude-code.mjs.map +1 -1
- package/dist/services/definitions/clawrouter.cjs +138 -0
- package/dist/services/definitions/clawrouter.cjs.map +1 -0
- package/dist/services/definitions/clawrouter.d.cts +7 -0
- package/dist/services/definitions/clawrouter.d.cts.map +1 -0
- package/dist/services/definitions/clawrouter.d.mts +7 -0
- package/dist/services/definitions/clawrouter.d.mts.map +1 -0
- package/dist/services/definitions/clawrouter.mjs +137 -0
- package/dist/services/definitions/clawrouter.mjs.map +1 -0
- package/dist/services/definitions/clickhouse.d.cts +1 -1
- package/dist/services/definitions/clickhouse.d.mts +1 -1
- package/dist/services/definitions/cloudflared.d.cts +1 -1
- package/dist/services/definitions/cloudflared.d.mts +1 -1
- package/dist/services/definitions/cmsaas-starter.d.cts +1 -1
- package/dist/services/definitions/cmsaas-starter.d.mts +1 -1
- package/dist/services/definitions/cockroachdb.d.cts +1 -1
- package/dist/services/definitions/cockroachdb.d.mts +1 -1
- package/dist/services/definitions/code-server.d.cts +1 -1
- package/dist/services/definitions/code-server.d.mts +1 -1
- package/dist/services/definitions/coder.d.cts +1 -1
- package/dist/services/definitions/coder.d.mts +1 -1
- package/dist/services/definitions/codex.cjs +1 -1
- package/dist/services/definitions/codex.cjs.map +1 -1
- package/dist/services/definitions/codex.d.cts +1 -1
- package/dist/services/definitions/codex.d.mts +1 -1
- package/dist/services/definitions/codex.mjs +1 -1
- package/dist/services/definitions/codex.mjs.map +1 -1
- package/dist/services/definitions/comfyui.d.cts +1 -1
- package/dist/services/definitions/comfyui.d.mts +1 -1
- package/dist/services/definitions/convex-dashboard.d.cts +1 -1
- package/dist/services/definitions/convex-dashboard.d.mts +1 -1
- package/dist/services/definitions/convex.cjs.map +1 -1
- package/dist/services/definitions/convex.d.cts +1 -1
- package/dist/services/definitions/convex.d.mts +1 -1
- package/dist/services/definitions/convex.mjs.map +1 -1
- package/dist/services/definitions/coolify.d.cts +1 -1
- package/dist/services/definitions/coolify.d.mts +1 -1
- package/dist/services/definitions/copaw.cjs +97 -0
- package/dist/services/definitions/copaw.cjs.map +1 -0
- package/dist/services/definitions/copaw.d.cts +7 -0
- package/dist/services/definitions/copaw.d.cts.map +1 -0
- package/dist/services/definitions/copaw.d.mts +7 -0
- package/dist/services/definitions/copaw.d.mts.map +1 -0
- package/dist/services/definitions/copaw.mjs +96 -0
- package/dist/services/definitions/copaw.mjs.map +1 -0
- package/dist/services/definitions/crowdsec.d.cts +1 -1
- package/dist/services/definitions/crowdsec.d.mts +1 -1
- package/dist/services/definitions/dagster.d.cts +1 -1
- package/dist/services/definitions/dagster.d.mts +1 -1
- package/dist/services/definitions/desktop-environment.cjs +2 -1
- package/dist/services/definitions/desktop-environment.cjs.map +1 -1
- package/dist/services/definitions/desktop-environment.d.cts +1 -1
- package/dist/services/definitions/desktop-environment.d.mts +1 -1
- package/dist/services/definitions/desktop-environment.mjs +2 -1
- package/dist/services/definitions/desktop-environment.mjs.map +1 -1
- package/dist/services/definitions/dify.d.cts +1 -1
- package/dist/services/definitions/dify.d.mts +1 -1
- package/dist/services/definitions/directus.d.cts +1 -1
- package/dist/services/definitions/directus.d.mts +1 -1
- package/dist/services/definitions/docsgpt.d.cts +1 -1
- package/dist/services/definitions/docsgpt.d.mts +1 -1
- package/dist/services/definitions/dokploy.d.cts +1 -1
- package/dist/services/definitions/dokploy.d.mts +1 -1
- package/dist/services/definitions/dozzle.d.cts +1 -1
- package/dist/services/definitions/dozzle.d.mts +1 -1
- package/dist/services/definitions/dragonfly.d.cts +1 -1
- package/dist/services/definitions/dragonfly.d.mts +1 -1
- package/dist/services/definitions/drone-ci.d.cts +1 -1
- package/dist/services/definitions/drone-ci.d.mts +1 -1
- package/dist/services/definitions/duplicati.d.cts +1 -1
- package/dist/services/definitions/duplicati.d.mts +1 -1
- package/dist/services/definitions/element-web.d.cts +1 -1
- package/dist/services/definitions/element-web.d.mts +1 -1
- package/dist/services/definitions/excalidraw.d.cts +1 -1
- package/dist/services/definitions/excalidraw.d.mts +1 -1
- package/dist/services/definitions/ffmpeg.d.cts +1 -1
- package/dist/services/definitions/ffmpeg.d.mts +1 -1
- package/dist/services/definitions/firecrawl-playwright.d.cts +1 -1
- package/dist/services/definitions/firecrawl-playwright.d.mts +1 -1
- package/dist/services/definitions/firecrawl.d.cts +1 -1
- package/dist/services/definitions/firecrawl.d.mts +1 -1
- package/dist/services/definitions/flagsmith.d.cts +1 -1
- package/dist/services/definitions/flagsmith.d.mts +1 -1
- package/dist/services/definitions/flowise.d.cts +1 -1
- package/dist/services/definitions/flowise.d.mts +1 -1
- package/dist/services/definitions/focalboard.d.cts +1 -1
- package/dist/services/definitions/focalboard.d.mts +1 -1
- package/dist/services/definitions/fonoster.d.cts +1 -1
- package/dist/services/definitions/fonoster.d.mts +1 -1
- package/dist/services/definitions/forgejo.d.cts +1 -1
- package/dist/services/definitions/forgejo.d.mts +1 -1
- package/dist/services/definitions/formbricks.d.cts +1 -1
- package/dist/services/definitions/formbricks.d.mts +1 -1
- package/dist/services/definitions/gemini-cli.d.cts +1 -1
- package/dist/services/definitions/gemini-cli.d.mts +1 -1
- package/dist/services/definitions/ghost.d.cts +1 -1
- package/dist/services/definitions/ghost.d.mts +1 -1
- package/dist/services/definitions/gitea.d.cts +1 -1
- package/dist/services/definitions/gitea.d.mts +1 -1
- package/dist/services/definitions/gotify.d.cts +1 -1
- package/dist/services/definitions/gotify.d.mts +1 -1
- package/dist/services/definitions/grafana.d.cts +1 -1
- package/dist/services/definitions/grafana.d.mts +1 -1
- package/dist/services/definitions/graylog.d.cts +1 -1
- package/dist/services/definitions/graylog.d.mts +1 -1
- package/dist/services/definitions/headscale.d.cts +1 -1
- package/dist/services/definitions/headscale.d.mts +1 -1
- package/dist/services/definitions/hedgedoc.d.cts +1 -1
- package/dist/services/definitions/hedgedoc.d.mts +1 -1
- package/dist/services/definitions/hexstrike.d.cts +1 -1
- package/dist/services/definitions/hexstrike.d.mts +1 -1
- package/dist/services/definitions/heyform.d.cts +1 -1
- package/dist/services/definitions/heyform.d.mts +1 -1
- package/dist/services/definitions/hindsight.cjs +4 -1
- package/dist/services/definitions/hindsight.cjs.map +1 -1
- package/dist/services/definitions/hindsight.d.cts +1 -1
- package/dist/services/definitions/hindsight.d.mts +1 -1
- package/dist/services/definitions/hindsight.mjs +4 -1
- package/dist/services/definitions/hindsight.mjs.map +1 -1
- package/dist/services/definitions/homeassistant.d.cts +1 -1
- package/dist/services/definitions/homeassistant.d.mts +1 -1
- package/dist/services/definitions/hoppscotch.d.cts +1 -1
- package/dist/services/definitions/hoppscotch.d.mts +1 -1
- package/dist/services/definitions/immich.d.cts +1 -1
- package/dist/services/definitions/immich.d.mts +1 -1
- package/dist/services/definitions/index.cjs +42 -6
- package/dist/services/definitions/index.cjs.map +1 -1
- package/dist/services/definitions/index.d.cts +16 -4
- package/dist/services/definitions/index.d.cts.map +1 -1
- package/dist/services/definitions/index.d.mts +16 -4
- package/dist/services/definitions/index.d.mts.map +1 -1
- package/dist/services/definitions/index.mjs +31 -7
- package/dist/services/definitions/index.mjs.map +1 -1
- package/dist/services/definitions/infisical.d.cts +1 -1
- package/dist/services/definitions/infisical.d.mts +1 -1
- package/dist/services/definitions/influxdb.d.cts +1 -1
- package/dist/services/definitions/influxdb.d.mts +1 -1
- package/dist/services/definitions/invoke-ai.d.cts +1 -1
- package/dist/services/definitions/invoke-ai.d.mts +1 -1
- package/dist/services/definitions/ixartz-saas.d.cts +1 -1
- package/dist/services/definitions/ixartz-saas.d.mts +1 -1
- package/dist/services/definitions/jaeger.d.cts +1 -1
- package/dist/services/definitions/jaeger.d.mts +1 -1
- package/dist/services/definitions/jan.d.cts +1 -1
- package/dist/services/definitions/jan.d.mts +1 -1
- package/dist/services/definitions/jellyfin.d.cts +1 -1
- package/dist/services/definitions/jellyfin.d.mts +1 -1
- package/dist/services/definitions/jenkins.d.cts +1 -1
- package/dist/services/definitions/jenkins.d.mts +1 -1
- package/dist/services/definitions/jitsi-meet.d.cts +1 -1
- package/dist/services/definitions/jitsi-meet.d.mts +1 -1
- package/dist/services/definitions/keycloak.d.cts +1 -1
- package/dist/services/definitions/keycloak.d.mts +1 -1
- package/dist/services/definitions/kimi.d.cts +1 -1
- package/dist/services/definitions/kimi.d.mts +1 -1
- package/dist/services/definitions/kong.d.cts +1 -1
- package/dist/services/definitions/kong.d.mts +1 -1
- package/dist/services/definitions/lago.d.cts +1 -1
- package/dist/services/definitions/lago.d.mts +1 -1
- package/dist/services/definitions/langflow.d.cts +1 -1
- package/dist/services/definitions/langflow.d.mts +1 -1
- package/dist/services/definitions/langfuse.d.cts +1 -1
- package/dist/services/definitions/langfuse.d.mts +1 -1
- package/dist/services/definitions/lasuite-meet-agents.d.cts +1 -1
- package/dist/services/definitions/lasuite-meet-agents.d.mts +1 -1
- package/dist/services/definitions/lasuite-meet-backend.d.cts +1 -1
- package/dist/services/definitions/lasuite-meet-backend.d.mts +1 -1
- package/dist/services/definitions/lasuite-meet-frontend.d.cts +1 -1
- package/dist/services/definitions/lasuite-meet-frontend.d.mts +1 -1
- package/dist/services/definitions/librechat.d.cts +1 -1
- package/dist/services/definitions/librechat.d.mts +1 -1
- package/dist/services/definitions/lightpanda.d.cts +1 -1
- package/dist/services/definitions/lightpanda.d.mts +1 -1
- package/dist/services/definitions/listmonk.d.cts +1 -1
- package/dist/services/definitions/listmonk.d.mts +1 -1
- package/dist/services/definitions/litellm.d.cts +1 -1
- package/dist/services/definitions/litellm.d.mts +1 -1
- package/dist/services/definitions/livekit.d.cts +1 -1
- package/dist/services/definitions/livekit.d.mts +1 -1
- package/dist/services/definitions/local-ai.d.cts +1 -1
- package/dist/services/definitions/local-ai.d.mts +1 -1
- package/dist/services/definitions/loki.d.cts +1 -1
- package/dist/services/definitions/loki.d.mts +1 -1
- package/dist/services/definitions/mariadb.d.cts +1 -1
- package/dist/services/definitions/mariadb.d.mts +1 -1
- package/dist/services/definitions/matomo.d.cts +1 -1
- package/dist/services/definitions/matomo.d.mts +1 -1
- package/dist/services/definitions/matrix-synapse.d.cts +1 -1
- package/dist/services/definitions/matrix-synapse.d.mts +1 -1
- package/dist/services/definitions/mattermost.d.cts +1 -1
- package/dist/services/definitions/mattermost.d.mts +1 -1
- package/dist/services/definitions/mautic.d.cts +1 -1
- package/dist/services/definitions/mautic.d.mts +1 -1
- package/dist/services/definitions/medusa.d.cts +1 -1
- package/dist/services/definitions/medusa.d.mts +1 -1
- package/dist/services/definitions/meilisearch.d.cts +1 -1
- package/dist/services/definitions/meilisearch.d.mts +1 -1
- package/dist/services/definitions/mem0.cjs +133 -0
- package/dist/services/definitions/mem0.cjs.map +1 -0
- package/dist/services/definitions/mem0.d.cts +7 -0
- package/dist/services/definitions/mem0.d.cts.map +1 -0
- package/dist/services/definitions/mem0.d.mts +7 -0
- package/dist/services/definitions/mem0.d.mts.map +1 -0
- package/dist/services/definitions/mem0.mjs +132 -0
- package/dist/services/definitions/mem0.mjs.map +1 -0
- package/dist/services/definitions/memu.cjs +96 -0
- package/dist/services/definitions/memu.cjs.map +1 -0
- package/dist/services/definitions/memu.d.cts +7 -0
- package/dist/services/definitions/memu.d.cts.map +1 -0
- package/dist/services/definitions/memu.d.mts +7 -0
- package/dist/services/definitions/memu.d.mts.map +1 -0
- package/dist/services/definitions/memu.mjs +95 -0
- package/dist/services/definitions/memu.mjs.map +1 -0
- package/dist/services/definitions/metabase.d.cts +1 -1
- package/dist/services/definitions/metabase.d.mts +1 -1
- package/dist/services/definitions/milvus.cjs +4 -1
- package/dist/services/definitions/milvus.cjs.map +1 -1
- package/dist/services/definitions/milvus.d.cts +1 -1
- package/dist/services/definitions/milvus.d.mts +1 -1
- package/dist/services/definitions/milvus.mjs +4 -1
- package/dist/services/definitions/milvus.mjs.map +1 -1
- package/dist/services/definitions/minio.d.cts +1 -1
- package/dist/services/definitions/minio.d.mts +1 -1
- package/dist/services/definitions/mission-control.d.cts +1 -1
- package/dist/services/definitions/mission-control.d.mts +1 -1
- package/dist/services/definitions/mixpost.d.cts +1 -1
- package/dist/services/definitions/mixpost.d.mts +1 -1
- package/dist/services/definitions/mosquitto.d.cts +1 -1
- package/dist/services/definitions/mosquitto.d.mts +1 -1
- package/dist/services/definitions/motion-canvas.d.cts +1 -1
- package/dist/services/definitions/motion-canvas.d.mts +1 -1
- package/dist/services/definitions/mysql.d.cts +1 -1
- package/dist/services/definitions/mysql.d.mts +1 -1
- package/dist/services/definitions/n8n.d.cts +1 -1
- package/dist/services/definitions/n8n.d.mts +1 -1
- package/dist/services/definitions/nanobot.cjs +86 -0
- package/dist/services/definitions/nanobot.cjs.map +1 -0
- package/dist/services/definitions/nanobot.d.cts +7 -0
- package/dist/services/definitions/nanobot.d.cts.map +1 -0
- package/dist/services/definitions/nanobot.d.mts +7 -0
- package/dist/services/definitions/nanobot.d.mts.map +1 -0
- package/dist/services/definitions/nanobot.mjs +85 -0
- package/dist/services/definitions/nanobot.mjs.map +1 -0
- package/dist/services/definitions/nanoclaw.cjs +79 -0
- package/dist/services/definitions/nanoclaw.cjs.map +1 -0
- package/dist/services/definitions/nanoclaw.d.cts +7 -0
- package/dist/services/definitions/nanoclaw.d.cts.map +1 -0
- package/dist/services/definitions/nanoclaw.d.mts +7 -0
- package/dist/services/definitions/nanoclaw.d.mts.map +1 -0
- package/dist/services/definitions/nanoclaw.mjs +78 -0
- package/dist/services/definitions/nanoclaw.mjs.map +1 -0
- package/dist/services/definitions/navidrome.d.cts +1 -1
- package/dist/services/definitions/navidrome.d.mts +1 -1
- package/dist/services/definitions/neo4j.d.cts +1 -1
- package/dist/services/definitions/neo4j.d.mts +1 -1
- package/dist/services/definitions/nextcloud.d.cts +1 -1
- package/dist/services/definitions/nextcloud.d.mts +1 -1
- package/dist/services/definitions/nginx-proxy-manager.d.cts +1 -1
- package/dist/services/definitions/nginx-proxy-manager.d.mts +1 -1
- package/dist/services/definitions/nocodb.d.cts +1 -1
- package/dist/services/definitions/nocodb.d.mts +1 -1
- package/dist/services/definitions/node-red.d.cts +1 -1
- package/dist/services/definitions/node-red.d.mts +1 -1
- package/dist/services/definitions/ntfy.d.cts +1 -1
- package/dist/services/definitions/ntfy.d.mts +1 -1
- package/dist/services/definitions/ollama.d.cts +1 -1
- package/dist/services/definitions/ollama.d.mts +1 -1
- package/dist/services/definitions/open-saas.d.cts +1 -1
- package/dist/services/definitions/open-saas.d.mts +1 -1
- package/dist/services/definitions/open-webui.d.cts +1 -1
- package/dist/services/definitions/open-webui.d.mts +1 -1
- package/dist/services/definitions/opencode.d.cts +1 -1
- package/dist/services/definitions/opencode.d.mts +1 -1
- package/dist/services/definitions/openhands.d.cts +1 -1
- package/dist/services/definitions/openhands.d.mts +1 -1
- package/dist/services/definitions/openpanel.d.cts +1 -1
- package/dist/services/definitions/openpanel.d.mts +1 -1
- package/dist/services/definitions/opensandbox.cjs +2 -1
- package/dist/services/definitions/opensandbox.cjs.map +1 -1
- package/dist/services/definitions/opensandbox.d.cts +1 -1
- package/dist/services/definitions/opensandbox.d.mts +1 -1
- package/dist/services/definitions/opensandbox.mjs +2 -1
- package/dist/services/definitions/opensandbox.mjs.map +1 -1
- package/dist/services/definitions/opensearch.d.cts +1 -1
- package/dist/services/definitions/opensearch.d.mts +1 -1
- package/dist/services/definitions/outline.d.cts +1 -1
- package/dist/services/definitions/outline.d.mts +1 -1
- package/dist/services/definitions/paperless-ngx.d.cts +1 -1
- package/dist/services/definitions/paperless-ngx.d.mts +1 -1
- package/dist/services/definitions/pentagi.d.cts +1 -1
- package/dist/services/definitions/pentagi.d.mts +1 -1
- package/dist/services/definitions/pentestagent.d.cts +1 -1
- package/dist/services/definitions/pentestagent.d.mts +1 -1
- package/dist/services/definitions/photoprism.d.cts +1 -1
- package/dist/services/definitions/photoprism.d.mts +1 -1
- package/dist/services/definitions/pihole.d.cts +1 -1
- package/dist/services/definitions/pihole.d.mts +1 -1
- package/dist/services/definitions/piper-tts.d.cts +1 -1
- package/dist/services/definitions/piper-tts.d.mts +1 -1
- package/dist/services/definitions/plane.d.cts +1 -1
- package/dist/services/definitions/plane.d.mts +1 -1
- package/dist/services/definitions/plausible.d.cts +1 -1
- package/dist/services/definitions/plausible.d.mts +1 -1
- package/dist/services/definitions/playwright-server.d.cts +1 -1
- package/dist/services/definitions/playwright-server.d.mts +1 -1
- package/dist/services/definitions/pocket-id.d.cts +1 -1
- package/dist/services/definitions/pocket-id.d.mts +1 -1
- package/dist/services/definitions/pocketbase.d.cts +1 -1
- package/dist/services/definitions/pocketbase.d.mts +1 -1
- package/dist/services/definitions/portainer.d.cts +1 -1
- package/dist/services/definitions/portainer.d.mts +1 -1
- package/dist/services/definitions/postgresql.d.cts +1 -1
- package/dist/services/definitions/postgresql.d.mts +1 -1
- package/dist/services/definitions/posthog.d.cts +1 -1
- package/dist/services/definitions/posthog.d.mts +1 -1
- package/dist/services/definitions/postiz.d.cts +1 -1
- package/dist/services/definitions/postiz.d.mts +1 -1
- package/dist/services/definitions/prometheus.d.cts +1 -1
- package/dist/services/definitions/prometheus.d.mts +1 -1
- package/dist/services/definitions/qdrant.d.cts +1 -1
- package/dist/services/definitions/qdrant.d.mts +1 -1
- package/dist/services/definitions/rabbitmq.d.cts +1 -1
- package/dist/services/definitions/rabbitmq.d.mts +1 -1
- package/dist/services/definitions/ragflow.d.cts +1 -1
- package/dist/services/definitions/ragflow.d.mts +1 -1
- package/dist/services/definitions/redis.d.cts +1 -1
- package/dist/services/definitions/redis.d.mts +1 -1
- package/dist/services/definitions/relaticle-horizon.cjs +48 -0
- package/dist/services/definitions/relaticle-horizon.cjs.map +1 -0
- package/dist/services/definitions/relaticle-horizon.d.cts +7 -0
- package/dist/services/definitions/relaticle-horizon.d.cts.map +1 -0
- package/dist/services/definitions/relaticle-horizon.d.mts +7 -0
- package/dist/services/definitions/relaticle-horizon.d.mts.map +1 -0
- package/dist/services/definitions/relaticle-horizon.mjs +48 -0
- package/dist/services/definitions/relaticle-horizon.mjs.map +1 -0
- package/dist/services/definitions/relaticle-scheduler.cjs +44 -0
- package/dist/services/definitions/relaticle-scheduler.cjs.map +1 -0
- package/dist/services/definitions/relaticle-scheduler.d.cts +7 -0
- package/dist/services/definitions/relaticle-scheduler.d.cts.map +1 -0
- package/dist/services/definitions/relaticle-scheduler.d.mts +7 -0
- package/dist/services/definitions/relaticle-scheduler.d.mts.map +1 -0
- package/dist/services/definitions/relaticle-scheduler.mjs +44 -0
- package/dist/services/definitions/relaticle-scheduler.mjs.map +1 -0
- package/dist/services/definitions/relaticle.cjs +151 -0
- package/dist/services/definitions/relaticle.cjs.map +1 -0
- package/dist/services/definitions/relaticle.d.cts +80 -0
- package/dist/services/definitions/relaticle.d.cts.map +1 -0
- package/dist/services/definitions/relaticle.d.mts +80 -0
- package/dist/services/definitions/relaticle.d.mts.map +1 -0
- package/dist/services/definitions/relaticle.mjs +149 -0
- package/dist/services/definitions/relaticle.mjs.map +1 -0
- package/dist/services/definitions/remotion.d.cts +1 -1
- package/dist/services/definitions/remotion.d.mts +1 -1
- package/dist/services/definitions/restic.d.cts +1 -1
- package/dist/services/definitions/restic.d.mts +1 -1
- package/dist/services/definitions/revolt.d.cts +1 -1
- package/dist/services/definitions/revolt.d.mts +1 -1
- package/dist/services/definitions/rocketchat.d.cts +1 -1
- package/dist/services/definitions/rocketchat.d.mts +1 -1
- package/dist/services/definitions/saleor.d.cts +1 -1
- package/dist/services/definitions/saleor.d.mts +1 -1
- package/dist/services/definitions/scrapling.d.cts +1 -1
- package/dist/services/definitions/scrapling.d.mts +1 -1
- package/dist/services/definitions/searxng.d.cts +1 -1
- package/dist/services/definitions/searxng.d.mts +1 -1
- package/dist/services/definitions/sentry.d.cts +1 -1
- package/dist/services/definitions/sentry.d.mts +1 -1
- package/dist/services/definitions/signoz.d.cts +1 -1
- package/dist/services/definitions/signoz.d.mts +1 -1
- package/dist/services/definitions/solidityguard.d.cts +1 -1
- package/dist/services/definitions/solidityguard.d.mts +1 -1
- package/dist/services/definitions/sonarqube.d.cts +1 -1
- package/dist/services/definitions/sonarqube.d.mts +1 -1
- package/dist/services/definitions/stable-diffusion.d.cts +1 -1
- package/dist/services/definitions/stable-diffusion.d.mts +1 -1
- package/dist/services/definitions/steel-browser.d.cts +1 -1
- package/dist/services/definitions/steel-browser.d.mts +1 -1
- package/dist/services/definitions/stirling-pdf.d.cts +1 -1
- package/dist/services/definitions/stirling-pdf.d.mts +1 -1
- package/dist/services/definitions/strapi.d.cts +1 -1
- package/dist/services/definitions/strapi.d.mts +1 -1
- package/dist/services/definitions/stream-gateway.d.cts +1 -1
- package/dist/services/definitions/stream-gateway.d.mts +1 -1
- package/dist/services/definitions/supabase.d.cts +1 -1
- package/dist/services/definitions/supabase.d.mts +1 -1
- package/dist/services/definitions/superset.d.cts +1 -1
- package/dist/services/definitions/superset.d.mts +1 -1
- package/dist/services/definitions/surrealdb.d.cts +1 -1
- package/dist/services/definitions/surrealdb.d.mts +1 -1
- package/dist/services/definitions/tabby-ml.d.cts +1 -1
- package/dist/services/definitions/tabby-ml.d.mts +1 -1
- package/dist/services/definitions/tailscale.d.cts +1 -1
- package/dist/services/definitions/tailscale.d.mts +1 -1
- package/dist/services/definitions/tempo.d.cts +1 -1
- package/dist/services/definitions/tempo.d.mts +1 -1
- package/dist/services/definitions/temporal.d.cts +1 -1
- package/dist/services/definitions/temporal.d.mts +1 -1
- package/dist/services/definitions/text-gen-webui.d.cts +1 -1
- package/dist/services/definitions/text-gen-webui.d.mts +1 -1
- package/dist/services/definitions/timescaledb.d.cts +1 -1
- package/dist/services/definitions/timescaledb.d.mts +1 -1
- package/dist/services/definitions/traefik.d.cts +1 -1
- package/dist/services/definitions/traefik.d.mts +1 -1
- package/dist/services/definitions/twenty-worker.cjs +73 -0
- package/dist/services/definitions/twenty-worker.cjs.map +1 -0
- package/dist/services/definitions/twenty-worker.d.cts +7 -0
- package/dist/services/definitions/twenty-worker.d.cts.map +1 -0
- package/dist/services/definitions/twenty-worker.d.mts +7 -0
- package/dist/services/definitions/twenty-worker.d.mts.map +1 -0
- package/dist/services/definitions/twenty-worker.mjs +72 -0
- package/dist/services/definitions/twenty-worker.mjs.map +1 -0
- package/dist/services/definitions/twenty.cjs +24 -2
- package/dist/services/definitions/twenty.cjs.map +1 -1
- package/dist/services/definitions/twenty.d.cts +1 -1
- package/dist/services/definitions/twenty.d.mts +1 -1
- package/dist/services/definitions/twenty.mjs +24 -2
- package/dist/services/definitions/twenty.mjs.map +1 -1
- package/dist/services/definitions/umami.d.cts +1 -1
- package/dist/services/definitions/umami.d.mts +1 -1
- package/dist/services/definitions/uptime-kuma.d.cts +1 -1
- package/dist/services/definitions/uptime-kuma.d.mts +1 -1
- package/dist/services/definitions/usesend.d.cts +1 -1
- package/dist/services/definitions/usesend.d.mts +1 -1
- package/dist/services/definitions/valkey.d.cts +1 -1
- package/dist/services/definitions/valkey.d.mts +1 -1
- package/dist/services/definitions/vault.d.cts +1 -1
- package/dist/services/definitions/vault.d.mts +1 -1
- package/dist/services/definitions/vaultwarden.d.cts +1 -1
- package/dist/services/definitions/vaultwarden.d.mts +1 -1
- package/dist/services/definitions/vector-log.d.cts +1 -1
- package/dist/services/definitions/vector-log.d.mts +1 -1
- package/dist/services/definitions/vikunja.d.cts +1 -1
- package/dist/services/definitions/vikunja.d.mts +1 -1
- package/dist/services/definitions/watchtower.d.cts +1 -1
- package/dist/services/definitions/watchtower.d.mts +1 -1
- package/dist/services/definitions/weaviate.cjs +4 -1
- package/dist/services/definitions/weaviate.cjs.map +1 -1
- package/dist/services/definitions/weaviate.d.cts +1 -1
- package/dist/services/definitions/weaviate.d.mts +1 -1
- package/dist/services/definitions/weaviate.mjs +4 -1
- package/dist/services/definitions/weaviate.mjs.map +1 -1
- package/dist/services/definitions/whisper.d.cts +1 -1
- package/dist/services/definitions/whisper.d.mts +1 -1
- package/dist/services/definitions/wireguard.d.cts +1 -1
- package/dist/services/definitions/wireguard.d.mts +1 -1
- package/dist/services/definitions/woodpecker-ci.d.cts +1 -1
- package/dist/services/definitions/woodpecker-ci.d.mts +1 -1
- package/dist/services/definitions/xyops.d.cts +1 -1
- package/dist/services/definitions/xyops.d.mts +1 -1
- package/dist/services/definitions/zeroclaw.cjs +103 -0
- package/dist/services/definitions/zeroclaw.cjs.map +1 -0
- package/dist/services/definitions/zeroclaw.d.cts +7 -0
- package/dist/services/definitions/zeroclaw.d.cts.map +1 -0
- package/dist/services/definitions/zeroclaw.d.mts +7 -0
- package/dist/services/definitions/zeroclaw.d.mts.map +1 -0
- package/dist/services/definitions/zeroclaw.mjs +102 -0
- package/dist/services/definitions/zeroclaw.mjs.map +1 -0
- package/dist/services/definitions/zulip.d.cts +1 -1
- package/dist/services/definitions/zulip.d.mts +1 -1
- package/dist/services/registry.d.cts +1 -1
- package/dist/services/registry.d.mts +1 -1
- package/dist/services/registry.test.cjs +8 -8
- package/dist/services/registry.test.cjs.map +1 -1
- package/dist/services/registry.test.mjs +8 -8
- package/dist/services/registry.test.mjs.map +1 -1
- package/dist/skills/registry.cjs +19 -0
- package/dist/skills/registry.cjs.map +1 -1
- package/dist/skills/registry.d.cts +1 -1
- package/dist/skills/registry.d.cts.map +1 -1
- package/dist/skills/registry.d.mts +1 -1
- package/dist/skills/registry.d.mts.map +1 -1
- package/dist/skills/registry.mjs +19 -0
- package/dist/skills/registry.mjs.map +1 -1
- package/dist/{skills-BSF7iNa4.cjs → skills-uPxJVmKk.cjs} +311 -1
- package/dist/skills-uPxJVmKk.cjs.map +1 -0
- package/dist/{test.CTcmp4Su-ClCHJ3FA.mjs → test.CTcmp4Su-BRa7-bTj.mjs} +2 -2
- package/dist/{test.CTcmp4Su-ClCHJ3FA.mjs.map → test.CTcmp4Su-BRa7-bTj.mjs.map} +1 -1
- package/dist/{test.CTcmp4Su-DlzTarwH.cjs → test.CTcmp4Su-BWSPM8ZQ.cjs} +19 -1
- package/dist/{test.CTcmp4Su-DlzTarwH.cjs.map → test.CTcmp4Su-BWSPM8ZQ.cjs.map} +1 -1
- package/dist/track-analytics.d.cts +1 -1
- package/dist/track-analytics.d.mts +1 -1
- package/dist/track-analytics.test.cjs +1 -1
- package/dist/track-analytics.test.mjs +1 -1
- package/dist/types-BREUfzzq.d.mts +77 -0
- package/dist/types-BREUfzzq.d.mts.map +1 -0
- package/dist/types-Bsn0XzSP.d.cts +95 -0
- package/dist/types-Bsn0XzSP.d.cts.map +1 -0
- package/dist/types-CyZ5mn6w.d.cts +77 -0
- package/dist/types-CyZ5mn6w.d.cts.map +1 -0
- package/dist/types-DIsPc-hb.d.cts +100 -0
- package/dist/types-DIsPc-hb.d.cts.map +1 -0
- package/dist/types-DbXajvYq.d.mts +100 -0
- package/dist/types-DbXajvYq.d.mts.map +1 -0
- package/dist/types-DwTKyCZ9.d.mts +95 -0
- package/dist/types-DwTKyCZ9.d.mts.map +1 -0
- package/dist/{types-CR83OJiq.d.cts → types-dyBnrHm9.d.cts} +5 -1
- package/dist/types-dyBnrHm9.d.cts.map +1 -0
- package/dist/{types-zYjGTuyn.d.mts → types-fRSnaZTX.d.mts} +5 -1
- package/dist/types-fRSnaZTX.d.mts.map +1 -0
- package/dist/types.cjs +7 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +2 -93
- package/dist/types.d.mts +2 -93
- package/dist/types.mjs +7 -0
- package/dist/types.mjs.map +1 -1
- package/dist/validator.cjs +1 -1
- package/dist/validator.d.cts +1 -1
- package/dist/validator.d.mts +1 -1
- package/dist/validator.test.cjs +1 -1
- package/dist/validator.test.mjs +1 -1
- package/dist/version-manager.d.cts +1 -1
- package/dist/version-manager.d.mts +1 -1
- package/dist/version-manager.test.cjs +3 -3
- package/dist/version-manager.test.cjs.map +1 -1
- package/dist/version-manager.test.mjs +3 -3
- package/dist/version-manager.test.mjs.map +1 -1
- package/dist/zeroclaw-B-0TuOfZ.d.mts +7 -0
- package/dist/zeroclaw-B-0TuOfZ.d.mts.map +1 -0
- package/dist/zeroclaw-BvKpDHKe.d.cts +7 -0
- package/dist/zeroclaw-BvKpDHKe.d.cts.map +1 -0
- package/package.json +6 -2
- package/src/addon-stack.test.ts +19 -23
- package/src/addon-stack.ts +67 -44
- package/src/composer.test.ts +59 -0
- package/src/composer.ts +135 -194
- package/src/deployers/coolify.ts +38 -0
- package/src/deployers/dokploy.ts +38 -0
- package/src/deployers/types.ts +2 -0
- package/src/frameworks/claude-code-fw.ts +118 -0
- package/src/frameworks/codex-fw.ts +102 -0
- package/src/frameworks/copaw.ts +154 -0
- package/src/frameworks/index.ts +46 -0
- package/src/frameworks/memu.ts +149 -0
- package/src/frameworks/nanobot.ts +156 -0
- package/src/frameworks/nanoclaw.ts +126 -0
- package/src/frameworks/openclaw.ts +325 -0
- package/src/frameworks/registry.test.ts +131 -0
- package/src/frameworks/registry.ts +28 -0
- package/src/frameworks/types.ts +146 -0
- package/src/frameworks/zeroclaw.ts +148 -0
- package/src/generate.test.ts +55 -4
- package/src/generate.ts +67 -14
- package/src/generators/caddy.test.ts +41 -1
- package/src/generators/caddy.ts +16 -2
- package/src/generators/clone-repos.test.ts +1 -1
- package/src/generators/clone-repos.ts +7 -2
- package/src/generators/env.test.ts +2 -2
- package/src/generators/env.ts +30 -180
- package/src/generators/health-check.ts +4 -4
- package/src/generators/openclaw-json.ts +57 -5
- package/src/generators/postgres-init.ts +9 -0
- package/src/generators/readme.ts +75 -9
- package/src/generators/scripts.test.ts +1 -1
- package/src/generators/skills.ts +316 -0
- package/src/generators/traefik.test.ts +13 -13
- package/src/index.ts +46 -7
- package/src/logger/__tests__/logger.test.ts +388 -0
- package/src/logger/index.ts +14 -0
- package/src/logger/logger.ts +326 -0
- package/src/logger/sinks/callback-sink.ts +13 -0
- package/src/logger/sinks/console-sink.ts +51 -0
- package/src/logger/sinks/file-sink.ts +121 -0
- package/src/logger/types.ts +90 -0
- package/src/migrations.ts +8 -1
- package/src/presets/presets.test.ts +4 -4
- package/src/presets/registry.test.ts +6 -6
- package/src/resolver.test.ts +31 -3
- package/src/resolver.ts +19 -3
- package/src/schema.test.ts +2 -2
- package/src/schema.ts +58 -17
- package/src/services/definitions/agent-browser.ts +177 -0
- package/src/services/definitions/apptension-saas.ts +2 -1
- package/src/services/definitions/chromadb.ts +1 -1
- package/src/services/definitions/claude-code.ts +1 -1
- package/src/services/definitions/clawrouter.ts +141 -0
- package/src/services/definitions/codex.ts +1 -1
- package/src/services/definitions/convex.ts +1 -2
- package/src/services/definitions/copaw.ts +101 -0
- package/src/services/definitions/desktop-environment.ts +1 -0
- package/src/services/definitions/hindsight.ts +3 -2
- package/src/services/definitions/index.ts +53 -16
- package/src/services/definitions/mem0.ts +132 -0
- package/src/services/definitions/memu.ts +96 -0
- package/src/services/definitions/milvus.ts +1 -1
- package/src/services/definitions/nanobot.ts +89 -0
- package/src/services/definitions/nanoclaw.ts +78 -0
- package/src/services/definitions/opensandbox.ts +6 -15
- package/src/services/definitions/relaticle-horizon.ts +48 -0
- package/src/services/definitions/relaticle-scheduler.ts +42 -0
- package/src/services/definitions/relaticle.ts +142 -0
- package/src/services/definitions/twenty-worker.ts +70 -0
- package/src/services/definitions/twenty.ts +16 -2
- package/src/services/definitions/weaviate.ts +1 -1
- package/src/services/definitions/zeroclaw.ts +102 -0
- package/src/services/registry.test.ts +7 -7
- package/src/skills/registry.ts +10 -0
- package/src/types.ts +11 -1
- package/src/version-manager.test.ts +2 -2
- package/dist/schema-BQnZrcw8.d.cts.map +0 -1
- package/dist/schema-SBpL0bdI.d.mts.map +0 -1
- package/dist/skills-BSF7iNa4.cjs.map +0 -1
- package/dist/types-CR83OJiq.d.cts.map +0 -1
- package/dist/types-zYjGTuyn.d.mts.map +0 -1
- package/dist/types.d.cts.map +0 -1
- package/dist/types.d.mts.map +0 -1
package/dist/addon-stack.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getFrameworkById } from "./frameworks/registry.mjs";
|
|
2
|
+
import "./frameworks/index.mjs";
|
|
1
3
|
import { getDbRequirements } from "./generators/postgres-init.mjs";
|
|
2
4
|
import { YAML_OPTIONS, buildCompanionService, quotedStr } from "./composer.mjs";
|
|
3
5
|
import { generateSkillFiles } from "./generators/skills.mjs";
|
|
@@ -7,31 +9,28 @@ import { AddonStackInputSchema, AddonStackUpdateInputSchema } from "./schema.mjs
|
|
|
7
9
|
import { parse, stringify } from "yaml";
|
|
8
10
|
import { randomBytes } from "node:crypto";
|
|
9
11
|
//#region src/addon-stack.ts
|
|
10
|
-
/**
|
|
11
|
-
const
|
|
12
|
-
"openclaw-gateway",
|
|
13
|
-
"openclaw-cli",
|
|
12
|
+
/** Base infrastructure service IDs that Clawexa's cloud-init provisions. */
|
|
13
|
+
const BASE_INFRA_SERVICE_IDS = [
|
|
14
14
|
"redis",
|
|
15
15
|
"postgresql",
|
|
16
16
|
"open-webui",
|
|
17
17
|
"caddy",
|
|
18
18
|
"traefik",
|
|
19
|
-
"postgres-setup"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
"postgres-setup"
|
|
20
|
+
];
|
|
21
|
+
/** Build the full set of infra IDs for a given framework. */
|
|
22
|
+
function getInfraServiceIds(framework) {
|
|
23
|
+
return new Set([
|
|
24
|
+
`${framework.id}-gateway`,
|
|
25
|
+
`${framework.id}-cli`,
|
|
26
|
+
...BASE_INFRA_SERVICE_IDS,
|
|
27
|
+
...framework.getMandatoryServices()
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
24
30
|
/** Env keys managed by Clawexa's cloud-init — never include in addon env output. */
|
|
25
31
|
const CLAWEXA_MANAGED_ENV_KEYS = new Set([
|
|
26
32
|
"COMPOSE_FILE",
|
|
27
33
|
"COMPOSE_PROFILES",
|
|
28
|
-
"OPENCLAW_VERSION",
|
|
29
|
-
"OPENCLAW_GATEWAY_TOKEN",
|
|
30
|
-
"OPENCLAW_GATEWAY_PORT",
|
|
31
|
-
"OPENCLAW_BRIDGE_PORT",
|
|
32
|
-
"OPENCLAW_GATEWAY_BIND",
|
|
33
|
-
"OPENCLAW_CONFIG_DIR",
|
|
34
|
-
"OPENCLAW_WORKSPACE_DIR",
|
|
35
34
|
"REDIS_PASSWORD",
|
|
36
35
|
"REDIS_HOST",
|
|
37
36
|
"REDIS_PORT",
|
|
@@ -41,6 +40,16 @@ const CLAWEXA_MANAGED_ENV_KEYS = new Set([
|
|
|
41
40
|
"POSTGRES_HOST",
|
|
42
41
|
"POSTGRES_PORT"
|
|
43
42
|
]);
|
|
43
|
+
/** Build the full set of managed env keys for a given framework. */
|
|
44
|
+
function getManagedEnvKeys(framework) {
|
|
45
|
+
const keys = new Set(CLAWEXA_MANAGED_ENV_KEYS);
|
|
46
|
+
for (const envLine of framework.getBaseEnvVars({
|
|
47
|
+
generateSecrets: false,
|
|
48
|
+
frameworkVersion: "latest",
|
|
49
|
+
frameworkImageVariant: "official"
|
|
50
|
+
})) keys.add(envLine.key);
|
|
51
|
+
return keys;
|
|
52
|
+
}
|
|
44
53
|
/** Sanitize instanceId into a valid Docker Compose project name. */
|
|
45
54
|
function sanitizeProjectName(instanceId) {
|
|
46
55
|
return instanceId.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0, 64) || "addon";
|
|
@@ -155,7 +164,7 @@ function resolvePortConflicts(addonServices, reservedPorts, portOverrides) {
|
|
|
155
164
|
if (userOverride) {
|
|
156
165
|
usedPorts.add(userOverride);
|
|
157
166
|
assignments[`${def.id}:${port.container}`] = userOverride;
|
|
158
|
-
|
|
167
|
+
overrides[def.id] ??= {};
|
|
159
168
|
overrides[def.id][String(port.host)] = userOverride;
|
|
160
169
|
continue;
|
|
161
170
|
}
|
|
@@ -163,7 +172,7 @@ function resolvePortConflicts(addonServices, reservedPorts, portOverrides) {
|
|
|
163
172
|
if (usedPorts.has(assignedPort)) {
|
|
164
173
|
assignedPort = port.host + 1e3;
|
|
165
174
|
while (usedPorts.has(assignedPort)) assignedPort++;
|
|
166
|
-
|
|
175
|
+
overrides[def.id] ??= {};
|
|
167
176
|
overrides[def.id][String(port.host)] = assignedPort;
|
|
168
177
|
}
|
|
169
178
|
usedPorts.add(assignedPort);
|
|
@@ -194,8 +203,11 @@ function generateAddonStack(rawInput) {
|
|
|
194
203
|
return emptyResult(`Invalid input: ${err instanceof Error ? err.message : String(err)}`);
|
|
195
204
|
}
|
|
196
205
|
const projectName = sanitizeProjectName(input.instanceId);
|
|
206
|
+
const framework = getFrameworkById("openclaw");
|
|
207
|
+
const infraIds = getInfraServiceIds(framework);
|
|
208
|
+
const managedKeys = getManagedEnvKeys(framework);
|
|
197
209
|
const addonServiceIds = input.services.filter((id) => {
|
|
198
|
-
if (
|
|
210
|
+
if (infraIds.has(id)) {
|
|
199
211
|
warnings.push(`Service "${id}" is managed by Clawexa infrastructure and was excluded.`);
|
|
200
212
|
return false;
|
|
201
213
|
}
|
|
@@ -238,7 +250,7 @@ function generateAddonStack(rawInput) {
|
|
|
238
250
|
for (const w of resolved.warnings) warnings.push(w.message);
|
|
239
251
|
const addonResolved = [];
|
|
240
252
|
for (const svc of resolved.services) {
|
|
241
|
-
if (
|
|
253
|
+
if (infraIds.has(svc.definition.id)) continue;
|
|
242
254
|
addonResolved.push(svc);
|
|
243
255
|
}
|
|
244
256
|
const deployableServices = [];
|
|
@@ -328,7 +340,7 @@ function generateAddonStack(rawInput) {
|
|
|
328
340
|
delete entry.profiles;
|
|
329
341
|
if (entry.depends_on) {
|
|
330
342
|
const deps = entry.depends_on;
|
|
331
|
-
for (const depId of Object.keys(deps)) if (
|
|
343
|
+
for (const depId of Object.keys(deps)) if (infraIds.has(depId)) delete deps[depId];
|
|
332
344
|
if (Object.keys(deps).length === 0) delete entry.depends_on;
|
|
333
345
|
}
|
|
334
346
|
services[def.id] = entry;
|
|
@@ -369,7 +381,7 @@ function generateAddonStack(rawInput) {
|
|
|
369
381
|
entrypoint: ["/bin/sh", "-c"],
|
|
370
382
|
command: [scriptLines.join("\n")],
|
|
371
383
|
restart: quotedStr("no"),
|
|
372
|
-
networks: [
|
|
384
|
+
networks: [framework.networkName]
|
|
373
385
|
};
|
|
374
386
|
for (const req of dbReqs) {
|
|
375
387
|
const svcEntry = services[req.serviceId];
|
|
@@ -399,7 +411,7 @@ function generateAddonStack(rawInput) {
|
|
|
399
411
|
envLines.push("");
|
|
400
412
|
}
|
|
401
413
|
}
|
|
402
|
-
const seenKeys = new Set([...
|
|
414
|
+
const seenKeys = new Set([...managedKeys, ...dbReqs.map((r) => r.passwordEnvVar)]);
|
|
403
415
|
const envVarGroups = [];
|
|
404
416
|
for (const svc of deployableServices) {
|
|
405
417
|
const def = svc.definition;
|
|
@@ -455,7 +467,7 @@ function generateAddonStack(rawInput) {
|
|
|
455
467
|
else finalEnvLines.push(line);
|
|
456
468
|
} else finalEnvLines.push(line);
|
|
457
469
|
}
|
|
458
|
-
for (const [key, value] of envValues) if (!quirkedKeys.has(key) && !seenKeys.has(key) && !
|
|
470
|
+
for (const [key, value] of envValues) if (!quirkedKeys.has(key) && !seenKeys.has(key) && !managedKeys.has(key)) {
|
|
459
471
|
finalEnvLines.push(`# Synced by env quirk`);
|
|
460
472
|
finalEnvLines.push(`${key}=${value}`);
|
|
461
473
|
finalEnvLines.push("");
|
|
@@ -521,7 +533,7 @@ function generateAddonStack(rawInput) {
|
|
|
521
533
|
for (const v of allVolumes) volumeMap[v] = null;
|
|
522
534
|
const composeDoc = { services };
|
|
523
535
|
if (Object.keys(volumeMap).length > 0) composeDoc.volumes = volumeMap;
|
|
524
|
-
composeDoc.networks = {
|
|
536
|
+
composeDoc.networks = { [framework.networkName]: { external: true } };
|
|
525
537
|
return {
|
|
526
538
|
composeOverride: stringify(composeDoc, YAML_OPTIONS),
|
|
527
539
|
envFile,
|
|
@@ -571,7 +583,6 @@ function updateAddonStack(rawInput) {
|
|
|
571
583
|
const eqIdx = trimmed.indexOf("=");
|
|
572
584
|
if (eqIdx > 0) existingEnvMap.set(trimmed.slice(0, eqIdx), trimmed.slice(eqIdx + 1));
|
|
573
585
|
}
|
|
574
|
-
new Set(input.addServices);
|
|
575
586
|
const removeSet = new Set(input.removeServices);
|
|
576
587
|
const desiredServiceIds = [...currentServiceIds.filter((id) => !removeSet.has(id)), ...input.addServices.filter((id) => !currentServiceIds.includes(id))];
|
|
577
588
|
const targetResult = generateAddonStack({
|
|
@@ -580,10 +591,12 @@ function updateAddonStack(rawInput) {
|
|
|
580
591
|
skillPacks: [],
|
|
581
592
|
platform: input.platform,
|
|
582
593
|
openclawVersion: input.openclawVersion,
|
|
594
|
+
existingServices: input.existingServices,
|
|
583
595
|
reservedPorts: input.reservedPorts,
|
|
584
596
|
generateSecrets: input.generateSecrets,
|
|
585
597
|
credentials: input.credentials,
|
|
586
598
|
portOverrides: input.portOverrides,
|
|
599
|
+
gpu: input.gpu,
|
|
587
600
|
aiProviders: input.aiProviders,
|
|
588
601
|
prebuiltImages: input.prebuiltImages
|
|
589
602
|
});
|
|
@@ -613,7 +626,7 @@ function updateAddonStack(rawInput) {
|
|
|
613
626
|
if (!def) continue;
|
|
614
627
|
for (const skill of def.skills) {
|
|
615
628
|
const skillPath = Object.keys(targetResult.skillFiles).find((path) => path.includes(skill.skillId));
|
|
616
|
-
if (skillPath) newSkillFiles[skillPath] = targetResult.skillFiles[skillPath];
|
|
629
|
+
if (skillPath && targetResult.skillFiles[skillPath]) newSkillFiles[skillPath] = targetResult.skillFiles[skillPath];
|
|
617
630
|
}
|
|
618
631
|
}
|
|
619
632
|
for (const id of removed) {
|
package/dist/addon-stack.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"addon-stack.mjs","names":["parseYaml"],"sources":["../src/addon-stack.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { stringify } from \"yaml\";\nimport { parse as parseYaml } from \"yaml\";\nimport { buildCompanionService, buildPostgresSetup, quotedStr, YAML_OPTIONS } from \"./composer.js\";\nimport { getDbRequirements } from \"./generators/postgres-init.js\";\nimport { generateSkillFiles } from \"./generators/skills.js\";\nimport { resolve } from \"./resolver.js\";\nimport { AddonStackInputSchema, AddonStackUpdateInputSchema } from \"./schema.js\";\nimport { getServiceById } from \"./services/registry.js\";\nimport type {\n\tAddonStackInput,\n\tAddonStackResult,\n\tAddonStackUpdateInput,\n\tAddonStackUpdateResult,\n\tComposeOptions,\n\tProxyRoute,\n\tResolvedService,\n\tResolverOutput,\n\tServiceDefinition,\n\tSkippedService,\n} from \"./types.js\";\n\n// ── Constants ────────────────────────────────────────────────────────────────\n\n/** Services that Clawexa's cloud-init already provisions (or are mandatory platform services). */\nconst INFRA_SERVICE_IDS = new Set([\n\t\"openclaw-gateway\",\n\t\"openclaw-cli\",\n\t\"redis\",\n\t\"postgresql\",\n\t\"open-webui\",\n\t\"caddy\",\n\t\"traefik\",\n\t\"postgres-setup\",\n\t// Mandatory platform services provisioned by Clawexa cloud-init\n\t\"convex\",\n\t\"convex-dashboard\",\n\t\"mission-control\",\n]);\n\n/** Env keys managed by Clawexa's cloud-init — never include in addon env output. */\nconst CLAWEXA_MANAGED_ENV_KEYS = new Set([\n\t\"COMPOSE_FILE\",\n\t\"COMPOSE_PROFILES\",\n\t\"OPENCLAW_VERSION\",\n\t\"OPENCLAW_GATEWAY_TOKEN\",\n\t\"OPENCLAW_GATEWAY_PORT\",\n\t\"OPENCLAW_BRIDGE_PORT\",\n\t\"OPENCLAW_GATEWAY_BIND\",\n\t\"OPENCLAW_CONFIG_DIR\",\n\t\"OPENCLAW_WORKSPACE_DIR\",\n\t\"REDIS_PASSWORD\",\n\t\"REDIS_HOST\",\n\t\"REDIS_PORT\",\n\t\"POSTGRES_USER\",\n\t\"POSTGRES_PASSWORD\",\n\t\"POSTGRES_DB\",\n\t\"POSTGRES_HOST\",\n\t\"POSTGRES_PORT\",\n]);\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\n/** Sanitize instanceId into a valid Docker Compose project name. */\nfunction sanitizeProjectName(instanceId: string): string {\n\treturn instanceId\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9-]/g, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\")\n\t\t.replace(/-{2,}/g, \"-\")\n\t\t.slice(0, 64) || \"addon\";\n}\n\n/** Generate a cryptographically secure hex secret of the given byte length. */\nfunction generateHexSecret(bytes: number): string {\n\treturn randomBytes(bytes).toString(\"hex\");\n}\n\n/** Generate a cryptographically secure base64url secret of the given byte length. */\nfunction generateBase64UrlSecret(bytes: number): string {\n\treturn randomBytes(bytes).toString(\"base64url\");\n}\n\n/**\n * Check if a service requires user-provided credentials that are missing.\n * Returns the list of missing credential keys, or empty if all are satisfied.\n *\n * When `generateSecrets` is true, empty-default secrets are auto-generated\n * (passwords, tokens, etc.) and are NOT considered missing. Only secrets\n * with `validation` regex patterns (indicating specific format like API keys)\n * are flagged as missing when not provided by the user.\n */\nfunction getMissingCredentials(\n\tdef: ServiceDefinition,\n\tuserCredentials: Record<string, string> | undefined,\n\tgenerateSecrets: boolean,\n): string[] {\n\tconst missing: string[] = [];\n\tfor (const env of def.environment) {\n\t\t// Skip if not required or not a secret\n\t\tif (!env.required || !env.secret) continue;\n\t\t// Skip if it has a non-empty default value or is a reference\n\t\tif (env.defaultValue && env.defaultValue.length > 0) continue;\n\t\t// Skip if user provided the credential\n\t\tif (userCredentials?.[env.key]) continue;\n\t\t// When generateSecrets is true, empty secrets are auto-generated\n\t\t// Only flag as missing if the env var has a validation pattern\n\t\t// (indicating it needs a specific format like an API key)\n\t\tif (generateSecrets && !env.validation) continue;\n\n\t\tmissing.push(env.key);\n\t}\n\treturn missing;\n}\n\n/**\n * Apply env quirks (from service definition) to the generated env values.\n * Handles: empty_string_crashes → set fixed value, min_length → generate longer secret,\n * must_sync → ensure two keys have the same value.\n */\nfunction applyEnvQuirks(\n\tdef: ServiceDefinition,\n\tenvValues: Map<string, string>,\n\tgenerateSecrets: boolean,\n): void {\n\tif (!def.envQuirks) return;\n\n\tfor (const quirk of def.envQuirks) {\n\t\tswitch (quirk.issue) {\n\t\t\tcase \"empty_string_crashes\": {\n\t\t\t\tif (quirk.fix.type === \"set_value\" && quirk.fix.value !== undefined) {\n\t\t\t\t\tenvValues.set(quirk.key, quirk.fix.value);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"min_length\": {\n\t\t\t\tif (!generateSecrets) break;\n\t\t\t\tconst current = envValues.get(quirk.key) || \"\";\n\t\t\t\tconst minBytes = quirk.fix.minBytes || 24;\n\t\t\t\tconst minHexLen = minBytes * 2;\n\t\t\t\tif (current.length < minHexLen) {\n\t\t\t\t\tif (quirk.fix.type === \"generate_hex\") {\n\t\t\t\t\t\tenvValues.set(quirk.key, generateHexSecret(minBytes));\n\t\t\t\t\t} else if (quirk.fix.type === \"generate_base64url\") {\n\t\t\t\t\t\tenvValues.set(quirk.key, generateBase64UrlSecret(minBytes));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"must_sync\": {\n\t\t\t\tif (quirk.fix.type === \"sync_with\" && quirk.fix.syncKey) {\n\t\t\t\t\tconst sourceValue = envValues.get(quirk.key) || envValues.get(quirk.fix.syncKey);\n\t\t\t\t\tif (sourceValue) {\n\t\t\t\t\t\tenvValues.set(quirk.key, sourceValue);\n\t\t\t\t\t\tenvValues.set(quirk.fix.syncKey, sourceValue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Build proxy routes from resolved addon services.\n */\nfunction buildProxyRoutes(services: ResolvedService[]): ProxyRoute[] {\n\tconst routes: ProxyRoute[] = [];\n\tfor (const { definition: def } of services) {\n\t\tconst exposedPort = def.ports.find((p) => p.exposed);\n\t\tif (!exposedPort) continue;\n\n\t\troutes.push({\n\t\t\tserviceId: def.id,\n\t\t\tpath: def.proxyPath || `/${def.id}`,\n\t\t\tport: exposedPort.container,\n\t\t\tprotocol: \"http\",\n\t\t\tstripPrefix: true,\n\t\t});\n\t}\n\treturn routes;\n}\n\n/**\n * Build a minimal ComposeOptions suitable for addon stack generation.\n * No proxy, no gateway, no hardening by default.\n */\nfunction buildAddonComposeOptions(projectName: string, input: AddonStackInput): ComposeOptions {\n\treturn {\n\t\tprojectName,\n\t\tproxy: \"none\",\n\t\tgpu: false,\n\t\tplatform: input.platform ?? \"linux/amd64\",\n\t\tdeployment: \"clawexa\",\n\t\topenclawVersion: input.openclawVersion ?? \"latest\",\n\t\topenclawImage: \"official\",\n\t\thardened: false, // Clawexa default: no cap_drop/security_opt\n\t\topenclawInstallMethod: \"docker\",\n\t};\n}\n\n/**\n * Resolve port conflicts between addon services and reserved ports.\n * Returns a map of serviceId → { originalPort → assignedPort }.\n */\nfunction resolvePortConflicts(\n\taddonServices: ResolvedService[],\n\treservedPorts: number[],\n\tportOverrides?: Record<string, Record<string, number>>,\n): { assignments: Record<string, number>; overrides: Record<string, Record<string, number>> } {\n\tconst usedPorts = new Set(reservedPorts);\n\tconst assignments: Record<string, number> = {};\n\tconst overrides: Record<string, Record<string, number>> = {};\n\n\tfor (const { definition: def } of addonServices) {\n\t\tfor (const port of def.ports) {\n\t\t\tif (!port.exposed) continue;\n\n\t\t\t// Check for user-specified override\n\t\t\tconst userOverride = portOverrides?.[def.id]?.[String(port.host)];\n\t\t\tif (userOverride) {\n\t\t\t\tusedPorts.add(userOverride);\n\t\t\t\tassignments[`${def.id}:${port.container}`] = userOverride;\n\t\t\t\tif (!overrides[def.id]) overrides[def.id] = {};\n\t\t\t\toverrides[def.id][String(port.host)] = userOverride;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet assignedPort = port.host;\n\t\t\tif (usedPorts.has(assignedPort)) {\n\t\t\t\t// Auto-reassign to port+1000 range\n\t\t\t\tassignedPort = port.host + 1000;\n\t\t\t\twhile (usedPorts.has(assignedPort)) {\n\t\t\t\t\tassignedPort++;\n\t\t\t\t}\n\t\t\t\tif (!overrides[def.id]) overrides[def.id] = {};\n\t\t\t\toverrides[def.id][String(port.host)] = assignedPort;\n\t\t\t}\n\t\t\tusedPorts.add(assignedPort);\n\t\t\tassignments[`${def.id}:${port.container}`] = assignedPort;\n\t\t}\n\t}\n\n\treturn { assignments, overrides };\n}\n\n// ── Main: generateAddonStack ─────────────────────────────────────────────────\n\n/**\n * Generates a Docker Compose override stack containing only addon services\n * for Clawexa managed instances. Infrastructure services (gateway, redis,\n * postgres, open-webui, caddy) are excluded since Clawexa's cloud-init\n * already provisions them.\n *\n * This function never throws. Errors are reported via `warnings` and\n * `metadata.skippedServices`.\n */\nexport function generateAddonStack(rawInput: AddonStackInput): AddonStackResult {\n\tconst warnings: string[] = [];\n\tconst skippedServices: SkippedService[] = [];\n\tconst generatedSecretKeys: string[] = [];\n\n\t// 1. Parse & validate input\n\tlet input: AddonStackInput;\n\ttry {\n\t\tinput = AddonStackInputSchema.parse(rawInput);\n\t} catch (err) {\n\t\treturn emptyResult(`Invalid input: ${err instanceof Error ? err.message : String(err)}`);\n\t}\n\n\tconst projectName = sanitizeProjectName(input.instanceId);\n\n\t// 2. Filter out infrastructure service IDs from request\n\tconst addonServiceIds = input.services.filter((id) => {\n\t\tif (INFRA_SERVICE_IDS.has(id)) {\n\t\t\twarnings.push(`Service \"${id}\" is managed by Clawexa infrastructure and was excluded.`);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t});\n\n\tif (addonServiceIds.length === 0) {\n\t\treturn emptyResult(\"No addon services requested (all were infrastructure services).\");\n\t}\n\n\t// 3. Validate all service IDs exist in registry\n\tconst validServiceIds: string[] = [];\n\tfor (const id of addonServiceIds) {\n\t\tconst svc = getServiceById(id);\n\t\tif (!svc) {\n\t\t\tskippedServices.push({\n\t\t\t\tserviceId: id,\n\t\t\t\treason: \"unknown_service\",\n\t\t\t\tdetails: `Service \"${id}\" does not exist in the registry.`,\n\t\t\t});\n\t\t} else {\n\t\t\tvalidServiceIds.push(id);\n\t\t}\n\t}\n\n\tif (validServiceIds.length === 0) {\n\t\treturn {\n\t\t\t...emptyResultBase(),\n\t\t\tmetadata: {\n\t\t\t\t...emptyResultBase().metadata,\n\t\t\t\tskippedServices,\n\t\t\t},\n\t\t\twarnings: [...warnings, \"No valid addon services to deploy.\"],\n\t\t};\n\t}\n\n\t// 4. Resolve dependencies\n\tlet resolved: ResolverOutput;\n\ttry {\n\t\tresolved = resolve({\n\t\t\tservices: validServiceIds,\n\t\t\tskillPacks: input.skillPacks,\n\t\t\taiProviders: input.aiProviders,\n\t\t\tplatform: input.platform ?? \"linux/amd64\",\n\t\t});\n\t} catch (err) {\n\t\treturn {\n\t\t\t...emptyResultBase(),\n\t\t\tmetadata: {\n\t\t\t\t...emptyResultBase().metadata,\n\t\t\t\tskippedServices,\n\t\t\t},\n\t\t\twarnings: [\n\t\t\t\t...warnings,\n\t\t\t\t`Dependency resolution failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t],\n\t\t};\n\t}\n\n\t// Forward resolver warnings\n\tfor (const w of resolved.warnings) {\n\t\twarnings.push(w.message);\n\t}\n\n\t// 5. Filter resolved services: keep only addon services (not infra)\n\tconst addonResolved: ResolvedService[] = [];\n\tfor (const svc of resolved.services) {\n\t\tif (INFRA_SERVICE_IDS.has(svc.definition.id)) continue;\n\t\taddonResolved.push(svc);\n\t}\n\n\t// 6. Check credentials, images, platform, GPU for each addon service\n\tconst deployableServices: ResolvedService[] = [];\n\tfor (const svc of addonResolved) {\n\t\tconst def = svc.definition;\n\n\t\t// Check for git-based services without prebuilt image\n\t\tif (def.gitSource && def.buildContext && !def.image) {\n\t\t\tconst prebuilt = def.prebuiltImage || input.prebuiltImages[def.id];\n\t\t\tif (!prebuilt) {\n\t\t\t\tskippedServices.push({\n\t\t\t\t\tserviceId: def.id,\n\t\t\t\t\treason: \"no_image\",\n\t\t\t\t\tdetails: `Service \"${def.name}\" requires building from source but no pre-built image is available.`,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Check GPU requirement — skip if host has no GPU support\n\t\tif (def.gpuRequired && !input.gpu) {\n\t\t\tskippedServices.push({\n\t\t\t\tserviceId: def.id,\n\t\t\t\treason: \"gpu_required\",\n\t\t\t\tdetails: `Service \"${def.name}\" requires a GPU but the host does not have GPU support.`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check user-provided credentials\n\t\tconst userCreds = input.credentials[def.id];\n\t\tconst missing = getMissingCredentials(def, userCreds, input.generateSecrets);\n\t\tif (missing.length > 0) {\n\t\t\tskippedServices.push({\n\t\t\t\tserviceId: def.id,\n\t\t\t\treason: \"missing_credentials\",\n\t\t\t\tdetails: `Service \"${def.name}\" requires credentials: ${missing.join(\", \")}`,\n\t\t\t\trequiredCredentials: missing,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tdeployableServices.push(svc);\n\t}\n\n\tif (deployableServices.length === 0) {\n\t\treturn {\n\t\t\t...emptyResultBase(),\n\t\t\tmetadata: {\n\t\t\t\t...emptyResultBase().metadata,\n\t\t\t\tskippedServices,\n\t\t\t},\n\t\t\twarnings: [...warnings, \"No deployable addon services after filtering.\"],\n\t\t};\n\t}\n\n\t// 7. Resolve port conflicts (include ports from existingServices)\n\tconst allReservedPorts = [...input.reservedPorts];\n\tfor (const existingId of input.existingServices) {\n\t\tconst existingDef = getServiceById(existingId);\n\t\tif (existingDef) {\n\t\t\tfor (const port of existingDef.ports) {\n\t\t\t\tif (port.exposed) allReservedPorts.push(port.host);\n\t\t\t}\n\t\t}\n\t}\n\tconst portConflicts = resolvePortConflicts(\n\t\tdeployableServices,\n\t\tallReservedPorts,\n\t\tinput.portOverrides,\n\t);\n\n\t// Build a fake \"full\" resolved output for buildCompanionService\n\t// It needs to see all services to resolve depends_on references\n\tconst addonResolvedOutput: ResolverOutput = {\n\t\tservices: deployableServices,\n\t\taddedDependencies: resolved.addedDependencies,\n\t\tremovedConflicts: resolved.removedConflicts,\n\t\twarnings: resolved.warnings,\n\t\terrors: [],\n\t\tisValid: true,\n\t\testimatedMemoryMB: deployableServices.reduce(\n\t\t\t(sum, s) => sum + (s.definition.minMemoryMB ?? 128),\n\t\t\t0,\n\t\t),\n\t\taiProviders: input.aiProviders ?? [],\n\t\tgsdRuntimes: [],\n\t};\n\n\t// 8. Build compose options (no hardening for Clawexa)\n\tconst composeOptions = buildAddonComposeOptions(projectName, input);\n\tif (Object.keys(portConflicts.overrides).length > 0) {\n\t\tcomposeOptions.portOverrides = portConflicts.overrides;\n\t}\n\n\t// 9. Build per-service entries\n\tconst services: Record<string, Record<string, unknown>> = {};\n\tconst allVolumes = new Set<string>();\n\tconst envValues = new Map<string, string>();\n\n\tfor (const svc of deployableServices) {\n\t\tconst def = svc.definition;\n\t\ttry {\n\t\t\t// Handle prebuilt image substitution for git-based services\n\t\t\tlet effectiveDef = def;\n\t\t\tif (def.gitSource && def.buildContext && !def.image) {\n\t\t\t\tconst prebuiltImage = def.prebuiltImage || input.prebuiltImages[def.id];\n\t\t\t\tif (prebuiltImage) {\n\t\t\t\t\tconst [img, tag] = prebuiltImage.includes(\":\")\n\t\t\t\t\t\t? prebuiltImage.split(\":\")\n\t\t\t\t\t\t: [prebuiltImage, \"latest\"];\n\t\t\t\t\teffectiveDef = {\n\t\t\t\t\t\t...def,\n\t\t\t\t\t\timage: img,\n\t\t\t\t\t\timageTag: tag,\n\t\t\t\t\t\tgitSource: undefined,\n\t\t\t\t\t\tbuildContext: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst { entry, volumeNames } = buildCompanionService(\n\t\t\t\teffectiveDef,\n\t\t\t\taddonResolvedOutput,\n\t\t\t\tcomposeOptions,\n\t\t\t\tallVolumes,\n\t\t\t);\n\n\t\t\t// Remove profiles from the service entry\n\t\t\tdelete (entry as Record<string, unknown>).profiles;\n\n\t\t\t// Remove depends_on references to infrastructure services\n\t\t\tif (entry.depends_on) {\n\t\t\t\tconst deps = entry.depends_on as Record<string, { condition: string }>;\n\t\t\t\tfor (const depId of Object.keys(deps)) {\n\t\t\t\t\tif (INFRA_SERVICE_IDS.has(depId)) {\n\t\t\t\t\t\tdelete deps[depId];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (Object.keys(deps).length === 0) {\n\t\t\t\t\tdelete entry.depends_on;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tservices[def.id] = entry;\n\t\t\tfor (const v of volumeNames) allVolumes.add(v);\n\n\t\t\t// Inject user-provided credentials into env\n\t\t\tconst userCreds = input.credentials[def.id];\n\t\t\tif (userCreds) {\n\t\t\t\tfor (const [key, value] of Object.entries(userCreds)) {\n\t\t\t\t\tenvValues.set(key, value);\n\t\t\t\t}\n\t\t\t\t// Sync referenced keys: if a user provides e.g. DB_POSTGRESDB_PASSWORD\n\t\t\t\t// and the env var's defaultValue is \"${N8N_DB_PASSWORD}\", sync the ref key\n\t\t\t\t// so postgres-setup uses the same password.\n\t\t\t\tfor (const envVar of def.environment) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tuserCreds[envVar.key] &&\n\t\t\t\t\t\tenvVar.defaultValue?.startsWith(\"${\") &&\n\t\t\t\t\t\tenvVar.defaultValue?.endsWith(\"}\")\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst refKey = envVar.defaultValue.slice(2, -1);\n\t\t\t\t\t\tenvValues.set(refKey, userCreds[envVar.key]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tskippedServices.push({\n\t\t\t\tserviceId: def.id,\n\t\t\t\treason: \"resolution_error\",\n\t\t\t\tdetails: `Failed to build compose entry: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t});\n\t\t\twarnings.push(`Failed to process service \"${def.name}\": ${err instanceof Error ? err.message : String(err)}`);\n\t\t}\n\t}\n\n\t// 10. Build postgres-setup if any addon needs a DB\n\t// We need to check if any of our deployable services require DB setup\n\t// and if postgresql is in the infrastructure (it is for Clawexa)\n\tconst dbReqs = getDbRequirements(addonResolvedOutput);\n\tif (dbReqs.length > 0) {\n\t\t// Build a custom postgres-setup that references the existing PostgreSQL\n\t\t// We can't use buildPostgresSetup directly because it checks for postgresql\n\t\t// in the resolved services. Instead, build it manually.\n\t\tconst scriptLines = [\"echo '=== PostgreSQL database setup (addon) ==='\", \"FAILED=0\"];\n\n\t\tfor (const req of dbReqs) {\n\t\t\tscriptLines.push(\n\t\t\t\t`echo \"Setting up database '${req.dbName}' with user '${req.dbUser}'...\"`,\n\t\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\t`psql -c \"ALTER ROLE ${req.dbUser} WITH LOGIN PASSWORD '$$${req.passwordEnvVar}'\"`,\n\t\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\t`psql -c \"GRANT ALL PRIVILEGES ON DATABASE ${req.dbName} TO ${req.dbUser}\" || FAILED=1`,\n\t\t\t\t`echo \" Done: ${req.dbName}\"`,\n\t\t\t);\n\t\t}\n\t\tscriptLines.push(\"echo '=== All databases ready ==='\", \"exit $$FAILED\");\n\n\t\tconst dbEnv: Record<string, string> = {\n\t\t\tPGHOST: \"postgresql\",\n\t\t\tPGUSER: \"${POSTGRES_USER:-openclaw}\",\n\t\t\tPGDATABASE: \"${POSTGRES_DB:-openclaw}\",\n\t\t\tPGPASSWORD: \"${POSTGRES_PASSWORD}\",\n\t\t};\n\t\tfor (const req of dbReqs) {\n\t\t\tdbEnv[req.passwordEnvVar] = `\\${${req.passwordEnvVar}}`;\n\t\t}\n\n\t\tservices[\"postgres-setup\"] = {\n\t\t\timage: \"postgres:17-alpine\",\n\t\t\tdepends_on: {\n\t\t\t\tpostgresql: { condition: \"service_healthy\" },\n\t\t\t},\n\t\t\tenvironment: dbEnv,\n\t\t\tentrypoint: [\"/bin/sh\", \"-c\"],\n\t\t\tcommand: [scriptLines.join(\"\\n\")],\n\t\t\trestart: quotedStr(\"no\"),\n\t\t\tnetworks: [\"openclaw-network\"],\n\t\t};\n\n\t\t// Update addon services that need DB to depend on postgres-setup\n\t\tfor (const req of dbReqs) {\n\t\t\tconst svcEntry = services[req.serviceId];\n\t\t\tif (svcEntry) {\n\t\t\t\tconst deps = (svcEntry.depends_on as Record<string, { condition: string }>) || {};\n\t\t\t\tdeps[\"postgres-setup\"] = { condition: \"service_completed_successfully\" };\n\t\t\t\tsvcEntry.depends_on = deps;\n\t\t\t}\n\t\t}\n\t}\n\n\t// 11. Generate secrets and env file\n\tconst envLines: string[] = [\n\t\t\"# ═══════════════════════════════════════════════════════════════════════════════\",\n\t\t\"# OpenClaw Addon Stack Environment\",\n\t\t`# Instance: ${input.instanceId}`,\n\t\t`# Generated at ${new Date().toISOString()}`,\n\t\t\"# ═══════════════════════════════════════════════════════════════════════════════\",\n\t\t\"\",\n\t];\n\n\t// DB passwords first\n\tif (dbReqs.length > 0) {\n\t\tenvLines.push(\"# ── Per-Service Database Passwords ──────────────────────────────────────\");\n\t\tfor (const req of dbReqs) {\n\t\t\tconst secretValue = input.generateSecrets ? generateHexSecret(24) : \"\";\n\t\t\tenvValues.set(req.passwordEnvVar, secretValue);\n\t\t\tif (secretValue) generatedSecretKeys.push(req.passwordEnvVar);\n\t\t\tenvLines.push(`# PostgreSQL password for ${req.serviceName} (db: ${req.dbName}, user: ${req.dbUser})`);\n\t\t\tenvLines.push(`${req.passwordEnvVar}=${secretValue}`);\n\t\t\tenvLines.push(\"\");\n\t\t}\n\t}\n\n\t// Per-service env vars\n\tconst seenKeys = new Set<string>([...CLAWEXA_MANAGED_ENV_KEYS, ...dbReqs.map((r) => r.passwordEnvVar)]);\n\tconst envVarGroups: AddonStackResult[\"envVars\"] = [];\n\n\tfor (const svc of deployableServices) {\n\t\tconst def = svc.definition;\n\t\tconst allEnvVars = [...def.environment, ...def.openclawEnvVars];\n\t\tif (allEnvVars.length === 0) continue;\n\n\t\tconst groupVars: AddonStackResult[\"envVars\"][number][\"vars\"] = [];\n\n\t\tenvLines.push(`# ── ${def.icon} ${def.name} ──────────────────────────────────────`);\n\n\t\tfor (const envVar of allEnvVars) {\n\t\t\tif (seenKeys.has(envVar.key)) continue;\n\t\t\tseenKeys.add(envVar.key);\n\n\t\t\t// Check if user provided this credential\n\t\t\tconst userValue = input.credentials[def.id]?.[envVar.key];\n\t\t\tlet actualValue: string;\n\n\t\t\tif (userValue !== undefined) {\n\t\t\t\tactualValue = userValue;\n\t\t\t} else if (envVar.secret) {\n\t\t\t\t// Resolve references like ${N8N_DB_PASSWORD}\n\t\t\t\tif (envVar.defaultValue.startsWith(\"${\") && envVar.defaultValue.endsWith(\"}\")) {\n\t\t\t\t\tconst refKey = envVar.defaultValue.slice(2, -1);\n\t\t\t\t\tactualValue = envValues.get(refKey) || envVar.defaultValue;\n\t\t\t\t} else if (input.generateSecrets) {\n\t\t\t\t\tactualValue = generateHexSecret(24);\n\t\t\t\t\tgeneratedSecretKeys.push(envVar.key);\n\t\t\t\t} else {\n\t\t\t\t\tactualValue = envVar.defaultValue;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tactualValue = envVar.defaultValue;\n\t\t\t}\n\n\t\t\tenvValues.set(envVar.key, actualValue);\n\n\t\t\tenvLines.push(`# ${envVar.description}`);\n\t\t\tenvLines.push(`${envVar.key}=${actualValue}`);\n\t\t\tenvLines.push(\"\");\n\n\t\t\tgroupVars.push({\n\t\t\t\tkey: envVar.key,\n\t\t\t\tdescription: envVar.description,\n\t\t\t\tvalue: actualValue,\n\t\t\t\tsecret: envVar.secret,\n\t\t\t});\n\t\t}\n\n\t\tif (groupVars.length > 0) {\n\t\t\tenvVarGroups.push({\n\t\t\t\tserviceName: def.name,\n\t\t\t\tvars: groupVars,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Apply env quirks after all values are generated\n\tfor (const svc of deployableServices) {\n\t\tapplyEnvQuirks(svc.definition, envValues, input.generateSecrets);\n\t}\n\n\t// Rebuild env lines from envValues (quirks may have modified values or introduced new keys)\n\tconst quirkedKeys = new Set<string>();\n\tconst finalEnvLines: string[] = [];\n\tfor (const line of envLines) {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed || trimmed.startsWith(\"#\")) {\n\t\t\tfinalEnvLines.push(line);\n\t\t\tcontinue;\n\t\t}\n\t\tconst eqIdx = trimmed.indexOf(\"=\");\n\t\tif (eqIdx > 0) {\n\t\t\tconst key = trimmed.slice(0, eqIdx);\n\t\t\tquirkedKeys.add(key);\n\t\t\tconst fixedValue = envValues.get(key);\n\t\t\tif (fixedValue !== undefined) {\n\t\t\t\tfinalEnvLines.push(`${key}=${fixedValue}`);\n\t\t\t} else {\n\t\t\t\tfinalEnvLines.push(line);\n\t\t\t}\n\t\t} else {\n\t\t\tfinalEnvLines.push(line);\n\t\t}\n\t}\n\t// Append any new keys introduced by quirks (e.g., must_sync creating a new key)\n\tfor (const [key, value] of envValues) {\n\t\tif (!quirkedKeys.has(key) && !seenKeys.has(key) && !CLAWEXA_MANAGED_ENV_KEYS.has(key)) {\n\t\t\tfinalEnvLines.push(`# Synced by env quirk`);\n\t\t\tfinalEnvLines.push(`${key}=${value}`);\n\t\t\tfinalEnvLines.push(\"\");\n\t\t}\n\t}\n\tconst envFile = finalEnvLines.join(\"\\n\");\n\n\t// 12. Generate skill files\n\tconst skillFiles = generateSkillFiles(addonResolvedOutput);\n\n\t// 13. Build openclaw config patch\n\tconst skillEntries: Record<string, { enabled: boolean }> = {};\n\tlet skillCount = 0;\n\tfor (const svc of deployableServices) {\n\t\tfor (const skill of svc.definition.skills) {\n\t\t\tif (skill.autoInstall) {\n\t\t\t\tskillEntries[skill.skillId] = { enabled: true };\n\t\t\t\tskillCount++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// 14. Build proxy routes\n\tconst proxyRoutes = buildProxyRoutes(deployableServices);\n\n\t// 14b. Build additional files (e.g. sandbox.toml for opensandbox)\n\tconst additionalFiles: Record<string, string> = {};\n\tif (deployableServices.some((s) => s.definition.id === \"opensandbox\")) {\n\t\tadditionalFiles[\"sandbox.toml\"] = [\n\t\t\t\"[server]\",\n\t\t\t'host = \"0.0.0.0\"',\n\t\t\t\"port = 8080\",\n\t\t\t'log_level = \"INFO\"',\n\t\t\t'api_key = \"${OPEN_SANDBOX_API_KEY}\"',\n\t\t\t\"\",\n\t\t\t\"[runtime]\",\n\t\t\t'type = \"docker\"',\n\t\t\t'execd_image = \"opensandbox/execd:v1.0.6\"',\n\t\t\t\"\",\n\t\t\t\"[docker]\",\n\t\t\t\"network_mode = \\\"bridge\\\"\",\n\t\t\t'drop_capabilities = [\"NET_ADMIN\", \"SYS_ADMIN\", \"SYS_PTRACE\", \"MKNOD\", \"NET_RAW\", \"SYS_RAWIO\"]',\n\t\t\t\"no_new_privileges = true\",\n\t\t\t\"pids_limit = 512\",\n\t\t\t\"\",\n\t\t\t\"[secure_runtime]\",\n\t\t\t'type = \"gvisor\"',\n\t\t\t\"\",\n\t\t].join(\"\\n\");\n\t}\n\n\t// 14c. Build pre-pull images list\n\tconst prePullImages: Array<{ image: string; priority: 1 | 2 | 3 }> = [];\n\tif (deployableServices.some((s) => s.definition.id === \"opensandbox\")) {\n\t\tprePullImages.push(\n\t\t\t// Priority 1: always pulled (core + Homespace)\n\t\t\t{ image: \"opensandbox/server:v1.0.6\", priority: 1 },\n\t\t\t{ image: \"opensandbox/execd:v1.0.6\", priority: 1 },\n\t\t\t{ image: \"opensandbox/desktop:latest\", priority: 1 },\n\t\t\t{ image: \"opensandbox/chrome:latest\", priority: 1 },\n\t\t\t// Priority 2: recommended (common languages)\n\t\t\t{ image: \"opensandbox/code-interpreter:python\", priority: 2 },\n\t\t\t{ image: \"opensandbox/code-interpreter:node\", priority: 2 },\n\t\t\t// Priority 3: optional (full multi-lang and IDE)\n\t\t\t{ image: \"opensandbox/code-interpreter:latest\", priority: 3 },\n\t\t\t{ image: \"opensandbox/vscode:latest\", priority: 3 },\n\t\t);\n\t}\n\n\t// 15. Compose single YAML\n\tconst volumeMap: Record<string, null> = {};\n\tfor (const v of allVolumes) {\n\t\tvolumeMap[v] = null;\n\t}\n\n\tconst composeDoc: Record<string, unknown> = {\n\t\tservices,\n\t};\n\n\tif (Object.keys(volumeMap).length > 0) {\n\t\tcomposeDoc.volumes = volumeMap;\n\t}\n\n\tcomposeDoc.networks = {\n\t\t\"openclaw-network\": {\n\t\t\texternal: true,\n\t\t},\n\t};\n\n\tconst composeOverride = stringify(composeDoc, YAML_OPTIONS);\n\n\t// 16. Return result\n\treturn {\n\t\tcomposeOverride,\n\t\tenvFile,\n\t\tenvVars: envVarGroups,\n\t\tskillFiles,\n\t\topenclawConfigPatch: {\n\t\t\tskills: { entries: skillEntries },\n\t\t},\n\t\tproxyRoutes,\n\t\tadditionalFiles,\n\t\tmetadata: {\n\t\t\tserviceCount: Object.keys(services).length,\n\t\t\tskillCount,\n\t\t\testimatedMemoryMB: addonResolvedOutput.estimatedMemoryMB,\n\t\t\tresolvedServices: deployableServices.map((s) => s.definition.id),\n\t\t\tskippedServices,\n\t\t\tgeneratedSecretKeys,\n\t\t\tportAssignments: portConflicts.assignments,\n\t\t\tprePullImages,\n\t\t},\n\t\twarnings,\n\t};\n}\n\n// ── Main: updateAddonStack ───────────────────────────────────────────────────\n\n/**\n * Incrementally updates an existing addon stack by adding or removing services.\n * Preserves existing env values (never overwrites user-customized values).\n *\n * This function never throws.\n */\nexport function updateAddonStack(rawInput: AddonStackUpdateInput): AddonStackUpdateResult {\n\tconst warnings: string[] = [];\n\n\t// 1. Parse & validate\n\tlet input: AddonStackUpdateInput;\n\ttry {\n\t\tinput = AddonStackUpdateInputSchema.parse(rawInput);\n\t} catch (err) {\n\t\treturn emptyUpdateResult(`Invalid input: ${err instanceof Error ? err.message : String(err)}`);\n\t}\n\n\t// 2. Parse existing compose YAML to extract current service list\n\tlet currentServiceIds: string[] = [];\n\ttry {\n\t\tconst existingCompose = parseYaml(input.currentCompose);\n\t\tif (existingCompose?.services && typeof existingCompose.services === \"object\") {\n\t\t\tcurrentServiceIds = Object.keys(existingCompose.services).filter(\n\t\t\t\t(id) => id !== \"postgres-setup\",\n\t\t\t);\n\t\t}\n\t} catch (err) {\n\t\twarnings.push(\n\t\t\t`Failed to parse existing compose YAML: ${err instanceof Error ? err.message : String(err)}`,\n\t\t);\n\t}\n\n\t// 3. Parse existing env into a map\n\tconst existingEnvMap = new Map<string, string>();\n\tfor (const line of input.currentEnv.split(\"\\n\")) {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed || trimmed.startsWith(\"#\")) continue;\n\t\tconst eqIdx = trimmed.indexOf(\"=\");\n\t\tif (eqIdx > 0) {\n\t\t\texistingEnvMap.set(trimmed.slice(0, eqIdx), trimmed.slice(eqIdx + 1));\n\t\t}\n\t}\n\n\t// 4. Compute desired service list\n\tconst addSet = new Set(input.addServices);\n\tconst removeSet = new Set(input.removeServices);\n\tconst desiredServiceIds = [\n\t\t...currentServiceIds.filter((id) => !removeSet.has(id)),\n\t\t...input.addServices.filter((id) => !currentServiceIds.includes(id)),\n\t];\n\n\t// 5. Generate the full target state\n\tconst targetResult = generateAddonStack({\n\t\tinstanceId: input.instanceId,\n\t\tservices: desiredServiceIds,\n\t\tskillPacks: [],\n\t\tplatform: input.platform,\n\t\topenclawVersion: input.openclawVersion,\n\t\treservedPorts: input.reservedPorts,\n\t\tgenerateSecrets: input.generateSecrets,\n\t\tcredentials: input.credentials,\n\t\tportOverrides: input.portOverrides,\n\t\taiProviders: input.aiProviders,\n\t\tprebuiltImages: input.prebuiltImages,\n\t});\n\n\t// 6. Merge env: preserve existing values, only add new ones\n\tconst mergedEnvLines: string[] = [];\n\tfor (const line of targetResult.envFile.split(\"\\n\")) {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed || trimmed.startsWith(\"#\")) {\n\t\t\tmergedEnvLines.push(line);\n\t\t\tcontinue;\n\t\t}\n\t\tconst eqIdx = trimmed.indexOf(\"=\");\n\t\tif (eqIdx > 0) {\n\t\t\tconst key = trimmed.slice(0, eqIdx);\n\t\t\tif (existingEnvMap.has(key)) {\n\t\t\t\t// Preserve existing value\n\t\t\t\tmergedEnvLines.push(`${key}=${existingEnvMap.get(key)}`);\n\t\t\t} else {\n\t\t\t\tmergedEnvLines.push(line);\n\t\t\t}\n\t\t} else {\n\t\t\tmergedEnvLines.push(line);\n\t\t}\n\t}\n\n\t// 7. Compute diffs\n\tconst currentSet = new Set(currentServiceIds);\n\tconst targetSet = new Set(targetResult.metadata.resolvedServices);\n\tconst added = [...targetSet].filter((id) => !currentSet.has(id));\n\tconst removed = [...currentSet].filter((id) => !targetSet.has(id));\n\tconst unchanged = [...currentSet].filter((id) => targetSet.has(id));\n\n\t// 8. Compute skill diffs\n\tconst newSkillFiles: Record<string, string> = {};\n\tconst removedSkillSlugs: string[] = [];\n\n\t// New skills from added services\n\tfor (const id of added) {\n\t\tconst def = getServiceById(id);\n\t\tif (!def) continue;\n\t\tfor (const skill of def.skills) {\n\t\t\tconst skillPath = Object.keys(targetResult.skillFiles).find(\n\t\t\t\t(path) => path.includes(skill.skillId),\n\t\t\t);\n\t\t\tif (skillPath) {\n\t\t\t\tnewSkillFiles[skillPath] = targetResult.skillFiles[skillPath];\n\t\t\t}\n\t\t}\n\t}\n\n\t// Removed skills from removed services\n\tfor (const id of removed) {\n\t\tconst def = getServiceById(id);\n\t\tif (!def) continue;\n\t\tfor (const skill of def.skills) {\n\t\t\tremovedSkillSlugs.push(skill.skillId);\n\t\t}\n\t}\n\n\t// 9. Proxy route diffs\n\tconst addProxyRoutes = targetResult.proxyRoutes.filter((r) => added.includes(r.serviceId));\n\tconst removeProxyRoutes = removed;\n\n\t// 10. Images to pull for new services\n\tconst imagesToPull: string[] = [];\n\tfor (const id of added) {\n\t\tconst def = getServiceById(id);\n\t\tif (def?.image && def?.imageTag) {\n\t\t\timagesToPull.push(`${def.image}:${def.imageTag}`);\n\t\t} else if (def?.prebuiltImage) {\n\t\t\timagesToPull.push(def.prebuiltImage);\n\t\t}\n\t}\n\n\t// 11. Estimate memory delta\n\tlet memoryDelta = 0;\n\tfor (const id of added) {\n\t\tconst def = getServiceById(id);\n\t\tmemoryDelta += def?.minMemoryMB ?? 128;\n\t}\n\tfor (const id of removed) {\n\t\tconst def = getServiceById(id);\n\t\tmemoryDelta -= def?.minMemoryMB ?? 128;\n\t}\n\n\t// Add existing skills to the config patch\n\tconst addSkillEntries: Record<string, { enabled: boolean }> = {};\n\tfor (const id of added) {\n\t\tconst def = getServiceById(id);\n\t\tif (!def) continue;\n\t\tfor (const skill of def.skills) {\n\t\t\tif (skill.autoInstall) {\n\t\t\t\taddSkillEntries[skill.skillId] = { enabled: true };\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tcomposeOverride: targetResult.composeOverride,\n\t\tenvFile: mergedEnvLines.join(\"\\n\"),\n\t\tnewSkillFiles,\n\t\tremovedSkillSlugs,\n\t\topenclawConfigPatch: {\n\t\t\tskills: {\n\t\t\t\tadd: addSkillEntries,\n\t\t\t\tremove: removedSkillSlugs,\n\t\t\t},\n\t\t},\n\t\taddProxyRoutes,\n\t\tremoveProxyRoutes,\n\t\timagesToPull,\n\t\trestartRequired: added, // New services need starting, not restarting\n\t\tmetadata: {\n\t\t\tadded,\n\t\t\tremoved,\n\t\t\tunchanged,\n\t\t\testimatedMemoryDelta: memoryDelta,\n\t\t},\n\t\twarnings: [...warnings, ...targetResult.warnings],\n\t};\n}\n\n// ── Empty Result Helpers ─────────────────────────────────────────────────────\n\nfunction emptyResultBase(): AddonStackResult {\n\treturn {\n\t\tcomposeOverride: \"services: {}\\n\",\n\t\tenvFile: \"\",\n\t\tenvVars: [],\n\t\tskillFiles: {},\n\t\topenclawConfigPatch: { skills: { entries: {} } },\n\t\tproxyRoutes: [],\n\t\tadditionalFiles: {},\n\t\tmetadata: {\n\t\t\tserviceCount: 0,\n\t\t\tskillCount: 0,\n\t\t\testimatedMemoryMB: 0,\n\t\t\tresolvedServices: [],\n\t\t\tskippedServices: [],\n\t\t\tgeneratedSecretKeys: [],\n\t\t\tportAssignments: {},\n\t\t\tprePullImages: [],\n\t\t},\n\t\twarnings: [],\n\t};\n}\n\nfunction emptyResult(warning: string): AddonStackResult {\n\treturn {\n\t\t...emptyResultBase(),\n\t\twarnings: [warning],\n\t};\n}\n\nfunction emptyUpdateResult(warning: string): AddonStackUpdateResult {\n\treturn {\n\t\tcomposeOverride: \"services: {}\\n\",\n\t\tenvFile: \"\",\n\t\tnewSkillFiles: {},\n\t\tremovedSkillSlugs: [],\n\t\topenclawConfigPatch: { skills: { add: {}, remove: [] } },\n\t\taddProxyRoutes: [],\n\t\tremoveProxyRoutes: [],\n\t\timagesToPull: [],\n\t\trestartRequired: [],\n\t\tmetadata: {\n\t\t\tadded: [],\n\t\t\tremoved: [],\n\t\t\tunchanged: [],\n\t\t\testimatedMemoryDelta: 0,\n\t\t},\n\t\twarnings: [warning],\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"addon-stack.mjs","names":["parseYaml"],"sources":["../src/addon-stack.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { parse as parseYaml, stringify } from \"yaml\";\nimport { buildCompanionService, quotedStr, YAML_OPTIONS } from \"./composer.js\";\nimport { getFrameworkById } from \"./frameworks/index.js\";\nimport type { AgentFrameworkDefinition } from \"./frameworks/types.js\";\nimport { getDbRequirements } from \"./generators/postgres-init.js\";\nimport { generateSkillFiles } from \"./generators/skills.js\";\nimport { resolve } from \"./resolver.js\";\nimport { AddonStackInputSchema, AddonStackUpdateInputSchema } from \"./schema.js\";\nimport { getServiceById } from \"./services/registry.js\";\nimport type {\n\tAddonStackInput,\n\tAddonStackResult,\n\tAddonStackUpdateInput,\n\tAddonStackUpdateResult,\n\tComposeOptions,\n\tProxyRoute,\n\tResolvedService,\n\tResolverOutput,\n\tServiceDefinition,\n\tSkippedService,\n} from \"./types.js\";\n\n// ── Constants ────────────────────────────────────────────────────────────────\n\n/** Base infrastructure service IDs that Clawexa's cloud-init provisions. */\nconst BASE_INFRA_SERVICE_IDS = [\n\t\"redis\",\n\t\"postgresql\",\n\t\"open-webui\",\n\t\"caddy\",\n\t\"traefik\",\n\t\"postgres-setup\",\n];\n\n/** Build the full set of infra IDs for a given framework. */\nfunction getInfraServiceIds(framework: AgentFrameworkDefinition): Set<string> {\n\treturn new Set([\n\t\t`${framework.id}-gateway`,\n\t\t`${framework.id}-cli`,\n\t\t...BASE_INFRA_SERVICE_IDS,\n\t\t...framework.getMandatoryServices(),\n\t]);\n}\n\n/** Env keys managed by Clawexa's cloud-init — never include in addon env output. */\nconst CLAWEXA_MANAGED_ENV_KEYS = new Set([\n\t\"COMPOSE_FILE\",\n\t\"COMPOSE_PROFILES\",\n\t\"REDIS_PASSWORD\",\n\t\"REDIS_HOST\",\n\t\"REDIS_PORT\",\n\t\"POSTGRES_USER\",\n\t\"POSTGRES_PASSWORD\",\n\t\"POSTGRES_DB\",\n\t\"POSTGRES_HOST\",\n\t\"POSTGRES_PORT\",\n]);\n\n/** Build the full set of managed env keys for a given framework. */\nfunction getManagedEnvKeys(framework: AgentFrameworkDefinition): Set<string> {\n\tconst keys = new Set(CLAWEXA_MANAGED_ENV_KEYS);\n\tfor (const envLine of framework.getBaseEnvVars({\n\t\tgenerateSecrets: false,\n\t\tframeworkVersion: \"latest\",\n\t\tframeworkImageVariant: \"official\",\n\t})) {\n\t\tkeys.add(envLine.key);\n\t}\n\treturn keys;\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\n/** Sanitize instanceId into a valid Docker Compose project name. */\nfunction sanitizeProjectName(instanceId: string): string {\n\treturn (\n\t\tinstanceId\n\t\t\t.toLowerCase()\n\t\t\t.replace(/[^a-z0-9-]/g, \"-\")\n\t\t\t.replace(/^-+|-+$/g, \"\")\n\t\t\t.replace(/-{2,}/g, \"-\")\n\t\t\t.slice(0, 64) || \"addon\"\n\t);\n}\n\n/** Generate a cryptographically secure hex secret of the given byte length. */\nfunction generateHexSecret(bytes: number): string {\n\treturn randomBytes(bytes).toString(\"hex\");\n}\n\n/** Generate a cryptographically secure base64url secret of the given byte length. */\nfunction generateBase64UrlSecret(bytes: number): string {\n\treturn randomBytes(bytes).toString(\"base64url\");\n}\n\n/**\n * Check if a service requires user-provided credentials that are missing.\n * Returns the list of missing credential keys, or empty if all are satisfied.\n *\n * When `generateSecrets` is true, empty-default secrets are auto-generated\n * (passwords, tokens, etc.) and are NOT considered missing. Only secrets\n * with `validation` regex patterns (indicating specific format like API keys)\n * are flagged as missing when not provided by the user.\n */\nfunction getMissingCredentials(\n\tdef: ServiceDefinition,\n\tuserCredentials: Record<string, string> | undefined,\n\tgenerateSecrets: boolean,\n): string[] {\n\tconst missing: string[] = [];\n\tfor (const env of def.environment) {\n\t\t// Skip if not required or not a secret\n\t\tif (!env.required || !env.secret) continue;\n\t\t// Skip if it has a non-empty default value or is a reference\n\t\tif (env.defaultValue && env.defaultValue.length > 0) continue;\n\t\t// Skip if user provided the credential\n\t\tif (userCredentials?.[env.key]) continue;\n\t\t// When generateSecrets is true, empty secrets are auto-generated\n\t\t// Only flag as missing if the env var has a validation pattern\n\t\t// (indicating it needs a specific format like an API key)\n\t\tif (generateSecrets && !env.validation) continue;\n\n\t\tmissing.push(env.key);\n\t}\n\treturn missing;\n}\n\n/**\n * Apply env quirks (from service definition) to the generated env values.\n * Handles: empty_string_crashes → set fixed value, min_length → generate longer secret,\n * must_sync → ensure two keys have the same value.\n */\nfunction applyEnvQuirks(\n\tdef: ServiceDefinition,\n\tenvValues: Map<string, string>,\n\tgenerateSecrets: boolean,\n): void {\n\tif (!def.envQuirks) return;\n\n\tfor (const quirk of def.envQuirks) {\n\t\tswitch (quirk.issue) {\n\t\t\tcase \"empty_string_crashes\": {\n\t\t\t\tif (quirk.fix.type === \"set_value\" && quirk.fix.value !== undefined) {\n\t\t\t\t\tenvValues.set(quirk.key, quirk.fix.value);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"min_length\": {\n\t\t\t\tif (!generateSecrets) break;\n\t\t\t\tconst current = envValues.get(quirk.key) || \"\";\n\t\t\t\tconst minBytes = quirk.fix.minBytes || 24;\n\t\t\t\tconst minHexLen = minBytes * 2;\n\t\t\t\tif (current.length < minHexLen) {\n\t\t\t\t\tif (quirk.fix.type === \"generate_hex\") {\n\t\t\t\t\t\tenvValues.set(quirk.key, generateHexSecret(minBytes));\n\t\t\t\t\t} else if (quirk.fix.type === \"generate_base64url\") {\n\t\t\t\t\t\tenvValues.set(quirk.key, generateBase64UrlSecret(minBytes));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"must_sync\": {\n\t\t\t\tif (quirk.fix.type === \"sync_with\" && quirk.fix.syncKey) {\n\t\t\t\t\tconst sourceValue = envValues.get(quirk.key) || envValues.get(quirk.fix.syncKey);\n\t\t\t\t\tif (sourceValue) {\n\t\t\t\t\t\tenvValues.set(quirk.key, sourceValue);\n\t\t\t\t\t\tenvValues.set(quirk.fix.syncKey, sourceValue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Build proxy routes from resolved addon services.\n */\nfunction buildProxyRoutes(services: ResolvedService[]): ProxyRoute[] {\n\tconst routes: ProxyRoute[] = [];\n\tfor (const { definition: def } of services) {\n\t\tconst exposedPort = def.ports.find((p) => p.exposed);\n\t\tif (!exposedPort) continue;\n\n\t\troutes.push({\n\t\t\tserviceId: def.id,\n\t\t\tpath: def.proxyPath || `/${def.id}`,\n\t\t\tport: exposedPort.container,\n\t\t\tprotocol: \"http\",\n\t\t\tstripPrefix: true,\n\t\t});\n\t}\n\treturn routes;\n}\n\n/**\n * Build a minimal ComposeOptions suitable for addon stack generation.\n * No proxy, no gateway, no hardening by default.\n */\nfunction buildAddonComposeOptions(projectName: string, input: AddonStackInput): ComposeOptions {\n\treturn {\n\t\tprojectName,\n\t\tproxy: \"none\",\n\t\tgpu: false,\n\t\tplatform: input.platform ?? \"linux/amd64\",\n\t\tdeployment: \"clawexa\",\n\t\topenclawVersion: input.openclawVersion ?? \"latest\",\n\t\topenclawImage: \"official\",\n\t\thardened: false, // Clawexa default: no cap_drop/security_opt\n\t\topenclawInstallMethod: \"docker\",\n\t};\n}\n\n/**\n * Resolve port conflicts between addon services and reserved ports.\n * Returns a map of serviceId → { originalPort → assignedPort }.\n */\nfunction resolvePortConflicts(\n\taddonServices: ResolvedService[],\n\treservedPorts: number[],\n\tportOverrides?: Record<string, Record<string, number>>,\n): { assignments: Record<string, number>; overrides: Record<string, Record<string, number>> } {\n\tconst usedPorts = new Set(reservedPorts);\n\tconst assignments: Record<string, number> = {};\n\tconst overrides: Record<string, Record<string, number>> = {};\n\n\tfor (const { definition: def } of addonServices) {\n\t\tfor (const port of def.ports) {\n\t\t\tif (!port.exposed) continue;\n\n\t\t\t// Check for user-specified override\n\t\t\tconst userOverride = portOverrides?.[def.id]?.[String(port.host)];\n\t\t\tif (userOverride) {\n\t\t\t\tusedPorts.add(userOverride);\n\t\t\t\tassignments[`${def.id}:${port.container}`] = userOverride;\n\t\t\t\toverrides[def.id] ??= {};\n\t\t\t\toverrides[def.id]![String(port.host)] = userOverride;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet assignedPort = port.host;\n\t\t\tif (usedPorts.has(assignedPort)) {\n\t\t\t\t// Auto-reassign to port+1000 range\n\t\t\t\tassignedPort = port.host + 1000;\n\t\t\t\twhile (usedPorts.has(assignedPort)) {\n\t\t\t\t\tassignedPort++;\n\t\t\t\t}\n\t\t\t\toverrides[def.id] ??= {};\n\t\t\t\toverrides[def.id]![String(port.host)] = assignedPort;\n\t\t\t}\n\t\t\tusedPorts.add(assignedPort);\n\t\t\tassignments[`${def.id}:${port.container}`] = assignedPort;\n\t\t}\n\t}\n\n\treturn { assignments, overrides };\n}\n\n// ── Main: generateAddonStack ─────────────────────────────────────────────────\n\n/**\n * Generates a Docker Compose override stack containing only addon services\n * for Clawexa managed instances. Infrastructure services (gateway, redis,\n * postgres, open-webui, caddy) are excluded since Clawexa's cloud-init\n * already provisions them.\n *\n * This function never throws. Errors are reported via `warnings` and\n * `metadata.skippedServices`.\n */\nexport function generateAddonStack(rawInput: AddonStackInput): AddonStackResult {\n\tconst warnings: string[] = [];\n\tconst skippedServices: SkippedService[] = [];\n\tconst generatedSecretKeys: string[] = [];\n\n\t// 1. Parse & validate input\n\tlet input: AddonStackInput;\n\ttry {\n\t\tinput = AddonStackInputSchema.parse(rawInput);\n\t} catch (err) {\n\t\treturn emptyResult(`Invalid input: ${err instanceof Error ? err.message : String(err)}`);\n\t}\n\n\tconst projectName = sanitizeProjectName(input.instanceId);\n\n\t// Resolve framework for this addon stack (defaults to openclaw)\n\tconst framework = getFrameworkById(\"openclaw\")!;\n\tconst infraIds = getInfraServiceIds(framework);\n\tconst managedKeys = getManagedEnvKeys(framework);\n\n\t// 2. Filter out infrastructure service IDs from request\n\tconst addonServiceIds = input.services.filter((id) => {\n\t\tif (infraIds.has(id)) {\n\t\t\twarnings.push(`Service \"${id}\" is managed by Clawexa infrastructure and was excluded.`);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t});\n\n\tif (addonServiceIds.length === 0) {\n\t\treturn emptyResult(\"No addon services requested (all were infrastructure services).\");\n\t}\n\n\t// 3. Validate all service IDs exist in registry\n\tconst validServiceIds: string[] = [];\n\tfor (const id of addonServiceIds) {\n\t\tconst svc = getServiceById(id);\n\t\tif (!svc) {\n\t\t\tskippedServices.push({\n\t\t\t\tserviceId: id,\n\t\t\t\treason: \"unknown_service\",\n\t\t\t\tdetails: `Service \"${id}\" does not exist in the registry.`,\n\t\t\t});\n\t\t} else {\n\t\t\tvalidServiceIds.push(id);\n\t\t}\n\t}\n\n\tif (validServiceIds.length === 0) {\n\t\treturn {\n\t\t\t...emptyResultBase(),\n\t\t\tmetadata: {\n\t\t\t\t...emptyResultBase().metadata,\n\t\t\t\tskippedServices,\n\t\t\t},\n\t\t\twarnings: [...warnings, \"No valid addon services to deploy.\"],\n\t\t};\n\t}\n\n\t// 4. Resolve dependencies\n\tlet resolved: ResolverOutput;\n\ttry {\n\t\tresolved = resolve({\n\t\t\tservices: validServiceIds,\n\t\t\tskillPacks: input.skillPacks,\n\t\t\taiProviders: input.aiProviders,\n\t\t\tplatform: input.platform ?? \"linux/amd64\",\n\t\t});\n\t} catch (err) {\n\t\treturn {\n\t\t\t...emptyResultBase(),\n\t\t\tmetadata: {\n\t\t\t\t...emptyResultBase().metadata,\n\t\t\t\tskippedServices,\n\t\t\t},\n\t\t\twarnings: [\n\t\t\t\t...warnings,\n\t\t\t\t`Dependency resolution failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t],\n\t\t};\n\t}\n\n\t// Forward resolver warnings\n\tfor (const w of resolved.warnings) {\n\t\twarnings.push(w.message);\n\t}\n\n\t// 5. Filter resolved services: keep only addon services (not infra)\n\tconst addonResolved: ResolvedService[] = [];\n\tfor (const svc of resolved.services) {\n\t\tif (infraIds.has(svc.definition.id)) continue;\n\t\taddonResolved.push(svc);\n\t}\n\n\t// 6. Check credentials, images, platform, GPU for each addon service\n\tconst deployableServices: ResolvedService[] = [];\n\tfor (const svc of addonResolved) {\n\t\tconst def = svc.definition;\n\n\t\t// Check for git-based services without prebuilt image\n\t\tif (def.gitSource && def.buildContext && !def.image) {\n\t\t\tconst prebuilt = def.prebuiltImage || input.prebuiltImages[def.id];\n\t\t\tif (!prebuilt) {\n\t\t\t\tskippedServices.push({\n\t\t\t\t\tserviceId: def.id,\n\t\t\t\t\treason: \"no_image\",\n\t\t\t\t\tdetails: `Service \"${def.name}\" requires building from source but no pre-built image is available.`,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Check GPU requirement — skip if host has no GPU support\n\t\tif (def.gpuRequired && !input.gpu) {\n\t\t\tskippedServices.push({\n\t\t\t\tserviceId: def.id,\n\t\t\t\treason: \"gpu_required\",\n\t\t\t\tdetails: `Service \"${def.name}\" requires a GPU but the host does not have GPU support.`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check user-provided credentials\n\t\tconst userCreds = input.credentials[def.id];\n\t\tconst missing = getMissingCredentials(def, userCreds, input.generateSecrets);\n\t\tif (missing.length > 0) {\n\t\t\tskippedServices.push({\n\t\t\t\tserviceId: def.id,\n\t\t\t\treason: \"missing_credentials\",\n\t\t\t\tdetails: `Service \"${def.name}\" requires credentials: ${missing.join(\", \")}`,\n\t\t\t\trequiredCredentials: missing,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tdeployableServices.push(svc);\n\t}\n\n\tif (deployableServices.length === 0) {\n\t\treturn {\n\t\t\t...emptyResultBase(),\n\t\t\tmetadata: {\n\t\t\t\t...emptyResultBase().metadata,\n\t\t\t\tskippedServices,\n\t\t\t},\n\t\t\twarnings: [...warnings, \"No deployable addon services after filtering.\"],\n\t\t};\n\t}\n\n\t// 7. Resolve port conflicts (include ports from existingServices)\n\tconst allReservedPorts = [...input.reservedPorts];\n\tfor (const existingId of input.existingServices) {\n\t\tconst existingDef = getServiceById(existingId);\n\t\tif (existingDef) {\n\t\t\tfor (const port of existingDef.ports) {\n\t\t\t\tif (port.exposed) allReservedPorts.push(port.host);\n\t\t\t}\n\t\t}\n\t}\n\tconst portConflicts = resolvePortConflicts(\n\t\tdeployableServices,\n\t\tallReservedPorts,\n\t\tinput.portOverrides,\n\t);\n\n\t// Build a fake \"full\" resolved output for buildCompanionService\n\t// It needs to see all services to resolve depends_on references\n\tconst addonResolvedOutput: ResolverOutput = {\n\t\tservices: deployableServices,\n\t\taddedDependencies: resolved.addedDependencies,\n\t\tremovedConflicts: resolved.removedConflicts,\n\t\twarnings: resolved.warnings,\n\t\terrors: [],\n\t\tisValid: true,\n\t\testimatedMemoryMB: deployableServices.reduce(\n\t\t\t(sum, s) => sum + (s.definition.minMemoryMB ?? 128),\n\t\t\t0,\n\t\t),\n\t\taiProviders: input.aiProviders ?? [],\n\t\tgsdRuntimes: [],\n\t};\n\n\t// 8. Build compose options (no hardening for Clawexa)\n\tconst composeOptions = buildAddonComposeOptions(projectName, input);\n\tif (Object.keys(portConflicts.overrides).length > 0) {\n\t\tcomposeOptions.portOverrides = portConflicts.overrides;\n\t}\n\n\t// 9. Build per-service entries\n\tconst services: Record<string, Record<string, unknown>> = {};\n\tconst allVolumes = new Set<string>();\n\tconst envValues = new Map<string, string>();\n\n\tfor (const svc of deployableServices) {\n\t\tconst def = svc.definition;\n\t\ttry {\n\t\t\t// Handle prebuilt image substitution for git-based services\n\t\t\tlet effectiveDef = def;\n\t\t\tif (def.gitSource && def.buildContext && !def.image) {\n\t\t\t\tconst prebuiltImage = def.prebuiltImage || input.prebuiltImages[def.id];\n\t\t\t\tif (prebuiltImage) {\n\t\t\t\t\tconst [img, tag] = prebuiltImage.includes(\":\")\n\t\t\t\t\t\t? prebuiltImage.split(\":\")\n\t\t\t\t\t\t: [prebuiltImage, \"latest\"];\n\t\t\t\t\teffectiveDef = {\n\t\t\t\t\t\t...def,\n\t\t\t\t\t\timage: img,\n\t\t\t\t\t\timageTag: tag,\n\t\t\t\t\t\tgitSource: undefined,\n\t\t\t\t\t\tbuildContext: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst { entry, volumeNames } = buildCompanionService(\n\t\t\t\teffectiveDef,\n\t\t\t\taddonResolvedOutput,\n\t\t\t\tcomposeOptions,\n\t\t\t\tallVolumes,\n\t\t\t);\n\n\t\t\t// Remove profiles from the service entry\n\t\t\tdelete (entry as Record<string, unknown>).profiles;\n\n\t\t\t// Remove depends_on references to infrastructure services\n\t\t\tif (entry.depends_on) {\n\t\t\t\tconst deps = entry.depends_on as Record<string, { condition: string }>;\n\t\t\t\tfor (const depId of Object.keys(deps)) {\n\t\t\t\t\tif (infraIds.has(depId)) {\n\t\t\t\t\t\tdelete deps[depId];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (Object.keys(deps).length === 0) {\n\t\t\t\t\tdelete entry.depends_on;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tservices[def.id] = entry;\n\t\t\tfor (const v of volumeNames) allVolumes.add(v);\n\n\t\t\t// Inject user-provided credentials into env\n\t\t\tconst userCreds = input.credentials[def.id];\n\t\t\tif (userCreds) {\n\t\t\t\tfor (const [key, value] of Object.entries(userCreds)) {\n\t\t\t\t\tenvValues.set(key, value);\n\t\t\t\t}\n\t\t\t\t// Sync referenced keys: if a user provides e.g. DB_POSTGRESDB_PASSWORD\n\t\t\t\t// and the env var's defaultValue is \"${N8N_DB_PASSWORD}\", sync the ref key\n\t\t\t\t// so postgres-setup uses the same password.\n\t\t\t\tfor (const envVar of def.environment) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tuserCreds[envVar.key] &&\n\t\t\t\t\t\tenvVar.defaultValue?.startsWith(\"${\") &&\n\t\t\t\t\t\tenvVar.defaultValue?.endsWith(\"}\")\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst refKey = envVar.defaultValue.slice(2, -1);\n\t\t\t\t\t\tenvValues.set(refKey, userCreds[envVar.key]!);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tskippedServices.push({\n\t\t\t\tserviceId: def.id,\n\t\t\t\treason: \"resolution_error\",\n\t\t\t\tdetails: `Failed to build compose entry: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t});\n\t\t\twarnings.push(\n\t\t\t\t`Failed to process service \"${def.name}\": ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// 10. Build postgres-setup if any addon needs a DB\n\t// We need to check if any of our deployable services require DB setup\n\t// and if postgresql is in the infrastructure (it is for Clawexa)\n\tconst dbReqs = getDbRequirements(addonResolvedOutput);\n\tif (dbReqs.length > 0) {\n\t\t// Build a custom postgres-setup that references the existing PostgreSQL\n\t\t// We can't use buildPostgresSetup directly because it checks for postgresql\n\t\t// in the resolved services. Instead, build it manually.\n\t\tconst scriptLines = [\"echo '=== PostgreSQL database setup (addon) ==='\", \"FAILED=0\"];\n\n\t\tfor (const req of dbReqs) {\n\t\t\tscriptLines.push(\n\t\t\t\t`echo \"Setting up database '${req.dbName}' with user '${req.dbUser}'...\"`,\n\t\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\t`psql -c \"ALTER ROLE ${req.dbUser} WITH LOGIN PASSWORD '$$${req.passwordEnvVar}'\"`,\n\t\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\t`psql -c \"GRANT ALL PRIVILEGES ON DATABASE ${req.dbName} TO ${req.dbUser}\" || FAILED=1`,\n\t\t\t\t`echo \" Done: ${req.dbName}\"`,\n\t\t\t);\n\t\t}\n\t\tscriptLines.push(\"echo '=== All databases ready ==='\", \"exit $$FAILED\");\n\n\t\tconst dbEnv: Record<string, string> = {\n\t\t\tPGHOST: \"postgresql\",\n\t\t\tPGUSER: \"${POSTGRES_USER:-openclaw}\",\n\t\t\tPGDATABASE: \"${POSTGRES_DB:-openclaw}\",\n\t\t\tPGPASSWORD: \"${POSTGRES_PASSWORD}\",\n\t\t};\n\t\tfor (const req of dbReqs) {\n\t\t\tdbEnv[req.passwordEnvVar] = `\\${${req.passwordEnvVar}}`;\n\t\t}\n\n\t\tservices[\"postgres-setup\"] = {\n\t\t\timage: \"postgres:17-alpine\",\n\t\t\tdepends_on: {\n\t\t\t\tpostgresql: { condition: \"service_healthy\" },\n\t\t\t},\n\t\t\tenvironment: dbEnv,\n\t\t\tentrypoint: [\"/bin/sh\", \"-c\"],\n\t\t\tcommand: [scriptLines.join(\"\\n\")],\n\t\t\trestart: quotedStr(\"no\"),\n\t\t\tnetworks: [framework.networkName],\n\t\t};\n\n\t\t// Update addon services that need DB to depend on postgres-setup\n\t\tfor (const req of dbReqs) {\n\t\t\tconst svcEntry = services[req.serviceId];\n\t\t\tif (svcEntry) {\n\t\t\t\tconst deps = (svcEntry.depends_on as Record<string, { condition: string }>) || {};\n\t\t\t\tdeps[\"postgres-setup\"] = { condition: \"service_completed_successfully\" };\n\t\t\t\tsvcEntry.depends_on = deps;\n\t\t\t}\n\t\t}\n\t}\n\n\t// 11. Generate secrets and env file\n\tconst envLines: string[] = [\n\t\t\"# ═══════════════════════════════════════════════════════════════════════════════\",\n\t\t\"# OpenClaw Addon Stack Environment\",\n\t\t`# Instance: ${input.instanceId}`,\n\t\t`# Generated at ${new Date().toISOString()}`,\n\t\t\"# ═══════════════════════════════════════════════════════════════════════════════\",\n\t\t\"\",\n\t];\n\n\t// DB passwords first\n\tif (dbReqs.length > 0) {\n\t\tenvLines.push(\"# ── Per-Service Database Passwords ──────────────────────────────────────\");\n\t\tfor (const req of dbReqs) {\n\t\t\tconst secretValue = input.generateSecrets ? generateHexSecret(24) : \"\";\n\t\t\tenvValues.set(req.passwordEnvVar, secretValue);\n\t\t\tif (secretValue) generatedSecretKeys.push(req.passwordEnvVar);\n\t\t\tenvLines.push(\n\t\t\t\t`# PostgreSQL password for ${req.serviceName} (db: ${req.dbName}, user: ${req.dbUser})`,\n\t\t\t);\n\t\t\tenvLines.push(`${req.passwordEnvVar}=${secretValue}`);\n\t\t\tenvLines.push(\"\");\n\t\t}\n\t}\n\n\t// Per-service env vars\n\tconst seenKeys = new Set<string>([...managedKeys, ...dbReqs.map((r) => r.passwordEnvVar)]);\n\tconst envVarGroups: AddonStackResult[\"envVars\"] = [];\n\n\tfor (const svc of deployableServices) {\n\t\tconst def = svc.definition;\n\t\tconst allEnvVars = [...def.environment, ...def.openclawEnvVars];\n\t\tif (allEnvVars.length === 0) continue;\n\n\t\tconst groupVars: AddonStackResult[\"envVars\"][number][\"vars\"] = [];\n\n\t\tenvLines.push(`# ── ${def.icon} ${def.name} ──────────────────────────────────────`);\n\n\t\tfor (const envVar of allEnvVars) {\n\t\t\tif (seenKeys.has(envVar.key)) continue;\n\t\t\tseenKeys.add(envVar.key);\n\n\t\t\t// Check if user provided this credential\n\t\t\tconst userValue = input.credentials[def.id]?.[envVar.key];\n\t\t\tlet actualValue: string;\n\n\t\t\tif (userValue !== undefined) {\n\t\t\t\tactualValue = userValue;\n\t\t\t} else if (envVar.secret) {\n\t\t\t\t// Resolve references like ${N8N_DB_PASSWORD}\n\t\t\t\tif (envVar.defaultValue.startsWith(\"${\") && envVar.defaultValue.endsWith(\"}\")) {\n\t\t\t\t\tconst refKey = envVar.defaultValue.slice(2, -1);\n\t\t\t\t\tactualValue = envValues.get(refKey) || envVar.defaultValue;\n\t\t\t\t} else if (input.generateSecrets) {\n\t\t\t\t\tactualValue = generateHexSecret(24);\n\t\t\t\t\tgeneratedSecretKeys.push(envVar.key);\n\t\t\t\t} else {\n\t\t\t\t\tactualValue = envVar.defaultValue;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tactualValue = envVar.defaultValue;\n\t\t\t}\n\n\t\t\tenvValues.set(envVar.key, actualValue);\n\n\t\t\tenvLines.push(`# ${envVar.description}`);\n\t\t\tenvLines.push(`${envVar.key}=${actualValue}`);\n\t\t\tenvLines.push(\"\");\n\n\t\t\tgroupVars.push({\n\t\t\t\tkey: envVar.key,\n\t\t\t\tdescription: envVar.description,\n\t\t\t\tvalue: actualValue,\n\t\t\t\tsecret: envVar.secret,\n\t\t\t});\n\t\t}\n\n\t\tif (groupVars.length > 0) {\n\t\t\tenvVarGroups.push({\n\t\t\t\tserviceName: def.name,\n\t\t\t\tvars: groupVars,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Apply env quirks after all values are generated\n\tfor (const svc of deployableServices) {\n\t\tapplyEnvQuirks(svc.definition, envValues, input.generateSecrets);\n\t}\n\n\t// Rebuild env lines from envValues (quirks may have modified values or introduced new keys)\n\tconst quirkedKeys = new Set<string>();\n\tconst finalEnvLines: string[] = [];\n\tfor (const line of envLines) {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed || trimmed.startsWith(\"#\")) {\n\t\t\tfinalEnvLines.push(line);\n\t\t\tcontinue;\n\t\t}\n\t\tconst eqIdx = trimmed.indexOf(\"=\");\n\t\tif (eqIdx > 0) {\n\t\t\tconst key = trimmed.slice(0, eqIdx);\n\t\t\tquirkedKeys.add(key);\n\t\t\tconst fixedValue = envValues.get(key);\n\t\t\tif (fixedValue !== undefined) {\n\t\t\t\tfinalEnvLines.push(`${key}=${fixedValue}`);\n\t\t\t} else {\n\t\t\t\tfinalEnvLines.push(line);\n\t\t\t}\n\t\t} else {\n\t\t\tfinalEnvLines.push(line);\n\t\t}\n\t}\n\t// Append any new keys introduced by quirks (e.g., must_sync creating a new key)\n\tfor (const [key, value] of envValues) {\n\t\tif (!quirkedKeys.has(key) && !seenKeys.has(key) && !managedKeys.has(key)) {\n\t\t\tfinalEnvLines.push(`# Synced by env quirk`);\n\t\t\tfinalEnvLines.push(`${key}=${value}`);\n\t\t\tfinalEnvLines.push(\"\");\n\t\t}\n\t}\n\tconst envFile = finalEnvLines.join(\"\\n\");\n\n\t// 12. Generate skill files\n\tconst skillFiles = generateSkillFiles(addonResolvedOutput);\n\n\t// 13. Build openclaw config patch\n\tconst skillEntries: Record<string, { enabled: boolean }> = {};\n\tlet skillCount = 0;\n\tfor (const svc of deployableServices) {\n\t\tfor (const skill of svc.definition.skills) {\n\t\t\tif (skill.autoInstall) {\n\t\t\t\tskillEntries[skill.skillId] = { enabled: true };\n\t\t\t\tskillCount++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// 14. Build proxy routes\n\tconst proxyRoutes = buildProxyRoutes(deployableServices);\n\n\t// 14b. Build additional files (e.g. sandbox.toml for opensandbox)\n\tconst additionalFiles: Record<string, string> = {};\n\tif (deployableServices.some((s) => s.definition.id === \"opensandbox\")) {\n\t\tadditionalFiles[\"sandbox.toml\"] = [\n\t\t\t\"[server]\",\n\t\t\t'host = \"0.0.0.0\"',\n\t\t\t\"port = 8080\",\n\t\t\t'log_level = \"INFO\"',\n\t\t\t'api_key = \"${OPEN_SANDBOX_API_KEY}\"',\n\t\t\t\"\",\n\t\t\t\"[runtime]\",\n\t\t\t'type = \"docker\"',\n\t\t\t'execd_image = \"opensandbox/execd:v1.0.6\"',\n\t\t\t\"\",\n\t\t\t\"[docker]\",\n\t\t\t'network_mode = \"bridge\"',\n\t\t\t'drop_capabilities = [\"NET_ADMIN\", \"SYS_ADMIN\", \"SYS_PTRACE\", \"MKNOD\", \"NET_RAW\", \"SYS_RAWIO\"]',\n\t\t\t\"no_new_privileges = true\",\n\t\t\t\"pids_limit = 512\",\n\t\t\t\"\",\n\t\t\t\"[secure_runtime]\",\n\t\t\t'type = \"gvisor\"',\n\t\t\t\"\",\n\t\t].join(\"\\n\");\n\t}\n\n\t// 14c. Build pre-pull images list\n\tconst prePullImages: Array<{ image: string; priority: 1 | 2 | 3 }> = [];\n\tif (deployableServices.some((s) => s.definition.id === \"opensandbox\")) {\n\t\tprePullImages.push(\n\t\t\t// Priority 1: always pulled (core + Homespace)\n\t\t\t{ image: \"opensandbox/server:v1.0.6\", priority: 1 },\n\t\t\t{ image: \"opensandbox/execd:v1.0.6\", priority: 1 },\n\t\t\t{ image: \"opensandbox/desktop:latest\", priority: 1 },\n\t\t\t{ image: \"opensandbox/chrome:latest\", priority: 1 },\n\t\t\t// Priority 2: recommended (common languages)\n\t\t\t{ image: \"opensandbox/code-interpreter:python\", priority: 2 },\n\t\t\t{ image: \"opensandbox/code-interpreter:node\", priority: 2 },\n\t\t\t// Priority 3: optional (full multi-lang and IDE)\n\t\t\t{ image: \"opensandbox/code-interpreter:latest\", priority: 3 },\n\t\t\t{ image: \"opensandbox/vscode:latest\", priority: 3 },\n\t\t);\n\t}\n\n\t// 15. Compose single YAML\n\tconst volumeMap: Record<string, null> = {};\n\tfor (const v of allVolumes) {\n\t\tvolumeMap[v] = null;\n\t}\n\n\tconst composeDoc: Record<string, unknown> = {\n\t\tservices,\n\t};\n\n\tif (Object.keys(volumeMap).length > 0) {\n\t\tcomposeDoc.volumes = volumeMap;\n\t}\n\n\tcomposeDoc.networks = {\n\t\t[framework.networkName]: {\n\t\t\texternal: true,\n\t\t},\n\t};\n\n\tconst composeOverride = stringify(composeDoc, YAML_OPTIONS);\n\n\t// 16. Return result\n\treturn {\n\t\tcomposeOverride,\n\t\tenvFile,\n\t\tenvVars: envVarGroups,\n\t\tskillFiles,\n\t\topenclawConfigPatch: {\n\t\t\tskills: { entries: skillEntries },\n\t\t},\n\t\tproxyRoutes,\n\t\tadditionalFiles,\n\t\tmetadata: {\n\t\t\tserviceCount: Object.keys(services).length,\n\t\t\tskillCount,\n\t\t\testimatedMemoryMB: addonResolvedOutput.estimatedMemoryMB,\n\t\t\tresolvedServices: deployableServices.map((s) => s.definition.id),\n\t\t\tskippedServices,\n\t\t\tgeneratedSecretKeys,\n\t\t\tportAssignments: portConflicts.assignments,\n\t\t\tprePullImages,\n\t\t},\n\t\twarnings,\n\t};\n}\n\n// ── Main: updateAddonStack ───────────────────────────────────────────────────\n\n/**\n * Incrementally updates an existing addon stack by adding or removing services.\n * Preserves existing env values (never overwrites user-customized values).\n *\n * This function never throws.\n */\nexport function updateAddonStack(rawInput: AddonStackUpdateInput): AddonStackUpdateResult {\n\tconst warnings: string[] = [];\n\n\t// 1. Parse & validate\n\tlet input: AddonStackUpdateInput;\n\ttry {\n\t\tinput = AddonStackUpdateInputSchema.parse(rawInput);\n\t} catch (err) {\n\t\treturn emptyUpdateResult(`Invalid input: ${err instanceof Error ? err.message : String(err)}`);\n\t}\n\n\t// 2. Parse existing compose YAML to extract current service list\n\tlet currentServiceIds: string[] = [];\n\ttry {\n\t\tconst existingCompose = parseYaml(input.currentCompose);\n\t\tif (existingCompose?.services && typeof existingCompose.services === \"object\") {\n\t\t\tcurrentServiceIds = Object.keys(existingCompose.services).filter(\n\t\t\t\t(id) => id !== \"postgres-setup\",\n\t\t\t);\n\t\t}\n\t} catch (err) {\n\t\twarnings.push(\n\t\t\t`Failed to parse existing compose YAML: ${err instanceof Error ? err.message : String(err)}`,\n\t\t);\n\t}\n\n\t// 3. Parse existing env into a map\n\tconst existingEnvMap = new Map<string, string>();\n\tfor (const line of input.currentEnv.split(\"\\n\")) {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed || trimmed.startsWith(\"#\")) continue;\n\t\tconst eqIdx = trimmed.indexOf(\"=\");\n\t\tif (eqIdx > 0) {\n\t\t\texistingEnvMap.set(trimmed.slice(0, eqIdx), trimmed.slice(eqIdx + 1));\n\t\t}\n\t}\n\n\t// 4. Compute desired service list\n\tconst removeSet = new Set(input.removeServices);\n\tconst desiredServiceIds = [\n\t\t...currentServiceIds.filter((id) => !removeSet.has(id)),\n\t\t...input.addServices.filter((id) => !currentServiceIds.includes(id)),\n\t];\n\n\t// 5. Generate the full target state\n\tconst targetResult = generateAddonStack({\n\t\tinstanceId: input.instanceId,\n\t\tservices: desiredServiceIds,\n\t\tskillPacks: [],\n\t\tplatform: input.platform,\n\t\topenclawVersion: input.openclawVersion,\n\t\texistingServices: input.existingServices,\n\t\treservedPorts: input.reservedPorts,\n\t\tgenerateSecrets: input.generateSecrets,\n\t\tcredentials: input.credentials,\n\t\tportOverrides: input.portOverrides,\n\t\tgpu: input.gpu,\n\t\taiProviders: input.aiProviders,\n\t\tprebuiltImages: input.prebuiltImages,\n\t});\n\n\t// 6. Merge env: preserve existing values, only add new ones\n\tconst mergedEnvLines: string[] = [];\n\tfor (const line of targetResult.envFile.split(\"\\n\")) {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed || trimmed.startsWith(\"#\")) {\n\t\t\tmergedEnvLines.push(line);\n\t\t\tcontinue;\n\t\t}\n\t\tconst eqIdx = trimmed.indexOf(\"=\");\n\t\tif (eqIdx > 0) {\n\t\t\tconst key = trimmed.slice(0, eqIdx);\n\t\t\tif (existingEnvMap.has(key)) {\n\t\t\t\t// Preserve existing value\n\t\t\t\tmergedEnvLines.push(`${key}=${existingEnvMap.get(key)}`);\n\t\t\t} else {\n\t\t\t\tmergedEnvLines.push(line);\n\t\t\t}\n\t\t} else {\n\t\t\tmergedEnvLines.push(line);\n\t\t}\n\t}\n\n\t// 7. Compute diffs\n\tconst currentSet = new Set(currentServiceIds);\n\tconst targetSet = new Set(targetResult.metadata.resolvedServices);\n\tconst added = [...targetSet].filter((id) => !currentSet.has(id));\n\tconst removed = [...currentSet].filter((id) => !targetSet.has(id));\n\tconst unchanged = [...currentSet].filter((id) => targetSet.has(id));\n\n\t// 8. Compute skill diffs\n\tconst newSkillFiles: Record<string, string> = {};\n\tconst removedSkillSlugs: string[] = [];\n\n\t// New skills from added services\n\tfor (const id of added) {\n\t\tconst def = getServiceById(id);\n\t\tif (!def) continue;\n\t\tfor (const skill of def.skills) {\n\t\t\tconst skillPath = Object.keys(targetResult.skillFiles).find((path) =>\n\t\t\t\tpath.includes(skill.skillId),\n\t\t\t);\n\t\t\tif (skillPath && targetResult.skillFiles[skillPath]) {\n\t\t\t\tnewSkillFiles[skillPath] = targetResult.skillFiles[skillPath];\n\t\t\t}\n\t\t}\n\t}\n\n\t// Removed skills from removed services\n\tfor (const id of removed) {\n\t\tconst def = getServiceById(id);\n\t\tif (!def) continue;\n\t\tfor (const skill of def.skills) {\n\t\t\tremovedSkillSlugs.push(skill.skillId);\n\t\t}\n\t}\n\n\t// 9. Proxy route diffs\n\tconst addProxyRoutes = targetResult.proxyRoutes.filter((r) => added.includes(r.serviceId));\n\tconst removeProxyRoutes = removed;\n\n\t// 10. Images to pull for new services\n\tconst imagesToPull: string[] = [];\n\tfor (const id of added) {\n\t\tconst def = getServiceById(id);\n\t\tif (def?.image && def?.imageTag) {\n\t\t\timagesToPull.push(`${def.image}:${def.imageTag}`);\n\t\t} else if (def?.prebuiltImage) {\n\t\t\timagesToPull.push(def.prebuiltImage);\n\t\t}\n\t}\n\n\t// 11. Estimate memory delta\n\tlet memoryDelta = 0;\n\tfor (const id of added) {\n\t\tconst def = getServiceById(id);\n\t\tmemoryDelta += def?.minMemoryMB ?? 128;\n\t}\n\tfor (const id of removed) {\n\t\tconst def = getServiceById(id);\n\t\tmemoryDelta -= def?.minMemoryMB ?? 128;\n\t}\n\n\t// Add existing skills to the config patch\n\tconst addSkillEntries: Record<string, { enabled: boolean }> = {};\n\tfor (const id of added) {\n\t\tconst def = getServiceById(id);\n\t\tif (!def) continue;\n\t\tfor (const skill of def.skills) {\n\t\t\tif (skill.autoInstall) {\n\t\t\t\taddSkillEntries[skill.skillId] = { enabled: true };\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tcomposeOverride: targetResult.composeOverride,\n\t\tenvFile: mergedEnvLines.join(\"\\n\"),\n\t\tnewSkillFiles,\n\t\tremovedSkillSlugs,\n\t\topenclawConfigPatch: {\n\t\t\tskills: {\n\t\t\t\tadd: addSkillEntries,\n\t\t\t\tremove: removedSkillSlugs,\n\t\t\t},\n\t\t},\n\t\taddProxyRoutes,\n\t\tremoveProxyRoutes,\n\t\timagesToPull,\n\t\trestartRequired: added, // New services need starting, not restarting\n\t\tmetadata: {\n\t\t\tadded,\n\t\t\tremoved,\n\t\t\tunchanged,\n\t\t\testimatedMemoryDelta: memoryDelta,\n\t\t},\n\t\twarnings: [...warnings, ...targetResult.warnings],\n\t};\n}\n\n// ── Empty Result Helpers ─────────────────────────────────────────────────────\n\nfunction emptyResultBase(): AddonStackResult {\n\treturn {\n\t\tcomposeOverride: \"services: {}\\n\",\n\t\tenvFile: \"\",\n\t\tenvVars: [],\n\t\tskillFiles: {},\n\t\topenclawConfigPatch: { skills: { entries: {} } },\n\t\tproxyRoutes: [],\n\t\tadditionalFiles: {},\n\t\tmetadata: {\n\t\t\tserviceCount: 0,\n\t\t\tskillCount: 0,\n\t\t\testimatedMemoryMB: 0,\n\t\t\tresolvedServices: [],\n\t\t\tskippedServices: [],\n\t\t\tgeneratedSecretKeys: [],\n\t\t\tportAssignments: {},\n\t\t\tprePullImages: [],\n\t\t},\n\t\twarnings: [],\n\t};\n}\n\nfunction emptyResult(warning: string): AddonStackResult {\n\treturn {\n\t\t...emptyResultBase(),\n\t\twarnings: [warning],\n\t};\n}\n\nfunction emptyUpdateResult(warning: string): AddonStackUpdateResult {\n\treturn {\n\t\tcomposeOverride: \"services: {}\\n\",\n\t\tenvFile: \"\",\n\t\tnewSkillFiles: {},\n\t\tremovedSkillSlugs: [],\n\t\topenclawConfigPatch: { skills: { add: {}, remove: [] } },\n\t\taddProxyRoutes: [],\n\t\tremoveProxyRoutes: [],\n\t\timagesToPull: [],\n\t\trestartRequired: [],\n\t\tmetadata: {\n\t\t\tadded: [],\n\t\t\tremoved: [],\n\t\t\tunchanged: [],\n\t\t\testimatedMemoryDelta: 0,\n\t\t},\n\t\twarnings: [warning],\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require("./skills-
|
|
2
|
-
const require_test_CTcmp4Su = require("./test.CTcmp4Su-
|
|
1
|
+
require("./skills-uPxJVmKk.cjs");
|
|
2
|
+
const require_test_CTcmp4Su = require("./test.CTcmp4Su-BWSPM8ZQ.cjs");
|
|
3
3
|
const require_schema = require("./schema.cjs");
|
|
4
4
|
const require_addon_stack = require("./addon-stack.cjs");
|
|
5
5
|
let yaml = require("yaml");
|
|
@@ -57,7 +57,7 @@ require_test_CTcmp4Su.describe("generateAddonStack", () => {
|
|
|
57
57
|
require_test_CTcmp4Su.globalExpect(result.envFile).toContain("N8N_DB_PASSWORD=");
|
|
58
58
|
const match = result.envFile.match(/N8N_DB_PASSWORD=([a-f0-9]+)/);
|
|
59
59
|
require_test_CTcmp4Su.globalExpect(match).not.toBeNull();
|
|
60
|
-
require_test_CTcmp4Su.globalExpect(match[1].length).toBe(48);
|
|
60
|
+
require_test_CTcmp4Su.globalExpect(match?.[1].length).toBe(48);
|
|
61
61
|
});
|
|
62
62
|
require_test_CTcmp4Su.it("gracefully handles unknown service IDs without throwing", () => {
|
|
63
63
|
const result = require_addon_stack.generateAddonStack({
|
|
@@ -90,8 +90,8 @@ require_test_CTcmp4Su.describe("generateAddonStack", () => {
|
|
|
90
90
|
services: ["n8n"]
|
|
91
91
|
}).proxyRoutes.find((r) => r.serviceId === "n8n");
|
|
92
92
|
require_test_CTcmp4Su.globalExpect(n8nRoute).toBeDefined();
|
|
93
|
-
require_test_CTcmp4Su.globalExpect(n8nRoute
|
|
94
|
-
require_test_CTcmp4Su.globalExpect(n8nRoute
|
|
93
|
+
require_test_CTcmp4Su.globalExpect(n8nRoute?.path).toBe("/n8n");
|
|
94
|
+
require_test_CTcmp4Su.globalExpect(n8nRoute?.port).toBe(5678);
|
|
95
95
|
});
|
|
96
96
|
require_test_CTcmp4Su.it("generates skill files and openclaw config patch", () => {
|
|
97
97
|
const result = require_addon_stack.generateAddonStack({
|
|
@@ -109,7 +109,7 @@ require_test_CTcmp4Su.describe("generateAddonStack", () => {
|
|
|
109
109
|
}).metadata.portAssignments;
|
|
110
110
|
const n8nAssignment = Object.entries(portAssignments).find(([key]) => key.startsWith("n8n:"));
|
|
111
111
|
require_test_CTcmp4Su.globalExpect(n8nAssignment).toBeDefined();
|
|
112
|
-
require_test_CTcmp4Su.globalExpect(n8nAssignment[1]).not.toBe(5678);
|
|
112
|
+
require_test_CTcmp4Su.globalExpect(n8nAssignment?.[1]).not.toBe(5678);
|
|
113
113
|
});
|
|
114
114
|
require_test_CTcmp4Su.it("sanitizes project name from instanceId", () => {
|
|
115
115
|
require_test_CTcmp4Su.globalExpect(require_addon_stack.generateAddonStack({
|
|
@@ -150,8 +150,8 @@ require_test_CTcmp4Su.describe("generateAddonStack", () => {
|
|
|
150
150
|
generateSecrets: true
|
|
151
151
|
}).envFile.match(/MEILI_MASTER_KEY=([^\n]+)/);
|
|
152
152
|
require_test_CTcmp4Su.globalExpect(match).not.toBeNull();
|
|
153
|
-
require_test_CTcmp4Su.globalExpect(match[1].length).toBeGreaterThanOrEqual(32);
|
|
154
|
-
require_test_CTcmp4Su.globalExpect(match[1]).not.toBe("");
|
|
153
|
+
require_test_CTcmp4Su.globalExpect(match?.[1].length).toBeGreaterThanOrEqual(32);
|
|
154
|
+
require_test_CTcmp4Su.globalExpect(match?.[1]).not.toBe("");
|
|
155
155
|
});
|
|
156
156
|
require_test_CTcmp4Su.it("applies env quirks to envFile output (grafana GF_SECURITY_ADMIN_PASSWORD)", () => {
|
|
157
157
|
const match = require_addon_stack.generateAddonStack({
|
|
@@ -160,7 +160,7 @@ require_test_CTcmp4Su.describe("generateAddonStack", () => {
|
|
|
160
160
|
generateSecrets: true
|
|
161
161
|
}).envFile.match(/GF_SECURITY_ADMIN_PASSWORD=([^\n]+)/);
|
|
162
162
|
require_test_CTcmp4Su.globalExpect(match).not.toBeNull();
|
|
163
|
-
require_test_CTcmp4Su.globalExpect(match[1].length).toBeGreaterThanOrEqual(22);
|
|
163
|
+
require_test_CTcmp4Su.globalExpect(match?.[1].length).toBeGreaterThanOrEqual(22);
|
|
164
164
|
});
|
|
165
165
|
require_test_CTcmp4Su.it("syncs DB_POSTGRESDB_PASSWORD with N8N_DB_PASSWORD via must_sync quirk", () => {
|
|
166
166
|
const result = require_addon_stack.generateAddonStack({
|
|
@@ -172,7 +172,7 @@ require_test_CTcmp4Su.describe("generateAddonStack", () => {
|
|
|
172
172
|
const dbPostgresPw = result.envFile.match(/DB_POSTGRESDB_PASSWORD=([^\n]+)/);
|
|
173
173
|
require_test_CTcmp4Su.globalExpect(n8nDbPw).not.toBeNull();
|
|
174
174
|
require_test_CTcmp4Su.globalExpect(dbPostgresPw).not.toBeNull();
|
|
175
|
-
require_test_CTcmp4Su.globalExpect(n8nDbPw[1]).toBe(dbPostgresPw[1]);
|
|
175
|
+
require_test_CTcmp4Su.globalExpect(n8nDbPw?.[1]).toBe(dbPostgresPw?.[1]);
|
|
176
176
|
});
|
|
177
177
|
require_test_CTcmp4Su.it("syncs user-provided credential with DB password reference", () => {
|
|
178
178
|
const customPassword = "my_custom_secure_password";
|
|
@@ -193,7 +193,7 @@ require_test_CTcmp4Su.describe("generateAddonStack", () => {
|
|
|
193
193
|
}).metadata.portAssignments;
|
|
194
194
|
const n8nAssignment = Object.entries(portAssignments).find(([key]) => key.startsWith("n8n:"));
|
|
195
195
|
require_test_CTcmp4Su.globalExpect(n8nAssignment).toBeDefined();
|
|
196
|
-
require_test_CTcmp4Su.globalExpect(n8nAssignment[1]).toBe(5678);
|
|
196
|
+
require_test_CTcmp4Su.globalExpect(n8nAssignment?.[1]).toBe(5678);
|
|
197
197
|
});
|
|
198
198
|
require_test_CTcmp4Su.it("does not dual-list GPU services in both skipped and resolved", () => {
|
|
199
199
|
const result = require_addon_stack.generateAddonStack({
|
|
@@ -233,7 +233,7 @@ require_test_CTcmp4Su.describe("generateAddonStack", () => {
|
|
|
233
233
|
generateSecrets: true
|
|
234
234
|
}).envFile.match(/OPEN_SANDBOX_API_KEY=([^\n]+)/);
|
|
235
235
|
require_test_CTcmp4Su.globalExpect(match).not.toBeNull();
|
|
236
|
-
require_test_CTcmp4Su.globalExpect(match[1].length).toBeGreaterThanOrEqual(43);
|
|
236
|
+
require_test_CTcmp4Su.globalExpect(match?.[1].length).toBeGreaterThanOrEqual(43);
|
|
237
237
|
});
|
|
238
238
|
require_test_CTcmp4Su.it("generates code-sandbox skill file and config patch", () => {
|
|
239
239
|
const result = require_addon_stack.generateAddonStack({
|
|
@@ -254,8 +254,8 @@ require_test_CTcmp4Su.describe("generateAddonStack", () => {
|
|
|
254
254
|
services: ["opensandbox"]
|
|
255
255
|
}).proxyRoutes.find((r) => r.serviceId === "opensandbox");
|
|
256
256
|
require_test_CTcmp4Su.globalExpect(route).toBeDefined();
|
|
257
|
-
require_test_CTcmp4Su.globalExpect(route
|
|
258
|
-
require_test_CTcmp4Su.globalExpect(route
|
|
257
|
+
require_test_CTcmp4Su.globalExpect(route?.path).toBe("/sandbox");
|
|
258
|
+
require_test_CTcmp4Su.globalExpect(route?.port).toBe(8080);
|
|
259
259
|
});
|
|
260
260
|
require_test_CTcmp4Su.it("generates sandbox.toml in additionalFiles", () => {
|
|
261
261
|
const result = require_addon_stack.generateAddonStack({
|