@better-openclaw/core 1.0.0

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 (461) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-lint.log +360 -0
  3. package/.turbo/turbo-typecheck.log +4 -0
  4. package/dist/bare-metal-partition.d.ts +22 -0
  5. package/dist/bare-metal-partition.d.ts.map +1 -0
  6. package/dist/bare-metal-partition.js +49 -0
  7. package/dist/bare-metal-partition.js.map +1 -0
  8. package/dist/composer.d.ts +17 -0
  9. package/dist/composer.d.ts.map +1 -0
  10. package/dist/composer.js +305 -0
  11. package/dist/composer.js.map +1 -0
  12. package/dist/generate.d.ts +7 -0
  13. package/dist/generate.d.ts.map +1 -0
  14. package/dist/generate.js +218 -0
  15. package/dist/generate.js.map +1 -0
  16. package/dist/generators/bare-metal-install.d.ts +18 -0
  17. package/dist/generators/bare-metal-install.d.ts.map +1 -0
  18. package/dist/generators/bare-metal-install.js +228 -0
  19. package/dist/generators/bare-metal-install.js.map +1 -0
  20. package/dist/generators/caddy.d.ts +13 -0
  21. package/dist/generators/caddy.d.ts.map +1 -0
  22. package/dist/generators/caddy.js +85 -0
  23. package/dist/generators/caddy.js.map +1 -0
  24. package/dist/generators/env.d.ts +42 -0
  25. package/dist/generators/env.d.ts.map +1 -0
  26. package/dist/generators/env.js +276 -0
  27. package/dist/generators/env.js.map +1 -0
  28. package/dist/generators/grafana.d.ts +19 -0
  29. package/dist/generators/grafana.d.ts.map +1 -0
  30. package/dist/generators/grafana.js +247 -0
  31. package/dist/generators/grafana.js.map +1 -0
  32. package/dist/generators/n8n-workflows.d.ts +7 -0
  33. package/dist/generators/n8n-workflows.d.ts.map +1 -0
  34. package/dist/generators/n8n-workflows.js +78 -0
  35. package/dist/generators/n8n-workflows.js.map +1 -0
  36. package/dist/generators/native-services.d.ts +13 -0
  37. package/dist/generators/native-services.d.ts.map +1 -0
  38. package/dist/generators/native-services.js +85 -0
  39. package/dist/generators/native-services.js.map +1 -0
  40. package/dist/generators/postgres-init.d.ts +27 -0
  41. package/dist/generators/postgres-init.d.ts.map +1 -0
  42. package/dist/generators/postgres-init.js +119 -0
  43. package/dist/generators/postgres-init.js.map +1 -0
  44. package/dist/generators/prometheus.d.ts +10 -0
  45. package/dist/generators/prometheus.d.ts.map +1 -0
  46. package/dist/generators/prometheus.js +79 -0
  47. package/dist/generators/prometheus.js.map +1 -0
  48. package/dist/generators/readme.d.ts +21 -0
  49. package/dist/generators/readme.d.ts.map +1 -0
  50. package/dist/generators/readme.js +224 -0
  51. package/dist/generators/readme.js.map +1 -0
  52. package/dist/generators/scripts.d.ts +7 -0
  53. package/dist/generators/scripts.d.ts.map +1 -0
  54. package/dist/generators/scripts.js +352 -0
  55. package/dist/generators/scripts.js.map +1 -0
  56. package/dist/generators/skills.d.ts +10 -0
  57. package/dist/generators/skills.d.ts.map +1 -0
  58. package/dist/generators/skills.js +596 -0
  59. package/dist/generators/skills.js.map +1 -0
  60. package/dist/index.d.ts +24 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +27 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/presets/registry.d.ts +5 -0
  65. package/dist/presets/registry.d.ts.map +1 -0
  66. package/dist/presets/registry.js +121 -0
  67. package/dist/presets/registry.js.map +1 -0
  68. package/dist/resolver.d.ts +17 -0
  69. package/dist/resolver.d.ts.map +1 -0
  70. package/dist/resolver.js +254 -0
  71. package/dist/resolver.js.map +1 -0
  72. package/dist/schema.d.ts +2426 -0
  73. package/dist/schema.d.ts.map +1 -0
  74. package/dist/schema.js +259 -0
  75. package/dist/schema.js.map +1 -0
  76. package/dist/services/definitions/anything-llm.d.ts +3 -0
  77. package/dist/services/definitions/anything-llm.d.ts.map +1 -0
  78. package/dist/services/definitions/anything-llm.js +39 -0
  79. package/dist/services/definitions/anything-llm.js.map +1 -0
  80. package/dist/services/definitions/appflowy.d.ts +3 -0
  81. package/dist/services/definitions/appflowy.d.ts.map +1 -0
  82. package/dist/services/definitions/appflowy.js +39 -0
  83. package/dist/services/definitions/appflowy.js.map +1 -0
  84. package/dist/services/definitions/beszel.d.ts +3 -0
  85. package/dist/services/definitions/beszel.d.ts.map +1 -0
  86. package/dist/services/definitions/beszel.js +39 -0
  87. package/dist/services/definitions/beszel.js.map +1 -0
  88. package/dist/services/definitions/browserless.d.ts +3 -0
  89. package/dist/services/definitions/browserless.d.ts.map +1 -0
  90. package/dist/services/definitions/browserless.js +77 -0
  91. package/dist/services/definitions/browserless.js.map +1 -0
  92. package/dist/services/definitions/caddy.d.ts +3 -0
  93. package/dist/services/definitions/caddy.d.ts.map +1 -0
  94. package/dist/services/definitions/caddy.js +50 -0
  95. package/dist/services/definitions/caddy.js.map +1 -0
  96. package/dist/services/definitions/chromadb.d.ts +3 -0
  97. package/dist/services/definitions/chromadb.d.ts.map +1 -0
  98. package/dist/services/definitions/chromadb.js +61 -0
  99. package/dist/services/definitions/chromadb.js.map +1 -0
  100. package/dist/services/definitions/claude-code.d.ts +3 -0
  101. package/dist/services/definitions/claude-code.d.ts.map +1 -0
  102. package/dist/services/definitions/claude-code.js +49 -0
  103. package/dist/services/definitions/claude-code.js.map +1 -0
  104. package/dist/services/definitions/code-server.d.ts +3 -0
  105. package/dist/services/definitions/code-server.d.ts.map +1 -0
  106. package/dist/services/definitions/code-server.js +61 -0
  107. package/dist/services/definitions/code-server.js.map +1 -0
  108. package/dist/services/definitions/codex.d.ts +3 -0
  109. package/dist/services/definitions/codex.d.ts.map +1 -0
  110. package/dist/services/definitions/codex.js +41 -0
  111. package/dist/services/definitions/codex.js.map +1 -0
  112. package/dist/services/definitions/coolify.d.ts +3 -0
  113. package/dist/services/definitions/coolify.d.ts.map +1 -0
  114. package/dist/services/definitions/coolify.js +44 -0
  115. package/dist/services/definitions/coolify.js.map +1 -0
  116. package/dist/services/definitions/dify.d.ts +3 -0
  117. package/dist/services/definitions/dify.d.ts.map +1 -0
  118. package/dist/services/definitions/dify.js +75 -0
  119. package/dist/services/definitions/dify.js.map +1 -0
  120. package/dist/services/definitions/docsgpt.d.ts +3 -0
  121. package/dist/services/definitions/docsgpt.d.ts.map +1 -0
  122. package/dist/services/definitions/docsgpt.js +39 -0
  123. package/dist/services/definitions/docsgpt.js.map +1 -0
  124. package/dist/services/definitions/dokploy.d.ts +3 -0
  125. package/dist/services/definitions/dokploy.d.ts.map +1 -0
  126. package/dist/services/definitions/dokploy.js +44 -0
  127. package/dist/services/definitions/dokploy.js.map +1 -0
  128. package/dist/services/definitions/dozzle.d.ts +3 -0
  129. package/dist/services/definitions/dozzle.d.ts.map +1 -0
  130. package/dist/services/definitions/dozzle.js +33 -0
  131. package/dist/services/definitions/dozzle.js.map +1 -0
  132. package/dist/services/definitions/ffmpeg.d.ts +3 -0
  133. package/dist/services/definitions/ffmpeg.d.ts.map +1 -0
  134. package/dist/services/definitions/ffmpeg.js +63 -0
  135. package/dist/services/definitions/ffmpeg.js.map +1 -0
  136. package/dist/services/definitions/flowise.d.ts +3 -0
  137. package/dist/services/definitions/flowise.d.ts.map +1 -0
  138. package/dist/services/definitions/flowise.js +39 -0
  139. package/dist/services/definitions/flowise.js.map +1 -0
  140. package/dist/services/definitions/gemini-cli.d.ts +3 -0
  141. package/dist/services/definitions/gemini-cli.d.ts.map +1 -0
  142. package/dist/services/definitions/gemini-cli.js +41 -0
  143. package/dist/services/definitions/gemini-cli.js.map +1 -0
  144. package/dist/services/definitions/gitea.d.ts +3 -0
  145. package/dist/services/definitions/gitea.d.ts.map +1 -0
  146. package/dist/services/definitions/gitea.js +45 -0
  147. package/dist/services/definitions/gitea.js.map +1 -0
  148. package/dist/services/definitions/gotify.d.ts +3 -0
  149. package/dist/services/definitions/gotify.d.ts.map +1 -0
  150. package/dist/services/definitions/gotify.js +60 -0
  151. package/dist/services/definitions/gotify.js.map +1 -0
  152. package/dist/services/definitions/grafana.d.ts +3 -0
  153. package/dist/services/definitions/grafana.d.ts.map +1 -0
  154. package/dist/services/definitions/grafana.js +61 -0
  155. package/dist/services/definitions/grafana.js.map +1 -0
  156. package/dist/services/definitions/index.d.ts +68 -0
  157. package/dist/services/definitions/index.d.ts.map +1 -0
  158. package/dist/services/definitions/index.js +198 -0
  159. package/dist/services/definitions/index.js.map +1 -0
  160. package/dist/services/definitions/kimi.d.ts +3 -0
  161. package/dist/services/definitions/kimi.d.ts.map +1 -0
  162. package/dist/services/definitions/kimi.js +41 -0
  163. package/dist/services/definitions/kimi.js.map +1 -0
  164. package/dist/services/definitions/lasuite-meet-agents.d.ts +3 -0
  165. package/dist/services/definitions/lasuite-meet-agents.d.ts.map +1 -0
  166. package/dist/services/definitions/lasuite-meet-agents.js +26 -0
  167. package/dist/services/definitions/lasuite-meet-agents.js.map +1 -0
  168. package/dist/services/definitions/lasuite-meet-backend.d.ts +3 -0
  169. package/dist/services/definitions/lasuite-meet-backend.d.ts.map +1 -0
  170. package/dist/services/definitions/lasuite-meet-backend.js +118 -0
  171. package/dist/services/definitions/lasuite-meet-backend.js.map +1 -0
  172. package/dist/services/definitions/lasuite-meet-frontend.d.ts +3 -0
  173. package/dist/services/definitions/lasuite-meet-frontend.d.ts.map +1 -0
  174. package/dist/services/definitions/lasuite-meet-frontend.js +31 -0
  175. package/dist/services/definitions/lasuite-meet-frontend.js.map +1 -0
  176. package/dist/services/definitions/librechat.d.ts +3 -0
  177. package/dist/services/definitions/librechat.d.ts.map +1 -0
  178. package/dist/services/definitions/librechat.js +47 -0
  179. package/dist/services/definitions/librechat.js.map +1 -0
  180. package/dist/services/definitions/lightpanda.d.ts +3 -0
  181. package/dist/services/definitions/lightpanda.d.ts.map +1 -0
  182. package/dist/services/definitions/lightpanda.js +59 -0
  183. package/dist/services/definitions/lightpanda.js.map +1 -0
  184. package/dist/services/definitions/litellm.d.ts +3 -0
  185. package/dist/services/definitions/litellm.d.ts.map +1 -0
  186. package/dist/services/definitions/litellm.js +41 -0
  187. package/dist/services/definitions/litellm.js.map +1 -0
  188. package/dist/services/definitions/livekit.d.ts +3 -0
  189. package/dist/services/definitions/livekit.d.ts.map +1 -0
  190. package/dist/services/definitions/livekit.js +31 -0
  191. package/dist/services/definitions/livekit.js.map +1 -0
  192. package/dist/services/definitions/matomo.d.ts +3 -0
  193. package/dist/services/definitions/matomo.d.ts.map +1 -0
  194. package/dist/services/definitions/matomo.js +75 -0
  195. package/dist/services/definitions/matomo.js.map +1 -0
  196. package/dist/services/definitions/matrix-synapse.d.ts +3 -0
  197. package/dist/services/definitions/matrix-synapse.d.ts.map +1 -0
  198. package/dist/services/definitions/matrix-synapse.js +82 -0
  199. package/dist/services/definitions/matrix-synapse.js.map +1 -0
  200. package/dist/services/definitions/mattermost.d.ts +3 -0
  201. package/dist/services/definitions/mattermost.d.ts.map +1 -0
  202. package/dist/services/definitions/mattermost.js +59 -0
  203. package/dist/services/definitions/mattermost.js.map +1 -0
  204. package/dist/services/definitions/meilisearch.d.ts +3 -0
  205. package/dist/services/definitions/meilisearch.d.ts.map +1 -0
  206. package/dist/services/definitions/meilisearch.js +76 -0
  207. package/dist/services/definitions/meilisearch.js.map +1 -0
  208. package/dist/services/definitions/minio.d.ts +3 -0
  209. package/dist/services/definitions/minio.d.ts.map +1 -0
  210. package/dist/services/definitions/minio.js +98 -0
  211. package/dist/services/definitions/minio.js.map +1 -0
  212. package/dist/services/definitions/mixpost.d.ts +3 -0
  213. package/dist/services/definitions/mixpost.d.ts.map +1 -0
  214. package/dist/services/definitions/mixpost.js +82 -0
  215. package/dist/services/definitions/mixpost.js.map +1 -0
  216. package/dist/services/definitions/motion-canvas.d.ts +3 -0
  217. package/dist/services/definitions/motion-canvas.d.ts.map +1 -0
  218. package/dist/services/definitions/motion-canvas.js +48 -0
  219. package/dist/services/definitions/motion-canvas.js.map +1 -0
  220. package/dist/services/definitions/n8n.d.ts +3 -0
  221. package/dist/services/definitions/n8n.d.ts.map +1 -0
  222. package/dist/services/definitions/n8n.js +146 -0
  223. package/dist/services/definitions/n8n.js.map +1 -0
  224. package/dist/services/definitions/nocodb.d.ts +3 -0
  225. package/dist/services/definitions/nocodb.d.ts.map +1 -0
  226. package/dist/services/definitions/nocodb.js +39 -0
  227. package/dist/services/definitions/nocodb.js.map +1 -0
  228. package/dist/services/definitions/ntfy.d.ts +3 -0
  229. package/dist/services/definitions/ntfy.d.ts.map +1 -0
  230. package/dist/services/definitions/ntfy.js +61 -0
  231. package/dist/services/definitions/ntfy.js.map +1 -0
  232. package/dist/services/definitions/ollama.d.ts +3 -0
  233. package/dist/services/definitions/ollama.d.ts.map +1 -0
  234. package/dist/services/definitions/ollama.js +61 -0
  235. package/dist/services/definitions/ollama.js.map +1 -0
  236. package/dist/services/definitions/open-webui.d.ts +3 -0
  237. package/dist/services/definitions/open-webui.d.ts.map +1 -0
  238. package/dist/services/definitions/open-webui.js +47 -0
  239. package/dist/services/definitions/open-webui.js.map +1 -0
  240. package/dist/services/definitions/opencode.d.ts +3 -0
  241. package/dist/services/definitions/opencode.d.ts.map +1 -0
  242. package/dist/services/definitions/opencode.js +47 -0
  243. package/dist/services/definitions/opencode.js.map +1 -0
  244. package/dist/services/definitions/openpanel.d.ts +3 -0
  245. package/dist/services/definitions/openpanel.d.ts.map +1 -0
  246. package/dist/services/definitions/openpanel.js +41 -0
  247. package/dist/services/definitions/openpanel.js.map +1 -0
  248. package/dist/services/definitions/outline.d.ts +3 -0
  249. package/dist/services/definitions/outline.d.ts.map +1 -0
  250. package/dist/services/definitions/outline.js +68 -0
  251. package/dist/services/definitions/outline.js.map +1 -0
  252. package/dist/services/definitions/paperless-ngx.d.ts +3 -0
  253. package/dist/services/definitions/paperless-ngx.d.ts.map +1 -0
  254. package/dist/services/definitions/paperless-ngx.js +85 -0
  255. package/dist/services/definitions/paperless-ngx.js.map +1 -0
  256. package/dist/services/definitions/playwright-server.d.ts +3 -0
  257. package/dist/services/definitions/playwright-server.d.ts.map +1 -0
  258. package/dist/services/definitions/playwright-server.js +34 -0
  259. package/dist/services/definitions/playwright-server.js.map +1 -0
  260. package/dist/services/definitions/portainer.d.ts +3 -0
  261. package/dist/services/definitions/portainer.d.ts.map +1 -0
  262. package/dist/services/definitions/portainer.js +44 -0
  263. package/dist/services/definitions/portainer.js.map +1 -0
  264. package/dist/services/definitions/postgresql.d.ts +3 -0
  265. package/dist/services/definitions/postgresql.d.ts.map +1 -0
  266. package/dist/services/definitions/postgresql.js +83 -0
  267. package/dist/services/definitions/postgresql.js.map +1 -0
  268. package/dist/services/definitions/postiz.d.ts +3 -0
  269. package/dist/services/definitions/postiz.d.ts.map +1 -0
  270. package/dist/services/definitions/postiz.js +79 -0
  271. package/dist/services/definitions/postiz.js.map +1 -0
  272. package/dist/services/definitions/prometheus.d.ts +3 -0
  273. package/dist/services/definitions/prometheus.d.ts.map +1 -0
  274. package/dist/services/definitions/prometheus.js +47 -0
  275. package/dist/services/definitions/prometheus.js.map +1 -0
  276. package/dist/services/definitions/qdrant.d.ts +3 -0
  277. package/dist/services/definitions/qdrant.d.ts.map +1 -0
  278. package/dist/services/definitions/qdrant.js +75 -0
  279. package/dist/services/definitions/qdrant.js.map +1 -0
  280. package/dist/services/definitions/redis.d.ts +3 -0
  281. package/dist/services/definitions/redis.d.ts.map +1 -0
  282. package/dist/services/definitions/redis.js +90 -0
  283. package/dist/services/definitions/redis.js.map +1 -0
  284. package/dist/services/definitions/remotion.d.ts +3 -0
  285. package/dist/services/definitions/remotion.d.ts.map +1 -0
  286. package/dist/services/definitions/remotion.js +48 -0
  287. package/dist/services/definitions/remotion.js.map +1 -0
  288. package/dist/services/definitions/rocketchat.d.ts +3 -0
  289. package/dist/services/definitions/rocketchat.d.ts.map +1 -0
  290. package/dist/services/definitions/rocketchat.js +54 -0
  291. package/dist/services/definitions/rocketchat.js.map +1 -0
  292. package/dist/services/definitions/searxng.d.ts +3 -0
  293. package/dist/services/definitions/searxng.d.ts.map +1 -0
  294. package/dist/services/definitions/searxng.js +61 -0
  295. package/dist/services/definitions/searxng.js.map +1 -0
  296. package/dist/services/definitions/stable-diffusion.d.ts +3 -0
  297. package/dist/services/definitions/stable-diffusion.d.ts.map +1 -0
  298. package/dist/services/definitions/stable-diffusion.js +44 -0
  299. package/dist/services/definitions/stable-diffusion.js.map +1 -0
  300. package/dist/services/definitions/steel-browser.d.ts +3 -0
  301. package/dist/services/definitions/steel-browser.d.ts.map +1 -0
  302. package/dist/services/definitions/steel-browser.js +68 -0
  303. package/dist/services/definitions/steel-browser.js.map +1 -0
  304. package/dist/services/definitions/tailscale.d.ts +3 -0
  305. package/dist/services/definitions/tailscale.d.ts.map +1 -0
  306. package/dist/services/definitions/tailscale.js +62 -0
  307. package/dist/services/definitions/tailscale.js.map +1 -0
  308. package/dist/services/definitions/temporal.d.ts +3 -0
  309. package/dist/services/definitions/temporal.d.ts.map +1 -0
  310. package/dist/services/definitions/temporal.js +97 -0
  311. package/dist/services/definitions/temporal.js.map +1 -0
  312. package/dist/services/definitions/traefik.d.ts +3 -0
  313. package/dist/services/definitions/traefik.d.ts.map +1 -0
  314. package/dist/services/definitions/traefik.js +52 -0
  315. package/dist/services/definitions/traefik.js.map +1 -0
  316. package/dist/services/definitions/umami.d.ts +3 -0
  317. package/dist/services/definitions/umami.d.ts.map +1 -0
  318. package/dist/services/definitions/umami.js +41 -0
  319. package/dist/services/definitions/umami.js.map +1 -0
  320. package/dist/services/definitions/uptime-kuma.d.ts +3 -0
  321. package/dist/services/definitions/uptime-kuma.d.ts.map +1 -0
  322. package/dist/services/definitions/uptime-kuma.js +46 -0
  323. package/dist/services/definitions/uptime-kuma.js.map +1 -0
  324. package/dist/services/definitions/valkey.d.ts +3 -0
  325. package/dist/services/definitions/valkey.d.ts.map +1 -0
  326. package/dist/services/definitions/valkey.js +60 -0
  327. package/dist/services/definitions/valkey.js.map +1 -0
  328. package/dist/services/definitions/watchtower.d.ts +3 -0
  329. package/dist/services/definitions/watchtower.d.ts.map +1 -0
  330. package/dist/services/definitions/watchtower.js +41 -0
  331. package/dist/services/definitions/watchtower.js.map +1 -0
  332. package/dist/services/definitions/weaviate.d.ts +3 -0
  333. package/dist/services/definitions/weaviate.d.ts.map +1 -0
  334. package/dist/services/definitions/weaviate.js +89 -0
  335. package/dist/services/definitions/weaviate.js.map +1 -0
  336. package/dist/services/definitions/whisper.d.ts +3 -0
  337. package/dist/services/definitions/whisper.d.ts.map +1 -0
  338. package/dist/services/definitions/whisper.js +55 -0
  339. package/dist/services/definitions/whisper.js.map +1 -0
  340. package/dist/services/registry.d.ts +12 -0
  341. package/dist/services/registry.d.ts.map +1 -0
  342. package/dist/services/registry.js +28 -0
  343. package/dist/services/registry.js.map +1 -0
  344. package/dist/skills/registry.d.ts +7 -0
  345. package/dist/skills/registry.d.ts.map +1 -0
  346. package/dist/skills/registry.js +111 -0
  347. package/dist/skills/registry.js.map +1 -0
  348. package/dist/types.d.ts +60 -0
  349. package/dist/types.d.ts.map +1 -0
  350. package/dist/types.js +20 -0
  351. package/dist/types.js.map +1 -0
  352. package/dist/validator.d.ts +16 -0
  353. package/dist/validator.d.ts.map +1 -0
  354. package/dist/validator.js +142 -0
  355. package/dist/validator.js.map +1 -0
  356. package/dist/version-manager.d.ts +10 -0
  357. package/dist/version-manager.d.ts.map +1 -0
  358. package/dist/version-manager.js +60 -0
  359. package/dist/version-manager.js.map +1 -0
  360. package/package.json +37 -0
  361. package/src/__snapshots__/composer.snapshot.test.ts.snap +903 -0
  362. package/src/bare-metal-partition.test.ts +63 -0
  363. package/src/bare-metal-partition.ts +64 -0
  364. package/src/composer.snapshot.test.ts +87 -0
  365. package/src/composer.test.ts +224 -0
  366. package/src/composer.ts +403 -0
  367. package/src/generate.test.ts +349 -0
  368. package/src/generate.ts +259 -0
  369. package/src/generators/bare-metal-install.test.ts +43 -0
  370. package/src/generators/bare-metal-install.ts +249 -0
  371. package/src/generators/caddy.ts +99 -0
  372. package/src/generators/env.ts +405 -0
  373. package/src/generators/grafana.ts +255 -0
  374. package/src/generators/n8n-workflows.ts +88 -0
  375. package/src/generators/native-services.ts +103 -0
  376. package/src/generators/postgres-init.ts +137 -0
  377. package/src/generators/prometheus.ts +89 -0
  378. package/src/generators/readme.ts +274 -0
  379. package/src/generators/scripts.ts +362 -0
  380. package/src/generators/skills.ts +628 -0
  381. package/src/index.ts +118 -0
  382. package/src/presets/registry.test.ts +32 -0
  383. package/src/presets/registry.ts +126 -0
  384. package/src/resolver.test.ts +204 -0
  385. package/src/resolver.ts +282 -0
  386. package/src/schema.test.ts +295 -0
  387. package/src/schema.ts +307 -0
  388. package/src/services/definitions/anything-llm.ts +46 -0
  389. package/src/services/definitions/appflowy.ts +46 -0
  390. package/src/services/definitions/beszel.ts +46 -0
  391. package/src/services/definitions/browserless.ts +84 -0
  392. package/src/services/definitions/caddy.ts +57 -0
  393. package/src/services/definitions/chromadb.ts +68 -0
  394. package/src/services/definitions/claude-code.ts +56 -0
  395. package/src/services/definitions/code-server.ts +67 -0
  396. package/src/services/definitions/codex.ts +48 -0
  397. package/src/services/definitions/coolify.ts +51 -0
  398. package/src/services/definitions/dify.ts +82 -0
  399. package/src/services/definitions/docsgpt.ts +45 -0
  400. package/src/services/definitions/dokploy.ts +51 -0
  401. package/src/services/definitions/dozzle.ts +40 -0
  402. package/src/services/definitions/ffmpeg.ts +70 -0
  403. package/src/services/definitions/flowise.ts +45 -0
  404. package/src/services/definitions/gemini-cli.ts +48 -0
  405. package/src/services/definitions/gitea.ts +52 -0
  406. package/src/services/definitions/gotify.ts +67 -0
  407. package/src/services/definitions/grafana.ts +68 -0
  408. package/src/services/definitions/index.ts +200 -0
  409. package/src/services/definitions/kimi.ts +48 -0
  410. package/src/services/definitions/lasuite-meet-agents.ts +33 -0
  411. package/src/services/definitions/lasuite-meet-backend.ts +125 -0
  412. package/src/services/definitions/lasuite-meet-frontend.ts +38 -0
  413. package/src/services/definitions/librechat.ts +54 -0
  414. package/src/services/definitions/lightpanda.ts +61 -0
  415. package/src/services/definitions/litellm.ts +48 -0
  416. package/src/services/definitions/livekit.ts +38 -0
  417. package/src/services/definitions/matomo.ts +82 -0
  418. package/src/services/definitions/matrix-synapse.ts +89 -0
  419. package/src/services/definitions/mattermost.ts +67 -0
  420. package/src/services/definitions/meilisearch.ts +83 -0
  421. package/src/services/definitions/minio.ts +105 -0
  422. package/src/services/definitions/mixpost.ts +89 -0
  423. package/src/services/definitions/motion-canvas.ts +55 -0
  424. package/src/services/definitions/n8n.ts +153 -0
  425. package/src/services/definitions/nocodb.ts +46 -0
  426. package/src/services/definitions/ntfy.ts +68 -0
  427. package/src/services/definitions/ollama.ts +68 -0
  428. package/src/services/definitions/open-webui.ts +54 -0
  429. package/src/services/definitions/opencode.ts +54 -0
  430. package/src/services/definitions/openpanel.ts +48 -0
  431. package/src/services/definitions/outline.ts +75 -0
  432. package/src/services/definitions/paperless-ngx.ts +92 -0
  433. package/src/services/definitions/playwright-server.ts +41 -0
  434. package/src/services/definitions/portainer.ts +50 -0
  435. package/src/services/definitions/postgresql.ts +90 -0
  436. package/src/services/definitions/postiz.ts +86 -0
  437. package/src/services/definitions/prometheus.ts +54 -0
  438. package/src/services/definitions/qdrant.ts +82 -0
  439. package/src/services/definitions/redis.ts +99 -0
  440. package/src/services/definitions/remotion.ts +55 -0
  441. package/src/services/definitions/rocketchat.ts +61 -0
  442. package/src/services/definitions/searxng.ts +68 -0
  443. package/src/services/definitions/stable-diffusion.ts +50 -0
  444. package/src/services/definitions/steel-browser.ts +70 -0
  445. package/src/services/definitions/tailscale.ts +69 -0
  446. package/src/services/definitions/temporal.ts +104 -0
  447. package/src/services/definitions/traefik.ts +60 -0
  448. package/src/services/definitions/umami.ts +48 -0
  449. package/src/services/definitions/uptime-kuma.ts +53 -0
  450. package/src/services/definitions/valkey.ts +67 -0
  451. package/src/services/definitions/watchtower.ts +47 -0
  452. package/src/services/definitions/weaviate.ts +96 -0
  453. package/src/services/definitions/whisper.ts +62 -0
  454. package/src/services/registry.test.ts +98 -0
  455. package/src/services/registry.ts +37 -0
  456. package/src/skills/registry.ts +126 -0
  457. package/src/types.ts +123 -0
  458. package/src/validator.test.ts +66 -0
  459. package/src/validator.ts +166 -0
  460. package/src/version-manager.ts +65 -0
  461. package/tsconfig.json +9 -0
@@ -0,0 +1,98 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ getAllServices,
4
+ getServiceById,
5
+ getServicesByCategory,
6
+ getServicesByTag,
7
+ } from "../services/registry.js";
8
+
9
+ describe("service registry", () => {
10
+ it("has at least 60 services registered", () => {
11
+ const services = getAllServices();
12
+ expect(services.length).toBeGreaterThanOrEqual(60);
13
+ });
14
+
15
+ it("finds redis by ID", () => {
16
+ const redis = getServiceById("redis");
17
+ expect(redis).toBeDefined();
18
+ expect(redis!.id).toBe("redis");
19
+ expect(redis!.name).toBe("Redis");
20
+ });
21
+
22
+ it("returns undefined for unknown ID", () => {
23
+ const result = getServiceById("nonexistent");
24
+ expect(result).toBeUndefined();
25
+ });
26
+
27
+ it("finds tailscale and it is mandatory", () => {
28
+ const tailscale = getServiceById("tailscale");
29
+ expect(tailscale).toBeDefined();
30
+ expect(tailscale!.id).toBe("tailscale");
31
+ expect(tailscale!.mandatory).toBe(true);
32
+ });
33
+
34
+ it("finds coolify, dokploy, livekit, and La Suite Meet services by ID", () => {
35
+ expect(getServiceById("coolify")).toBeDefined();
36
+ expect(getServiceById("dokploy")).toBeDefined();
37
+ expect(getServiceById("livekit")).toBeDefined();
38
+ expect(getServiceById("lasuite-meet-backend")).toBeDefined();
39
+ expect(getServiceById("lasuite-meet-frontend")).toBeDefined();
40
+ expect(getServiceById("lasuite-meet-agents")).toBeDefined();
41
+ });
42
+
43
+ it("filters by category", () => {
44
+ const databases = getServicesByCategory("database");
45
+ const ids = databases.map((s) => s.id);
46
+ expect(ids).toContain("redis");
47
+ expect(databases.length).toBeGreaterThanOrEqual(1);
48
+ });
49
+
50
+ it("filters by tag", () => {
51
+ const cacheServices = getServicesByTag("cache");
52
+ const ids = cacheServices.map((s) => s.id);
53
+ expect(ids).toContain("redis");
54
+ expect(cacheServices.length).toBeGreaterThanOrEqual(1);
55
+ });
56
+
57
+ it("all service IDs are unique", () => {
58
+ const services = getAllServices();
59
+ const ids = services.map((s) => s.id);
60
+ const uniqueIds = new Set(ids);
61
+ expect(uniqueIds.size).toBe(ids.length);
62
+ });
63
+
64
+ it("no two services have the same exposed host port", () => {
65
+ const services = getAllServices();
66
+ const portMap = new Map<number, string[]>();
67
+
68
+ for (const svc of services) {
69
+ for (const port of svc.ports) {
70
+ if (!port.exposed) continue;
71
+ const existing = portMap.get(port.host) ?? [];
72
+ existing.push(svc.id);
73
+ portMap.set(port.host, existing);
74
+ }
75
+ }
76
+
77
+ // Informational: log any shared ports but don't hard-fail,
78
+ // since users may not enable all services simultaneously.
79
+ const conflicts: string[] = [];
80
+ for (const [port, serviceIds] of portMap) {
81
+ if (serviceIds.length > 1) {
82
+ conflicts.push(`Port ${port} shared by: ${serviceIds.join(", ")}`);
83
+ }
84
+ }
85
+
86
+ // This is informational — warn if there are conflicts but still pass.
87
+ // If you want to enforce unique ports, change the next line to:
88
+ // expect(conflicts).toHaveLength(0);
89
+ if (conflicts.length > 0) {
90
+ console.warn(
91
+ `[informational] ${conflicts.length} port overlap(s) detected:\n ${conflicts.join("\n ")}`,
92
+ );
93
+ }
94
+
95
+ // At minimum, verify we checked something
96
+ expect(portMap.size).toBeGreaterThan(0);
97
+ });
98
+ });
@@ -0,0 +1,37 @@
1
+ import type { ServiceCategory, ServiceDefinition } from "../types.js";
2
+ import { allServiceDefinitions } from "./definitions/index.js";
3
+
4
+ // Build the registry map and validate no duplicates
5
+ const registryMap = new Map<string, ServiceDefinition>();
6
+
7
+ for (const def of allServiceDefinitions) {
8
+ if (registryMap.has(def.id)) {
9
+ throw new Error(
10
+ `Duplicate service definition ID: "${def.id}". Each service must have a unique ID.`,
11
+ );
12
+ }
13
+ registryMap.set(def.id, def);
14
+ }
15
+
16
+ /** Readonly map of all registered services indexed by ID */
17
+ export const serviceRegistry: ReadonlyMap<string, ServiceDefinition> = registryMap;
18
+
19
+ /** Look up a service by its unique ID */
20
+ export function getServiceById(id: string): ServiceDefinition | undefined {
21
+ return registryMap.get(id);
22
+ }
23
+
24
+ /** Get all services in a given category */
25
+ export function getServicesByCategory(category: ServiceCategory): ServiceDefinition[] {
26
+ return allServiceDefinitions.filter((s) => s.category === category);
27
+ }
28
+
29
+ /** Get all registered services */
30
+ export function getAllServices(): ServiceDefinition[] {
31
+ return [...allServiceDefinitions];
32
+ }
33
+
34
+ /** Get services matching a specific tag */
35
+ export function getServicesByTag(tag: string): ServiceDefinition[] {
36
+ return allServiceDefinitions.filter((s) => s.tags.includes(tag));
37
+ }
@@ -0,0 +1,126 @@
1
+ import type { SkillPack } from "../types.js";
2
+
3
+ const skillPacks: SkillPack[] = [
4
+ {
5
+ id: "video-creator",
6
+ name: "Video Creator",
7
+ description:
8
+ "Create and process videos programmatically with FFmpeg, Remotion, and MinIO storage",
9
+ requiredServices: ["ffmpeg", "remotion", "minio"],
10
+ skills: ["ffmpeg-process", "remotion-render", "minio-storage"],
11
+ icon: "🎬",
12
+ tags: ["video", "media", "rendering"],
13
+ },
14
+ {
15
+ id: "research-agent",
16
+ name: "Research Agent",
17
+ description: "Research the web, store findings in vector memory, and scrape full pages",
18
+ requiredServices: ["qdrant", "searxng", "browserless"],
19
+ skills: ["qdrant-memory", "searxng-search", "browserless-browse"],
20
+ icon: "🔬",
21
+ tags: ["research", "rag", "web-scraping"],
22
+ },
23
+ {
24
+ id: "social-media",
25
+ name: "Social Media",
26
+ description:
27
+ "Process and manage social media content with video tools, caching, and asset storage",
28
+ requiredServices: ["ffmpeg", "redis", "minio"],
29
+ skills: ["ffmpeg-process", "redis-cache", "minio-storage"],
30
+ icon: "📱",
31
+ tags: ["social", "content", "scheduling"],
32
+ },
33
+ {
34
+ id: "dev-ops",
35
+ name: "DevOps",
36
+ description: "Monitor services, automate workflows, and manage infrastructure alerts",
37
+ requiredServices: ["n8n", "redis", "uptime-kuma", "grafana", "prometheus"],
38
+ skills: ["n8n-trigger", "redis-cache"],
39
+ icon: "⚙️",
40
+ tags: ["devops", "monitoring", "automation"],
41
+ },
42
+ {
43
+ id: "knowledge-base",
44
+ name: "Knowledge Base",
45
+ description:
46
+ "Index documents with vector search and full-text search for comprehensive retrieval",
47
+ requiredServices: ["qdrant", "postgresql", "meilisearch"],
48
+ skills: ["qdrant-memory"],
49
+ icon: "📚",
50
+ tags: ["knowledge", "search", "indexing"],
51
+ },
52
+ {
53
+ id: "local-ai",
54
+ name: "Local AI",
55
+ description: "Run local LLM inference and speech-to-text transcription without external APIs",
56
+ requiredServices: ["ollama", "whisper"],
57
+ skills: ["ollama-local-llm", "whisper-transcribe"],
58
+ icon: "🤖",
59
+ tags: ["local-llm", "transcription", "offline"],
60
+ },
61
+ {
62
+ id: "content-creator",
63
+ name: "Content Creator",
64
+ description:
65
+ "Full social media content pipeline with scheduling, media processing, analytics, and storage",
66
+ requiredServices: ["postiz", "ffmpeg", "minio", "redis", "postgresql"],
67
+ skills: ["ffmpeg-process", "minio-storage", "redis-cache"],
68
+ icon: "📱",
69
+ tags: ["social-media", "content", "scheduling", "analytics"],
70
+ },
71
+ {
72
+ id: "ai-playground",
73
+ name: "AI Playground",
74
+ description:
75
+ "Full AI experimentation stack with chat UIs, LLM gateway, local models, and document chat",
76
+ requiredServices: ["ollama", "open-webui", "litellm"],
77
+ skills: ["ollama-local-llm"],
78
+ icon: "🧪",
79
+ tags: ["ai", "llm", "playground", "experimentation"],
80
+ },
81
+ {
82
+ id: "coding-team",
83
+ name: "Coding Team",
84
+ description:
85
+ "AI-powered development environment with coding agents, Git hosting, and browser IDE",
86
+ requiredServices: ["claude-code", "gitea", "code-server"],
87
+ skills: [],
88
+ icon: "💻",
89
+ tags: ["coding", "development", "ide", "git"],
90
+ },
91
+ {
92
+ id: "knowledge-hub",
93
+ name: "Knowledge Hub",
94
+ description:
95
+ "Enterprise knowledge management with wiki, document processing, analytics, and vector search",
96
+ requiredServices: ["outline", "paperless-ngx", "qdrant", "postgresql", "redis"],
97
+ skills: ["qdrant-memory"],
98
+ icon: "📚",
99
+ tags: ["knowledge", "wiki", "documents", "search"],
100
+ },
101
+ ];
102
+
103
+ const packMap = new Map<string, SkillPack>();
104
+ for (const pack of skillPacks) {
105
+ if (packMap.has(pack.id)) {
106
+ throw new Error(`Duplicate skill pack ID: "${pack.id}"`);
107
+ }
108
+ packMap.set(pack.id, pack);
109
+ }
110
+
111
+ export const skillPackRegistry: ReadonlyMap<string, SkillPack> = packMap;
112
+
113
+ export function getSkillPackById(id: string): SkillPack | undefined {
114
+ return packMap.get(id);
115
+ }
116
+
117
+ export function getAllSkillPacks(): SkillPack[] {
118
+ return [...skillPacks];
119
+ }
120
+
121
+ /** Get skill packs whose required services are all present in the given service list */
122
+ export function getCompatibleSkillPacks(availableServiceIds: string[]): SkillPack[] {
123
+ return skillPacks.filter((pack) =>
124
+ pack.requiredServices.every((req) => availableServiceIds.includes(req)),
125
+ );
126
+ }
package/src/types.ts ADDED
@@ -0,0 +1,123 @@
1
+ import type { z } from "zod";
2
+ import type {
3
+ AddedDependencySchema,
4
+ ApiErrorSchema,
5
+ ComposeOptionsSchema,
6
+ DeploymentTargetSchema,
7
+ DeploymentTypeSchema,
8
+ DeploySchema,
9
+ EnvVariableSchema,
10
+ ErrorSchema,
11
+ GenerationInputSchema,
12
+ HealthCheckSchema,
13
+ MaturitySchema,
14
+ NativePlatformSchema,
15
+ NativeRecipeSchema,
16
+ OutputFormatSchema,
17
+ PlatformSchema,
18
+ PortMappingSchema,
19
+ PresetSchema,
20
+ ProxyTypeSchema,
21
+ ResolvedServiceSchema,
22
+ ResolverOutputSchema,
23
+ ResourceLimitsSchema,
24
+ RestartPolicySchema,
25
+ ServiceCategorySchema,
26
+ ServiceDefinitionSchema,
27
+ SkillBindingSchema,
28
+ SkillPackSchema,
29
+ ValidateRequestSchema,
30
+ ValidateResponseSchema,
31
+ VolumeMappingSchema,
32
+ WarningSchema,
33
+ } from "./schema.js";
34
+
35
+ // ─── Inferred Types ─────────────────────────────────────────────────────────
36
+
37
+ export type ServiceCategory = z.infer<typeof ServiceCategorySchema>;
38
+ export type Maturity = z.infer<typeof MaturitySchema>;
39
+ export type Platform = z.infer<typeof PlatformSchema>;
40
+ export type RestartPolicy = z.infer<typeof RestartPolicySchema>;
41
+ export type ProxyType = z.infer<typeof ProxyTypeSchema>;
42
+ export type DeploymentTarget = z.infer<typeof DeploymentTargetSchema>;
43
+ export type DeploymentType = z.infer<typeof DeploymentTypeSchema>;
44
+ export type NativePlatform = z.infer<typeof NativePlatformSchema>;
45
+ export type NativeRecipe = z.infer<typeof NativeRecipeSchema>;
46
+ export type OutputFormat = z.infer<typeof OutputFormatSchema>;
47
+
48
+ export type PortMapping = z.infer<typeof PortMappingSchema>;
49
+ export type VolumeMapping = z.infer<typeof VolumeMappingSchema>;
50
+ export type EnvVariable = z.infer<typeof EnvVariableSchema>;
51
+ export type HealthCheck = z.infer<typeof HealthCheckSchema>;
52
+ export type ResourceLimits = z.infer<typeof ResourceLimitsSchema>;
53
+ export type Deploy = z.infer<typeof DeploySchema>;
54
+ export type SkillBinding = z.infer<typeof SkillBindingSchema>;
55
+
56
+ export type ServiceDefinition = z.infer<typeof ServiceDefinitionSchema>;
57
+ export type SkillPack = z.infer<typeof SkillPackSchema>;
58
+ export type Preset = z.infer<typeof PresetSchema>;
59
+
60
+ export type GenerationInput = z.infer<typeof GenerationInputSchema>;
61
+ export type ComposeOptions = z.infer<typeof ComposeOptionsSchema>;
62
+ export type ResolvedService = z.infer<typeof ResolvedServiceSchema>;
63
+ export type AddedDependency = z.infer<typeof AddedDependencySchema>;
64
+ export type Warning = z.infer<typeof WarningSchema>;
65
+ export type ResolverError = z.infer<typeof ErrorSchema>;
66
+ export type ResolverOutput = z.infer<typeof ResolverOutputSchema>;
67
+
68
+ export type ValidateRequest = z.infer<typeof ValidateRequestSchema>;
69
+ export type ValidateResponse = z.infer<typeof ValidateResponseSchema>;
70
+ export type ApiError = z.infer<typeof ApiErrorSchema>;
71
+
72
+ // ─── Additional Types ───────────────────────────────────────────────────────
73
+
74
+ export interface ResolverInput {
75
+ services: string[];
76
+ skillPacks: string[];
77
+ proxy?: ProxyType;
78
+ gpu?: boolean;
79
+ platform?: Platform;
80
+ monitoring?: boolean;
81
+ }
82
+
83
+ export interface GeneratedFiles {
84
+ [path: string]: string;
85
+ }
86
+
87
+ export interface GenerationMetadata {
88
+ serviceCount: number;
89
+ skillCount: number;
90
+ estimatedMemoryMB: number;
91
+ generatedAt: string;
92
+ }
93
+
94
+ export interface GenerationResult {
95
+ files: GeneratedFiles;
96
+ metadata: GenerationMetadata;
97
+ }
98
+
99
+ export interface CategoryInfo {
100
+ id: ServiceCategory;
101
+ name: string;
102
+ icon: string;
103
+ }
104
+
105
+ export const SERVICE_CATEGORIES: CategoryInfo[] = [
106
+ { id: "coding-agent", name: "AI Coding Agents", icon: "💻" },
107
+ { id: "ai-platform", name: "AI Platforms & Chat UIs", icon: "🧪" },
108
+ { id: "ai", name: "AI / Local Models", icon: "🤖" },
109
+ { id: "automation", name: "Automation & Workflows", icon: "🔄" },
110
+ { id: "vector-db", name: "Vector Databases", icon: "🧠" },
111
+ { id: "media", name: "Media & Video", icon: "🎬" },
112
+ { id: "social-media", name: "Social Media", icon: "📱" },
113
+ { id: "analytics", name: "Analytics", icon: "📊" },
114
+ { id: "knowledge", name: "Knowledge & Documents", icon: "📚" },
115
+ { id: "storage", name: "Object Storage", icon: "💾" },
116
+ { id: "database", name: "Databases & Caching", icon: "🗄️" },
117
+ { id: "dev-tools", name: "Developer Tools", icon: "🛠️" },
118
+ { id: "proxy", name: "Reverse Proxy", icon: "🌐" },
119
+ { id: "monitoring", name: "Monitoring", icon: "📡" },
120
+ { id: "browser", name: "Browser Automation", icon: "🌐" },
121
+ { id: "search", name: "Search", icon: "🔍" },
122
+ { id: "communication", name: "Notifications", icon: "🔔" },
123
+ ];
@@ -0,0 +1,66 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { compose } from "./composer.js";
3
+ import { resolve } from "./resolver.js";
4
+ import type { ComposeOptions } from "./types.js";
5
+ import { validate } from "./validator.js";
6
+
7
+ const defaultComposeOptions: ComposeOptions = {
8
+ projectName: "test-project",
9
+ proxy: "none",
10
+ gpu: false,
11
+ platform: "linux/amd64",
12
+ deployment: "local",
13
+ openclawVersion: "latest",
14
+ };
15
+
16
+ describe("validate", () => {
17
+ it("validates a valid minimal stack", () => {
18
+ const resolved = resolve({ services: ["redis"], skillPacks: [] });
19
+ const yaml = compose(resolved, defaultComposeOptions);
20
+ const result = validate(resolved, yaml);
21
+
22
+ expect(result.valid).toBe(true);
23
+ expect(result.errors).toHaveLength(0);
24
+ });
25
+
26
+ it("detects invalid YAML", () => {
27
+ const resolved = resolve({ services: ["redis"], skillPacks: [] });
28
+ const brokenYaml =
29
+ "services:\n redis:\n image: redis\n bad_indent:\n- broken\n::: invalid";
30
+
31
+ const result = validate(resolved, brokenYaml);
32
+
33
+ const yamlErrors = result.errors.filter((e) => e.type === "yaml_invalid");
34
+ expect(yamlErrors.length).toBeGreaterThanOrEqual(1);
35
+ });
36
+
37
+ it("detects invalid domain", () => {
38
+ const resolved = resolve({ services: ["redis"], skillPacks: [] });
39
+ const yaml = compose(resolved, defaultComposeOptions);
40
+ const result = validate(resolved, yaml, { domain: "not-a-domain" });
41
+
42
+ const domainErrors = result.errors.filter((e) => e.type === "invalid_domain");
43
+ expect(domainErrors.length).toBeGreaterThanOrEqual(1);
44
+ });
45
+
46
+ it("accepts valid domain", () => {
47
+ const resolved = resolve({ services: ["redis"], skillPacks: [] });
48
+ const yaml = compose(resolved, defaultComposeOptions);
49
+ const result = validate(resolved, yaml, { domain: "example.com" });
50
+
51
+ const domainErrors = result.errors.filter((e) => e.type === "invalid_domain");
52
+ expect(domainErrors).toHaveLength(0);
53
+ });
54
+
55
+ it("validates full stack without errors", () => {
56
+ const resolved = resolve({
57
+ services: ["redis", "qdrant", "n8n", "minio", "browserless", "searxng"],
58
+ skillPacks: [],
59
+ });
60
+ const yaml = compose(resolved, defaultComposeOptions);
61
+ const result = validate(resolved, yaml);
62
+
63
+ expect(result.valid).toBe(true);
64
+ expect(result.errors).toHaveLength(0);
65
+ });
66
+ });
@@ -0,0 +1,166 @@
1
+ import { parse } from "yaml";
2
+ import type { ResolverError, ResolverOutput, Warning } from "./types.js";
3
+
4
+ export interface ValidationResult {
5
+ valid: boolean;
6
+ errors: ResolverError[];
7
+ warnings: Warning[];
8
+ }
9
+
10
+ /**
11
+ * Validates a complete generated stack before writing files.
12
+ * Checks for port conflicts, volume uniqueness, env completeness,
13
+ * dependency ordering, YAML validity, and more.
14
+ */
15
+ export function validate(
16
+ resolved: ResolverOutput,
17
+ composedYaml: string,
18
+ options: { domain?: string; generateSecrets?: boolean } = {},
19
+ ): ValidationResult {
20
+ const errors: ResolverError[] = [];
21
+ const warnings: Warning[] = [];
22
+
23
+ checkPortConflicts(resolved, errors);
24
+ checkVolumeUniqueness(resolved, errors);
25
+ checkEnvCompleteness(resolved, errors, warnings, options.generateSecrets ?? true);
26
+ checkNetworkConsistency(resolved, warnings);
27
+ checkDependencyDAG(resolved, errors);
28
+ checkYamlValidity(composedYaml, errors);
29
+
30
+ if (options.domain) {
31
+ checkDomainFormat(options.domain, errors);
32
+ }
33
+
34
+ return {
35
+ valid: errors.length === 0,
36
+ errors,
37
+ warnings,
38
+ };
39
+ }
40
+
41
+ function checkPortConflicts(resolved: ResolverOutput, errors: ResolverError[]): void {
42
+ const hostPorts = new Map<number, string>();
43
+ for (const svc of resolved.services) {
44
+ for (const port of svc.definition.ports) {
45
+ if (!port.exposed) continue;
46
+ const existing = hostPorts.get(port.host);
47
+ if (existing) {
48
+ errors.push({
49
+ type: "port_conflict",
50
+ message: `Port ${port.host} is used by both "${existing}" and "${svc.definition.name}"`,
51
+ });
52
+ } else {
53
+ hostPorts.set(port.host, svc.definition.name);
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ function checkVolumeUniqueness(resolved: ResolverOutput, errors: ResolverError[]): void {
60
+ const volumeNames = new Map<string, string>();
61
+ for (const svc of resolved.services) {
62
+ for (const vol of svc.definition.volumes) {
63
+ const existing = volumeNames.get(vol.name);
64
+ if (existing && existing !== svc.definition.id) {
65
+ errors.push({
66
+ type: "volume_conflict",
67
+ message: `Volume name "${vol.name}" is used by both "${existing}" and "${svc.definition.id}"`,
68
+ });
69
+ } else {
70
+ volumeNames.set(vol.name, svc.definition.id);
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ function checkEnvCompleteness(
77
+ resolved: ResolverOutput,
78
+ errors: ResolverError[],
79
+ warnings: Warning[],
80
+ generateSecrets: boolean,
81
+ ): void {
82
+ for (const svc of resolved.services) {
83
+ for (const envVar of svc.definition.environment) {
84
+ if (envVar.required && !envVar.defaultValue && !envVar.secret) {
85
+ errors.push({
86
+ type: "missing_env",
87
+ message: `Required environment variable "${envVar.key}" for "${svc.definition.name}" has no default value`,
88
+ });
89
+ }
90
+ if (envVar.secret && !generateSecrets && !envVar.defaultValue) {
91
+ warnings.push({
92
+ type: "secret_needed",
93
+ message: `Secret "${envVar.key}" for "${svc.definition.name}" needs to be configured manually`,
94
+ });
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ function checkNetworkConsistency(resolved: ResolverOutput, warnings: Warning[]): void {
101
+ for (const svc of resolved.services) {
102
+ if (!svc.definition.networks.includes("openclaw-network")) {
103
+ warnings.push({
104
+ type: "network",
105
+ message: `Service "${svc.definition.name}" is not on openclaw-network — it may not be reachable from OpenClaw`,
106
+ });
107
+ }
108
+ }
109
+ }
110
+
111
+ function checkDependencyDAG(resolved: ResolverOutput, errors: ResolverError[]): void {
112
+ const ids = new Set(resolved.services.map((s) => s.definition.id));
113
+ const visited = new Set<string>();
114
+ const inStack = new Set<string>();
115
+
116
+ const adjList = new Map<string, string[]>();
117
+ for (const svc of resolved.services) {
118
+ const deps = [...svc.definition.requires, ...svc.definition.dependsOn].filter((d) =>
119
+ ids.has(d),
120
+ );
121
+ adjList.set(svc.definition.id, deps);
122
+ }
123
+
124
+ function hasCycle(node: string): boolean {
125
+ if (inStack.has(node)) return true;
126
+ if (visited.has(node)) return false;
127
+ visited.add(node);
128
+ inStack.add(node);
129
+ for (const dep of adjList.get(node) ?? []) {
130
+ if (hasCycle(dep)) return true;
131
+ }
132
+ inStack.delete(node);
133
+ return false;
134
+ }
135
+
136
+ for (const id of ids) {
137
+ if (hasCycle(id)) {
138
+ errors.push({
139
+ type: "cycle",
140
+ message: `Circular dependency detected involving "${id}"`,
141
+ });
142
+ break;
143
+ }
144
+ }
145
+ }
146
+
147
+ function checkYamlValidity(yaml: string, errors: ResolverError[]): void {
148
+ try {
149
+ parse(yaml);
150
+ } catch (e) {
151
+ errors.push({
152
+ type: "yaml_invalid",
153
+ message: `Generated YAML is not valid: ${e instanceof Error ? e.message : String(e)}`,
154
+ });
155
+ }
156
+ }
157
+
158
+ function checkDomainFormat(domain: string, errors: ResolverError[]): void {
159
+ const domainRegex = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
160
+ if (!domainRegex.test(domain)) {
161
+ errors.push({
162
+ type: "invalid_domain",
163
+ message: `"${domain}" is not a valid domain name`,
164
+ });
165
+ }
166
+ }
@@ -0,0 +1,65 @@
1
+ import { getServiceById } from "./services/registry.js";
2
+ import type { ResolverOutput, ServiceDefinition, Warning } from "./types.js";
3
+
4
+ /** Get the pinned image tag for a service */
5
+ export function getImageTag(serviceId: string): string | undefined {
6
+ const svc = getServiceById(serviceId);
7
+ return svc?.imageTag;
8
+ }
9
+
10
+ /** Get the full image reference (image:tag) for a service */
11
+ export function getImageReference(serviceId: string): string | undefined {
12
+ const svc = getServiceById(serviceId);
13
+ if (!svc) return undefined;
14
+ return `${svc.image}:${svc.imageTag}`;
15
+ }
16
+
17
+ /** Pin all service image tags in a resolved output (returns a copy) */
18
+ export function pinImageTags(resolved: ResolverOutput): ResolverOutput {
19
+ return {
20
+ ...resolved,
21
+ services: resolved.services.map((s) => ({
22
+ ...s,
23
+ definition: { ...s.definition },
24
+ })),
25
+ };
26
+ }
27
+
28
+ /** Check for known compatibility issues between services */
29
+ export function checkCompatibility(services: ServiceDefinition[]): Warning[] {
30
+ const warnings: Warning[] = [];
31
+ const ids = new Set(services.map((s) => s.id));
32
+
33
+ // Redis + Valkey conflict (should already be caught by resolver, but double-check)
34
+ if (ids.has("redis") && ids.has("valkey")) {
35
+ warnings.push({
36
+ type: "compatibility",
37
+ message: "Redis and Valkey cannot coexist. Choose one.",
38
+ });
39
+ }
40
+ // Caddy + Traefik conflict
41
+ if (ids.has("caddy") && ids.has("traefik")) {
42
+ warnings.push({
43
+ type: "compatibility",
44
+ message: "Caddy and Traefik cannot coexist. Choose one reverse proxy.",
45
+ });
46
+ }
47
+ // Multiple vector DBs warning
48
+ const vectorDbs = ["qdrant", "chromadb", "weaviate"].filter((id) => ids.has(id));
49
+ if (vectorDbs.length > 1) {
50
+ warnings.push({
51
+ type: "compatibility",
52
+ message: `Multiple vector databases selected (${vectorDbs.join(", ")}). Consider using just one to reduce resource usage.`,
53
+ });
54
+ }
55
+ // GPU services without GPU
56
+ const gpuServices = services.filter((s) => s.gpuRequired);
57
+ if (gpuServices.length > 0) {
58
+ warnings.push({
59
+ type: "compatibility",
60
+ message: `Services requiring GPU: ${gpuServices.map((s) => s.name).join(", ")}. Ensure NVIDIA Container Toolkit is installed.`,
61
+ });
62
+ }
63
+
64
+ return warnings;
65
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src/**/*.ts"],
8
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
9
+ }