@better-openclaw/core 1.0.23 → 1.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/dist/addon-stack.cjs +673 -0
  2. package/dist/addon-stack.cjs.map +1 -0
  3. package/dist/addon-stack.d.cts +23 -0
  4. package/dist/addon-stack.d.cts.map +1 -0
  5. package/dist/addon-stack.d.mts +23 -0
  6. package/dist/addon-stack.d.mts.map +1 -0
  7. package/dist/addon-stack.mjs +671 -0
  8. package/dist/addon-stack.mjs.map +1 -0
  9. package/dist/addon-stack.test.cjs +349 -0
  10. package/dist/addon-stack.test.cjs.map +1 -0
  11. package/dist/addon-stack.test.d.cts +1 -0
  12. package/dist/addon-stack.test.d.mts +1 -0
  13. package/dist/addon-stack.test.mjs +349 -0
  14. package/dist/addon-stack.test.mjs.map +1 -0
  15. package/dist/bare-metal-partition.test.cjs +20 -21
  16. package/dist/bare-metal-partition.test.cjs.map +1 -1
  17. package/dist/bare-metal-partition.test.mjs +4 -5
  18. package/dist/bare-metal-partition.test.mjs.map +1 -1
  19. package/dist/composer.cjs +17 -1
  20. package/dist/composer.cjs.map +1 -1
  21. package/dist/composer.d.cts +24 -1
  22. package/dist/composer.d.cts.map +1 -1
  23. package/dist/composer.d.mts +24 -1
  24. package/dist/composer.d.mts.map +1 -1
  25. package/dist/composer.mjs +14 -2
  26. package/dist/composer.mjs.map +1 -1
  27. package/dist/composer.snapshot.test.cjs +20 -20
  28. package/dist/composer.snapshot.test.cjs.map +1 -1
  29. package/dist/composer.snapshot.test.mjs +2 -2
  30. package/dist/composer.test.cjs +53 -52
  31. package/dist/composer.test.cjs.map +1 -1
  32. package/dist/composer.test.mjs +4 -3
  33. package/dist/composer.test.mjs.map +1 -1
  34. package/dist/deployers/strip-host-ports.test.cjs +26 -26
  35. package/dist/deployers/strip-host-ports.test.cjs.map +1 -1
  36. package/dist/deployers/strip-host-ports.test.mjs +1 -1
  37. package/dist/generate.cjs +8 -4
  38. package/dist/generate.cjs.map +1 -1
  39. package/dist/generate.d.cts.map +1 -1
  40. package/dist/generate.d.mts.map +1 -1
  41. package/dist/generate.mjs +9 -5
  42. package/dist/generate.mjs.map +1 -1
  43. package/dist/generate.test.cjs +55 -55
  44. package/dist/generate.test.cjs.map +1 -1
  45. package/dist/generate.test.mjs +2 -2
  46. package/dist/generate.test.mjs.map +1 -1
  47. package/dist/generators/bare-metal-install.test.cjs +18 -18
  48. package/dist/generators/bare-metal-install.test.cjs.map +1 -1
  49. package/dist/generators/bare-metal-install.test.mjs +1 -1
  50. package/dist/generators/caddy.test.cjs +13 -13
  51. package/dist/generators/caddy.test.cjs.map +1 -1
  52. package/dist/generators/caddy.test.mjs +1 -1
  53. package/dist/generators/clone-repos.cjs +140 -0
  54. package/dist/generators/clone-repos.cjs.map +1 -0
  55. package/dist/generators/clone-repos.d.cts +11 -0
  56. package/dist/generators/clone-repos.d.cts.map +1 -0
  57. package/dist/generators/clone-repos.d.mts +11 -0
  58. package/dist/generators/clone-repos.d.mts.map +1 -0
  59. package/dist/generators/clone-repos.mjs +139 -0
  60. package/dist/generators/clone-repos.mjs.map +1 -0
  61. package/dist/generators/clone-repos.test.cjs +140 -0
  62. package/dist/generators/clone-repos.test.cjs.map +1 -0
  63. package/dist/generators/clone-repos.test.d.cts +1 -0
  64. package/dist/generators/clone-repos.test.d.mts +1 -0
  65. package/dist/generators/clone-repos.test.mjs +141 -0
  66. package/dist/generators/clone-repos.test.mjs.map +1 -0
  67. package/dist/generators/env.test.cjs +17 -17
  68. package/dist/generators/env.test.cjs.map +1 -1
  69. package/dist/generators/env.test.mjs +1 -1
  70. package/dist/generators/health-check.test.cjs +39 -39
  71. package/dist/generators/health-check.test.cjs.map +1 -1
  72. package/dist/generators/health-check.test.mjs +1 -1
  73. package/dist/generators/postgres-init.cjs +20 -0
  74. package/dist/generators/postgres-init.cjs.map +1 -1
  75. package/dist/generators/postgres-init.d.cts.map +1 -1
  76. package/dist/generators/postgres-init.d.mts.map +1 -1
  77. package/dist/generators/postgres-init.mjs +20 -0
  78. package/dist/generators/postgres-init.mjs.map +1 -1
  79. package/dist/generators/scripts.cjs +332 -3
  80. package/dist/generators/scripts.cjs.map +1 -1
  81. package/dist/generators/scripts.d.cts +3 -1
  82. package/dist/generators/scripts.d.cts.map +1 -1
  83. package/dist/generators/scripts.d.mts +3 -1
  84. package/dist/generators/scripts.d.mts.map +1 -1
  85. package/dist/generators/scripts.mjs +332 -3
  86. package/dist/generators/scripts.mjs.map +1 -1
  87. package/dist/generators/scripts.test.cjs +57 -23
  88. package/dist/generators/scripts.test.cjs.map +1 -1
  89. package/dist/generators/scripts.test.mjs +39 -5
  90. package/dist/generators/scripts.test.mjs.map +1 -1
  91. package/dist/generators/stack-manifest.cjs +1 -0
  92. package/dist/generators/stack-manifest.cjs.map +1 -1
  93. package/dist/generators/stack-manifest.d.cts +3 -2
  94. package/dist/generators/stack-manifest.d.cts.map +1 -1
  95. package/dist/generators/stack-manifest.d.mts +3 -2
  96. package/dist/generators/stack-manifest.d.mts.map +1 -1
  97. package/dist/generators/stack-manifest.mjs +1 -0
  98. package/dist/generators/stack-manifest.mjs.map +1 -1
  99. package/dist/generators/traefik.test.cjs +32 -32
  100. package/dist/generators/traefik.test.cjs.map +1 -1
  101. package/dist/generators/traefik.test.mjs +1 -1
  102. package/dist/index.cjs +28 -5
  103. package/dist/index.d.cts +7 -4
  104. package/dist/index.d.mts +7 -4
  105. package/dist/index.mjs +10 -7
  106. package/dist/migrations.test.cjs +16 -16
  107. package/dist/migrations.test.cjs.map +1 -1
  108. package/dist/migrations.test.mjs +1 -1
  109. package/dist/presets/registry.cjs.map +1 -1
  110. package/dist/presets/registry.d.cts.map +1 -1
  111. package/dist/presets/registry.d.mts.map +1 -1
  112. package/dist/presets/registry.mjs.map +1 -1
  113. package/dist/presets/registry.test.cjs +14 -14
  114. package/dist/presets/registry.test.cjs.map +1 -1
  115. package/dist/presets/registry.test.mjs +1 -1
  116. package/dist/resolver.cjs +8 -0
  117. package/dist/resolver.cjs.map +1 -1
  118. package/dist/resolver.mjs +9 -1
  119. package/dist/resolver.mjs.map +1 -1
  120. package/dist/resolver.test.cjs +125 -90
  121. package/dist/resolver.test.cjs.map +1 -1
  122. package/dist/resolver.test.mjs +47 -12
  123. package/dist/resolver.test.mjs.map +1 -1
  124. package/dist/{schema-B4c64P8N.d.cts → schema-CKBRu-Rt.d.cts} +355 -8
  125. package/dist/schema-CKBRu-Rt.d.cts.map +1 -0
  126. package/dist/{schema-CXNhYci1.d.mts → schema-Dn-_Jpb6.d.mts} +355 -8
  127. package/dist/schema-Dn-_Jpb6.d.mts.map +1 -0
  128. package/dist/schema.cjs +160 -5
  129. package/dist/schema.cjs.map +1 -1
  130. package/dist/schema.d.cts +2 -2
  131. package/dist/schema.d.mts +2 -2
  132. package/dist/schema.mjs +150 -6
  133. package/dist/schema.mjs.map +1 -1
  134. package/dist/schema.test.cjs +86 -86
  135. package/dist/schema.test.cjs.map +1 -1
  136. package/dist/schema.test.mjs +1 -1
  137. package/dist/services/definitions/apptension-saas.cjs +87 -0
  138. package/dist/services/definitions/apptension-saas.cjs.map +1 -0
  139. package/dist/services/definitions/apptension-saas.d.cts +7 -0
  140. package/dist/services/definitions/apptension-saas.d.cts.map +1 -0
  141. package/dist/services/definitions/apptension-saas.d.mts +7 -0
  142. package/dist/services/definitions/apptension-saas.d.mts.map +1 -0
  143. package/dist/services/definitions/apptension-saas.mjs +86 -0
  144. package/dist/services/definitions/apptension-saas.mjs.map +1 -0
  145. package/dist/services/definitions/boxyhq-saas.cjs +88 -0
  146. package/dist/services/definitions/boxyhq-saas.cjs.map +1 -0
  147. package/dist/services/definitions/boxyhq-saas.d.cts +7 -0
  148. package/dist/services/definitions/boxyhq-saas.d.cts.map +1 -0
  149. package/dist/services/definitions/boxyhq-saas.d.mts +7 -0
  150. package/dist/services/definitions/boxyhq-saas.d.mts.map +1 -0
  151. package/dist/services/definitions/boxyhq-saas.mjs +87 -0
  152. package/dist/services/definitions/boxyhq-saas.mjs.map +1 -0
  153. package/dist/services/definitions/browserless.cjs +4 -1
  154. package/dist/services/definitions/browserless.cjs.map +1 -1
  155. package/dist/services/definitions/browserless.mjs +4 -1
  156. package/dist/services/definitions/browserless.mjs.map +1 -1
  157. package/dist/services/definitions/cmsaas-starter.cjs +86 -0
  158. package/dist/services/definitions/cmsaas-starter.cjs.map +1 -0
  159. package/dist/services/definitions/cmsaas-starter.d.cts +7 -0
  160. package/dist/services/definitions/cmsaas-starter.d.cts.map +1 -0
  161. package/dist/services/definitions/cmsaas-starter.d.mts +7 -0
  162. package/dist/services/definitions/cmsaas-starter.d.mts.map +1 -0
  163. package/dist/services/definitions/cmsaas-starter.mjs +85 -0
  164. package/dist/services/definitions/cmsaas-starter.mjs.map +1 -0
  165. package/dist/services/definitions/convex.cjs +43 -1
  166. package/dist/services/definitions/convex.cjs.map +1 -1
  167. package/dist/services/definitions/convex.mjs +43 -1
  168. package/dist/services/definitions/convex.mjs.map +1 -1
  169. package/dist/services/definitions/grafana.cjs +11 -1
  170. package/dist/services/definitions/grafana.cjs.map +1 -1
  171. package/dist/services/definitions/grafana.mjs +11 -1
  172. package/dist/services/definitions/grafana.mjs.map +1 -1
  173. package/dist/services/definitions/index.cjs +51 -36
  174. package/dist/services/definitions/index.cjs.map +1 -1
  175. package/dist/services/definitions/index.d.cts +30 -25
  176. package/dist/services/definitions/index.d.cts.map +1 -1
  177. package/dist/services/definitions/index.d.mts +30 -25
  178. package/dist/services/definitions/index.d.mts.map +1 -1
  179. package/dist/services/definitions/index.mjs +47 -37
  180. package/dist/services/definitions/index.mjs.map +1 -1
  181. package/dist/services/definitions/ixartz-saas.cjs +88 -0
  182. package/dist/services/definitions/ixartz-saas.cjs.map +1 -0
  183. package/dist/services/definitions/ixartz-saas.d.cts +7 -0
  184. package/dist/services/definitions/ixartz-saas.d.cts.map +1 -0
  185. package/dist/services/definitions/ixartz-saas.d.mts +7 -0
  186. package/dist/services/definitions/ixartz-saas.d.mts.map +1 -0
  187. package/dist/services/definitions/ixartz-saas.mjs +87 -0
  188. package/dist/services/definitions/ixartz-saas.mjs.map +1 -0
  189. package/dist/services/definitions/meilisearch.cjs +11 -1
  190. package/dist/services/definitions/meilisearch.cjs.map +1 -1
  191. package/dist/services/definitions/meilisearch.mjs +11 -1
  192. package/dist/services/definitions/meilisearch.mjs.map +1 -1
  193. package/dist/services/definitions/minio.cjs +3 -1
  194. package/dist/services/definitions/minio.cjs.map +1 -1
  195. package/dist/services/definitions/minio.mjs +3 -1
  196. package/dist/services/definitions/minio.mjs.map +1 -1
  197. package/dist/services/definitions/mission-control.cjs +16 -2
  198. package/dist/services/definitions/mission-control.cjs.map +1 -1
  199. package/dist/services/definitions/mission-control.mjs +16 -2
  200. package/dist/services/definitions/mission-control.mjs.map +1 -1
  201. package/dist/services/definitions/n8n.cjs +11 -1
  202. package/dist/services/definitions/n8n.cjs.map +1 -1
  203. package/dist/services/definitions/n8n.mjs +11 -1
  204. package/dist/services/definitions/n8n.mjs.map +1 -1
  205. package/dist/services/definitions/ollama.cjs +3 -1
  206. package/dist/services/definitions/ollama.cjs.map +1 -1
  207. package/dist/services/definitions/ollama.mjs +3 -1
  208. package/dist/services/definitions/ollama.mjs.map +1 -1
  209. package/dist/services/definitions/open-saas.cjs +81 -0
  210. package/dist/services/definitions/open-saas.cjs.map +1 -0
  211. package/dist/services/definitions/open-saas.d.cts +7 -0
  212. package/dist/services/definitions/open-saas.d.cts.map +1 -0
  213. package/dist/services/definitions/open-saas.d.mts +7 -0
  214. package/dist/services/definitions/open-saas.d.mts.map +1 -0
  215. package/dist/services/definitions/open-saas.mjs +80 -0
  216. package/dist/services/definitions/open-saas.mjs.map +1 -0
  217. package/dist/services/definitions/qdrant.cjs +3 -1
  218. package/dist/services/definitions/qdrant.cjs.map +1 -1
  219. package/dist/services/definitions/qdrant.mjs +3 -1
  220. package/dist/services/definitions/qdrant.mjs.map +1 -1
  221. package/dist/services/definitions/searxng.cjs +8 -1
  222. package/dist/services/definitions/searxng.cjs.map +1 -1
  223. package/dist/services/definitions/searxng.mjs +8 -1
  224. package/dist/services/definitions/searxng.mjs.map +1 -1
  225. package/dist/services/definitions/uptime-kuma.cjs +8 -1
  226. package/dist/services/definitions/uptime-kuma.cjs.map +1 -1
  227. package/dist/services/definitions/uptime-kuma.mjs +8 -1
  228. package/dist/services/definitions/uptime-kuma.mjs.map +1 -1
  229. package/dist/services/registry.cjs +3 -0
  230. package/dist/services/registry.cjs.map +1 -1
  231. package/dist/services/registry.d.cts.map +1 -1
  232. package/dist/services/registry.d.mts.map +1 -1
  233. package/dist/services/registry.mjs +3 -0
  234. package/dist/services/registry.mjs.map +1 -1
  235. package/dist/services/registry.test.cjs +40 -33
  236. package/dist/services/registry.test.cjs.map +1 -1
  237. package/dist/services/registry.test.mjs +8 -1
  238. package/dist/services/registry.test.mjs.map +1 -1
  239. package/dist/{skill-manifest-BVUXU0__.mjs → skill-manifest-6XhrhWsG.mjs} +49 -1
  240. package/dist/{skill-manifest--IgY9REK.cjs.map → skill-manifest-6XhrhWsG.mjs.map} +1 -1
  241. package/dist/{skill-manifest--IgY9REK.cjs → skill-manifest-B8znSsym.cjs} +49 -1
  242. package/dist/{skill-manifest-BVUXU0__.mjs.map → skill-manifest-B8znSsym.cjs.map} +1 -1
  243. package/dist/skills/registry.cjs +3 -3
  244. package/dist/skills/registry.cjs.map +1 -1
  245. package/dist/skills/registry.mjs +3 -3
  246. package/dist/skills/registry.mjs.map +1 -1
  247. package/dist/skills/skill-manifest.cjs +1 -1
  248. package/dist/skills/skill-manifest.mjs +1 -1
  249. package/dist/{vi.2VT5v0um-DvC3SVNc.mjs → test.CTcmp4Su-ClCHJ3FA.mjs} +6793 -6403
  250. package/dist/test.CTcmp4Su-ClCHJ3FA.mjs.map +1 -0
  251. package/dist/{vi.2VT5v0um-CRqXre87.cjs → test.CTcmp4Su-DlzTarwH.cjs} +6793 -6403
  252. package/dist/test.CTcmp4Su-DlzTarwH.cjs.map +1 -0
  253. package/dist/track-analytics.cjs +50 -0
  254. package/dist/track-analytics.cjs.map +1 -0
  255. package/dist/track-analytics.d.cts +34 -0
  256. package/dist/track-analytics.d.cts.map +1 -0
  257. package/dist/track-analytics.d.mts +34 -0
  258. package/dist/track-analytics.d.mts.map +1 -0
  259. package/dist/track-analytics.mjs +48 -0
  260. package/dist/track-analytics.mjs.map +1 -0
  261. package/dist/track-analytics.test.cjs +91 -0
  262. package/dist/track-analytics.test.cjs.map +1 -0
  263. package/dist/track-analytics.test.d.cts +1 -0
  264. package/dist/track-analytics.test.d.mts +1 -0
  265. package/dist/track-analytics.test.mjs +92 -0
  266. package/dist/track-analytics.test.mjs.map +1 -0
  267. package/dist/types.cjs +7 -0
  268. package/dist/types.cjs.map +1 -1
  269. package/dist/types.d.cts +12 -2
  270. package/dist/types.d.cts.map +1 -1
  271. package/dist/types.d.mts +12 -2
  272. package/dist/types.d.mts.map +1 -1
  273. package/dist/types.mjs +7 -0
  274. package/dist/types.mjs.map +1 -1
  275. package/dist/validator.test.cjs +15 -15
  276. package/dist/validator.test.cjs.map +1 -1
  277. package/dist/validator.test.mjs +2 -2
  278. package/dist/version-manager.cjs +1 -1
  279. package/dist/version-manager.cjs.map +1 -1
  280. package/dist/version-manager.mjs +1 -1
  281. package/dist/version-manager.mjs.map +1 -1
  282. package/dist/version-manager.test.cjs +40 -38
  283. package/dist/version-manager.test.cjs.map +1 -1
  284. package/dist/version-manager.test.mjs +7 -5
  285. package/dist/version-manager.test.mjs.map +1 -1
  286. package/package.json +4 -4
  287. package/src/__snapshots__/composer.snapshot.test.ts.snap +160 -0
  288. package/src/addon-stack.test.ts +490 -0
  289. package/src/addon-stack.ts +998 -0
  290. package/src/bare-metal-partition.test.ts +4 -3
  291. package/src/composer.test.ts +4 -2
  292. package/src/composer.ts +24 -5
  293. package/src/generate.test.ts +2 -1
  294. package/src/generate.ts +10 -1
  295. package/src/generators/clone-repos.test.ts +154 -0
  296. package/src/generators/clone-repos.ts +159 -0
  297. package/src/generators/postgres-init.ts +17 -0
  298. package/src/generators/scripts.test.ts +52 -4
  299. package/src/generators/scripts.ts +351 -3
  300. package/src/generators/stack-manifest.ts +4 -2
  301. package/src/index.ts +28 -2
  302. package/src/presets/registry.ts +241 -329
  303. package/src/resolver.test.ts +53 -15
  304. package/src/resolver.ts +13 -1
  305. package/src/schema.ts +216 -4
  306. package/src/services/definitions/apptension-saas.ts +84 -0
  307. package/src/services/definitions/boxyhq-saas.ts +84 -0
  308. package/src/services/definitions/browserless.ts +3 -0
  309. package/src/services/definitions/cmsaas-starter.ts +84 -0
  310. package/src/services/definitions/convex.ts +31 -0
  311. package/src/services/definitions/grafana.ts +9 -0
  312. package/src/services/definitions/index.ts +90 -70
  313. package/src/services/definitions/ixartz-saas.ts +84 -0
  314. package/src/services/definitions/meilisearch.ts +9 -0
  315. package/src/services/definitions/minio.ts +2 -0
  316. package/src/services/definitions/mission-control.ts +19 -2
  317. package/src/services/definitions/n8n.ts +9 -0
  318. package/src/services/definitions/ollama.ts +2 -0
  319. package/src/services/definitions/open-saas.ts +79 -0
  320. package/src/services/definitions/qdrant.ts +2 -0
  321. package/src/services/definitions/searxng.ts +3 -0
  322. package/src/services/definitions/uptime-kuma.ts +3 -0
  323. package/src/services/registry.test.ts +8 -0
  324. package/src/services/registry.ts +7 -0
  325. package/src/skills/manifest.json +64 -0
  326. package/src/skills/registry.ts +3 -3
  327. package/src/track-analytics.test.ts +82 -0
  328. package/src/track-analytics.ts +76 -0
  329. package/src/types.ts +29 -0
  330. package/src/version-manager.test.ts +10 -5
  331. package/src/version-manager.ts +1 -1
  332. package/dist/schema-B4c64P8N.d.cts.map +0 -1
  333. package/dist/schema-CXNhYci1.d.mts.map +0 -1
  334. package/dist/vi.2VT5v0um-CRqXre87.cjs.map +0 -1
  335. package/dist/vi.2VT5v0um-DvC3SVNc.mjs.map +0 -1
@@ -0,0 +1,673 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_skills = require("./skills-BlzpHmpH.cjs");
3
+ const require_generators_postgres_init = require("./generators/postgres-init.cjs");
4
+ const require_composer = require("./composer.cjs");
5
+ const require_services_registry = require("./services/registry.cjs");
6
+ const require_resolver = require("./resolver.cjs");
7
+ const require_schema = require("./schema.cjs");
8
+ let yaml = require("yaml");
9
+ let node_crypto = require("node:crypto");
10
+ //#region src/addon-stack.ts
11
+ /** Services that Clawexa's cloud-init already provisions (or are mandatory platform services). */
12
+ const INFRA_SERVICE_IDS = new Set([
13
+ "openclaw-gateway",
14
+ "openclaw-cli",
15
+ "redis",
16
+ "postgresql",
17
+ "open-webui",
18
+ "caddy",
19
+ "traefik",
20
+ "postgres-setup",
21
+ "convex",
22
+ "convex-dashboard",
23
+ "mission-control"
24
+ ]);
25
+ /** Env keys managed by Clawexa's cloud-init — never include in addon env output. */
26
+ const CLAWEXA_MANAGED_ENV_KEYS = new Set([
27
+ "COMPOSE_FILE",
28
+ "COMPOSE_PROFILES",
29
+ "OPENCLAW_VERSION",
30
+ "OPENCLAW_GATEWAY_TOKEN",
31
+ "OPENCLAW_GATEWAY_PORT",
32
+ "OPENCLAW_BRIDGE_PORT",
33
+ "OPENCLAW_GATEWAY_BIND",
34
+ "OPENCLAW_CONFIG_DIR",
35
+ "OPENCLAW_WORKSPACE_DIR",
36
+ "REDIS_PASSWORD",
37
+ "REDIS_HOST",
38
+ "REDIS_PORT",
39
+ "POSTGRES_USER",
40
+ "POSTGRES_PASSWORD",
41
+ "POSTGRES_DB",
42
+ "POSTGRES_HOST",
43
+ "POSTGRES_PORT"
44
+ ]);
45
+ /** Sanitize instanceId into a valid Docker Compose project name. */
46
+ function sanitizeProjectName(instanceId) {
47
+ return instanceId.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0, 64) || "addon";
48
+ }
49
+ /** Generate a cryptographically secure hex secret of the given byte length. */
50
+ function generateHexSecret(bytes) {
51
+ return (0, node_crypto.randomBytes)(bytes).toString("hex");
52
+ }
53
+ /** Generate a cryptographically secure base64url secret of the given byte length. */
54
+ function generateBase64UrlSecret(bytes) {
55
+ return (0, node_crypto.randomBytes)(bytes).toString("base64url");
56
+ }
57
+ /**
58
+ * Check if a service requires user-provided credentials that are missing.
59
+ * Returns the list of missing credential keys, or empty if all are satisfied.
60
+ *
61
+ * When `generateSecrets` is true, empty-default secrets are auto-generated
62
+ * (passwords, tokens, etc.) and are NOT considered missing. Only secrets
63
+ * with `validation` regex patterns (indicating specific format like API keys)
64
+ * are flagged as missing when not provided by the user.
65
+ */
66
+ function getMissingCredentials(def, userCredentials, generateSecrets) {
67
+ const missing = [];
68
+ for (const env of def.environment) {
69
+ if (!env.required || !env.secret) continue;
70
+ if (env.defaultValue && env.defaultValue.length > 0) continue;
71
+ if (userCredentials?.[env.key]) continue;
72
+ if (generateSecrets && !env.validation) continue;
73
+ missing.push(env.key);
74
+ }
75
+ return missing;
76
+ }
77
+ /**
78
+ * Apply env quirks (from service definition) to the generated env values.
79
+ * Handles: empty_string_crashes → set fixed value, min_length → generate longer secret,
80
+ * must_sync → ensure two keys have the same value.
81
+ */
82
+ function applyEnvQuirks(def, envValues, generateSecrets) {
83
+ if (!def.envQuirks) return;
84
+ for (const quirk of def.envQuirks) switch (quirk.issue) {
85
+ case "empty_string_crashes":
86
+ if (quirk.fix.type === "set_value" && quirk.fix.value !== void 0) envValues.set(quirk.key, quirk.fix.value);
87
+ break;
88
+ case "min_length": {
89
+ if (!generateSecrets) break;
90
+ const current = envValues.get(quirk.key) || "";
91
+ const minBytes = quirk.fix.minBytes || 24;
92
+ const minHexLen = minBytes * 2;
93
+ if (current.length < minHexLen) {
94
+ if (quirk.fix.type === "generate_hex") envValues.set(quirk.key, generateHexSecret(minBytes));
95
+ else if (quirk.fix.type === "generate_base64url") envValues.set(quirk.key, generateBase64UrlSecret(minBytes));
96
+ }
97
+ break;
98
+ }
99
+ case "must_sync":
100
+ if (quirk.fix.type === "sync_with" && quirk.fix.syncKey) {
101
+ const sourceValue = envValues.get(quirk.key) || envValues.get(quirk.fix.syncKey);
102
+ if (sourceValue) {
103
+ envValues.set(quirk.key, sourceValue);
104
+ envValues.set(quirk.fix.syncKey, sourceValue);
105
+ }
106
+ }
107
+ break;
108
+ }
109
+ }
110
+ /**
111
+ * Build proxy routes from resolved addon services.
112
+ */
113
+ function buildProxyRoutes(services) {
114
+ const routes = [];
115
+ for (const { definition: def } of services) {
116
+ const exposedPort = def.ports.find((p) => p.exposed);
117
+ if (!exposedPort) continue;
118
+ routes.push({
119
+ serviceId: def.id,
120
+ path: def.proxyPath || `/${def.id}`,
121
+ port: exposedPort.container,
122
+ protocol: "http",
123
+ stripPrefix: true
124
+ });
125
+ }
126
+ return routes;
127
+ }
128
+ /**
129
+ * Build a minimal ComposeOptions suitable for addon stack generation.
130
+ * No proxy, no gateway, no hardening by default.
131
+ */
132
+ function buildAddonComposeOptions(projectName, input) {
133
+ return {
134
+ projectName,
135
+ proxy: "none",
136
+ gpu: false,
137
+ platform: input.platform ?? "linux/amd64",
138
+ deployment: "clawexa",
139
+ openclawVersion: input.openclawVersion ?? "latest",
140
+ openclawImage: "official",
141
+ hardened: false,
142
+ openclawInstallMethod: "docker"
143
+ };
144
+ }
145
+ /**
146
+ * Resolve port conflicts between addon services and reserved ports.
147
+ * Returns a map of serviceId → { originalPort → assignedPort }.
148
+ */
149
+ function resolvePortConflicts(addonServices, reservedPorts, portOverrides) {
150
+ const usedPorts = new Set(reservedPorts);
151
+ const assignments = {};
152
+ const overrides = {};
153
+ for (const { definition: def } of addonServices) for (const port of def.ports) {
154
+ if (!port.exposed) continue;
155
+ const userOverride = portOverrides?.[def.id]?.[String(port.host)];
156
+ if (userOverride) {
157
+ usedPorts.add(userOverride);
158
+ assignments[`${def.id}:${port.container}`] = userOverride;
159
+ if (!overrides[def.id]) overrides[def.id] = {};
160
+ overrides[def.id][String(port.host)] = userOverride;
161
+ continue;
162
+ }
163
+ let assignedPort = port.host;
164
+ if (usedPorts.has(assignedPort)) {
165
+ assignedPort = port.host + 1e3;
166
+ while (usedPorts.has(assignedPort)) assignedPort++;
167
+ if (!overrides[def.id]) overrides[def.id] = {};
168
+ overrides[def.id][String(port.host)] = assignedPort;
169
+ }
170
+ usedPorts.add(assignedPort);
171
+ assignments[`${def.id}:${port.container}`] = assignedPort;
172
+ }
173
+ return {
174
+ assignments,
175
+ overrides
176
+ };
177
+ }
178
+ /**
179
+ * Generates a Docker Compose override stack containing only addon services
180
+ * for Clawexa managed instances. Infrastructure services (gateway, redis,
181
+ * postgres, open-webui, caddy) are excluded since Clawexa's cloud-init
182
+ * already provisions them.
183
+ *
184
+ * This function never throws. Errors are reported via `warnings` and
185
+ * `metadata.skippedServices`.
186
+ */
187
+ function generateAddonStack(rawInput) {
188
+ const warnings = [];
189
+ const skippedServices = [];
190
+ const generatedSecretKeys = [];
191
+ let input;
192
+ try {
193
+ input = require_schema.AddonStackInputSchema.parse(rawInput);
194
+ } catch (err) {
195
+ return emptyResult(`Invalid input: ${err instanceof Error ? err.message : String(err)}`);
196
+ }
197
+ const projectName = sanitizeProjectName(input.instanceId);
198
+ const addonServiceIds = input.services.filter((id) => {
199
+ if (INFRA_SERVICE_IDS.has(id)) {
200
+ warnings.push(`Service "${id}" is managed by Clawexa infrastructure and was excluded.`);
201
+ return false;
202
+ }
203
+ return true;
204
+ });
205
+ if (addonServiceIds.length === 0) return emptyResult("No addon services requested (all were infrastructure services).");
206
+ const validServiceIds = [];
207
+ for (const id of addonServiceIds) if (!require_services_registry.getServiceById(id)) skippedServices.push({
208
+ serviceId: id,
209
+ reason: "unknown_service",
210
+ details: `Service "${id}" does not exist in the registry.`
211
+ });
212
+ else validServiceIds.push(id);
213
+ if (validServiceIds.length === 0) return {
214
+ ...emptyResultBase(),
215
+ metadata: {
216
+ ...emptyResultBase().metadata,
217
+ skippedServices
218
+ },
219
+ warnings: [...warnings, "No valid addon services to deploy."]
220
+ };
221
+ let resolved;
222
+ try {
223
+ resolved = require_resolver.resolve({
224
+ services: validServiceIds,
225
+ skillPacks: input.skillPacks,
226
+ aiProviders: input.aiProviders,
227
+ platform: input.platform ?? "linux/amd64"
228
+ });
229
+ } catch (err) {
230
+ return {
231
+ ...emptyResultBase(),
232
+ metadata: {
233
+ ...emptyResultBase().metadata,
234
+ skippedServices
235
+ },
236
+ warnings: [...warnings, `Dependency resolution failed: ${err instanceof Error ? err.message : String(err)}`]
237
+ };
238
+ }
239
+ for (const w of resolved.warnings) warnings.push(w.message);
240
+ const addonResolved = [];
241
+ for (const svc of resolved.services) {
242
+ if (INFRA_SERVICE_IDS.has(svc.definition.id)) continue;
243
+ addonResolved.push(svc);
244
+ }
245
+ const deployableServices = [];
246
+ for (const svc of addonResolved) {
247
+ const def = svc.definition;
248
+ if (def.gitSource && def.buildContext && !def.image) {
249
+ if (!(def.prebuiltImage || input.prebuiltImages[def.id])) {
250
+ skippedServices.push({
251
+ serviceId: def.id,
252
+ reason: "no_image",
253
+ details: `Service "${def.name}" requires building from source but no pre-built image is available.`
254
+ });
255
+ continue;
256
+ }
257
+ }
258
+ if (def.gpuRequired && !input.gpu) {
259
+ skippedServices.push({
260
+ serviceId: def.id,
261
+ reason: "gpu_required",
262
+ details: `Service "${def.name}" requires a GPU but the host does not have GPU support.`
263
+ });
264
+ continue;
265
+ }
266
+ const userCreds = input.credentials[def.id];
267
+ const missing = getMissingCredentials(def, userCreds, input.generateSecrets);
268
+ if (missing.length > 0) {
269
+ skippedServices.push({
270
+ serviceId: def.id,
271
+ reason: "missing_credentials",
272
+ details: `Service "${def.name}" requires credentials: ${missing.join(", ")}`,
273
+ requiredCredentials: missing
274
+ });
275
+ continue;
276
+ }
277
+ deployableServices.push(svc);
278
+ }
279
+ if (deployableServices.length === 0) return {
280
+ ...emptyResultBase(),
281
+ metadata: {
282
+ ...emptyResultBase().metadata,
283
+ skippedServices
284
+ },
285
+ warnings: [...warnings, "No deployable addon services after filtering."]
286
+ };
287
+ const allReservedPorts = [...input.reservedPorts];
288
+ for (const existingId of input.existingServices) {
289
+ const existingDef = require_services_registry.getServiceById(existingId);
290
+ if (existingDef) {
291
+ for (const port of existingDef.ports) if (port.exposed) allReservedPorts.push(port.host);
292
+ }
293
+ }
294
+ const portConflicts = resolvePortConflicts(deployableServices, allReservedPorts, input.portOverrides);
295
+ const addonResolvedOutput = {
296
+ services: deployableServices,
297
+ addedDependencies: resolved.addedDependencies,
298
+ removedConflicts: resolved.removedConflicts,
299
+ warnings: resolved.warnings,
300
+ errors: [],
301
+ isValid: true,
302
+ estimatedMemoryMB: deployableServices.reduce((sum, s) => sum + (s.definition.minMemoryMB ?? 128), 0),
303
+ aiProviders: input.aiProviders ?? [],
304
+ gsdRuntimes: []
305
+ };
306
+ const composeOptions = buildAddonComposeOptions(projectName, input);
307
+ if (Object.keys(portConflicts.overrides).length > 0) composeOptions.portOverrides = portConflicts.overrides;
308
+ const services = {};
309
+ const allVolumes = /* @__PURE__ */ new Set();
310
+ const envValues = /* @__PURE__ */ new Map();
311
+ for (const svc of deployableServices) {
312
+ const def = svc.definition;
313
+ try {
314
+ let effectiveDef = def;
315
+ if (def.gitSource && def.buildContext && !def.image) {
316
+ const prebuiltImage = def.prebuiltImage || input.prebuiltImages[def.id];
317
+ if (prebuiltImage) {
318
+ const [img, tag] = prebuiltImage.includes(":") ? prebuiltImage.split(":") : [prebuiltImage, "latest"];
319
+ effectiveDef = {
320
+ ...def,
321
+ image: img,
322
+ imageTag: tag,
323
+ gitSource: void 0,
324
+ buildContext: void 0
325
+ };
326
+ }
327
+ }
328
+ const { entry, volumeNames } = require_composer.buildCompanionService(effectiveDef, addonResolvedOutput, composeOptions, allVolumes);
329
+ delete entry.profiles;
330
+ if (entry.depends_on) {
331
+ const deps = entry.depends_on;
332
+ for (const depId of Object.keys(deps)) if (INFRA_SERVICE_IDS.has(depId)) delete deps[depId];
333
+ if (Object.keys(deps).length === 0) delete entry.depends_on;
334
+ }
335
+ services[def.id] = entry;
336
+ for (const v of volumeNames) allVolumes.add(v);
337
+ const userCreds = input.credentials[def.id];
338
+ if (userCreds) {
339
+ for (const [key, value] of Object.entries(userCreds)) envValues.set(key, value);
340
+ for (const envVar of def.environment) if (userCreds[envVar.key] && envVar.defaultValue?.startsWith("${") && envVar.defaultValue?.endsWith("}")) {
341
+ const refKey = envVar.defaultValue.slice(2, -1);
342
+ envValues.set(refKey, userCreds[envVar.key]);
343
+ }
344
+ }
345
+ } catch (err) {
346
+ skippedServices.push({
347
+ serviceId: def.id,
348
+ reason: "resolution_error",
349
+ details: `Failed to build compose entry: ${err instanceof Error ? err.message : String(err)}`
350
+ });
351
+ warnings.push(`Failed to process service "${def.name}": ${err instanceof Error ? err.message : String(err)}`);
352
+ }
353
+ }
354
+ const dbReqs = require_generators_postgres_init.getDbRequirements(addonResolvedOutput);
355
+ if (dbReqs.length > 0) {
356
+ const scriptLines = ["echo '=== PostgreSQL database setup (addon) ==='", "FAILED=0"];
357
+ for (const req of dbReqs) scriptLines.push(`echo "Setting up database '${req.dbName}' with user '${req.dbUser}'..."`, `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}'"`, `psql -c "ALTER ROLE ${req.dbUser} WITH LOGIN PASSWORD '$$${req.passwordEnvVar}'"`, `psql -tc "SELECT 1 FROM pg_database WHERE datname='${req.dbName}'" | grep -q 1 || psql -c "CREATE DATABASE ${req.dbName} OWNER ${req.dbUser}"`, `psql -c "GRANT ALL PRIVILEGES ON DATABASE ${req.dbName} TO ${req.dbUser}" || FAILED=1`, `echo " Done: ${req.dbName}"`);
358
+ scriptLines.push("echo '=== All databases ready ==='", "exit $$FAILED");
359
+ const dbEnv = {
360
+ PGHOST: "postgresql",
361
+ PGUSER: "${POSTGRES_USER:-openclaw}",
362
+ PGDATABASE: "${POSTGRES_DB:-openclaw}",
363
+ PGPASSWORD: "${POSTGRES_PASSWORD}"
364
+ };
365
+ for (const req of dbReqs) dbEnv[req.passwordEnvVar] = `\${${req.passwordEnvVar}}`;
366
+ services["postgres-setup"] = {
367
+ image: "postgres:17-alpine",
368
+ depends_on: { postgresql: { condition: "service_healthy" } },
369
+ environment: dbEnv,
370
+ entrypoint: ["/bin/sh", "-c"],
371
+ command: [scriptLines.join("\n")],
372
+ restart: require_composer.quotedStr("no"),
373
+ networks: ["openclaw-network"]
374
+ };
375
+ for (const req of dbReqs) {
376
+ const svcEntry = services[req.serviceId];
377
+ if (svcEntry) {
378
+ const deps = svcEntry.depends_on || {};
379
+ deps["postgres-setup"] = { condition: "service_completed_successfully" };
380
+ svcEntry.depends_on = deps;
381
+ }
382
+ }
383
+ }
384
+ const envLines = [
385
+ "# ═══════════════════════════════════════════════════════════════════════════════",
386
+ "# OpenClaw Addon Stack Environment",
387
+ `# Instance: ${input.instanceId}`,
388
+ `# Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`,
389
+ "# ═══════════════════════════════════════════════════════════════════════════════",
390
+ ""
391
+ ];
392
+ if (dbReqs.length > 0) {
393
+ envLines.push("# ── Per-Service Database Passwords ──────────────────────────────────────");
394
+ for (const req of dbReqs) {
395
+ const secretValue = input.generateSecrets ? generateHexSecret(24) : "";
396
+ envValues.set(req.passwordEnvVar, secretValue);
397
+ if (secretValue) generatedSecretKeys.push(req.passwordEnvVar);
398
+ envLines.push(`# PostgreSQL password for ${req.serviceName} (db: ${req.dbName}, user: ${req.dbUser})`);
399
+ envLines.push(`${req.passwordEnvVar}=${secretValue}`);
400
+ envLines.push("");
401
+ }
402
+ }
403
+ const seenKeys = new Set([...CLAWEXA_MANAGED_ENV_KEYS, ...dbReqs.map((r) => r.passwordEnvVar)]);
404
+ const envVarGroups = [];
405
+ for (const svc of deployableServices) {
406
+ const def = svc.definition;
407
+ const allEnvVars = [...def.environment, ...def.openclawEnvVars];
408
+ if (allEnvVars.length === 0) continue;
409
+ const groupVars = [];
410
+ envLines.push(`# ── ${def.icon} ${def.name} ──────────────────────────────────────`);
411
+ for (const envVar of allEnvVars) {
412
+ if (seenKeys.has(envVar.key)) continue;
413
+ seenKeys.add(envVar.key);
414
+ const userValue = input.credentials[def.id]?.[envVar.key];
415
+ let actualValue;
416
+ if (userValue !== void 0) actualValue = userValue;
417
+ else if (envVar.secret) if (envVar.defaultValue.startsWith("${") && envVar.defaultValue.endsWith("}")) {
418
+ const refKey = envVar.defaultValue.slice(2, -1);
419
+ actualValue = envValues.get(refKey) || envVar.defaultValue;
420
+ } else if (input.generateSecrets) {
421
+ actualValue = generateHexSecret(24);
422
+ generatedSecretKeys.push(envVar.key);
423
+ } else actualValue = envVar.defaultValue;
424
+ else actualValue = envVar.defaultValue;
425
+ envValues.set(envVar.key, actualValue);
426
+ envLines.push(`# ${envVar.description}`);
427
+ envLines.push(`${envVar.key}=${actualValue}`);
428
+ envLines.push("");
429
+ groupVars.push({
430
+ key: envVar.key,
431
+ description: envVar.description,
432
+ value: actualValue,
433
+ secret: envVar.secret
434
+ });
435
+ }
436
+ if (groupVars.length > 0) envVarGroups.push({
437
+ serviceName: def.name,
438
+ vars: groupVars
439
+ });
440
+ }
441
+ for (const svc of deployableServices) applyEnvQuirks(svc.definition, envValues, input.generateSecrets);
442
+ const quirkedKeys = /* @__PURE__ */ new Set();
443
+ const finalEnvLines = [];
444
+ for (const line of envLines) {
445
+ const trimmed = line.trim();
446
+ if (!trimmed || trimmed.startsWith("#")) {
447
+ finalEnvLines.push(line);
448
+ continue;
449
+ }
450
+ const eqIdx = trimmed.indexOf("=");
451
+ if (eqIdx > 0) {
452
+ const key = trimmed.slice(0, eqIdx);
453
+ quirkedKeys.add(key);
454
+ const fixedValue = envValues.get(key);
455
+ if (fixedValue !== void 0) finalEnvLines.push(`${key}=${fixedValue}`);
456
+ else finalEnvLines.push(line);
457
+ } else finalEnvLines.push(line);
458
+ }
459
+ for (const [key, value] of envValues) if (!quirkedKeys.has(key) && !seenKeys.has(key) && !CLAWEXA_MANAGED_ENV_KEYS.has(key)) {
460
+ finalEnvLines.push(`# Synced by env quirk`);
461
+ finalEnvLines.push(`${key}=${value}`);
462
+ finalEnvLines.push("");
463
+ }
464
+ const envFile = finalEnvLines.join("\n");
465
+ const skillFiles = require_skills.generateSkillFiles(addonResolvedOutput);
466
+ const skillEntries = {};
467
+ let skillCount = 0;
468
+ for (const svc of deployableServices) for (const skill of svc.definition.skills) if (skill.autoInstall) {
469
+ skillEntries[skill.skillId] = { enabled: true };
470
+ skillCount++;
471
+ }
472
+ const proxyRoutes = buildProxyRoutes(deployableServices);
473
+ const volumeMap = {};
474
+ for (const v of allVolumes) volumeMap[v] = null;
475
+ const composeDoc = { services };
476
+ if (Object.keys(volumeMap).length > 0) composeDoc.volumes = volumeMap;
477
+ composeDoc.networks = { "openclaw-network": { external: true } };
478
+ return {
479
+ composeOverride: (0, yaml.stringify)(composeDoc, require_composer.YAML_OPTIONS),
480
+ envFile,
481
+ envVars: envVarGroups,
482
+ skillFiles,
483
+ openclawConfigPatch: { skills: { entries: skillEntries } },
484
+ proxyRoutes,
485
+ metadata: {
486
+ serviceCount: Object.keys(services).length,
487
+ skillCount,
488
+ estimatedMemoryMB: addonResolvedOutput.estimatedMemoryMB,
489
+ resolvedServices: deployableServices.map((s) => s.definition.id),
490
+ skippedServices,
491
+ generatedSecretKeys,
492
+ portAssignments: portConflicts.assignments
493
+ },
494
+ warnings
495
+ };
496
+ }
497
+ /**
498
+ * Incrementally updates an existing addon stack by adding or removing services.
499
+ * Preserves existing env values (never overwrites user-customized values).
500
+ *
501
+ * This function never throws.
502
+ */
503
+ function updateAddonStack(rawInput) {
504
+ const warnings = [];
505
+ let input;
506
+ try {
507
+ input = require_schema.AddonStackUpdateInputSchema.parse(rawInput);
508
+ } catch (err) {
509
+ return emptyUpdateResult(`Invalid input: ${err instanceof Error ? err.message : String(err)}`);
510
+ }
511
+ let currentServiceIds = [];
512
+ try {
513
+ const existingCompose = (0, yaml.parse)(input.currentCompose);
514
+ if (existingCompose?.services && typeof existingCompose.services === "object") currentServiceIds = Object.keys(existingCompose.services).filter((id) => id !== "postgres-setup");
515
+ } catch (err) {
516
+ warnings.push(`Failed to parse existing compose YAML: ${err instanceof Error ? err.message : String(err)}`);
517
+ }
518
+ const existingEnvMap = /* @__PURE__ */ new Map();
519
+ for (const line of input.currentEnv.split("\n")) {
520
+ const trimmed = line.trim();
521
+ if (!trimmed || trimmed.startsWith("#")) continue;
522
+ const eqIdx = trimmed.indexOf("=");
523
+ if (eqIdx > 0) existingEnvMap.set(trimmed.slice(0, eqIdx), trimmed.slice(eqIdx + 1));
524
+ }
525
+ new Set(input.addServices);
526
+ const removeSet = new Set(input.removeServices);
527
+ const desiredServiceIds = [...currentServiceIds.filter((id) => !removeSet.has(id)), ...input.addServices.filter((id) => !currentServiceIds.includes(id))];
528
+ const targetResult = generateAddonStack({
529
+ instanceId: input.instanceId,
530
+ services: desiredServiceIds,
531
+ skillPacks: [],
532
+ platform: input.platform,
533
+ openclawVersion: input.openclawVersion,
534
+ reservedPorts: input.reservedPorts,
535
+ generateSecrets: input.generateSecrets,
536
+ credentials: input.credentials,
537
+ portOverrides: input.portOverrides,
538
+ aiProviders: input.aiProviders,
539
+ prebuiltImages: input.prebuiltImages
540
+ });
541
+ const mergedEnvLines = [];
542
+ for (const line of targetResult.envFile.split("\n")) {
543
+ const trimmed = line.trim();
544
+ if (!trimmed || trimmed.startsWith("#")) {
545
+ mergedEnvLines.push(line);
546
+ continue;
547
+ }
548
+ const eqIdx = trimmed.indexOf("=");
549
+ if (eqIdx > 0) {
550
+ const key = trimmed.slice(0, eqIdx);
551
+ if (existingEnvMap.has(key)) mergedEnvLines.push(`${key}=${existingEnvMap.get(key)}`);
552
+ else mergedEnvLines.push(line);
553
+ } else mergedEnvLines.push(line);
554
+ }
555
+ const currentSet = new Set(currentServiceIds);
556
+ const targetSet = new Set(targetResult.metadata.resolvedServices);
557
+ const added = [...targetSet].filter((id) => !currentSet.has(id));
558
+ const removed = [...currentSet].filter((id) => !targetSet.has(id));
559
+ const unchanged = [...currentSet].filter((id) => targetSet.has(id));
560
+ const newSkillFiles = {};
561
+ const removedSkillSlugs = [];
562
+ for (const id of added) {
563
+ const def = require_services_registry.getServiceById(id);
564
+ if (!def) continue;
565
+ for (const skill of def.skills) {
566
+ const skillPath = Object.keys(targetResult.skillFiles).find((path) => path.includes(skill.skillId));
567
+ if (skillPath) newSkillFiles[skillPath] = targetResult.skillFiles[skillPath];
568
+ }
569
+ }
570
+ for (const id of removed) {
571
+ const def = require_services_registry.getServiceById(id);
572
+ if (!def) continue;
573
+ for (const skill of def.skills) removedSkillSlugs.push(skill.skillId);
574
+ }
575
+ const addProxyRoutes = targetResult.proxyRoutes.filter((r) => added.includes(r.serviceId));
576
+ const removeProxyRoutes = removed;
577
+ const imagesToPull = [];
578
+ for (const id of added) {
579
+ const def = require_services_registry.getServiceById(id);
580
+ if (def?.image && def?.imageTag) imagesToPull.push(`${def.image}:${def.imageTag}`);
581
+ else if (def?.prebuiltImage) imagesToPull.push(def.prebuiltImage);
582
+ }
583
+ let memoryDelta = 0;
584
+ for (const id of added) {
585
+ const def = require_services_registry.getServiceById(id);
586
+ memoryDelta += def?.minMemoryMB ?? 128;
587
+ }
588
+ for (const id of removed) {
589
+ const def = require_services_registry.getServiceById(id);
590
+ memoryDelta -= def?.minMemoryMB ?? 128;
591
+ }
592
+ const addSkillEntries = {};
593
+ for (const id of added) {
594
+ const def = require_services_registry.getServiceById(id);
595
+ if (!def) continue;
596
+ for (const skill of def.skills) if (skill.autoInstall) addSkillEntries[skill.skillId] = { enabled: true };
597
+ }
598
+ return {
599
+ composeOverride: targetResult.composeOverride,
600
+ envFile: mergedEnvLines.join("\n"),
601
+ newSkillFiles,
602
+ removedSkillSlugs,
603
+ openclawConfigPatch: { skills: {
604
+ add: addSkillEntries,
605
+ remove: removedSkillSlugs
606
+ } },
607
+ addProxyRoutes,
608
+ removeProxyRoutes,
609
+ imagesToPull,
610
+ restartRequired: added,
611
+ metadata: {
612
+ added,
613
+ removed,
614
+ unchanged,
615
+ estimatedMemoryDelta: memoryDelta
616
+ },
617
+ warnings: [...warnings, ...targetResult.warnings]
618
+ };
619
+ }
620
+ function emptyResultBase() {
621
+ return {
622
+ composeOverride: "services: {}\n",
623
+ envFile: "",
624
+ envVars: [],
625
+ skillFiles: {},
626
+ openclawConfigPatch: { skills: { entries: {} } },
627
+ proxyRoutes: [],
628
+ metadata: {
629
+ serviceCount: 0,
630
+ skillCount: 0,
631
+ estimatedMemoryMB: 0,
632
+ resolvedServices: [],
633
+ skippedServices: [],
634
+ generatedSecretKeys: [],
635
+ portAssignments: {}
636
+ },
637
+ warnings: []
638
+ };
639
+ }
640
+ function emptyResult(warning) {
641
+ return {
642
+ ...emptyResultBase(),
643
+ warnings: [warning]
644
+ };
645
+ }
646
+ function emptyUpdateResult(warning) {
647
+ return {
648
+ composeOverride: "services: {}\n",
649
+ envFile: "",
650
+ newSkillFiles: {},
651
+ removedSkillSlugs: [],
652
+ openclawConfigPatch: { skills: {
653
+ add: {},
654
+ remove: []
655
+ } },
656
+ addProxyRoutes: [],
657
+ removeProxyRoutes: [],
658
+ imagesToPull: [],
659
+ restartRequired: [],
660
+ metadata: {
661
+ added: [],
662
+ removed: [],
663
+ unchanged: [],
664
+ estimatedMemoryDelta: 0
665
+ },
666
+ warnings: [warning]
667
+ };
668
+ }
669
+ //#endregion
670
+ exports.generateAddonStack = generateAddonStack;
671
+ exports.updateAddonStack = updateAddonStack;
672
+
673
+ //# sourceMappingURL=addon-stack.cjs.map