@better-openclaw/core 1.0.26 → 1.0.31

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 (1122) hide show
  1. package/README.md +67 -7
  2. package/dist/addon-stack.cjs +41 -28
  3. package/dist/addon-stack.cjs.map +1 -1
  4. package/dist/addon-stack.d.cts +1 -1
  5. package/dist/addon-stack.d.cts.map +1 -1
  6. package/dist/addon-stack.d.mts +1 -1
  7. package/dist/addon-stack.d.mts.map +1 -1
  8. package/dist/addon-stack.mjs +40 -27
  9. package/dist/addon-stack.mjs.map +1 -1
  10. package/dist/addon-stack.test.cjs +14 -14
  11. package/dist/addon-stack.test.cjs.map +1 -1
  12. package/dist/addon-stack.test.mjs +13 -13
  13. package/dist/addon-stack.test.mjs.map +1 -1
  14. package/dist/bare-metal-partition.d.cts +1 -1
  15. package/dist/bare-metal-partition.d.mts +1 -1
  16. package/dist/bare-metal-partition.test.cjs +1 -1
  17. package/dist/bare-metal-partition.test.mjs +1 -1
  18. package/dist/callback-sink-CdMKYayY.d.mts +15 -0
  19. package/dist/callback-sink-CdMKYayY.d.mts.map +1 -0
  20. package/dist/callback-sink-iNThWRnQ.d.cts +15 -0
  21. package/dist/callback-sink-iNThWRnQ.d.cts.map +1 -0
  22. package/dist/claude-code-fw-CrbAlzTM.d.mts +7 -0
  23. package/dist/claude-code-fw-CrbAlzTM.d.mts.map +1 -0
  24. package/dist/claude-code-fw-dKYH2prH.d.cts +7 -0
  25. package/dist/claude-code-fw-dKYH2prH.d.cts.map +1 -0
  26. package/dist/codex-fw-Ct8q4pzo.d.mts +7 -0
  27. package/dist/codex-fw-Ct8q4pzo.d.mts.map +1 -0
  28. package/dist/codex-fw-l7Ku8ZI8.d.cts +7 -0
  29. package/dist/codex-fw-l7Ku8ZI8.d.cts.map +1 -0
  30. package/dist/compose-validation.test.cjs +1 -1
  31. package/dist/composer.cjs +89 -129
  32. package/dist/composer.cjs.map +1 -1
  33. package/dist/composer.d.cts +3 -3
  34. package/dist/composer.d.cts.map +1 -1
  35. package/dist/composer.d.mts +3 -3
  36. package/dist/composer.d.mts.map +1 -1
  37. package/dist/composer.mjs +88 -128
  38. package/dist/composer.mjs.map +1 -1
  39. package/dist/composer.snapshot.test.cjs +1 -1
  40. package/dist/composer.snapshot.test.mjs +1 -1
  41. package/dist/composer.test.cjs +53 -2
  42. package/dist/composer.test.cjs.map +1 -1
  43. package/dist/composer.test.mjs +52 -1
  44. package/dist/composer.test.mjs.map +1 -1
  45. package/dist/console-sink-BHQpRyCt.d.cts +13 -0
  46. package/dist/console-sink-BHQpRyCt.d.cts.map +1 -0
  47. package/dist/console-sink-bouG_M1K.d.mts +13 -0
  48. package/dist/console-sink-bouG_M1K.d.mts.map +1 -0
  49. package/dist/{coolify-BVGGcMrT.d.mts → coolify-D5E0z01M.d.mts} +2 -2
  50. package/dist/{coolify-BVGGcMrT.d.mts.map → coolify-D5E0z01M.d.mts.map} +1 -1
  51. package/dist/{coolify-vlb1G9V2.d.cts → coolify-DjHecHCr.d.cts} +2 -2
  52. package/dist/{coolify-vlb1G9V2.d.cts.map → coolify-DjHecHCr.d.cts.map} +1 -1
  53. package/dist/copaw-BQyjioz7.d.cts +7 -0
  54. package/dist/copaw-BQyjioz7.d.cts.map +1 -0
  55. package/dist/copaw-C_3N7eQu.d.mts +7 -0
  56. package/dist/copaw-C_3N7eQu.d.mts.map +1 -0
  57. package/dist/deployers/coolify.cjs +25 -0
  58. package/dist/deployers/coolify.cjs.map +1 -1
  59. package/dist/deployers/coolify.d.cts +1 -1
  60. package/dist/deployers/coolify.d.mts +1 -1
  61. package/dist/deployers/coolify.mjs +25 -0
  62. package/dist/deployers/coolify.mjs.map +1 -1
  63. package/dist/deployers/coolify.test.cjs +156 -0
  64. package/dist/deployers/coolify.test.cjs.map +1 -0
  65. package/dist/deployers/coolify.test.d.cts +1 -0
  66. package/dist/deployers/coolify.test.d.mts +1 -0
  67. package/dist/deployers/coolify.test.mjs +157 -0
  68. package/dist/deployers/coolify.test.mjs.map +1 -0
  69. package/dist/deployers/dokploy.cjs +25 -0
  70. package/dist/deployers/dokploy.cjs.map +1 -1
  71. package/dist/deployers/dokploy.d.cts +1 -1
  72. package/dist/deployers/dokploy.d.mts +1 -1
  73. package/dist/deployers/dokploy.mjs +25 -0
  74. package/dist/deployers/dokploy.mjs.map +1 -1
  75. package/dist/deployers/dokploy.test.cjs +108 -0
  76. package/dist/deployers/dokploy.test.cjs.map +1 -0
  77. package/dist/deployers/dokploy.test.d.cts +1 -0
  78. package/dist/deployers/dokploy.test.d.mts +1 -0
  79. package/dist/deployers/dokploy.test.mjs +109 -0
  80. package/dist/deployers/dokploy.test.mjs.map +1 -0
  81. package/dist/deployers/index.d.cts +3 -3
  82. package/dist/deployers/index.d.mts +3 -3
  83. package/dist/deployers/strip-host-ports.cjs +1 -1
  84. package/dist/deployers/strip-host-ports.test.cjs +1 -1
  85. package/dist/deployers/strip-host-ports.test.mjs +1 -1
  86. package/dist/deployers/types.d.cts +1 -1
  87. package/dist/deployers/types.d.mts +1 -1
  88. package/dist/{dokploy-8cbrxUun.d.cts → dokploy-BnVDjWCV.d.cts} +2 -2
  89. package/dist/{dokploy-8cbrxUun.d.cts.map → dokploy-BnVDjWCV.d.cts.map} +1 -1
  90. package/dist/{dokploy-BTflLhTM.d.mts → dokploy-BqLdCfyR.d.mts} +2 -2
  91. package/dist/{dokploy-BTflLhTM.d.mts.map → dokploy-BqLdCfyR.d.mts.map} +1 -1
  92. package/dist/frameworks/claude-code-fw.cjs +79 -0
  93. package/dist/frameworks/claude-code-fw.cjs.map +1 -0
  94. package/dist/frameworks/claude-code-fw.d.cts +2 -0
  95. package/dist/frameworks/claude-code-fw.d.mts +2 -0
  96. package/dist/frameworks/claude-code-fw.mjs +78 -0
  97. package/dist/frameworks/claude-code-fw.mjs.map +1 -0
  98. package/dist/frameworks/codex-fw.cjs +67 -0
  99. package/dist/frameworks/codex-fw.cjs.map +1 -0
  100. package/dist/frameworks/codex-fw.d.cts +2 -0
  101. package/dist/frameworks/codex-fw.d.mts +2 -0
  102. package/dist/frameworks/codex-fw.mjs +66 -0
  103. package/dist/frameworks/codex-fw.mjs.map +1 -0
  104. package/dist/frameworks/copaw.cjs +118 -0
  105. package/dist/frameworks/copaw.cjs.map +1 -0
  106. package/dist/frameworks/copaw.d.cts +2 -0
  107. package/dist/frameworks/copaw.d.mts +2 -0
  108. package/dist/frameworks/copaw.mjs +117 -0
  109. package/dist/frameworks/copaw.mjs.map +1 -0
  110. package/dist/frameworks/frameworks.test.cjs +94 -0
  111. package/dist/frameworks/frameworks.test.cjs.map +1 -0
  112. package/dist/frameworks/frameworks.test.d.cts +1 -0
  113. package/dist/frameworks/frameworks.test.d.mts +1 -0
  114. package/dist/frameworks/frameworks.test.mjs +94 -0
  115. package/dist/frameworks/frameworks.test.mjs.map +1 -0
  116. package/dist/frameworks/index.cjs +35 -0
  117. package/dist/frameworks/index.cjs.map +1 -0
  118. package/dist/frameworks/index.d.cts +11 -0
  119. package/dist/frameworks/index.d.mts +11 -0
  120. package/dist/frameworks/index.mjs +22 -0
  121. package/dist/frameworks/index.mjs.map +1 -0
  122. package/dist/frameworks/memu.cjs +118 -0
  123. package/dist/frameworks/memu.cjs.map +1 -0
  124. package/dist/frameworks/memu.d.cts +2 -0
  125. package/dist/frameworks/memu.d.mts +2 -0
  126. package/dist/frameworks/memu.mjs +117 -0
  127. package/dist/frameworks/memu.mjs.map +1 -0
  128. package/dist/frameworks/nanobot.cjs +126 -0
  129. package/dist/frameworks/nanobot.cjs.map +1 -0
  130. package/dist/frameworks/nanobot.d.cts +2 -0
  131. package/dist/frameworks/nanobot.d.mts +2 -0
  132. package/dist/frameworks/nanobot.mjs +125 -0
  133. package/dist/frameworks/nanobot.mjs.map +1 -0
  134. package/dist/frameworks/nanoclaw.cjs +99 -0
  135. package/dist/frameworks/nanoclaw.cjs.map +1 -0
  136. package/dist/frameworks/nanoclaw.d.cts +2 -0
  137. package/dist/frameworks/nanoclaw.d.mts +2 -0
  138. package/dist/frameworks/nanoclaw.mjs +98 -0
  139. package/dist/frameworks/nanoclaw.mjs.map +1 -0
  140. package/dist/frameworks/openclaw.cjs +253 -0
  141. package/dist/frameworks/openclaw.cjs.map +1 -0
  142. package/dist/frameworks/openclaw.d.cts +2 -0
  143. package/dist/frameworks/openclaw.d.mts +2 -0
  144. package/dist/frameworks/openclaw.mjs +251 -0
  145. package/dist/frameworks/openclaw.mjs.map +1 -0
  146. package/dist/frameworks/registry.cjs +31 -0
  147. package/dist/frameworks/registry.cjs.map +1 -0
  148. package/dist/frameworks/registry.d.cts +2 -0
  149. package/dist/frameworks/registry.d.mts +2 -0
  150. package/dist/frameworks/registry.mjs +26 -0
  151. package/dist/frameworks/registry.mjs.map +1 -0
  152. package/dist/frameworks/registry.test.cjs +110 -0
  153. package/dist/frameworks/registry.test.cjs.map +1 -0
  154. package/dist/frameworks/registry.test.d.cts +1 -0
  155. package/dist/frameworks/registry.test.d.mts +1 -0
  156. package/dist/frameworks/registry.test.mjs +111 -0
  157. package/dist/frameworks/registry.test.mjs.map +1 -0
  158. package/dist/frameworks/types.cjs +0 -0
  159. package/dist/frameworks/types.d.cts +2 -0
  160. package/dist/frameworks/types.d.mts +2 -0
  161. package/dist/frameworks/types.mjs +1 -0
  162. package/dist/frameworks/zeroclaw.cjs +123 -0
  163. package/dist/frameworks/zeroclaw.cjs.map +1 -0
  164. package/dist/frameworks/zeroclaw.d.cts +2 -0
  165. package/dist/frameworks/zeroclaw.d.mts +2 -0
  166. package/dist/frameworks/zeroclaw.mjs +122 -0
  167. package/dist/frameworks/zeroclaw.mjs.map +1 -0
  168. package/dist/generate.cjs +64 -13
  169. package/dist/generate.cjs.map +1 -1
  170. package/dist/generate.d.cts +5 -2
  171. package/dist/generate.d.cts.map +1 -1
  172. package/dist/generate.d.mts +5 -2
  173. package/dist/generate.d.mts.map +1 -1
  174. package/dist/generate.mjs +63 -12
  175. package/dist/generate.mjs.map +1 -1
  176. package/dist/generate.test.cjs +42 -6
  177. package/dist/generate.test.cjs.map +1 -1
  178. package/dist/generate.test.mjs +41 -5
  179. package/dist/generate.test.mjs.map +1 -1
  180. package/dist/generators/bare-metal-install.d.cts +1 -1
  181. package/dist/generators/bare-metal-install.d.mts +1 -1
  182. package/dist/generators/bare-metal-install.test.cjs +1 -1
  183. package/dist/generators/bare-metal-install.test.mjs +1 -1
  184. package/dist/generators/caddy.cjs +9 -3
  185. package/dist/generators/caddy.cjs.map +1 -1
  186. package/dist/generators/caddy.d.cts +1 -1
  187. package/dist/generators/caddy.d.mts +1 -1
  188. package/dist/generators/caddy.mjs +9 -3
  189. package/dist/generators/caddy.mjs.map +1 -1
  190. package/dist/generators/caddy.test.cjs +30 -2
  191. package/dist/generators/caddy.test.cjs.map +1 -1
  192. package/dist/generators/caddy.test.mjs +30 -2
  193. package/dist/generators/caddy.test.mjs.map +1 -1
  194. package/dist/generators/clone-repos.cjs.map +1 -1
  195. package/dist/generators/clone-repos.d.cts +1 -1
  196. package/dist/generators/clone-repos.d.mts +1 -1
  197. package/dist/generators/clone-repos.mjs.map +1 -1
  198. package/dist/generators/clone-repos.test.cjs +1 -1
  199. package/dist/generators/clone-repos.test.cjs.map +1 -1
  200. package/dist/generators/clone-repos.test.mjs +1 -1
  201. package/dist/generators/clone-repos.test.mjs.map +1 -1
  202. package/dist/generators/cloud-init.test.cjs +58 -0
  203. package/dist/generators/cloud-init.test.cjs.map +1 -0
  204. package/dist/generators/cloud-init.test.d.cts +1 -0
  205. package/dist/generators/cloud-init.test.d.mts +1 -0
  206. package/dist/generators/cloud-init.test.mjs +59 -0
  207. package/dist/generators/cloud-init.test.mjs.map +1 -0
  208. package/dist/generators/env.cjs +28 -103
  209. package/dist/generators/env.cjs.map +1 -1
  210. package/dist/generators/env.d.cts +4 -1
  211. package/dist/generators/env.d.cts.map +1 -1
  212. package/dist/generators/env.d.mts +4 -1
  213. package/dist/generators/env.d.mts.map +1 -1
  214. package/dist/generators/env.mjs +27 -102
  215. package/dist/generators/env.mjs.map +1 -1
  216. package/dist/generators/env.test.cjs +3 -3
  217. package/dist/generators/env.test.cjs.map +1 -1
  218. package/dist/generators/env.test.mjs +3 -3
  219. package/dist/generators/env.test.mjs.map +1 -1
  220. package/dist/generators/get-shit-done.d.cts +1 -1
  221. package/dist/generators/get-shit-done.d.mts +1 -1
  222. package/dist/generators/get-shit-done.test.cjs +48 -0
  223. package/dist/generators/get-shit-done.test.cjs.map +1 -0
  224. package/dist/generators/get-shit-done.test.d.cts +1 -0
  225. package/dist/generators/get-shit-done.test.d.mts +1 -0
  226. package/dist/generators/get-shit-done.test.mjs +49 -0
  227. package/dist/generators/get-shit-done.test.mjs.map +1 -0
  228. package/dist/generators/grafana.test.cjs +74 -0
  229. package/dist/generators/grafana.test.cjs.map +1 -0
  230. package/dist/generators/grafana.test.d.cts +1 -0
  231. package/dist/generators/grafana.test.d.mts +1 -0
  232. package/dist/generators/grafana.test.mjs +74 -0
  233. package/dist/generators/grafana.test.mjs.map +1 -0
  234. package/dist/generators/health-check.cjs +6 -6
  235. package/dist/generators/health-check.cjs.map +1 -1
  236. package/dist/generators/health-check.d.cts +1 -1
  237. package/dist/generators/health-check.d.mts +1 -1
  238. package/dist/generators/health-check.mjs +6 -6
  239. package/dist/generators/health-check.mjs.map +1 -1
  240. package/dist/generators/health-check.test.cjs +1 -1
  241. package/dist/generators/health-check.test.mjs +1 -1
  242. package/dist/generators/n8n-workflows.d.cts +1 -1
  243. package/dist/generators/n8n-workflows.d.mts +1 -1
  244. package/dist/generators/n8n-workflows.test.cjs +75 -0
  245. package/dist/generators/n8n-workflows.test.cjs.map +1 -0
  246. package/dist/generators/n8n-workflows.test.d.cts +1 -0
  247. package/dist/generators/n8n-workflows.test.d.mts +1 -0
  248. package/dist/generators/n8n-workflows.test.mjs +76 -0
  249. package/dist/generators/n8n-workflows.test.mjs.map +1 -0
  250. package/dist/generators/native-services.d.cts +1 -1
  251. package/dist/generators/native-services.d.mts +1 -1
  252. package/dist/generators/openclaw-install-script.test.cjs +35 -0
  253. package/dist/generators/openclaw-install-script.test.cjs.map +1 -0
  254. package/dist/generators/openclaw-install-script.test.d.cts +1 -0
  255. package/dist/generators/openclaw-install-script.test.d.mts +1 -0
  256. package/dist/generators/openclaw-install-script.test.mjs +36 -0
  257. package/dist/generators/openclaw-install-script.test.mjs.map +1 -0
  258. package/dist/generators/openclaw-json.cjs +60 -4
  259. package/dist/generators/openclaw-json.cjs.map +1 -1
  260. package/dist/generators/openclaw-json.d.cts +1 -5
  261. package/dist/generators/openclaw-json.d.cts.map +1 -1
  262. package/dist/generators/openclaw-json.d.mts +1 -5
  263. package/dist/generators/openclaw-json.d.mts.map +1 -1
  264. package/dist/generators/openclaw-json.mjs +60 -4
  265. package/dist/generators/openclaw-json.mjs.map +1 -1
  266. package/dist/generators/postgres-init.cjs +20 -0
  267. package/dist/generators/postgres-init.cjs.map +1 -1
  268. package/dist/generators/postgres-init.d.cts +1 -1
  269. package/dist/generators/postgres-init.d.cts.map +1 -1
  270. package/dist/generators/postgres-init.d.mts +1 -1
  271. package/dist/generators/postgres-init.d.mts.map +1 -1
  272. package/dist/generators/postgres-init.mjs +20 -0
  273. package/dist/generators/postgres-init.mjs.map +1 -1
  274. package/dist/generators/postgres-init.test.cjs +111 -0
  275. package/dist/generators/postgres-init.test.cjs.map +1 -0
  276. package/dist/generators/postgres-init.test.d.cts +1 -0
  277. package/dist/generators/postgres-init.test.d.mts +1 -0
  278. package/dist/generators/postgres-init.test.mjs +112 -0
  279. package/dist/generators/postgres-init.test.mjs.map +1 -0
  280. package/dist/generators/prometheus.d.cts +1 -1
  281. package/dist/generators/prometheus.d.mts +1 -1
  282. package/dist/generators/prometheus.test.cjs +99 -0
  283. package/dist/generators/prometheus.test.cjs.map +1 -0
  284. package/dist/generators/prometheus.test.d.cts +1 -0
  285. package/dist/generators/prometheus.test.d.mts +1 -0
  286. package/dist/generators/prometheus.test.mjs +99 -0
  287. package/dist/generators/prometheus.test.mjs.map +1 -0
  288. package/dist/generators/readme.cjs +50 -7
  289. package/dist/generators/readme.cjs.map +1 -1
  290. package/dist/generators/readme.d.cts +3 -1
  291. package/dist/generators/readme.d.cts.map +1 -1
  292. package/dist/generators/readme.d.mts +3 -1
  293. package/dist/generators/readme.d.mts.map +1 -1
  294. package/dist/generators/readme.mjs +50 -7
  295. package/dist/generators/readme.mjs.map +1 -1
  296. package/dist/generators/scripts.test.cjs +2 -2
  297. package/dist/generators/scripts.test.cjs.map +1 -1
  298. package/dist/generators/scripts.test.mjs +2 -2
  299. package/dist/generators/scripts.test.mjs.map +1 -1
  300. package/dist/generators/skills.cjs +1 -1
  301. package/dist/generators/skills.d.cts +1 -1
  302. package/dist/generators/skills.d.cts.map +1 -1
  303. package/dist/generators/skills.d.mts +1 -1
  304. package/dist/generators/skills.d.mts.map +1 -1
  305. package/dist/generators/skills.mjs +310 -0
  306. package/dist/generators/skills.mjs.map +1 -1
  307. package/dist/generators/stack-manifest.d.cts +1 -1
  308. package/dist/generators/stack-manifest.d.mts +1 -1
  309. package/dist/generators/stack-manifest.test.cjs +97 -0
  310. package/dist/generators/stack-manifest.test.cjs.map +1 -0
  311. package/dist/generators/stack-manifest.test.d.cts +1 -0
  312. package/dist/generators/stack-manifest.test.d.mts +1 -0
  313. package/dist/generators/stack-manifest.test.mjs +98 -0
  314. package/dist/generators/stack-manifest.test.mjs.map +1 -0
  315. package/dist/generators/traefik.d.cts +1 -1
  316. package/dist/generators/traefik.d.mts +1 -1
  317. package/dist/generators/traefik.test.cjs +12 -12
  318. package/dist/generators/traefik.test.cjs.map +1 -1
  319. package/dist/generators/traefik.test.mjs +12 -12
  320. package/dist/generators/traefik.test.mjs.map +1 -1
  321. package/dist/index.cjs +18 -2
  322. package/dist/index.d.cts +13 -7
  323. package/dist/index.d.mts +13 -7
  324. package/dist/index.mjs +9 -3
  325. package/dist/logger/__tests__/logger.test.cjs +308 -0
  326. package/dist/logger/__tests__/logger.test.cjs.map +1 -0
  327. package/dist/logger/__tests__/logger.test.d.cts +1 -0
  328. package/dist/logger/__tests__/logger.test.d.mts +1 -0
  329. package/dist/logger/__tests__/logger.test.mjs +308 -0
  330. package/dist/logger/__tests__/logger.test.mjs.map +1 -0
  331. package/dist/logger/index.cjs +8 -0
  332. package/dist/logger/index.d.cts +5 -0
  333. package/dist/logger/index.d.mts +5 -0
  334. package/dist/logger/index.mjs +4 -0
  335. package/dist/logger/logger.cjs +270 -0
  336. package/dist/logger/logger.cjs.map +1 -0
  337. package/dist/logger/logger.d.cts +2 -0
  338. package/dist/logger/logger.d.mts +2 -0
  339. package/dist/logger/logger.mjs +267 -0
  340. package/dist/logger/logger.mjs.map +1 -0
  341. package/dist/logger/sinks/callback-sink.cjs +18 -0
  342. package/dist/logger/sinks/callback-sink.cjs.map +1 -0
  343. package/dist/logger/sinks/callback-sink.d.cts +2 -0
  344. package/dist/logger/sinks/callback-sink.d.mts +2 -0
  345. package/dist/logger/sinks/callback-sink.mjs +17 -0
  346. package/dist/logger/sinks/callback-sink.mjs.map +1 -0
  347. package/dist/logger/sinks/console-sink.cjs +39 -0
  348. package/dist/logger/sinks/console-sink.cjs.map +1 -0
  349. package/dist/logger/sinks/console-sink.d.cts +2 -0
  350. package/dist/logger/sinks/console-sink.d.mts +2 -0
  351. package/dist/logger/sinks/console-sink.mjs +38 -0
  352. package/dist/logger/sinks/console-sink.mjs.map +1 -0
  353. package/dist/logger/sinks/file-sink.cjs +72 -0
  354. package/dist/logger/sinks/file-sink.cjs.map +1 -0
  355. package/dist/logger/sinks/file-sink.d.cts +36 -0
  356. package/dist/logger/sinks/file-sink.d.cts.map +1 -0
  357. package/dist/logger/sinks/file-sink.d.mts +36 -0
  358. package/dist/logger/sinks/file-sink.d.mts.map +1 -0
  359. package/dist/logger/sinks/file-sink.mjs +70 -0
  360. package/dist/logger/sinks/file-sink.mjs.map +1 -0
  361. package/dist/logger/types.cjs +0 -0
  362. package/dist/logger/types.d.cts +2 -0
  363. package/dist/logger/types.d.mts +2 -0
  364. package/dist/logger/types.mjs +1 -0
  365. package/dist/logger-CZ0Qnyiu.d.mts +66 -0
  366. package/dist/logger-CZ0Qnyiu.d.mts.map +1 -0
  367. package/dist/logger-DDfwai-A.d.cts +66 -0
  368. package/dist/logger-DDfwai-A.d.cts.map +1 -0
  369. package/dist/memu-BfRXtwmL.d.cts +7 -0
  370. package/dist/memu-BfRXtwmL.d.cts.map +1 -0
  371. package/dist/memu-C-fXbWr-.d.mts +7 -0
  372. package/dist/memu-C-fXbWr-.d.mts.map +1 -0
  373. package/dist/migrations.cjs +17 -9
  374. package/dist/migrations.cjs.map +1 -1
  375. package/dist/migrations.d.cts +1 -1
  376. package/dist/migrations.d.cts.map +1 -1
  377. package/dist/migrations.d.mts +1 -1
  378. package/dist/migrations.d.mts.map +1 -1
  379. package/dist/migrations.mjs +17 -9
  380. package/dist/migrations.mjs.map +1 -1
  381. package/dist/migrations.test.cjs +4 -4
  382. package/dist/migrations.test.mjs +4 -4
  383. package/dist/nanobot-2c0bS4lu.d.cts +7 -0
  384. package/dist/nanobot-2c0bS4lu.d.cts.map +1 -0
  385. package/dist/nanobot-BUO6pVQc.d.mts +7 -0
  386. package/dist/nanobot-BUO6pVQc.d.mts.map +1 -0
  387. package/dist/nanoclaw-CgHubEig.d.mts +7 -0
  388. package/dist/nanoclaw-CgHubEig.d.mts.map +1 -0
  389. package/dist/nanoclaw-Dvz0ef6J.d.cts +7 -0
  390. package/dist/nanoclaw-Dvz0ef6J.d.cts.map +1 -0
  391. package/dist/openclaw-D8lVecjC.d.mts +7 -0
  392. package/dist/openclaw-D8lVecjC.d.mts.map +1 -0
  393. package/dist/openclaw-DThj8i3N.d.cts +7 -0
  394. package/dist/openclaw-DThj8i3N.d.cts.map +1 -0
  395. package/dist/port-scanner.d.cts +1 -1
  396. package/dist/port-scanner.d.mts +1 -1
  397. package/dist/port-scanner.test.cjs +155 -0
  398. package/dist/port-scanner.test.cjs.map +1 -0
  399. package/dist/port-scanner.test.d.cts +1 -0
  400. package/dist/port-scanner.test.d.mts +1 -0
  401. package/dist/port-scanner.test.mjs +156 -0
  402. package/dist/port-scanner.test.mjs.map +1 -0
  403. package/dist/presets/presets.test.cjs +5 -5
  404. package/dist/presets/presets.test.cjs.map +1 -1
  405. package/dist/presets/presets.test.mjs +4 -4
  406. package/dist/presets/presets.test.mjs.map +1 -1
  407. package/dist/presets/registry.d.cts +1 -1
  408. package/dist/presets/registry.d.mts +1 -1
  409. package/dist/presets/registry.test.cjs +7 -7
  410. package/dist/presets/registry.test.cjs.map +1 -1
  411. package/dist/presets/registry.test.mjs +7 -7
  412. package/dist/presets/registry.test.mjs.map +1 -1
  413. package/dist/registry-DHX7vnVo.d.cts +16 -0
  414. package/dist/registry-DHX7vnVo.d.cts.map +1 -0
  415. package/dist/registry-DTAILnga.d.mts +16 -0
  416. package/dist/registry-DTAILnga.d.mts.map +1 -0
  417. package/dist/resolver.cjs +13 -3
  418. package/dist/resolver.cjs.map +1 -1
  419. package/dist/resolver.d.cts +1 -1
  420. package/dist/resolver.d.cts.map +1 -1
  421. package/dist/resolver.d.mts +1 -1
  422. package/dist/resolver.d.mts.map +1 -1
  423. package/dist/resolver.mjs +13 -3
  424. package/dist/resolver.mjs.map +1 -1
  425. package/dist/resolver.test.cjs +35 -3
  426. package/dist/resolver.test.cjs.map +1 -1
  427. package/dist/resolver.test.mjs +35 -3
  428. package/dist/resolver.test.mjs.map +1 -1
  429. package/dist/{schema-BQnZrcw8.d.cts → schema-BSl9wiFe.d.mts} +134 -2
  430. package/dist/schema-BSl9wiFe.d.mts.map +1 -0
  431. package/dist/{schema-SBpL0bdI.d.mts → schema-Bd9l2r7p.d.cts} +134 -2
  432. package/dist/schema-Bd9l2r7p.d.cts.map +1 -0
  433. package/dist/schema.cjs +28 -4
  434. package/dist/schema.cjs.map +1 -1
  435. package/dist/schema.d.cts +2 -2
  436. package/dist/schema.d.mts +2 -2
  437. package/dist/schema.mjs +27 -4
  438. package/dist/schema.mjs.map +1 -1
  439. package/dist/schema.test.cjs +3 -3
  440. package/dist/schema.test.cjs.map +1 -1
  441. package/dist/schema.test.mjs +3 -3
  442. package/dist/schema.test.mjs.map +1 -1
  443. package/dist/services/definitions/adguard-home.d.cts +1 -1
  444. package/dist/services/definitions/adguard-home.d.mts +1 -1
  445. package/dist/services/definitions/agent-browser.cjs +161 -0
  446. package/dist/services/definitions/agent-browser.cjs.map +1 -0
  447. package/dist/services/definitions/agent-browser.d.cts +7 -0
  448. package/dist/services/definitions/agent-browser.d.cts.map +1 -0
  449. package/dist/services/definitions/agent-browser.d.mts +7 -0
  450. package/dist/services/definitions/agent-browser.d.mts.map +1 -0
  451. package/dist/services/definitions/agent-browser.mjs +160 -0
  452. package/dist/services/definitions/agent-browser.mjs.map +1 -0
  453. package/dist/services/definitions/airbyte.d.cts +1 -1
  454. package/dist/services/definitions/airbyte.d.mts +1 -1
  455. package/dist/services/definitions/airflow.d.cts +1 -1
  456. package/dist/services/definitions/airflow.d.mts +1 -1
  457. package/dist/services/definitions/anything-llm.d.cts +1 -1
  458. package/dist/services/definitions/anything-llm.d.mts +1 -1
  459. package/dist/services/definitions/appflowy.d.cts +1 -1
  460. package/dist/services/definitions/appflowy.d.mts +1 -1
  461. package/dist/services/definitions/apptension-saas.cjs.map +1 -1
  462. package/dist/services/definitions/apptension-saas.d.cts +1 -1
  463. package/dist/services/definitions/apptension-saas.d.mts +1 -1
  464. package/dist/services/definitions/apptension-saas.mjs.map +1 -1
  465. package/dist/services/definitions/appwrite.d.cts +1 -1
  466. package/dist/services/definitions/appwrite.d.mts +1 -1
  467. package/dist/services/definitions/audiobookshelf.d.cts +1 -1
  468. package/dist/services/definitions/audiobookshelf.d.mts +1 -1
  469. package/dist/services/definitions/authelia.d.cts +1 -1
  470. package/dist/services/definitions/authelia.d.mts +1 -1
  471. package/dist/services/definitions/authentik.d.cts +1 -1
  472. package/dist/services/definitions/authentik.d.mts +1 -1
  473. package/dist/services/definitions/axolotl.d.cts +1 -1
  474. package/dist/services/definitions/axolotl.d.mts +1 -1
  475. package/dist/services/definitions/baserow.d.cts +1 -1
  476. package/dist/services/definitions/baserow.d.mts +1 -1
  477. package/dist/services/definitions/beszel.d.cts +1 -1
  478. package/dist/services/definitions/beszel.d.mts +1 -1
  479. package/dist/services/definitions/boxyhq-saas.d.cts +1 -1
  480. package/dist/services/definitions/boxyhq-saas.d.mts +1 -1
  481. package/dist/services/definitions/browserless.d.cts +1 -1
  482. package/dist/services/definitions/browserless.d.mts +1 -1
  483. package/dist/services/definitions/burnlink.d.cts +1 -1
  484. package/dist/services/definitions/burnlink.d.mts +1 -1
  485. package/dist/services/definitions/caddy.d.cts +1 -1
  486. package/dist/services/definitions/caddy.d.mts +1 -1
  487. package/dist/services/definitions/cal-com.d.cts +1 -1
  488. package/dist/services/definitions/cal-com.d.mts +1 -1
  489. package/dist/services/definitions/calibre-web.d.cts +1 -1
  490. package/dist/services/definitions/calibre-web.d.mts +1 -1
  491. package/dist/services/definitions/chatwoot-worker.d.cts +1 -1
  492. package/dist/services/definitions/chatwoot-worker.d.mts +1 -1
  493. package/dist/services/definitions/chatwoot.d.cts +1 -1
  494. package/dist/services/definitions/chatwoot.d.mts +1 -1
  495. package/dist/services/definitions/chromadb.cjs +4 -1
  496. package/dist/services/definitions/chromadb.cjs.map +1 -1
  497. package/dist/services/definitions/chromadb.d.cts +1 -1
  498. package/dist/services/definitions/chromadb.d.mts +1 -1
  499. package/dist/services/definitions/chromadb.mjs +4 -1
  500. package/dist/services/definitions/chromadb.mjs.map +1 -1
  501. package/dist/services/definitions/claude-code.cjs +1 -1
  502. package/dist/services/definitions/claude-code.cjs.map +1 -1
  503. package/dist/services/definitions/claude-code.d.cts +1 -1
  504. package/dist/services/definitions/claude-code.d.mts +1 -1
  505. package/dist/services/definitions/claude-code.mjs +1 -1
  506. package/dist/services/definitions/claude-code.mjs.map +1 -1
  507. package/dist/services/definitions/clawrouter.cjs +138 -0
  508. package/dist/services/definitions/clawrouter.cjs.map +1 -0
  509. package/dist/services/definitions/clawrouter.d.cts +7 -0
  510. package/dist/services/definitions/clawrouter.d.cts.map +1 -0
  511. package/dist/services/definitions/clawrouter.d.mts +7 -0
  512. package/dist/services/definitions/clawrouter.d.mts.map +1 -0
  513. package/dist/services/definitions/clawrouter.mjs +137 -0
  514. package/dist/services/definitions/clawrouter.mjs.map +1 -0
  515. package/dist/services/definitions/clickhouse.d.cts +1 -1
  516. package/dist/services/definitions/clickhouse.d.mts +1 -1
  517. package/dist/services/definitions/cloudflared.d.cts +1 -1
  518. package/dist/services/definitions/cloudflared.d.mts +1 -1
  519. package/dist/services/definitions/cmsaas-starter.d.cts +1 -1
  520. package/dist/services/definitions/cmsaas-starter.d.mts +1 -1
  521. package/dist/services/definitions/cockroachdb.d.cts +1 -1
  522. package/dist/services/definitions/cockroachdb.d.mts +1 -1
  523. package/dist/services/definitions/code-server.d.cts +1 -1
  524. package/dist/services/definitions/code-server.d.mts +1 -1
  525. package/dist/services/definitions/coder.d.cts +1 -1
  526. package/dist/services/definitions/coder.d.mts +1 -1
  527. package/dist/services/definitions/codex.cjs +1 -1
  528. package/dist/services/definitions/codex.cjs.map +1 -1
  529. package/dist/services/definitions/codex.d.cts +1 -1
  530. package/dist/services/definitions/codex.d.mts +1 -1
  531. package/dist/services/definitions/codex.mjs +1 -1
  532. package/dist/services/definitions/codex.mjs.map +1 -1
  533. package/dist/services/definitions/comfyui.d.cts +1 -1
  534. package/dist/services/definitions/comfyui.d.mts +1 -1
  535. package/dist/services/definitions/convex-dashboard.d.cts +1 -1
  536. package/dist/services/definitions/convex-dashboard.d.mts +1 -1
  537. package/dist/services/definitions/convex.cjs.map +1 -1
  538. package/dist/services/definitions/convex.d.cts +1 -1
  539. package/dist/services/definitions/convex.d.mts +1 -1
  540. package/dist/services/definitions/convex.mjs.map +1 -1
  541. package/dist/services/definitions/coolify.d.cts +1 -1
  542. package/dist/services/definitions/coolify.d.mts +1 -1
  543. package/dist/services/definitions/copaw.cjs +97 -0
  544. package/dist/services/definitions/copaw.cjs.map +1 -0
  545. package/dist/services/definitions/copaw.d.cts +7 -0
  546. package/dist/services/definitions/copaw.d.cts.map +1 -0
  547. package/dist/services/definitions/copaw.d.mts +7 -0
  548. package/dist/services/definitions/copaw.d.mts.map +1 -0
  549. package/dist/services/definitions/copaw.mjs +96 -0
  550. package/dist/services/definitions/copaw.mjs.map +1 -0
  551. package/dist/services/definitions/crowdsec.d.cts +1 -1
  552. package/dist/services/definitions/crowdsec.d.mts +1 -1
  553. package/dist/services/definitions/dagster.d.cts +1 -1
  554. package/dist/services/definitions/dagster.d.mts +1 -1
  555. package/dist/services/definitions/desktop-environment.cjs +2 -1
  556. package/dist/services/definitions/desktop-environment.cjs.map +1 -1
  557. package/dist/services/definitions/desktop-environment.d.cts +1 -1
  558. package/dist/services/definitions/desktop-environment.d.mts +1 -1
  559. package/dist/services/definitions/desktop-environment.mjs +2 -1
  560. package/dist/services/definitions/desktop-environment.mjs.map +1 -1
  561. package/dist/services/definitions/dify.d.cts +1 -1
  562. package/dist/services/definitions/dify.d.mts +1 -1
  563. package/dist/services/definitions/directus.d.cts +1 -1
  564. package/dist/services/definitions/directus.d.mts +1 -1
  565. package/dist/services/definitions/docsgpt.d.cts +1 -1
  566. package/dist/services/definitions/docsgpt.d.mts +1 -1
  567. package/dist/services/definitions/dokploy.d.cts +1 -1
  568. package/dist/services/definitions/dokploy.d.mts +1 -1
  569. package/dist/services/definitions/dozzle.d.cts +1 -1
  570. package/dist/services/definitions/dozzle.d.mts +1 -1
  571. package/dist/services/definitions/dragonfly.d.cts +1 -1
  572. package/dist/services/definitions/dragonfly.d.mts +1 -1
  573. package/dist/services/definitions/drone-ci.d.cts +1 -1
  574. package/dist/services/definitions/drone-ci.d.mts +1 -1
  575. package/dist/services/definitions/duplicati.d.cts +1 -1
  576. package/dist/services/definitions/duplicati.d.mts +1 -1
  577. package/dist/services/definitions/element-web.d.cts +1 -1
  578. package/dist/services/definitions/element-web.d.mts +1 -1
  579. package/dist/services/definitions/excalidraw.d.cts +1 -1
  580. package/dist/services/definitions/excalidraw.d.mts +1 -1
  581. package/dist/services/definitions/ffmpeg.d.cts +1 -1
  582. package/dist/services/definitions/ffmpeg.d.mts +1 -1
  583. package/dist/services/definitions/firecrawl-playwright.d.cts +1 -1
  584. package/dist/services/definitions/firecrawl-playwright.d.mts +1 -1
  585. package/dist/services/definitions/firecrawl.d.cts +1 -1
  586. package/dist/services/definitions/firecrawl.d.mts +1 -1
  587. package/dist/services/definitions/flagsmith.d.cts +1 -1
  588. package/dist/services/definitions/flagsmith.d.mts +1 -1
  589. package/dist/services/definitions/flowise.d.cts +1 -1
  590. package/dist/services/definitions/flowise.d.mts +1 -1
  591. package/dist/services/definitions/focalboard.d.cts +1 -1
  592. package/dist/services/definitions/focalboard.d.mts +1 -1
  593. package/dist/services/definitions/fonoster.d.cts +1 -1
  594. package/dist/services/definitions/fonoster.d.mts +1 -1
  595. package/dist/services/definitions/forgejo.d.cts +1 -1
  596. package/dist/services/definitions/forgejo.d.mts +1 -1
  597. package/dist/services/definitions/formbricks.d.cts +1 -1
  598. package/dist/services/definitions/formbricks.d.mts +1 -1
  599. package/dist/services/definitions/gemini-cli.d.cts +1 -1
  600. package/dist/services/definitions/gemini-cli.d.mts +1 -1
  601. package/dist/services/definitions/ghost.d.cts +1 -1
  602. package/dist/services/definitions/ghost.d.mts +1 -1
  603. package/dist/services/definitions/gitea.d.cts +1 -1
  604. package/dist/services/definitions/gitea.d.mts +1 -1
  605. package/dist/services/definitions/gotify.d.cts +1 -1
  606. package/dist/services/definitions/gotify.d.mts +1 -1
  607. package/dist/services/definitions/grafana.d.cts +1 -1
  608. package/dist/services/definitions/grafana.d.mts +1 -1
  609. package/dist/services/definitions/graylog.d.cts +1 -1
  610. package/dist/services/definitions/graylog.d.mts +1 -1
  611. package/dist/services/definitions/headscale.d.cts +1 -1
  612. package/dist/services/definitions/headscale.d.mts +1 -1
  613. package/dist/services/definitions/hedgedoc.d.cts +1 -1
  614. package/dist/services/definitions/hedgedoc.d.mts +1 -1
  615. package/dist/services/definitions/hexstrike.d.cts +1 -1
  616. package/dist/services/definitions/hexstrike.d.mts +1 -1
  617. package/dist/services/definitions/heyform.d.cts +1 -1
  618. package/dist/services/definitions/heyform.d.mts +1 -1
  619. package/dist/services/definitions/hindsight.cjs +4 -1
  620. package/dist/services/definitions/hindsight.cjs.map +1 -1
  621. package/dist/services/definitions/hindsight.d.cts +1 -1
  622. package/dist/services/definitions/hindsight.d.mts +1 -1
  623. package/dist/services/definitions/hindsight.mjs +4 -1
  624. package/dist/services/definitions/hindsight.mjs.map +1 -1
  625. package/dist/services/definitions/homeassistant.d.cts +1 -1
  626. package/dist/services/definitions/homeassistant.d.mts +1 -1
  627. package/dist/services/definitions/hoppscotch.d.cts +1 -1
  628. package/dist/services/definitions/hoppscotch.d.mts +1 -1
  629. package/dist/services/definitions/immich.d.cts +1 -1
  630. package/dist/services/definitions/immich.d.mts +1 -1
  631. package/dist/services/definitions/index.cjs +42 -6
  632. package/dist/services/definitions/index.cjs.map +1 -1
  633. package/dist/services/definitions/index.d.cts +16 -4
  634. package/dist/services/definitions/index.d.cts.map +1 -1
  635. package/dist/services/definitions/index.d.mts +16 -4
  636. package/dist/services/definitions/index.d.mts.map +1 -1
  637. package/dist/services/definitions/index.mjs +31 -7
  638. package/dist/services/definitions/index.mjs.map +1 -1
  639. package/dist/services/definitions/infisical.d.cts +1 -1
  640. package/dist/services/definitions/infisical.d.mts +1 -1
  641. package/dist/services/definitions/influxdb.d.cts +1 -1
  642. package/dist/services/definitions/influxdb.d.mts +1 -1
  643. package/dist/services/definitions/invoke-ai.d.cts +1 -1
  644. package/dist/services/definitions/invoke-ai.d.mts +1 -1
  645. package/dist/services/definitions/ixartz-saas.d.cts +1 -1
  646. package/dist/services/definitions/ixartz-saas.d.mts +1 -1
  647. package/dist/services/definitions/jaeger.d.cts +1 -1
  648. package/dist/services/definitions/jaeger.d.mts +1 -1
  649. package/dist/services/definitions/jan.d.cts +1 -1
  650. package/dist/services/definitions/jan.d.mts +1 -1
  651. package/dist/services/definitions/jellyfin.d.cts +1 -1
  652. package/dist/services/definitions/jellyfin.d.mts +1 -1
  653. package/dist/services/definitions/jenkins.d.cts +1 -1
  654. package/dist/services/definitions/jenkins.d.mts +1 -1
  655. package/dist/services/definitions/jitsi-meet.d.cts +1 -1
  656. package/dist/services/definitions/jitsi-meet.d.mts +1 -1
  657. package/dist/services/definitions/keycloak.d.cts +1 -1
  658. package/dist/services/definitions/keycloak.d.mts +1 -1
  659. package/dist/services/definitions/kimi.d.cts +1 -1
  660. package/dist/services/definitions/kimi.d.mts +1 -1
  661. package/dist/services/definitions/kong.d.cts +1 -1
  662. package/dist/services/definitions/kong.d.mts +1 -1
  663. package/dist/services/definitions/lago.d.cts +1 -1
  664. package/dist/services/definitions/lago.d.mts +1 -1
  665. package/dist/services/definitions/langflow.d.cts +1 -1
  666. package/dist/services/definitions/langflow.d.mts +1 -1
  667. package/dist/services/definitions/langfuse.d.cts +1 -1
  668. package/dist/services/definitions/langfuse.d.mts +1 -1
  669. package/dist/services/definitions/lasuite-meet-agents.d.cts +1 -1
  670. package/dist/services/definitions/lasuite-meet-agents.d.mts +1 -1
  671. package/dist/services/definitions/lasuite-meet-backend.d.cts +1 -1
  672. package/dist/services/definitions/lasuite-meet-backend.d.mts +1 -1
  673. package/dist/services/definitions/lasuite-meet-frontend.d.cts +1 -1
  674. package/dist/services/definitions/lasuite-meet-frontend.d.mts +1 -1
  675. package/dist/services/definitions/librechat.d.cts +1 -1
  676. package/dist/services/definitions/librechat.d.mts +1 -1
  677. package/dist/services/definitions/lightpanda.d.cts +1 -1
  678. package/dist/services/definitions/lightpanda.d.mts +1 -1
  679. package/dist/services/definitions/listmonk.d.cts +1 -1
  680. package/dist/services/definitions/listmonk.d.mts +1 -1
  681. package/dist/services/definitions/litellm.d.cts +1 -1
  682. package/dist/services/definitions/litellm.d.mts +1 -1
  683. package/dist/services/definitions/livekit.d.cts +1 -1
  684. package/dist/services/definitions/livekit.d.mts +1 -1
  685. package/dist/services/definitions/local-ai.d.cts +1 -1
  686. package/dist/services/definitions/local-ai.d.mts +1 -1
  687. package/dist/services/definitions/loki.d.cts +1 -1
  688. package/dist/services/definitions/loki.d.mts +1 -1
  689. package/dist/services/definitions/mariadb.d.cts +1 -1
  690. package/dist/services/definitions/mariadb.d.mts +1 -1
  691. package/dist/services/definitions/matomo.d.cts +1 -1
  692. package/dist/services/definitions/matomo.d.mts +1 -1
  693. package/dist/services/definitions/matrix-synapse.d.cts +1 -1
  694. package/dist/services/definitions/matrix-synapse.d.mts +1 -1
  695. package/dist/services/definitions/mattermost.d.cts +1 -1
  696. package/dist/services/definitions/mattermost.d.mts +1 -1
  697. package/dist/services/definitions/mautic.d.cts +1 -1
  698. package/dist/services/definitions/mautic.d.mts +1 -1
  699. package/dist/services/definitions/medusa.d.cts +1 -1
  700. package/dist/services/definitions/medusa.d.mts +1 -1
  701. package/dist/services/definitions/meilisearch.d.cts +1 -1
  702. package/dist/services/definitions/meilisearch.d.mts +1 -1
  703. package/dist/services/definitions/mem0.cjs +133 -0
  704. package/dist/services/definitions/mem0.cjs.map +1 -0
  705. package/dist/services/definitions/mem0.d.cts +7 -0
  706. package/dist/services/definitions/mem0.d.cts.map +1 -0
  707. package/dist/services/definitions/mem0.d.mts +7 -0
  708. package/dist/services/definitions/mem0.d.mts.map +1 -0
  709. package/dist/services/definitions/mem0.mjs +132 -0
  710. package/dist/services/definitions/mem0.mjs.map +1 -0
  711. package/dist/services/definitions/memu.cjs +96 -0
  712. package/dist/services/definitions/memu.cjs.map +1 -0
  713. package/dist/services/definitions/memu.d.cts +7 -0
  714. package/dist/services/definitions/memu.d.cts.map +1 -0
  715. package/dist/services/definitions/memu.d.mts +7 -0
  716. package/dist/services/definitions/memu.d.mts.map +1 -0
  717. package/dist/services/definitions/memu.mjs +95 -0
  718. package/dist/services/definitions/memu.mjs.map +1 -0
  719. package/dist/services/definitions/metabase.d.cts +1 -1
  720. package/dist/services/definitions/metabase.d.mts +1 -1
  721. package/dist/services/definitions/milvus.cjs +4 -1
  722. package/dist/services/definitions/milvus.cjs.map +1 -1
  723. package/dist/services/definitions/milvus.d.cts +1 -1
  724. package/dist/services/definitions/milvus.d.mts +1 -1
  725. package/dist/services/definitions/milvus.mjs +4 -1
  726. package/dist/services/definitions/milvus.mjs.map +1 -1
  727. package/dist/services/definitions/minio.d.cts +1 -1
  728. package/dist/services/definitions/minio.d.mts +1 -1
  729. package/dist/services/definitions/mission-control.d.cts +1 -1
  730. package/dist/services/definitions/mission-control.d.mts +1 -1
  731. package/dist/services/definitions/mixpost.d.cts +1 -1
  732. package/dist/services/definitions/mixpost.d.mts +1 -1
  733. package/dist/services/definitions/mosquitto.d.cts +1 -1
  734. package/dist/services/definitions/mosquitto.d.mts +1 -1
  735. package/dist/services/definitions/motion-canvas.d.cts +1 -1
  736. package/dist/services/definitions/motion-canvas.d.mts +1 -1
  737. package/dist/services/definitions/mysql.d.cts +1 -1
  738. package/dist/services/definitions/mysql.d.mts +1 -1
  739. package/dist/services/definitions/n8n.d.cts +1 -1
  740. package/dist/services/definitions/n8n.d.mts +1 -1
  741. package/dist/services/definitions/nanobot.cjs +86 -0
  742. package/dist/services/definitions/nanobot.cjs.map +1 -0
  743. package/dist/services/definitions/nanobot.d.cts +7 -0
  744. package/dist/services/definitions/nanobot.d.cts.map +1 -0
  745. package/dist/services/definitions/nanobot.d.mts +7 -0
  746. package/dist/services/definitions/nanobot.d.mts.map +1 -0
  747. package/dist/services/definitions/nanobot.mjs +85 -0
  748. package/dist/services/definitions/nanobot.mjs.map +1 -0
  749. package/dist/services/definitions/nanoclaw.cjs +79 -0
  750. package/dist/services/definitions/nanoclaw.cjs.map +1 -0
  751. package/dist/services/definitions/nanoclaw.d.cts +7 -0
  752. package/dist/services/definitions/nanoclaw.d.cts.map +1 -0
  753. package/dist/services/definitions/nanoclaw.d.mts +7 -0
  754. package/dist/services/definitions/nanoclaw.d.mts.map +1 -0
  755. package/dist/services/definitions/nanoclaw.mjs +78 -0
  756. package/dist/services/definitions/nanoclaw.mjs.map +1 -0
  757. package/dist/services/definitions/navidrome.d.cts +1 -1
  758. package/dist/services/definitions/navidrome.d.mts +1 -1
  759. package/dist/services/definitions/neo4j.d.cts +1 -1
  760. package/dist/services/definitions/neo4j.d.mts +1 -1
  761. package/dist/services/definitions/nextcloud.d.cts +1 -1
  762. package/dist/services/definitions/nextcloud.d.mts +1 -1
  763. package/dist/services/definitions/nginx-proxy-manager.d.cts +1 -1
  764. package/dist/services/definitions/nginx-proxy-manager.d.mts +1 -1
  765. package/dist/services/definitions/nocodb.d.cts +1 -1
  766. package/dist/services/definitions/nocodb.d.mts +1 -1
  767. package/dist/services/definitions/node-red.d.cts +1 -1
  768. package/dist/services/definitions/node-red.d.mts +1 -1
  769. package/dist/services/definitions/ntfy.d.cts +1 -1
  770. package/dist/services/definitions/ntfy.d.mts +1 -1
  771. package/dist/services/definitions/ollama.d.cts +1 -1
  772. package/dist/services/definitions/ollama.d.mts +1 -1
  773. package/dist/services/definitions/open-saas.d.cts +1 -1
  774. package/dist/services/definitions/open-saas.d.mts +1 -1
  775. package/dist/services/definitions/open-webui.d.cts +1 -1
  776. package/dist/services/definitions/open-webui.d.mts +1 -1
  777. package/dist/services/definitions/opencode.d.cts +1 -1
  778. package/dist/services/definitions/opencode.d.mts +1 -1
  779. package/dist/services/definitions/openhands.d.cts +1 -1
  780. package/dist/services/definitions/openhands.d.mts +1 -1
  781. package/dist/services/definitions/openpanel.d.cts +1 -1
  782. package/dist/services/definitions/openpanel.d.mts +1 -1
  783. package/dist/services/definitions/opensandbox.cjs +2 -1
  784. package/dist/services/definitions/opensandbox.cjs.map +1 -1
  785. package/dist/services/definitions/opensandbox.d.cts +1 -1
  786. package/dist/services/definitions/opensandbox.d.mts +1 -1
  787. package/dist/services/definitions/opensandbox.mjs +2 -1
  788. package/dist/services/definitions/opensandbox.mjs.map +1 -1
  789. package/dist/services/definitions/opensearch.d.cts +1 -1
  790. package/dist/services/definitions/opensearch.d.mts +1 -1
  791. package/dist/services/definitions/outline.d.cts +1 -1
  792. package/dist/services/definitions/outline.d.mts +1 -1
  793. package/dist/services/definitions/paperless-ngx.d.cts +1 -1
  794. package/dist/services/definitions/paperless-ngx.d.mts +1 -1
  795. package/dist/services/definitions/pentagi.d.cts +1 -1
  796. package/dist/services/definitions/pentagi.d.mts +1 -1
  797. package/dist/services/definitions/pentestagent.d.cts +1 -1
  798. package/dist/services/definitions/pentestagent.d.mts +1 -1
  799. package/dist/services/definitions/photoprism.d.cts +1 -1
  800. package/dist/services/definitions/photoprism.d.mts +1 -1
  801. package/dist/services/definitions/pihole.d.cts +1 -1
  802. package/dist/services/definitions/pihole.d.mts +1 -1
  803. package/dist/services/definitions/piper-tts.d.cts +1 -1
  804. package/dist/services/definitions/piper-tts.d.mts +1 -1
  805. package/dist/services/definitions/plane.d.cts +1 -1
  806. package/dist/services/definitions/plane.d.mts +1 -1
  807. package/dist/services/definitions/plausible.d.cts +1 -1
  808. package/dist/services/definitions/plausible.d.mts +1 -1
  809. package/dist/services/definitions/playwright-server.d.cts +1 -1
  810. package/dist/services/definitions/playwright-server.d.mts +1 -1
  811. package/dist/services/definitions/pocket-id.d.cts +1 -1
  812. package/dist/services/definitions/pocket-id.d.mts +1 -1
  813. package/dist/services/definitions/pocketbase.d.cts +1 -1
  814. package/dist/services/definitions/pocketbase.d.mts +1 -1
  815. package/dist/services/definitions/portainer.d.cts +1 -1
  816. package/dist/services/definitions/portainer.d.mts +1 -1
  817. package/dist/services/definitions/postgresql.d.cts +1 -1
  818. package/dist/services/definitions/postgresql.d.mts +1 -1
  819. package/dist/services/definitions/posthog.d.cts +1 -1
  820. package/dist/services/definitions/posthog.d.mts +1 -1
  821. package/dist/services/definitions/postiz.d.cts +1 -1
  822. package/dist/services/definitions/postiz.d.mts +1 -1
  823. package/dist/services/definitions/prometheus.d.cts +1 -1
  824. package/dist/services/definitions/prometheus.d.mts +1 -1
  825. package/dist/services/definitions/qdrant.d.cts +1 -1
  826. package/dist/services/definitions/qdrant.d.mts +1 -1
  827. package/dist/services/definitions/rabbitmq.d.cts +1 -1
  828. package/dist/services/definitions/rabbitmq.d.mts +1 -1
  829. package/dist/services/definitions/ragflow.d.cts +1 -1
  830. package/dist/services/definitions/ragflow.d.mts +1 -1
  831. package/dist/services/definitions/redis.d.cts +1 -1
  832. package/dist/services/definitions/redis.d.mts +1 -1
  833. package/dist/services/definitions/relaticle-horizon.cjs +48 -0
  834. package/dist/services/definitions/relaticle-horizon.cjs.map +1 -0
  835. package/dist/services/definitions/relaticle-horizon.d.cts +7 -0
  836. package/dist/services/definitions/relaticle-horizon.d.cts.map +1 -0
  837. package/dist/services/definitions/relaticle-horizon.d.mts +7 -0
  838. package/dist/services/definitions/relaticle-horizon.d.mts.map +1 -0
  839. package/dist/services/definitions/relaticle-horizon.mjs +48 -0
  840. package/dist/services/definitions/relaticle-horizon.mjs.map +1 -0
  841. package/dist/services/definitions/relaticle-scheduler.cjs +44 -0
  842. package/dist/services/definitions/relaticle-scheduler.cjs.map +1 -0
  843. package/dist/services/definitions/relaticle-scheduler.d.cts +7 -0
  844. package/dist/services/definitions/relaticle-scheduler.d.cts.map +1 -0
  845. package/dist/services/definitions/relaticle-scheduler.d.mts +7 -0
  846. package/dist/services/definitions/relaticle-scheduler.d.mts.map +1 -0
  847. package/dist/services/definitions/relaticle-scheduler.mjs +44 -0
  848. package/dist/services/definitions/relaticle-scheduler.mjs.map +1 -0
  849. package/dist/services/definitions/relaticle.cjs +151 -0
  850. package/dist/services/definitions/relaticle.cjs.map +1 -0
  851. package/dist/services/definitions/relaticle.d.cts +80 -0
  852. package/dist/services/definitions/relaticle.d.cts.map +1 -0
  853. package/dist/services/definitions/relaticle.d.mts +80 -0
  854. package/dist/services/definitions/relaticle.d.mts.map +1 -0
  855. package/dist/services/definitions/relaticle.mjs +149 -0
  856. package/dist/services/definitions/relaticle.mjs.map +1 -0
  857. package/dist/services/definitions/remotion.d.cts +1 -1
  858. package/dist/services/definitions/remotion.d.mts +1 -1
  859. package/dist/services/definitions/restic.d.cts +1 -1
  860. package/dist/services/definitions/restic.d.mts +1 -1
  861. package/dist/services/definitions/revolt.d.cts +1 -1
  862. package/dist/services/definitions/revolt.d.mts +1 -1
  863. package/dist/services/definitions/rocketchat.d.cts +1 -1
  864. package/dist/services/definitions/rocketchat.d.mts +1 -1
  865. package/dist/services/definitions/saleor.d.cts +1 -1
  866. package/dist/services/definitions/saleor.d.mts +1 -1
  867. package/dist/services/definitions/scrapling.d.cts +1 -1
  868. package/dist/services/definitions/scrapling.d.mts +1 -1
  869. package/dist/services/definitions/searxng.d.cts +1 -1
  870. package/dist/services/definitions/searxng.d.mts +1 -1
  871. package/dist/services/definitions/sentry.d.cts +1 -1
  872. package/dist/services/definitions/sentry.d.mts +1 -1
  873. package/dist/services/definitions/signoz.d.cts +1 -1
  874. package/dist/services/definitions/signoz.d.mts +1 -1
  875. package/dist/services/definitions/solidityguard.d.cts +1 -1
  876. package/dist/services/definitions/solidityguard.d.mts +1 -1
  877. package/dist/services/definitions/sonarqube.d.cts +1 -1
  878. package/dist/services/definitions/sonarqube.d.mts +1 -1
  879. package/dist/services/definitions/stable-diffusion.d.cts +1 -1
  880. package/dist/services/definitions/stable-diffusion.d.mts +1 -1
  881. package/dist/services/definitions/steel-browser.d.cts +1 -1
  882. package/dist/services/definitions/steel-browser.d.mts +1 -1
  883. package/dist/services/definitions/stirling-pdf.d.cts +1 -1
  884. package/dist/services/definitions/stirling-pdf.d.mts +1 -1
  885. package/dist/services/definitions/strapi.d.cts +1 -1
  886. package/dist/services/definitions/strapi.d.mts +1 -1
  887. package/dist/services/definitions/stream-gateway.d.cts +1 -1
  888. package/dist/services/definitions/stream-gateway.d.mts +1 -1
  889. package/dist/services/definitions/supabase.d.cts +1 -1
  890. package/dist/services/definitions/supabase.d.mts +1 -1
  891. package/dist/services/definitions/superset.d.cts +1 -1
  892. package/dist/services/definitions/superset.d.mts +1 -1
  893. package/dist/services/definitions/surrealdb.d.cts +1 -1
  894. package/dist/services/definitions/surrealdb.d.mts +1 -1
  895. package/dist/services/definitions/tabby-ml.d.cts +1 -1
  896. package/dist/services/definitions/tabby-ml.d.mts +1 -1
  897. package/dist/services/definitions/tailscale.d.cts +1 -1
  898. package/dist/services/definitions/tailscale.d.mts +1 -1
  899. package/dist/services/definitions/tempo.d.cts +1 -1
  900. package/dist/services/definitions/tempo.d.mts +1 -1
  901. package/dist/services/definitions/temporal.d.cts +1 -1
  902. package/dist/services/definitions/temporal.d.mts +1 -1
  903. package/dist/services/definitions/text-gen-webui.d.cts +1 -1
  904. package/dist/services/definitions/text-gen-webui.d.mts +1 -1
  905. package/dist/services/definitions/timescaledb.d.cts +1 -1
  906. package/dist/services/definitions/timescaledb.d.mts +1 -1
  907. package/dist/services/definitions/traefik.d.cts +1 -1
  908. package/dist/services/definitions/traefik.d.mts +1 -1
  909. package/dist/services/definitions/twenty-worker.cjs +73 -0
  910. package/dist/services/definitions/twenty-worker.cjs.map +1 -0
  911. package/dist/services/definitions/twenty-worker.d.cts +7 -0
  912. package/dist/services/definitions/twenty-worker.d.cts.map +1 -0
  913. package/dist/services/definitions/twenty-worker.d.mts +7 -0
  914. package/dist/services/definitions/twenty-worker.d.mts.map +1 -0
  915. package/dist/services/definitions/twenty-worker.mjs +72 -0
  916. package/dist/services/definitions/twenty-worker.mjs.map +1 -0
  917. package/dist/services/definitions/twenty.cjs +24 -2
  918. package/dist/services/definitions/twenty.cjs.map +1 -1
  919. package/dist/services/definitions/twenty.d.cts +1 -1
  920. package/dist/services/definitions/twenty.d.mts +1 -1
  921. package/dist/services/definitions/twenty.mjs +24 -2
  922. package/dist/services/definitions/twenty.mjs.map +1 -1
  923. package/dist/services/definitions/umami.d.cts +1 -1
  924. package/dist/services/definitions/umami.d.mts +1 -1
  925. package/dist/services/definitions/uptime-kuma.d.cts +1 -1
  926. package/dist/services/definitions/uptime-kuma.d.mts +1 -1
  927. package/dist/services/definitions/usesend.d.cts +1 -1
  928. package/dist/services/definitions/usesend.d.mts +1 -1
  929. package/dist/services/definitions/valkey.d.cts +1 -1
  930. package/dist/services/definitions/valkey.d.mts +1 -1
  931. package/dist/services/definitions/vault.d.cts +1 -1
  932. package/dist/services/definitions/vault.d.mts +1 -1
  933. package/dist/services/definitions/vaultwarden.d.cts +1 -1
  934. package/dist/services/definitions/vaultwarden.d.mts +1 -1
  935. package/dist/services/definitions/vector-log.d.cts +1 -1
  936. package/dist/services/definitions/vector-log.d.mts +1 -1
  937. package/dist/services/definitions/vikunja.d.cts +1 -1
  938. package/dist/services/definitions/vikunja.d.mts +1 -1
  939. package/dist/services/definitions/watchtower.d.cts +1 -1
  940. package/dist/services/definitions/watchtower.d.mts +1 -1
  941. package/dist/services/definitions/weaviate.cjs +4 -1
  942. package/dist/services/definitions/weaviate.cjs.map +1 -1
  943. package/dist/services/definitions/weaviate.d.cts +1 -1
  944. package/dist/services/definitions/weaviate.d.mts +1 -1
  945. package/dist/services/definitions/weaviate.mjs +4 -1
  946. package/dist/services/definitions/weaviate.mjs.map +1 -1
  947. package/dist/services/definitions/whisper.d.cts +1 -1
  948. package/dist/services/definitions/whisper.d.mts +1 -1
  949. package/dist/services/definitions/wireguard.d.cts +1 -1
  950. package/dist/services/definitions/wireguard.d.mts +1 -1
  951. package/dist/services/definitions/woodpecker-ci.d.cts +1 -1
  952. package/dist/services/definitions/woodpecker-ci.d.mts +1 -1
  953. package/dist/services/definitions/xyops.d.cts +1 -1
  954. package/dist/services/definitions/xyops.d.mts +1 -1
  955. package/dist/services/definitions/zeroclaw.cjs +103 -0
  956. package/dist/services/definitions/zeroclaw.cjs.map +1 -0
  957. package/dist/services/definitions/zeroclaw.d.cts +7 -0
  958. package/dist/services/definitions/zeroclaw.d.cts.map +1 -0
  959. package/dist/services/definitions/zeroclaw.d.mts +7 -0
  960. package/dist/services/definitions/zeroclaw.d.mts.map +1 -0
  961. package/dist/services/definitions/zeroclaw.mjs +102 -0
  962. package/dist/services/definitions/zeroclaw.mjs.map +1 -0
  963. package/dist/services/definitions/zulip.d.cts +1 -1
  964. package/dist/services/definitions/zulip.d.mts +1 -1
  965. package/dist/services/registry.d.cts +1 -1
  966. package/dist/services/registry.d.mts +1 -1
  967. package/dist/services/registry.test.cjs +8 -8
  968. package/dist/services/registry.test.cjs.map +1 -1
  969. package/dist/services/registry.test.mjs +8 -8
  970. package/dist/services/registry.test.mjs.map +1 -1
  971. package/dist/skills/registry.cjs +19 -0
  972. package/dist/skills/registry.cjs.map +1 -1
  973. package/dist/skills/registry.d.cts +1 -1
  974. package/dist/skills/registry.d.cts.map +1 -1
  975. package/dist/skills/registry.d.mts +1 -1
  976. package/dist/skills/registry.d.mts.map +1 -1
  977. package/dist/skills/registry.mjs +19 -0
  978. package/dist/skills/registry.mjs.map +1 -1
  979. package/dist/{skills-BSF7iNa4.cjs → skills-uPxJVmKk.cjs} +311 -1
  980. package/dist/skills-uPxJVmKk.cjs.map +1 -0
  981. package/dist/{test.CTcmp4Su-ClCHJ3FA.mjs → test.CTcmp4Su-BRa7-bTj.mjs} +2 -2
  982. package/dist/{test.CTcmp4Su-ClCHJ3FA.mjs.map → test.CTcmp4Su-BRa7-bTj.mjs.map} +1 -1
  983. package/dist/{test.CTcmp4Su-DlzTarwH.cjs → test.CTcmp4Su-BWSPM8ZQ.cjs} +19 -1
  984. package/dist/{test.CTcmp4Su-DlzTarwH.cjs.map → test.CTcmp4Su-BWSPM8ZQ.cjs.map} +1 -1
  985. package/dist/track-analytics.d.cts +1 -1
  986. package/dist/track-analytics.d.mts +1 -1
  987. package/dist/track-analytics.test.cjs +1 -1
  988. package/dist/track-analytics.test.mjs +1 -1
  989. package/dist/types-BREUfzzq.d.mts +77 -0
  990. package/dist/types-BREUfzzq.d.mts.map +1 -0
  991. package/dist/types-Bsn0XzSP.d.cts +95 -0
  992. package/dist/types-Bsn0XzSP.d.cts.map +1 -0
  993. package/dist/types-CyZ5mn6w.d.cts +77 -0
  994. package/dist/types-CyZ5mn6w.d.cts.map +1 -0
  995. package/dist/types-DIsPc-hb.d.cts +100 -0
  996. package/dist/types-DIsPc-hb.d.cts.map +1 -0
  997. package/dist/types-DbXajvYq.d.mts +100 -0
  998. package/dist/types-DbXajvYq.d.mts.map +1 -0
  999. package/dist/types-DwTKyCZ9.d.mts +95 -0
  1000. package/dist/types-DwTKyCZ9.d.mts.map +1 -0
  1001. package/dist/{types-CR83OJiq.d.cts → types-dyBnrHm9.d.cts} +5 -1
  1002. package/dist/types-dyBnrHm9.d.cts.map +1 -0
  1003. package/dist/{types-zYjGTuyn.d.mts → types-fRSnaZTX.d.mts} +5 -1
  1004. package/dist/types-fRSnaZTX.d.mts.map +1 -0
  1005. package/dist/types.cjs +7 -0
  1006. package/dist/types.cjs.map +1 -1
  1007. package/dist/types.d.cts +2 -93
  1008. package/dist/types.d.mts +2 -93
  1009. package/dist/types.mjs +7 -0
  1010. package/dist/types.mjs.map +1 -1
  1011. package/dist/validator.cjs +1 -1
  1012. package/dist/validator.d.cts +1 -1
  1013. package/dist/validator.d.mts +1 -1
  1014. package/dist/validator.test.cjs +1 -1
  1015. package/dist/validator.test.mjs +1 -1
  1016. package/dist/version-manager.d.cts +1 -1
  1017. package/dist/version-manager.d.mts +1 -1
  1018. package/dist/version-manager.test.cjs +3 -3
  1019. package/dist/version-manager.test.cjs.map +1 -1
  1020. package/dist/version-manager.test.mjs +3 -3
  1021. package/dist/version-manager.test.mjs.map +1 -1
  1022. package/dist/zeroclaw-B-0TuOfZ.d.mts +7 -0
  1023. package/dist/zeroclaw-B-0TuOfZ.d.mts.map +1 -0
  1024. package/dist/zeroclaw-BvKpDHKe.d.cts +7 -0
  1025. package/dist/zeroclaw-BvKpDHKe.d.cts.map +1 -0
  1026. package/package.json +6 -2
  1027. package/src/addon-stack.test.ts +19 -23
  1028. package/src/addon-stack.ts +67 -44
  1029. package/src/composer.test.ts +59 -0
  1030. package/src/composer.ts +135 -194
  1031. package/src/deployers/coolify.test.ts +180 -0
  1032. package/src/deployers/coolify.ts +38 -0
  1033. package/src/deployers/dokploy.test.ts +120 -0
  1034. package/src/deployers/dokploy.ts +38 -0
  1035. package/src/deployers/types.ts +2 -0
  1036. package/src/frameworks/claude-code-fw.ts +118 -0
  1037. package/src/frameworks/codex-fw.ts +102 -0
  1038. package/src/frameworks/copaw.ts +154 -0
  1039. package/src/frameworks/frameworks.test.ts +119 -0
  1040. package/src/frameworks/index.ts +46 -0
  1041. package/src/frameworks/memu.ts +149 -0
  1042. package/src/frameworks/nanobot.ts +156 -0
  1043. package/src/frameworks/nanoclaw.ts +126 -0
  1044. package/src/frameworks/openclaw.ts +325 -0
  1045. package/src/frameworks/registry.test.ts +131 -0
  1046. package/src/frameworks/registry.ts +28 -0
  1047. package/src/frameworks/types.ts +146 -0
  1048. package/src/frameworks/zeroclaw.ts +148 -0
  1049. package/src/generate.test.ts +55 -4
  1050. package/src/generate.ts +67 -14
  1051. package/src/generators/caddy.test.ts +41 -1
  1052. package/src/generators/caddy.ts +16 -2
  1053. package/src/generators/clone-repos.test.ts +1 -1
  1054. package/src/generators/clone-repos.ts +7 -2
  1055. package/src/generators/cloud-init.test.ts +70 -0
  1056. package/src/generators/env.test.ts +2 -2
  1057. package/src/generators/env.ts +30 -180
  1058. package/src/generators/get-shit-done.test.ts +54 -0
  1059. package/src/generators/grafana.test.ts +90 -0
  1060. package/src/generators/health-check.ts +4 -4
  1061. package/src/generators/n8n-workflows.test.ts +80 -0
  1062. package/src/generators/openclaw-install-script.test.ts +42 -0
  1063. package/src/generators/openclaw-json.ts +57 -5
  1064. package/src/generators/postgres-init.test.ts +116 -0
  1065. package/src/generators/postgres-init.ts +9 -0
  1066. package/src/generators/prometheus.test.ts +108 -0
  1067. package/src/generators/readme.ts +75 -9
  1068. package/src/generators/scripts.test.ts +1 -1
  1069. package/src/generators/skills.ts +316 -0
  1070. package/src/generators/stack-manifest.test.ts +104 -0
  1071. package/src/generators/traefik.test.ts +13 -13
  1072. package/src/index.ts +47 -7
  1073. package/src/logger/__tests__/logger.test.ts +388 -0
  1074. package/src/logger/index.ts +15 -0
  1075. package/src/logger/logger.ts +326 -0
  1076. package/src/logger/sinks/callback-sink.ts +13 -0
  1077. package/src/logger/sinks/console-sink.ts +51 -0
  1078. package/src/logger/sinks/file-sink.ts +121 -0
  1079. package/src/logger/types.ts +90 -0
  1080. package/src/migrations.ts +8 -1
  1081. package/src/port-scanner.test.ts +167 -0
  1082. package/src/presets/presets.test.ts +4 -4
  1083. package/src/presets/registry.test.ts +6 -6
  1084. package/src/resolver.test.ts +31 -3
  1085. package/src/resolver.ts +19 -3
  1086. package/src/schema.test.ts +2 -2
  1087. package/src/schema.ts +58 -17
  1088. package/src/services/definitions/agent-browser.ts +177 -0
  1089. package/src/services/definitions/apptension-saas.ts +2 -1
  1090. package/src/services/definitions/chromadb.ts +1 -1
  1091. package/src/services/definitions/claude-code.ts +1 -1
  1092. package/src/services/definitions/clawrouter.ts +141 -0
  1093. package/src/services/definitions/codex.ts +1 -1
  1094. package/src/services/definitions/convex.ts +1 -2
  1095. package/src/services/definitions/copaw.ts +101 -0
  1096. package/src/services/definitions/desktop-environment.ts +1 -0
  1097. package/src/services/definitions/hindsight.ts +3 -2
  1098. package/src/services/definitions/index.ts +53 -16
  1099. package/src/services/definitions/mem0.ts +132 -0
  1100. package/src/services/definitions/memu.ts +96 -0
  1101. package/src/services/definitions/milvus.ts +1 -1
  1102. package/src/services/definitions/nanobot.ts +89 -0
  1103. package/src/services/definitions/nanoclaw.ts +78 -0
  1104. package/src/services/definitions/opensandbox.ts +6 -15
  1105. package/src/services/definitions/relaticle-horizon.ts +48 -0
  1106. package/src/services/definitions/relaticle-scheduler.ts +42 -0
  1107. package/src/services/definitions/relaticle.ts +142 -0
  1108. package/src/services/definitions/twenty-worker.ts +70 -0
  1109. package/src/services/definitions/twenty.ts +16 -2
  1110. package/src/services/definitions/weaviate.ts +1 -1
  1111. package/src/services/definitions/zeroclaw.ts +102 -0
  1112. package/src/services/registry.test.ts +7 -7
  1113. package/src/skills/registry.ts +10 -0
  1114. package/src/types.ts +11 -1
  1115. package/src/version-manager.test.ts +2 -2
  1116. package/dist/schema-BQnZrcw8.d.cts.map +0 -1
  1117. package/dist/schema-SBpL0bdI.d.mts.map +0 -1
  1118. package/dist/skills-BSF7iNa4.cjs.map +0 -1
  1119. package/dist/types-CR83OJiq.d.cts.map +0 -1
  1120. package/dist/types-zYjGTuyn.d.mts.map +0 -1
  1121. package/dist/types.d.cts.map +0 -1
  1122. package/dist/types.d.mts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"addon-stack.test.cjs","names":["describe","generateAddonStack","expect","updateAddonStack","AddonStackInputSchema"],"sources":["../src/addon-stack.test.ts"],"sourcesContent":["import { parse } from \"yaml\";\nimport { describe, expect, it } from \"vitest\";\nimport { generateAddonStack, updateAddonStack } from \"./addon-stack.js\";\nimport { AddonStackInputSchema } from \"./schema.js\";\n\ndescribe(\"generateAddonStack\", () => {\n\tit(\"generates valid compose YAML with a single service (qdrant)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Resolver may generate warnings about recommended services; that's fine\n\t\texpect(result.metadata.serviceCount).toBeGreaterThan(0);\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\n\t\t// Parse YAML to verify it's valid\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\n\t\t// Should NOT contain infrastructure services\n\t\texpect(composed.services).not.toHaveProperty(\"openclaw-gateway\");\n\t\texpect(composed.services).not.toHaveProperty(\"openclaw-cli\");\n\t\texpect(composed.services).not.toHaveProperty(\"redis\");\n\t\texpect(composed.services).not.toHaveProperty(\"postgresql\");\n\t\texpect(composed.services).not.toHaveProperty(\"open-webui\");\n\t\texpect(composed.services).not.toHaveProperty(\"caddy\");\n\n\t\t// Should have openclaw-network as external\n\t\texpect(composed.networks).toHaveProperty(\"openclaw-network\");\n\t\texpect(composed.networks[\"openclaw-network\"].external).toBe(true);\n\t});\n\n\tit(\"does not include profiles on any service\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"meilisearch\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\tfor (const [, svc] of Object.entries(composed.services)) {\n\t\t\texpect(svc).not.toHaveProperty(\"profiles\");\n\t\t}\n\t});\n\n\tit(\"does not apply cap_drop or security_opt by default\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\tconst qdrant = composed.services.qdrant;\n\t\texpect(qdrant).not.toHaveProperty(\"cap_drop\");\n\t\texpect(qdrant).not.toHaveProperty(\"security_opt\");\n\t});\n\n\tit(\"includes postgres-setup when a DB-dependent service is requested (n8n)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"n8n\");\n\t\texpect(composed.services).toHaveProperty(\"postgres-setup\");\n\n\t\t// postgres-setup should depend on existing postgresql\n\t\texpect(composed.services[\"postgres-setup\"].depends_on).toHaveProperty(\"postgresql\");\n\n\t\t// n8n should depend on postgres-setup\n\t\texpect(composed.services.n8n.depends_on).toHaveProperty(\"postgres-setup\");\n\t});\n\n\tit(\"generates DB passwords in env file for DB-dependent services\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\texpect(result.envFile).toContain(\"N8N_DB_PASSWORD=\");\n\t\t// Password should be non-empty (48 hex chars = 24 bytes)\n\t\tconst match = result.envFile.match(/N8N_DB_PASSWORD=([a-f0-9]+)/);\n\t\texpect(match).not.toBeNull();\n\t\texpect(match![1].length).toBe(48);\n\t});\n\n\tit(\"gracefully handles unknown service IDs without throwing\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"nonexistent-service\", \"qdrant\"],\n\t\t});\n\n\t\t// Should NOT throw\n\t\texpect(result.metadata.skippedServices).toEqual(\n\t\t\texpect.arrayContaining([\n\t\t\t\texpect.objectContaining({\n\t\t\t\t\tserviceId: \"nonexistent-service\",\n\t\t\t\t\treason: \"unknown_service\",\n\t\t\t\t}),\n\t\t\t]),\n\t\t);\n\n\t\t// Valid service should still be included\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\t});\n\n\tit(\"excludes infrastructure services from the request\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"redis\", \"postgresql\", \"qdrant\"],\n\t\t});\n\n\t\texpect(result.warnings).toEqual(\n\t\t\texpect.arrayContaining([\n\t\t\t\texpect.stringContaining(\"redis\"),\n\t\t\t\texpect.stringContaining(\"postgresql\"),\n\t\t\t]),\n\t\t);\n\t\texpect(result.metadata.resolvedServices).not.toContain(\"redis\");\n\t\texpect(result.metadata.resolvedServices).not.toContain(\"postgresql\");\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\t});\n\n\tit(\"generates proxy routes from proxyPath\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\tconst n8nRoute = result.proxyRoutes.find((r) => r.serviceId === \"n8n\");\n\t\texpect(n8nRoute).toBeDefined();\n\t\texpect(n8nRoute!.path).toBe(\"/n8n\");\n\t\texpect(n8nRoute!.port).toBe(5678);\n\t});\n\n\tit(\"generates skill files and openclaw config patch\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\t// n8n has skill binding: n8n-trigger\n\t\texpect(result.openclawConfigPatch.skills.entries).toHaveProperty(\"n8n-trigger\");\n\t\texpect(result.openclawConfigPatch.skills.entries[\"n8n-trigger\"].enabled).toBe(true);\n\t});\n\n\tit(\"resolves port conflicts with reserved ports\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\treservedPorts: [5678], // n8n's default port\n\t\t});\n\n\t\t// Port should be reassigned\n\t\tconst portAssignments = result.metadata.portAssignments;\n\t\tconst n8nAssignment = Object.entries(portAssignments).find(([key]) =>\n\t\t\tkey.startsWith(\"n8n:\"),\n\t\t);\n\t\texpect(n8nAssignment).toBeDefined();\n\t\texpect(n8nAssignment![1]).not.toBe(5678);\n\t});\n\n\tit(\"sanitizes project name from instanceId\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"My_Instance_123\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Should succeed without error\n\t\texpect(result.metadata.serviceCount).toBeGreaterThan(0);\n\t});\n\n\tit(\"returns env vars grouped by service\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\texpect(result.envVars.length).toBeGreaterThanOrEqual(0);\n\t});\n\n\tit(\"returns empty result for no services\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [],\n\t\t});\n\n\t\texpect(result.composeOverride).toContain(\"services: {}\");\n\t\texpect(result.warnings.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"returns empty result when all services are infrastructure\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"redis\", \"postgresql\", \"caddy\"],\n\t\t});\n\n\t\texpect(result.metadata.serviceCount).toBe(0);\n\t\texpect(result.warnings.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"applies env quirks to envFile output (meilisearch MEILI_MASTER_KEY min_length)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// MEILI_MASTER_KEY should be a base64url string of at least 32 chars (24 bytes)\n\t\tconst match = result.envFile.match(/MEILI_MASTER_KEY=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\texpect(match![1].length).toBeGreaterThanOrEqual(32);\n\t\t// Should not be empty\n\t\texpect(match![1]).not.toBe(\"\");\n\t});\n\n\tit(\"applies env quirks to envFile output (grafana GF_SECURITY_ADMIN_PASSWORD)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"grafana\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst match = result.envFile.match(/GF_SECURITY_ADMIN_PASSWORD=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\t// Should be at least 22 chars (16 bytes base64url)\n\t\texpect(match![1].length).toBeGreaterThanOrEqual(22);\n\t});\n\n\tit(\"syncs DB_POSTGRESDB_PASSWORD with N8N_DB_PASSWORD via must_sync quirk\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst n8nDbPw = result.envFile.match(/N8N_DB_PASSWORD=([^\\n]+)/);\n\t\tconst dbPostgresPw = result.envFile.match(/DB_POSTGRESDB_PASSWORD=([^\\n]+)/);\n\t\texpect(n8nDbPw).not.toBeNull();\n\t\texpect(dbPostgresPw).not.toBeNull();\n\t\t// Both passwords must match due to must_sync quirk\n\t\texpect(n8nDbPw![1]).toBe(dbPostgresPw![1]);\n\t});\n\n\tit(\"syncs user-provided credential with DB password reference\", () => {\n\t\tconst customPassword = \"my_custom_secure_password\";\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t\tcredentials: {\n\t\t\t\tn8n: { DB_POSTGRESDB_PASSWORD: customPassword },\n\t\t\t},\n\t\t});\n\n\t\t// User value should be used for both the service env var and the ref key\n\t\texpect(result.envFile).toContain(`DB_POSTGRESDB_PASSWORD=${customPassword}`);\n\t\texpect(result.envFile).toContain(`N8N_DB_PASSWORD=${customPassword}`);\n\t});\n\n\tit(\"uses existingServices ports for conflict detection\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\t// n8n uses port 5678, qdrant uses 6333 — qdrant as existing should not conflict\n\t\t\texistingServices: [\"qdrant\"],\n\t\t});\n\n\t\t// n8n should still get its default port (no conflict with qdrant)\n\t\tconst portAssignments = result.metadata.portAssignments;\n\t\tconst n8nAssignment = Object.entries(portAssignments).find(([key]) =>\n\t\t\tkey.startsWith(\"n8n:\"),\n\t\t);\n\t\texpect(n8nAssignment).toBeDefined();\n\t\texpect(n8nAssignment![1]).toBe(5678);\n\t});\n\n\tit(\"does not dual-list GPU services in both skipped and resolved\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"ollama\"],\n\t\t\tgpu: false,\n\t\t});\n\n\t\t// ollama requires GPU — without gpu: true, it should be skipped only\n\t\tconst isSkipped = result.metadata.skippedServices.some(\n\t\t\t(s) => s.serviceId === \"ollama\" && s.reason === \"gpu_required\",\n\t\t);\n\t\tconst isResolved = result.metadata.resolvedServices.includes(\"ollama\");\n\n\t\t// Must be in exactly one list, not both\n\t\tif (isSkipped) {\n\t\t\texpect(isResolved).toBe(false);\n\t\t}\n\t});\n\n\tit(\"includes GPU services when gpu: true is set\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"ollama\"],\n\t\t\tgpu: true,\n\t\t});\n\n\t\t// Should NOT be skipped when GPU support is available\n\t\tconst isSkipped = result.metadata.skippedServices.some(\n\t\t\t(s) => s.serviceId === \"ollama\" && s.reason === \"gpu_required\",\n\t\t);\n\t\texpect(isSkipped).toBe(false);\n\t\texpect(result.metadata.resolvedServices).toContain(\"ollama\");\n\t});\n\n\t// ── OpenSandbox ────────────────────────────────────────────────────────\n\n\tit(\"generates valid compose YAML for opensandbox\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.metadata.resolvedServices).toContain(\"opensandbox\");\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"opensandbox\");\n\n\t\t// Should have Docker socket mount\n\t\tconst volumes = composed.services.opensandbox.volumes as string[];\n\t\texpect(volumes.some((v: string) => v.includes(\"/var/run/docker.sock\"))).toBe(true);\n\n\t\t// Should have config bind mount\n\t\texpect(volumes.some((v: string) => v.includes(\"sandbox.toml\"))).toBe(true);\n\t});\n\n\tit(\"generates OPEN_SANDBOX_API_KEY with min 32 bytes via env quirk\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst match = result.envFile.match(/OPEN_SANDBOX_API_KEY=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\t// 32 bytes base64url ≈ 43 chars\n\t\texpect(match![1].length).toBeGreaterThanOrEqual(43);\n\t});\n\n\tit(\"generates code-sandbox skill file and config patch\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\t// Skill config patch\n\t\texpect(result.openclawConfigPatch.skills.entries).toHaveProperty(\"code-sandbox\");\n\t\texpect(result.openclawConfigPatch.skills.entries[\"code-sandbox\"].enabled).toBe(true);\n\n\t\t// Skill file content should exist and contain key actions\n\t\tconst skillFile = Object.values(result.skillFiles).find((content) =>\n\t\t\tcontent.includes(\"code-sandbox\"),\n\t\t);\n\t\texpect(skillFile).toBeDefined();\n\t\texpect(skillFile).toContain(\"execute_code\");\n\t\texpect(skillFile).toContain(\"create_desktop\");\n\t\texpect(skillFile).toContain(\"get_preview_url\");\n\t});\n\n\tit(\"generates proxy route for opensandbox at /sandbox\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst route = result.proxyRoutes.find((r) => r.serviceId === \"opensandbox\");\n\t\texpect(route).toBeDefined();\n\t\texpect(route!.path).toBe(\"/sandbox\");\n\t\texpect(route!.port).toBe(8080);\n\t});\n\n\tit(\"generates sandbox.toml in additionalFiles\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.additionalFiles).toHaveProperty(\"sandbox.toml\");\n\t\tconst toml = result.additionalFiles[\"sandbox.toml\"];\n\t\texpect(toml).toContain('[server]');\n\t\texpect(toml).toContain('api_key = \"${OPEN_SANDBOX_API_KEY}\"');\n\t\texpect(toml).toContain('[runtime]');\n\t\texpect(toml).toContain('[docker]');\n\t\texpect(toml).toContain('[secure_runtime]');\n\t\texpect(toml).toContain('type = \"gvisor\"');\n\t});\n\n\tit(\"populates prePullImages with 8 images across 3 priority tiers\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst images = result.metadata.prePullImages;\n\t\texpect(images.length).toBe(8);\n\n\t\t// Priority 1: core + Homespace\n\t\tconst p1 = images.filter((i) => i.priority === 1);\n\t\texpect(p1.length).toBe(4);\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/server:v1.0.6\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/execd:v1.0.6\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/desktop:latest\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/chrome:latest\");\n\n\t\t// Priority 2: common languages\n\t\tconst p2 = images.filter((i) => i.priority === 2);\n\t\texpect(p2.length).toBe(2);\n\t\texpect(p2.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:python\");\n\t\texpect(p2.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:node\");\n\n\t\t// Priority 3: optional\n\t\tconst p3 = images.filter((i) => i.priority === 3);\n\t\texpect(p3.length).toBe(2);\n\t\texpect(p3.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:latest\");\n\t\texpect(p3.map((i) => i.image)).toContain(\"opensandbox/vscode:latest\");\n\t});\n\n\tit(\"resolves port conflict between opensandbox and searxng (both 8080)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\", \"searxng\"],\n\t\t});\n\n\t\texpect(result.metadata.resolvedServices).toContain(\"opensandbox\");\n\t\texpect(result.metadata.resolvedServices).toContain(\"searxng\");\n\n\t\t// Both should have port assignments, but they should differ\n\t\tconst assignments = result.metadata.portAssignments;\n\t\tconst opensandboxPort = assignments[\"opensandbox:8080\"];\n\t\tconst searxngPort = assignments[\"searxng:8080\"];\n\t\texpect(opensandboxPort).toBeDefined();\n\t\texpect(searxngPort).toBeDefined();\n\t\texpect(opensandboxPort).not.toBe(searxngPort);\n\t});\n\n\tit(\"does not include opensandbox in prePullImages when not selected\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\texpect(result.metadata.prePullImages.length).toBe(0);\n\t\texpect(result.additionalFiles).not.toHaveProperty(\"sandbox.toml\");\n\t});\n\n\tit(\"opensandbox does not require postgresql (no postgres-setup)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).not.toHaveProperty(\"postgres-setup\");\n\t});\n\n\tit(\"accounts for 768MB memory in estimatedMemoryMB\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.metadata.estimatedMemoryMB).toBeGreaterThanOrEqual(768);\n\t});\n});\n\ndescribe(\"updateAddonStack\", () => {\n\tit(\"adds a service to an existing stack\", () => {\n\t\t// First generate a base stack\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Then add meilisearch\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.metadata.added).toContain(\"meilisearch\");\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\t\texpect(composed.services).toHaveProperty(\"meilisearch\");\n\t});\n\n\tit(\"removes a service from an existing stack\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"meilisearch\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\tremoveServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.metadata.removed).toContain(\"meilisearch\");\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\t\texpect(composed.services).not.toHaveProperty(\"meilisearch\");\n\t});\n\n\tit(\"preserves existing env values when adding a service\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// Simulate user editing an env value\n\t\tconst customEnv = base.envFile.replace(\n\t\t\t/QDRANT_API_KEY=[a-f0-9]+/,\n\t\t\t\"QDRANT_API_KEY=my_custom_key\",\n\t\t);\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: customEnv,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// If QDRANT_API_KEY was in the original env, it should be preserved\n\t\tif (customEnv.includes(\"QDRANT_API_KEY=my_custom_key\")) {\n\t\t\texpect(result.envFile).toContain(\"QDRANT_API_KEY=my_custom_key\");\n\t\t}\n\t});\n\n\tit(\"returns images to pull for added services\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.imagesToPull.length).toBeGreaterThan(0);\n\t\texpect(result.imagesToPull.some((img) => img.includes(\"meilisearch\"))).toBe(true);\n\t});\n\n\tit(\"handles empty update gracefully\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t});\n\n\t\texpect(result.metadata.added).toEqual([]);\n\t\texpect(result.metadata.removed).toEqual([]);\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\t});\n\n\tit(\"respects generateSecrets: false during update\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// With generateSecrets: false, services requiring secrets (like meilisearch)\n\t\t// may be skipped as missing_credentials. Provide credentials explicitly.\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: false,\n\t\t\tcredentials: {\n\t\t\t\tmeilisearch: { MEILI_MASTER_KEY: \"test-master-key-1234567890\" },\n\t\t\t},\n\t\t});\n\n\t\t// Meilisearch should be added since we provided the required credential\n\t\texpect(result.metadata.added).toContain(\"meilisearch\");\n\t});\n\n\tit(\"forwards portOverrides during update\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"n8n\"],\n\t\t\tportOverrides: { n8n: { \"5678\": 9999 } },\n\t\t});\n\n\t\t// n8n should be present with the port override\n\t\texpect(result.metadata.added).toContain(\"n8n\");\n\t});\n});\n\ndescribe(\"AddonStackInputSchema\", () => {\n\tit(\"accepts valid input\", () => {\n\t\tconst result = AddonStackInputSchema.safeParse({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"n8n\"],\n\t\t});\n\t\texpect(result.success).toBe(true);\n\t});\n\n\tit(\"requires instanceId\", () => {\n\t\tconst result = AddonStackInputSchema.safeParse({\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\t\texpect(result.success).toBe(false);\n\t});\n\n\tit(\"defaults empty arrays and booleans\", () => {\n\t\tconst result = AddonStackInputSchema.parse({\n\t\t\tinstanceId: \"test\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\t\texpect(result.skillPacks).toEqual([]);\n\t\texpect(result.reservedPorts).toEqual([]);\n\t\texpect(result.generateSecrets).toBe(true);\n\t\texpect(result.platform).toBe(\"linux/amd64\");\n\t});\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"addon-stack.test.cjs","names":["describe","generateAddonStack","expect","updateAddonStack","AddonStackInputSchema"],"sources":["../src/addon-stack.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { parse } from \"yaml\";\nimport { generateAddonStack, updateAddonStack } from \"./addon-stack.js\";\nimport { AddonStackInputSchema } from \"./schema.js\";\n\ndescribe(\"generateAddonStack\", () => {\n\tit(\"generates valid compose YAML with a single service (qdrant)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Resolver may generate warnings about recommended services; that's fine\n\t\texpect(result.metadata.serviceCount).toBeGreaterThan(0);\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\n\t\t// Parse YAML to verify it's valid\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\n\t\t// Should NOT contain infrastructure services\n\t\texpect(composed.services).not.toHaveProperty(\"openclaw-gateway\");\n\t\texpect(composed.services).not.toHaveProperty(\"openclaw-cli\");\n\t\texpect(composed.services).not.toHaveProperty(\"redis\");\n\t\texpect(composed.services).not.toHaveProperty(\"postgresql\");\n\t\texpect(composed.services).not.toHaveProperty(\"open-webui\");\n\t\texpect(composed.services).not.toHaveProperty(\"caddy\");\n\n\t\t// Should have openclaw-network as external\n\t\texpect(composed.networks).toHaveProperty(\"openclaw-network\");\n\t\texpect(composed.networks[\"openclaw-network\"].external).toBe(true);\n\t});\n\n\tit(\"does not include profiles on any service\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"meilisearch\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\tfor (const [, svc] of Object.entries(composed.services)) {\n\t\t\texpect(svc).not.toHaveProperty(\"profiles\");\n\t\t}\n\t});\n\n\tit(\"does not apply cap_drop or security_opt by default\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\tconst qdrant = composed.services.qdrant;\n\t\texpect(qdrant).not.toHaveProperty(\"cap_drop\");\n\t\texpect(qdrant).not.toHaveProperty(\"security_opt\");\n\t});\n\n\tit(\"includes postgres-setup when a DB-dependent service is requested (n8n)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"n8n\");\n\t\texpect(composed.services).toHaveProperty(\"postgres-setup\");\n\n\t\t// postgres-setup should depend on existing postgresql\n\t\texpect(composed.services[\"postgres-setup\"].depends_on).toHaveProperty(\"postgresql\");\n\n\t\t// n8n should depend on postgres-setup\n\t\texpect(composed.services.n8n.depends_on).toHaveProperty(\"postgres-setup\");\n\t});\n\n\tit(\"generates DB passwords in env file for DB-dependent services\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\texpect(result.envFile).toContain(\"N8N_DB_PASSWORD=\");\n\t\t// Password should be non-empty (48 hex chars = 24 bytes)\n\t\tconst match = result.envFile.match(/N8N_DB_PASSWORD=([a-f0-9]+)/);\n\t\texpect(match).not.toBeNull();\n\t\texpect(match?.[1].length).toBe(48);\n\t});\n\n\tit(\"gracefully handles unknown service IDs without throwing\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"nonexistent-service\", \"qdrant\"],\n\t\t});\n\n\t\t// Should NOT throw\n\t\texpect(result.metadata.skippedServices).toEqual(\n\t\t\texpect.arrayContaining([\n\t\t\t\texpect.objectContaining({\n\t\t\t\t\tserviceId: \"nonexistent-service\",\n\t\t\t\t\treason: \"unknown_service\",\n\t\t\t\t}),\n\t\t\t]),\n\t\t);\n\n\t\t// Valid service should still be included\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\t});\n\n\tit(\"excludes infrastructure services from the request\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"redis\", \"postgresql\", \"qdrant\"],\n\t\t});\n\n\t\texpect(result.warnings).toEqual(\n\t\t\texpect.arrayContaining([\n\t\t\t\texpect.stringContaining(\"redis\"),\n\t\t\t\texpect.stringContaining(\"postgresql\"),\n\t\t\t]),\n\t\t);\n\t\texpect(result.metadata.resolvedServices).not.toContain(\"redis\");\n\t\texpect(result.metadata.resolvedServices).not.toContain(\"postgresql\");\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\t});\n\n\tit(\"generates proxy routes from proxyPath\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\tconst n8nRoute = result.proxyRoutes.find((r) => r.serviceId === \"n8n\");\n\t\texpect(n8nRoute).toBeDefined();\n\t\texpect(n8nRoute?.path).toBe(\"/n8n\");\n\t\texpect(n8nRoute?.port).toBe(5678);\n\t});\n\n\tit(\"generates skill files and openclaw config patch\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\t// n8n has skill binding: n8n-trigger\n\t\texpect(result.openclawConfigPatch.skills.entries).toHaveProperty(\"n8n-trigger\");\n\t\texpect(result.openclawConfigPatch.skills.entries[\"n8n-trigger\"].enabled).toBe(true);\n\t});\n\n\tit(\"resolves port conflicts with reserved ports\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\treservedPorts: [5678], // n8n's default port\n\t\t});\n\n\t\t// Port should be reassigned\n\t\tconst portAssignments = result.metadata.portAssignments;\n\t\tconst n8nAssignment = Object.entries(portAssignments).find(([key]) => key.startsWith(\"n8n:\"));\n\t\texpect(n8nAssignment).toBeDefined();\n\t\texpect(n8nAssignment?.[1]).not.toBe(5678);\n\t});\n\n\tit(\"sanitizes project name from instanceId\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"My_Instance_123\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Should succeed without error\n\t\texpect(result.metadata.serviceCount).toBeGreaterThan(0);\n\t});\n\n\tit(\"returns env vars grouped by service\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\texpect(result.envVars.length).toBeGreaterThanOrEqual(0);\n\t});\n\n\tit(\"returns empty result for no services\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [],\n\t\t});\n\n\t\texpect(result.composeOverride).toContain(\"services: {}\");\n\t\texpect(result.warnings.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"returns empty result when all services are infrastructure\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"redis\", \"postgresql\", \"caddy\"],\n\t\t});\n\n\t\texpect(result.metadata.serviceCount).toBe(0);\n\t\texpect(result.warnings.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"applies env quirks to envFile output (meilisearch MEILI_MASTER_KEY min_length)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// MEILI_MASTER_KEY should be a base64url string of at least 32 chars (24 bytes)\n\t\tconst match = result.envFile.match(/MEILI_MASTER_KEY=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\texpect(match?.[1].length).toBeGreaterThanOrEqual(32);\n\t\t// Should not be empty\n\t\texpect(match?.[1]).not.toBe(\"\");\n\t});\n\n\tit(\"applies env quirks to envFile output (grafana GF_SECURITY_ADMIN_PASSWORD)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"grafana\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst match = result.envFile.match(/GF_SECURITY_ADMIN_PASSWORD=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\t// Should be at least 22 chars (16 bytes base64url)\n\t\texpect(match?.[1].length).toBeGreaterThanOrEqual(22);\n\t});\n\n\tit(\"syncs DB_POSTGRESDB_PASSWORD with N8N_DB_PASSWORD via must_sync quirk\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst n8nDbPw = result.envFile.match(/N8N_DB_PASSWORD=([^\\n]+)/);\n\t\tconst dbPostgresPw = result.envFile.match(/DB_POSTGRESDB_PASSWORD=([^\\n]+)/);\n\t\texpect(n8nDbPw).not.toBeNull();\n\t\texpect(dbPostgresPw).not.toBeNull();\n\t\t// Both passwords must match due to must_sync quirk\n\t\texpect(n8nDbPw?.[1]).toBe(dbPostgresPw?.[1]);\n\t});\n\n\tit(\"syncs user-provided credential with DB password reference\", () => {\n\t\tconst customPassword = \"my_custom_secure_password\";\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t\tcredentials: {\n\t\t\t\tn8n: { DB_POSTGRESDB_PASSWORD: customPassword },\n\t\t\t},\n\t\t});\n\n\t\t// User value should be used for both the service env var and the ref key\n\t\texpect(result.envFile).toContain(`DB_POSTGRESDB_PASSWORD=${customPassword}`);\n\t\texpect(result.envFile).toContain(`N8N_DB_PASSWORD=${customPassword}`);\n\t});\n\n\tit(\"uses existingServices ports for conflict detection\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\t// n8n uses port 5678, qdrant uses 6333 — qdrant as existing should not conflict\n\t\t\texistingServices: [\"qdrant\"],\n\t\t});\n\n\t\t// n8n should still get its default port (no conflict with qdrant)\n\t\tconst portAssignments = result.metadata.portAssignments;\n\t\tconst n8nAssignment = Object.entries(portAssignments).find(([key]) => key.startsWith(\"n8n:\"));\n\t\texpect(n8nAssignment).toBeDefined();\n\t\texpect(n8nAssignment?.[1]).toBe(5678);\n\t});\n\n\tit(\"does not dual-list GPU services in both skipped and resolved\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"ollama\"],\n\t\t\tgpu: false,\n\t\t});\n\n\t\t// ollama requires GPU — without gpu: true, it should be skipped only\n\t\tconst isSkipped = result.metadata.skippedServices.some(\n\t\t\t(s) => s.serviceId === \"ollama\" && s.reason === \"gpu_required\",\n\t\t);\n\t\tconst isResolved = result.metadata.resolvedServices.includes(\"ollama\");\n\n\t\t// Must be in exactly one list, not both\n\t\tif (isSkipped) {\n\t\t\texpect(isResolved).toBe(false);\n\t\t}\n\t});\n\n\tit(\"includes GPU services when gpu: true is set\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"ollama\"],\n\t\t\tgpu: true,\n\t\t});\n\n\t\t// Should NOT be skipped when GPU support is available\n\t\tconst isSkipped = result.metadata.skippedServices.some(\n\t\t\t(s) => s.serviceId === \"ollama\" && s.reason === \"gpu_required\",\n\t\t);\n\t\texpect(isSkipped).toBe(false);\n\t\texpect(result.metadata.resolvedServices).toContain(\"ollama\");\n\t});\n\n\t// ── OpenSandbox ────────────────────────────────────────────────────────\n\n\tit(\"generates valid compose YAML for opensandbox\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.metadata.resolvedServices).toContain(\"opensandbox\");\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"opensandbox\");\n\n\t\t// Should have Docker socket mount\n\t\tconst volumes = composed.services.opensandbox.volumes as string[];\n\t\texpect(volumes.some((v: string) => v.includes(\"/var/run/docker.sock\"))).toBe(true);\n\n\t\t// Should have config bind mount\n\t\texpect(volumes.some((v: string) => v.includes(\"sandbox.toml\"))).toBe(true);\n\t});\n\n\tit(\"generates OPEN_SANDBOX_API_KEY with min 32 bytes via env quirk\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst match = result.envFile.match(/OPEN_SANDBOX_API_KEY=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\t// 32 bytes base64url ≈ 43 chars\n\t\texpect(match?.[1].length).toBeGreaterThanOrEqual(43);\n\t});\n\n\tit(\"generates code-sandbox skill file and config patch\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\t// Skill config patch\n\t\texpect(result.openclawConfigPatch.skills.entries).toHaveProperty(\"code-sandbox\");\n\t\texpect(result.openclawConfigPatch.skills.entries[\"code-sandbox\"].enabled).toBe(true);\n\n\t\t// Skill file content should exist and contain key actions\n\t\tconst skillFile = Object.values(result.skillFiles).find((content) =>\n\t\t\tcontent.includes(\"code-sandbox\"),\n\t\t);\n\t\texpect(skillFile).toBeDefined();\n\t\texpect(skillFile).toContain(\"execute_code\");\n\t\texpect(skillFile).toContain(\"create_desktop\");\n\t\texpect(skillFile).toContain(\"get_preview_url\");\n\t});\n\n\tit(\"generates proxy route for opensandbox at /sandbox\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst route = result.proxyRoutes.find((r) => r.serviceId === \"opensandbox\");\n\t\texpect(route).toBeDefined();\n\t\texpect(route?.path).toBe(\"/sandbox\");\n\t\texpect(route?.port).toBe(8080);\n\t});\n\n\tit(\"generates sandbox.toml in additionalFiles\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.additionalFiles).toHaveProperty(\"sandbox.toml\");\n\t\tconst toml = result.additionalFiles[\"sandbox.toml\"];\n\t\texpect(toml).toContain(\"[server]\");\n\t\texpect(toml).toContain('api_key = \"${OPEN_SANDBOX_API_KEY}\"');\n\t\texpect(toml).toContain(\"[runtime]\");\n\t\texpect(toml).toContain(\"[docker]\");\n\t\texpect(toml).toContain(\"[secure_runtime]\");\n\t\texpect(toml).toContain('type = \"gvisor\"');\n\t});\n\n\tit(\"populates prePullImages with 8 images across 3 priority tiers\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst images = result.metadata.prePullImages;\n\t\texpect(images.length).toBe(8);\n\n\t\t// Priority 1: core + Homespace\n\t\tconst p1 = images.filter((i) => i.priority === 1);\n\t\texpect(p1.length).toBe(4);\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/server:v1.0.6\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/execd:v1.0.6\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/desktop:latest\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/chrome:latest\");\n\n\t\t// Priority 2: common languages\n\t\tconst p2 = images.filter((i) => i.priority === 2);\n\t\texpect(p2.length).toBe(2);\n\t\texpect(p2.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:python\");\n\t\texpect(p2.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:node\");\n\n\t\t// Priority 3: optional\n\t\tconst p3 = images.filter((i) => i.priority === 3);\n\t\texpect(p3.length).toBe(2);\n\t\texpect(p3.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:latest\");\n\t\texpect(p3.map((i) => i.image)).toContain(\"opensandbox/vscode:latest\");\n\t});\n\n\tit(\"resolves port conflict between opensandbox and searxng (both 8080)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\", \"searxng\"],\n\t\t});\n\n\t\texpect(result.metadata.resolvedServices).toContain(\"opensandbox\");\n\t\texpect(result.metadata.resolvedServices).toContain(\"searxng\");\n\n\t\t// Both should have port assignments, but they should differ\n\t\tconst assignments = result.metadata.portAssignments;\n\t\tconst opensandboxPort = assignments[\"opensandbox:8080\"];\n\t\tconst searxngPort = assignments[\"searxng:8080\"];\n\t\texpect(opensandboxPort).toBeDefined();\n\t\texpect(searxngPort).toBeDefined();\n\t\texpect(opensandboxPort).not.toBe(searxngPort);\n\t});\n\n\tit(\"does not include opensandbox in prePullImages when not selected\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\texpect(result.metadata.prePullImages.length).toBe(0);\n\t\texpect(result.additionalFiles).not.toHaveProperty(\"sandbox.toml\");\n\t});\n\n\tit(\"opensandbox does not require postgresql (no postgres-setup)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).not.toHaveProperty(\"postgres-setup\");\n\t});\n\n\tit(\"accounts for 768MB memory in estimatedMemoryMB\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.metadata.estimatedMemoryMB).toBeGreaterThanOrEqual(768);\n\t});\n});\n\ndescribe(\"updateAddonStack\", () => {\n\tit(\"adds a service to an existing stack\", () => {\n\t\t// First generate a base stack\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Then add meilisearch\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.metadata.added).toContain(\"meilisearch\");\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\t\texpect(composed.services).toHaveProperty(\"meilisearch\");\n\t});\n\n\tit(\"removes a service from an existing stack\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"meilisearch\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\tremoveServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.metadata.removed).toContain(\"meilisearch\");\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\t\texpect(composed.services).not.toHaveProperty(\"meilisearch\");\n\t});\n\n\tit(\"preserves existing env values when adding a service\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// Simulate user editing an env value\n\t\tconst customEnv = base.envFile.replace(\n\t\t\t/QDRANT_API_KEY=[a-f0-9]+/,\n\t\t\t\"QDRANT_API_KEY=my_custom_key\",\n\t\t);\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: customEnv,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// If QDRANT_API_KEY was in the original env, it should be preserved\n\t\tif (customEnv.includes(\"QDRANT_API_KEY=my_custom_key\")) {\n\t\t\texpect(result.envFile).toContain(\"QDRANT_API_KEY=my_custom_key\");\n\t\t}\n\t});\n\n\tit(\"returns images to pull for added services\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.imagesToPull.length).toBeGreaterThan(0);\n\t\texpect(result.imagesToPull.some((img) => img.includes(\"meilisearch\"))).toBe(true);\n\t});\n\n\tit(\"handles empty update gracefully\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t});\n\n\t\texpect(result.metadata.added).toEqual([]);\n\t\texpect(result.metadata.removed).toEqual([]);\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\t});\n\n\tit(\"respects generateSecrets: false during update\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// With generateSecrets: false, services requiring secrets (like meilisearch)\n\t\t// may be skipped as missing_credentials. Provide credentials explicitly.\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: false,\n\t\t\tcredentials: {\n\t\t\t\tmeilisearch: { MEILI_MASTER_KEY: \"test-master-key-1234567890\" },\n\t\t\t},\n\t\t});\n\n\t\t// Meilisearch should be added since we provided the required credential\n\t\texpect(result.metadata.added).toContain(\"meilisearch\");\n\t});\n\n\tit(\"forwards portOverrides during update\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"n8n\"],\n\t\t\tportOverrides: { n8n: { \"5678\": 9999 } },\n\t\t});\n\n\t\t// n8n should be present with the port override\n\t\texpect(result.metadata.added).toContain(\"n8n\");\n\t});\n});\n\ndescribe(\"AddonStackInputSchema\", () => {\n\tit(\"accepts valid input\", () => {\n\t\tconst result = AddonStackInputSchema.safeParse({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"n8n\"],\n\t\t});\n\t\texpect(result.success).toBe(true);\n\t});\n\n\tit(\"requires instanceId\", () => {\n\t\tconst result = AddonStackInputSchema.safeParse({\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\t\texpect(result.success).toBe(false);\n\t});\n\n\tit(\"defaults empty arrays and booleans\", () => {\n\t\tconst result = AddonStackInputSchema.parse({\n\t\t\tinstanceId: \"test\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\t\texpect(result.skillPacks).toEqual([]);\n\t\texpect(result.reservedPorts).toEqual([]);\n\t\texpect(result.generateSecrets).toBe(true);\n\t\texpect(result.platform).toBe(\"linux/amd64\");\n\t});\n});\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { n as describe, r as it, t as globalExpect } from "./test.CTcmp4Su-ClCHJ3FA.mjs";
1
+ import { a as describe, o as it, t as globalExpect } from "./test.CTcmp4Su-BRa7-bTj.mjs";
2
2
  import { AddonStackInputSchema } from "./schema.mjs";
3
3
  import { generateAddonStack, updateAddonStack } from "./addon-stack.mjs";
4
4
  import { parse } from "yaml";
@@ -56,7 +56,7 @@ describe("generateAddonStack", () => {
56
56
  globalExpect(result.envFile).toContain("N8N_DB_PASSWORD=");
57
57
  const match = result.envFile.match(/N8N_DB_PASSWORD=([a-f0-9]+)/);
58
58
  globalExpect(match).not.toBeNull();
59
- globalExpect(match[1].length).toBe(48);
59
+ globalExpect(match?.[1].length).toBe(48);
60
60
  });
61
61
  it("gracefully handles unknown service IDs without throwing", () => {
62
62
  const result = generateAddonStack({
@@ -89,8 +89,8 @@ describe("generateAddonStack", () => {
89
89
  services: ["n8n"]
90
90
  }).proxyRoutes.find((r) => r.serviceId === "n8n");
91
91
  globalExpect(n8nRoute).toBeDefined();
92
- globalExpect(n8nRoute.path).toBe("/n8n");
93
- globalExpect(n8nRoute.port).toBe(5678);
92
+ globalExpect(n8nRoute?.path).toBe("/n8n");
93
+ globalExpect(n8nRoute?.port).toBe(5678);
94
94
  });
95
95
  it("generates skill files and openclaw config patch", () => {
96
96
  const result = generateAddonStack({
@@ -108,7 +108,7 @@ describe("generateAddonStack", () => {
108
108
  }).metadata.portAssignments;
109
109
  const n8nAssignment = Object.entries(portAssignments).find(([key]) => key.startsWith("n8n:"));
110
110
  globalExpect(n8nAssignment).toBeDefined();
111
- globalExpect(n8nAssignment[1]).not.toBe(5678);
111
+ globalExpect(n8nAssignment?.[1]).not.toBe(5678);
112
112
  });
113
113
  it("sanitizes project name from instanceId", () => {
114
114
  globalExpect(generateAddonStack({
@@ -149,8 +149,8 @@ describe("generateAddonStack", () => {
149
149
  generateSecrets: true
150
150
  }).envFile.match(/MEILI_MASTER_KEY=([^\n]+)/);
151
151
  globalExpect(match).not.toBeNull();
152
- globalExpect(match[1].length).toBeGreaterThanOrEqual(32);
153
- globalExpect(match[1]).not.toBe("");
152
+ globalExpect(match?.[1].length).toBeGreaterThanOrEqual(32);
153
+ globalExpect(match?.[1]).not.toBe("");
154
154
  });
155
155
  it("applies env quirks to envFile output (grafana GF_SECURITY_ADMIN_PASSWORD)", () => {
156
156
  const match = generateAddonStack({
@@ -159,7 +159,7 @@ describe("generateAddonStack", () => {
159
159
  generateSecrets: true
160
160
  }).envFile.match(/GF_SECURITY_ADMIN_PASSWORD=([^\n]+)/);
161
161
  globalExpect(match).not.toBeNull();
162
- globalExpect(match[1].length).toBeGreaterThanOrEqual(22);
162
+ globalExpect(match?.[1].length).toBeGreaterThanOrEqual(22);
163
163
  });
164
164
  it("syncs DB_POSTGRESDB_PASSWORD with N8N_DB_PASSWORD via must_sync quirk", () => {
165
165
  const result = generateAddonStack({
@@ -171,7 +171,7 @@ describe("generateAddonStack", () => {
171
171
  const dbPostgresPw = result.envFile.match(/DB_POSTGRESDB_PASSWORD=([^\n]+)/);
172
172
  globalExpect(n8nDbPw).not.toBeNull();
173
173
  globalExpect(dbPostgresPw).not.toBeNull();
174
- globalExpect(n8nDbPw[1]).toBe(dbPostgresPw[1]);
174
+ globalExpect(n8nDbPw?.[1]).toBe(dbPostgresPw?.[1]);
175
175
  });
176
176
  it("syncs user-provided credential with DB password reference", () => {
177
177
  const customPassword = "my_custom_secure_password";
@@ -192,7 +192,7 @@ describe("generateAddonStack", () => {
192
192
  }).metadata.portAssignments;
193
193
  const n8nAssignment = Object.entries(portAssignments).find(([key]) => key.startsWith("n8n:"));
194
194
  globalExpect(n8nAssignment).toBeDefined();
195
- globalExpect(n8nAssignment[1]).toBe(5678);
195
+ globalExpect(n8nAssignment?.[1]).toBe(5678);
196
196
  });
197
197
  it("does not dual-list GPU services in both skipped and resolved", () => {
198
198
  const result = generateAddonStack({
@@ -232,7 +232,7 @@ describe("generateAddonStack", () => {
232
232
  generateSecrets: true
233
233
  }).envFile.match(/OPEN_SANDBOX_API_KEY=([^\n]+)/);
234
234
  globalExpect(match).not.toBeNull();
235
- globalExpect(match[1].length).toBeGreaterThanOrEqual(43);
235
+ globalExpect(match?.[1].length).toBeGreaterThanOrEqual(43);
236
236
  });
237
237
  it("generates code-sandbox skill file and config patch", () => {
238
238
  const result = generateAddonStack({
@@ -253,8 +253,8 @@ describe("generateAddonStack", () => {
253
253
  services: ["opensandbox"]
254
254
  }).proxyRoutes.find((r) => r.serviceId === "opensandbox");
255
255
  globalExpect(route).toBeDefined();
256
- globalExpect(route.path).toBe("/sandbox");
257
- globalExpect(route.port).toBe(8080);
256
+ globalExpect(route?.path).toBe("/sandbox");
257
+ globalExpect(route?.port).toBe(8080);
258
258
  });
259
259
  it("generates sandbox.toml in additionalFiles", () => {
260
260
  const result = generateAddonStack({
@@ -1 +1 @@
1
- {"version":3,"file":"addon-stack.test.mjs","names":["expect"],"sources":["../src/addon-stack.test.ts"],"sourcesContent":["import { parse } from \"yaml\";\nimport { describe, expect, it } from \"vitest\";\nimport { generateAddonStack, updateAddonStack } from \"./addon-stack.js\";\nimport { AddonStackInputSchema } from \"./schema.js\";\n\ndescribe(\"generateAddonStack\", () => {\n\tit(\"generates valid compose YAML with a single service (qdrant)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Resolver may generate warnings about recommended services; that's fine\n\t\texpect(result.metadata.serviceCount).toBeGreaterThan(0);\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\n\t\t// Parse YAML to verify it's valid\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\n\t\t// Should NOT contain infrastructure services\n\t\texpect(composed.services).not.toHaveProperty(\"openclaw-gateway\");\n\t\texpect(composed.services).not.toHaveProperty(\"openclaw-cli\");\n\t\texpect(composed.services).not.toHaveProperty(\"redis\");\n\t\texpect(composed.services).not.toHaveProperty(\"postgresql\");\n\t\texpect(composed.services).not.toHaveProperty(\"open-webui\");\n\t\texpect(composed.services).not.toHaveProperty(\"caddy\");\n\n\t\t// Should have openclaw-network as external\n\t\texpect(composed.networks).toHaveProperty(\"openclaw-network\");\n\t\texpect(composed.networks[\"openclaw-network\"].external).toBe(true);\n\t});\n\n\tit(\"does not include profiles on any service\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"meilisearch\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\tfor (const [, svc] of Object.entries(composed.services)) {\n\t\t\texpect(svc).not.toHaveProperty(\"profiles\");\n\t\t}\n\t});\n\n\tit(\"does not apply cap_drop or security_opt by default\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\tconst qdrant = composed.services.qdrant;\n\t\texpect(qdrant).not.toHaveProperty(\"cap_drop\");\n\t\texpect(qdrant).not.toHaveProperty(\"security_opt\");\n\t});\n\n\tit(\"includes postgres-setup when a DB-dependent service is requested (n8n)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"n8n\");\n\t\texpect(composed.services).toHaveProperty(\"postgres-setup\");\n\n\t\t// postgres-setup should depend on existing postgresql\n\t\texpect(composed.services[\"postgres-setup\"].depends_on).toHaveProperty(\"postgresql\");\n\n\t\t// n8n should depend on postgres-setup\n\t\texpect(composed.services.n8n.depends_on).toHaveProperty(\"postgres-setup\");\n\t});\n\n\tit(\"generates DB passwords in env file for DB-dependent services\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\texpect(result.envFile).toContain(\"N8N_DB_PASSWORD=\");\n\t\t// Password should be non-empty (48 hex chars = 24 bytes)\n\t\tconst match = result.envFile.match(/N8N_DB_PASSWORD=([a-f0-9]+)/);\n\t\texpect(match).not.toBeNull();\n\t\texpect(match![1].length).toBe(48);\n\t});\n\n\tit(\"gracefully handles unknown service IDs without throwing\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"nonexistent-service\", \"qdrant\"],\n\t\t});\n\n\t\t// Should NOT throw\n\t\texpect(result.metadata.skippedServices).toEqual(\n\t\t\texpect.arrayContaining([\n\t\t\t\texpect.objectContaining({\n\t\t\t\t\tserviceId: \"nonexistent-service\",\n\t\t\t\t\treason: \"unknown_service\",\n\t\t\t\t}),\n\t\t\t]),\n\t\t);\n\n\t\t// Valid service should still be included\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\t});\n\n\tit(\"excludes infrastructure services from the request\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"redis\", \"postgresql\", \"qdrant\"],\n\t\t});\n\n\t\texpect(result.warnings).toEqual(\n\t\t\texpect.arrayContaining([\n\t\t\t\texpect.stringContaining(\"redis\"),\n\t\t\t\texpect.stringContaining(\"postgresql\"),\n\t\t\t]),\n\t\t);\n\t\texpect(result.metadata.resolvedServices).not.toContain(\"redis\");\n\t\texpect(result.metadata.resolvedServices).not.toContain(\"postgresql\");\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\t});\n\n\tit(\"generates proxy routes from proxyPath\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\tconst n8nRoute = result.proxyRoutes.find((r) => r.serviceId === \"n8n\");\n\t\texpect(n8nRoute).toBeDefined();\n\t\texpect(n8nRoute!.path).toBe(\"/n8n\");\n\t\texpect(n8nRoute!.port).toBe(5678);\n\t});\n\n\tit(\"generates skill files and openclaw config patch\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\t// n8n has skill binding: n8n-trigger\n\t\texpect(result.openclawConfigPatch.skills.entries).toHaveProperty(\"n8n-trigger\");\n\t\texpect(result.openclawConfigPatch.skills.entries[\"n8n-trigger\"].enabled).toBe(true);\n\t});\n\n\tit(\"resolves port conflicts with reserved ports\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\treservedPorts: [5678], // n8n's default port\n\t\t});\n\n\t\t// Port should be reassigned\n\t\tconst portAssignments = result.metadata.portAssignments;\n\t\tconst n8nAssignment = Object.entries(portAssignments).find(([key]) =>\n\t\t\tkey.startsWith(\"n8n:\"),\n\t\t);\n\t\texpect(n8nAssignment).toBeDefined();\n\t\texpect(n8nAssignment![1]).not.toBe(5678);\n\t});\n\n\tit(\"sanitizes project name from instanceId\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"My_Instance_123\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Should succeed without error\n\t\texpect(result.metadata.serviceCount).toBeGreaterThan(0);\n\t});\n\n\tit(\"returns env vars grouped by service\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\texpect(result.envVars.length).toBeGreaterThanOrEqual(0);\n\t});\n\n\tit(\"returns empty result for no services\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [],\n\t\t});\n\n\t\texpect(result.composeOverride).toContain(\"services: {}\");\n\t\texpect(result.warnings.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"returns empty result when all services are infrastructure\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"redis\", \"postgresql\", \"caddy\"],\n\t\t});\n\n\t\texpect(result.metadata.serviceCount).toBe(0);\n\t\texpect(result.warnings.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"applies env quirks to envFile output (meilisearch MEILI_MASTER_KEY min_length)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// MEILI_MASTER_KEY should be a base64url string of at least 32 chars (24 bytes)\n\t\tconst match = result.envFile.match(/MEILI_MASTER_KEY=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\texpect(match![1].length).toBeGreaterThanOrEqual(32);\n\t\t// Should not be empty\n\t\texpect(match![1]).not.toBe(\"\");\n\t});\n\n\tit(\"applies env quirks to envFile output (grafana GF_SECURITY_ADMIN_PASSWORD)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"grafana\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst match = result.envFile.match(/GF_SECURITY_ADMIN_PASSWORD=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\t// Should be at least 22 chars (16 bytes base64url)\n\t\texpect(match![1].length).toBeGreaterThanOrEqual(22);\n\t});\n\n\tit(\"syncs DB_POSTGRESDB_PASSWORD with N8N_DB_PASSWORD via must_sync quirk\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst n8nDbPw = result.envFile.match(/N8N_DB_PASSWORD=([^\\n]+)/);\n\t\tconst dbPostgresPw = result.envFile.match(/DB_POSTGRESDB_PASSWORD=([^\\n]+)/);\n\t\texpect(n8nDbPw).not.toBeNull();\n\t\texpect(dbPostgresPw).not.toBeNull();\n\t\t// Both passwords must match due to must_sync quirk\n\t\texpect(n8nDbPw![1]).toBe(dbPostgresPw![1]);\n\t});\n\n\tit(\"syncs user-provided credential with DB password reference\", () => {\n\t\tconst customPassword = \"my_custom_secure_password\";\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t\tcredentials: {\n\t\t\t\tn8n: { DB_POSTGRESDB_PASSWORD: customPassword },\n\t\t\t},\n\t\t});\n\n\t\t// User value should be used for both the service env var and the ref key\n\t\texpect(result.envFile).toContain(`DB_POSTGRESDB_PASSWORD=${customPassword}`);\n\t\texpect(result.envFile).toContain(`N8N_DB_PASSWORD=${customPassword}`);\n\t});\n\n\tit(\"uses existingServices ports for conflict detection\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\t// n8n uses port 5678, qdrant uses 6333 — qdrant as existing should not conflict\n\t\t\texistingServices: [\"qdrant\"],\n\t\t});\n\n\t\t// n8n should still get its default port (no conflict with qdrant)\n\t\tconst portAssignments = result.metadata.portAssignments;\n\t\tconst n8nAssignment = Object.entries(portAssignments).find(([key]) =>\n\t\t\tkey.startsWith(\"n8n:\"),\n\t\t);\n\t\texpect(n8nAssignment).toBeDefined();\n\t\texpect(n8nAssignment![1]).toBe(5678);\n\t});\n\n\tit(\"does not dual-list GPU services in both skipped and resolved\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"ollama\"],\n\t\t\tgpu: false,\n\t\t});\n\n\t\t// ollama requires GPU — without gpu: true, it should be skipped only\n\t\tconst isSkipped = result.metadata.skippedServices.some(\n\t\t\t(s) => s.serviceId === \"ollama\" && s.reason === \"gpu_required\",\n\t\t);\n\t\tconst isResolved = result.metadata.resolvedServices.includes(\"ollama\");\n\n\t\t// Must be in exactly one list, not both\n\t\tif (isSkipped) {\n\t\t\texpect(isResolved).toBe(false);\n\t\t}\n\t});\n\n\tit(\"includes GPU services when gpu: true is set\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"ollama\"],\n\t\t\tgpu: true,\n\t\t});\n\n\t\t// Should NOT be skipped when GPU support is available\n\t\tconst isSkipped = result.metadata.skippedServices.some(\n\t\t\t(s) => s.serviceId === \"ollama\" && s.reason === \"gpu_required\",\n\t\t);\n\t\texpect(isSkipped).toBe(false);\n\t\texpect(result.metadata.resolvedServices).toContain(\"ollama\");\n\t});\n\n\t// ── OpenSandbox ────────────────────────────────────────────────────────\n\n\tit(\"generates valid compose YAML for opensandbox\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.metadata.resolvedServices).toContain(\"opensandbox\");\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"opensandbox\");\n\n\t\t// Should have Docker socket mount\n\t\tconst volumes = composed.services.opensandbox.volumes as string[];\n\t\texpect(volumes.some((v: string) => v.includes(\"/var/run/docker.sock\"))).toBe(true);\n\n\t\t// Should have config bind mount\n\t\texpect(volumes.some((v: string) => v.includes(\"sandbox.toml\"))).toBe(true);\n\t});\n\n\tit(\"generates OPEN_SANDBOX_API_KEY with min 32 bytes via env quirk\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst match = result.envFile.match(/OPEN_SANDBOX_API_KEY=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\t// 32 bytes base64url ≈ 43 chars\n\t\texpect(match![1].length).toBeGreaterThanOrEqual(43);\n\t});\n\n\tit(\"generates code-sandbox skill file and config patch\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\t// Skill config patch\n\t\texpect(result.openclawConfigPatch.skills.entries).toHaveProperty(\"code-sandbox\");\n\t\texpect(result.openclawConfigPatch.skills.entries[\"code-sandbox\"].enabled).toBe(true);\n\n\t\t// Skill file content should exist and contain key actions\n\t\tconst skillFile = Object.values(result.skillFiles).find((content) =>\n\t\t\tcontent.includes(\"code-sandbox\"),\n\t\t);\n\t\texpect(skillFile).toBeDefined();\n\t\texpect(skillFile).toContain(\"execute_code\");\n\t\texpect(skillFile).toContain(\"create_desktop\");\n\t\texpect(skillFile).toContain(\"get_preview_url\");\n\t});\n\n\tit(\"generates proxy route for opensandbox at /sandbox\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst route = result.proxyRoutes.find((r) => r.serviceId === \"opensandbox\");\n\t\texpect(route).toBeDefined();\n\t\texpect(route!.path).toBe(\"/sandbox\");\n\t\texpect(route!.port).toBe(8080);\n\t});\n\n\tit(\"generates sandbox.toml in additionalFiles\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.additionalFiles).toHaveProperty(\"sandbox.toml\");\n\t\tconst toml = result.additionalFiles[\"sandbox.toml\"];\n\t\texpect(toml).toContain('[server]');\n\t\texpect(toml).toContain('api_key = \"${OPEN_SANDBOX_API_KEY}\"');\n\t\texpect(toml).toContain('[runtime]');\n\t\texpect(toml).toContain('[docker]');\n\t\texpect(toml).toContain('[secure_runtime]');\n\t\texpect(toml).toContain('type = \"gvisor\"');\n\t});\n\n\tit(\"populates prePullImages with 8 images across 3 priority tiers\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst images = result.metadata.prePullImages;\n\t\texpect(images.length).toBe(8);\n\n\t\t// Priority 1: core + Homespace\n\t\tconst p1 = images.filter((i) => i.priority === 1);\n\t\texpect(p1.length).toBe(4);\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/server:v1.0.6\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/execd:v1.0.6\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/desktop:latest\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/chrome:latest\");\n\n\t\t// Priority 2: common languages\n\t\tconst p2 = images.filter((i) => i.priority === 2);\n\t\texpect(p2.length).toBe(2);\n\t\texpect(p2.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:python\");\n\t\texpect(p2.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:node\");\n\n\t\t// Priority 3: optional\n\t\tconst p3 = images.filter((i) => i.priority === 3);\n\t\texpect(p3.length).toBe(2);\n\t\texpect(p3.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:latest\");\n\t\texpect(p3.map((i) => i.image)).toContain(\"opensandbox/vscode:latest\");\n\t});\n\n\tit(\"resolves port conflict between opensandbox and searxng (both 8080)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\", \"searxng\"],\n\t\t});\n\n\t\texpect(result.metadata.resolvedServices).toContain(\"opensandbox\");\n\t\texpect(result.metadata.resolvedServices).toContain(\"searxng\");\n\n\t\t// Both should have port assignments, but they should differ\n\t\tconst assignments = result.metadata.portAssignments;\n\t\tconst opensandboxPort = assignments[\"opensandbox:8080\"];\n\t\tconst searxngPort = assignments[\"searxng:8080\"];\n\t\texpect(opensandboxPort).toBeDefined();\n\t\texpect(searxngPort).toBeDefined();\n\t\texpect(opensandboxPort).not.toBe(searxngPort);\n\t});\n\n\tit(\"does not include opensandbox in prePullImages when not selected\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\texpect(result.metadata.prePullImages.length).toBe(0);\n\t\texpect(result.additionalFiles).not.toHaveProperty(\"sandbox.toml\");\n\t});\n\n\tit(\"opensandbox does not require postgresql (no postgres-setup)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).not.toHaveProperty(\"postgres-setup\");\n\t});\n\n\tit(\"accounts for 768MB memory in estimatedMemoryMB\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.metadata.estimatedMemoryMB).toBeGreaterThanOrEqual(768);\n\t});\n});\n\ndescribe(\"updateAddonStack\", () => {\n\tit(\"adds a service to an existing stack\", () => {\n\t\t// First generate a base stack\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Then add meilisearch\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.metadata.added).toContain(\"meilisearch\");\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\t\texpect(composed.services).toHaveProperty(\"meilisearch\");\n\t});\n\n\tit(\"removes a service from an existing stack\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"meilisearch\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\tremoveServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.metadata.removed).toContain(\"meilisearch\");\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\t\texpect(composed.services).not.toHaveProperty(\"meilisearch\");\n\t});\n\n\tit(\"preserves existing env values when adding a service\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// Simulate user editing an env value\n\t\tconst customEnv = base.envFile.replace(\n\t\t\t/QDRANT_API_KEY=[a-f0-9]+/,\n\t\t\t\"QDRANT_API_KEY=my_custom_key\",\n\t\t);\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: customEnv,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// If QDRANT_API_KEY was in the original env, it should be preserved\n\t\tif (customEnv.includes(\"QDRANT_API_KEY=my_custom_key\")) {\n\t\t\texpect(result.envFile).toContain(\"QDRANT_API_KEY=my_custom_key\");\n\t\t}\n\t});\n\n\tit(\"returns images to pull for added services\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.imagesToPull.length).toBeGreaterThan(0);\n\t\texpect(result.imagesToPull.some((img) => img.includes(\"meilisearch\"))).toBe(true);\n\t});\n\n\tit(\"handles empty update gracefully\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t});\n\n\t\texpect(result.metadata.added).toEqual([]);\n\t\texpect(result.metadata.removed).toEqual([]);\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\t});\n\n\tit(\"respects generateSecrets: false during update\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// With generateSecrets: false, services requiring secrets (like meilisearch)\n\t\t// may be skipped as missing_credentials. Provide credentials explicitly.\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: false,\n\t\t\tcredentials: {\n\t\t\t\tmeilisearch: { MEILI_MASTER_KEY: \"test-master-key-1234567890\" },\n\t\t\t},\n\t\t});\n\n\t\t// Meilisearch should be added since we provided the required credential\n\t\texpect(result.metadata.added).toContain(\"meilisearch\");\n\t});\n\n\tit(\"forwards portOverrides during update\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"n8n\"],\n\t\t\tportOverrides: { n8n: { \"5678\": 9999 } },\n\t\t});\n\n\t\t// n8n should be present with the port override\n\t\texpect(result.metadata.added).toContain(\"n8n\");\n\t});\n});\n\ndescribe(\"AddonStackInputSchema\", () => {\n\tit(\"accepts valid input\", () => {\n\t\tconst result = AddonStackInputSchema.safeParse({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"n8n\"],\n\t\t});\n\t\texpect(result.success).toBe(true);\n\t});\n\n\tit(\"requires instanceId\", () => {\n\t\tconst result = AddonStackInputSchema.safeParse({\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\t\texpect(result.success).toBe(false);\n\t});\n\n\tit(\"defaults empty arrays and booleans\", () => {\n\t\tconst result = AddonStackInputSchema.parse({\n\t\t\tinstanceId: \"test\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\t\texpect(result.skillPacks).toEqual([]);\n\t\texpect(result.reservedPorts).toEqual([]);\n\t\texpect(result.generateSecrets).toBe(true);\n\t\texpect(result.platform).toBe(\"linux/amd64\");\n\t});\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"addon-stack.test.mjs","names":["expect"],"sources":["../src/addon-stack.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { parse } from \"yaml\";\nimport { generateAddonStack, updateAddonStack } from \"./addon-stack.js\";\nimport { AddonStackInputSchema } from \"./schema.js\";\n\ndescribe(\"generateAddonStack\", () => {\n\tit(\"generates valid compose YAML with a single service (qdrant)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Resolver may generate warnings about recommended services; that's fine\n\t\texpect(result.metadata.serviceCount).toBeGreaterThan(0);\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\n\t\t// Parse YAML to verify it's valid\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\n\t\t// Should NOT contain infrastructure services\n\t\texpect(composed.services).not.toHaveProperty(\"openclaw-gateway\");\n\t\texpect(composed.services).not.toHaveProperty(\"openclaw-cli\");\n\t\texpect(composed.services).not.toHaveProperty(\"redis\");\n\t\texpect(composed.services).not.toHaveProperty(\"postgresql\");\n\t\texpect(composed.services).not.toHaveProperty(\"open-webui\");\n\t\texpect(composed.services).not.toHaveProperty(\"caddy\");\n\n\t\t// Should have openclaw-network as external\n\t\texpect(composed.networks).toHaveProperty(\"openclaw-network\");\n\t\texpect(composed.networks[\"openclaw-network\"].external).toBe(true);\n\t});\n\n\tit(\"does not include profiles on any service\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"meilisearch\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\tfor (const [, svc] of Object.entries(composed.services)) {\n\t\t\texpect(svc).not.toHaveProperty(\"profiles\");\n\t\t}\n\t});\n\n\tit(\"does not apply cap_drop or security_opt by default\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\tconst qdrant = composed.services.qdrant;\n\t\texpect(qdrant).not.toHaveProperty(\"cap_drop\");\n\t\texpect(qdrant).not.toHaveProperty(\"security_opt\");\n\t});\n\n\tit(\"includes postgres-setup when a DB-dependent service is requested (n8n)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"n8n\");\n\t\texpect(composed.services).toHaveProperty(\"postgres-setup\");\n\n\t\t// postgres-setup should depend on existing postgresql\n\t\texpect(composed.services[\"postgres-setup\"].depends_on).toHaveProperty(\"postgresql\");\n\n\t\t// n8n should depend on postgres-setup\n\t\texpect(composed.services.n8n.depends_on).toHaveProperty(\"postgres-setup\");\n\t});\n\n\tit(\"generates DB passwords in env file for DB-dependent services\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\texpect(result.envFile).toContain(\"N8N_DB_PASSWORD=\");\n\t\t// Password should be non-empty (48 hex chars = 24 bytes)\n\t\tconst match = result.envFile.match(/N8N_DB_PASSWORD=([a-f0-9]+)/);\n\t\texpect(match).not.toBeNull();\n\t\texpect(match?.[1].length).toBe(48);\n\t});\n\n\tit(\"gracefully handles unknown service IDs without throwing\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"nonexistent-service\", \"qdrant\"],\n\t\t});\n\n\t\t// Should NOT throw\n\t\texpect(result.metadata.skippedServices).toEqual(\n\t\t\texpect.arrayContaining([\n\t\t\t\texpect.objectContaining({\n\t\t\t\t\tserviceId: \"nonexistent-service\",\n\t\t\t\t\treason: \"unknown_service\",\n\t\t\t\t}),\n\t\t\t]),\n\t\t);\n\n\t\t// Valid service should still be included\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\t});\n\n\tit(\"excludes infrastructure services from the request\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"redis\", \"postgresql\", \"qdrant\"],\n\t\t});\n\n\t\texpect(result.warnings).toEqual(\n\t\t\texpect.arrayContaining([\n\t\t\t\texpect.stringContaining(\"redis\"),\n\t\t\t\texpect.stringContaining(\"postgresql\"),\n\t\t\t]),\n\t\t);\n\t\texpect(result.metadata.resolvedServices).not.toContain(\"redis\");\n\t\texpect(result.metadata.resolvedServices).not.toContain(\"postgresql\");\n\t\texpect(result.metadata.resolvedServices).toContain(\"qdrant\");\n\t});\n\n\tit(\"generates proxy routes from proxyPath\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\tconst n8nRoute = result.proxyRoutes.find((r) => r.serviceId === \"n8n\");\n\t\texpect(n8nRoute).toBeDefined();\n\t\texpect(n8nRoute?.path).toBe(\"/n8n\");\n\t\texpect(n8nRoute?.port).toBe(5678);\n\t});\n\n\tit(\"generates skill files and openclaw config patch\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t});\n\n\t\t// n8n has skill binding: n8n-trigger\n\t\texpect(result.openclawConfigPatch.skills.entries).toHaveProperty(\"n8n-trigger\");\n\t\texpect(result.openclawConfigPatch.skills.entries[\"n8n-trigger\"].enabled).toBe(true);\n\t});\n\n\tit(\"resolves port conflicts with reserved ports\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\treservedPorts: [5678], // n8n's default port\n\t\t});\n\n\t\t// Port should be reassigned\n\t\tconst portAssignments = result.metadata.portAssignments;\n\t\tconst n8nAssignment = Object.entries(portAssignments).find(([key]) => key.startsWith(\"n8n:\"));\n\t\texpect(n8nAssignment).toBeDefined();\n\t\texpect(n8nAssignment?.[1]).not.toBe(5678);\n\t});\n\n\tit(\"sanitizes project name from instanceId\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"My_Instance_123\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Should succeed without error\n\t\texpect(result.metadata.serviceCount).toBeGreaterThan(0);\n\t});\n\n\tit(\"returns env vars grouped by service\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\texpect(result.envVars.length).toBeGreaterThanOrEqual(0);\n\t});\n\n\tit(\"returns empty result for no services\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [],\n\t\t});\n\n\t\texpect(result.composeOverride).toContain(\"services: {}\");\n\t\texpect(result.warnings.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"returns empty result when all services are infrastructure\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"redis\", \"postgresql\", \"caddy\"],\n\t\t});\n\n\t\texpect(result.metadata.serviceCount).toBe(0);\n\t\texpect(result.warnings.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"applies env quirks to envFile output (meilisearch MEILI_MASTER_KEY min_length)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// MEILI_MASTER_KEY should be a base64url string of at least 32 chars (24 bytes)\n\t\tconst match = result.envFile.match(/MEILI_MASTER_KEY=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\texpect(match?.[1].length).toBeGreaterThanOrEqual(32);\n\t\t// Should not be empty\n\t\texpect(match?.[1]).not.toBe(\"\");\n\t});\n\n\tit(\"applies env quirks to envFile output (grafana GF_SECURITY_ADMIN_PASSWORD)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"grafana\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst match = result.envFile.match(/GF_SECURITY_ADMIN_PASSWORD=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\t// Should be at least 22 chars (16 bytes base64url)\n\t\texpect(match?.[1].length).toBeGreaterThanOrEqual(22);\n\t});\n\n\tit(\"syncs DB_POSTGRESDB_PASSWORD with N8N_DB_PASSWORD via must_sync quirk\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst n8nDbPw = result.envFile.match(/N8N_DB_PASSWORD=([^\\n]+)/);\n\t\tconst dbPostgresPw = result.envFile.match(/DB_POSTGRESDB_PASSWORD=([^\\n]+)/);\n\t\texpect(n8nDbPw).not.toBeNull();\n\t\texpect(dbPostgresPw).not.toBeNull();\n\t\t// Both passwords must match due to must_sync quirk\n\t\texpect(n8nDbPw?.[1]).toBe(dbPostgresPw?.[1]);\n\t});\n\n\tit(\"syncs user-provided credential with DB password reference\", () => {\n\t\tconst customPassword = \"my_custom_secure_password\";\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\tgenerateSecrets: true,\n\t\t\tcredentials: {\n\t\t\t\tn8n: { DB_POSTGRESDB_PASSWORD: customPassword },\n\t\t\t},\n\t\t});\n\n\t\t// User value should be used for both the service env var and the ref key\n\t\texpect(result.envFile).toContain(`DB_POSTGRESDB_PASSWORD=${customPassword}`);\n\t\texpect(result.envFile).toContain(`N8N_DB_PASSWORD=${customPassword}`);\n\t});\n\n\tit(\"uses existingServices ports for conflict detection\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"n8n\"],\n\t\t\t// n8n uses port 5678, qdrant uses 6333 — qdrant as existing should not conflict\n\t\t\texistingServices: [\"qdrant\"],\n\t\t});\n\n\t\t// n8n should still get its default port (no conflict with qdrant)\n\t\tconst portAssignments = result.metadata.portAssignments;\n\t\tconst n8nAssignment = Object.entries(portAssignments).find(([key]) => key.startsWith(\"n8n:\"));\n\t\texpect(n8nAssignment).toBeDefined();\n\t\texpect(n8nAssignment?.[1]).toBe(5678);\n\t});\n\n\tit(\"does not dual-list GPU services in both skipped and resolved\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"ollama\"],\n\t\t\tgpu: false,\n\t\t});\n\n\t\t// ollama requires GPU — without gpu: true, it should be skipped only\n\t\tconst isSkipped = result.metadata.skippedServices.some(\n\t\t\t(s) => s.serviceId === \"ollama\" && s.reason === \"gpu_required\",\n\t\t);\n\t\tconst isResolved = result.metadata.resolvedServices.includes(\"ollama\");\n\n\t\t// Must be in exactly one list, not both\n\t\tif (isSkipped) {\n\t\t\texpect(isResolved).toBe(false);\n\t\t}\n\t});\n\n\tit(\"includes GPU services when gpu: true is set\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"ollama\"],\n\t\t\tgpu: true,\n\t\t});\n\n\t\t// Should NOT be skipped when GPU support is available\n\t\tconst isSkipped = result.metadata.skippedServices.some(\n\t\t\t(s) => s.serviceId === \"ollama\" && s.reason === \"gpu_required\",\n\t\t);\n\t\texpect(isSkipped).toBe(false);\n\t\texpect(result.metadata.resolvedServices).toContain(\"ollama\");\n\t});\n\n\t// ── OpenSandbox ────────────────────────────────────────────────────────\n\n\tit(\"generates valid compose YAML for opensandbox\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.metadata.resolvedServices).toContain(\"opensandbox\");\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"opensandbox\");\n\n\t\t// Should have Docker socket mount\n\t\tconst volumes = composed.services.opensandbox.volumes as string[];\n\t\texpect(volumes.some((v: string) => v.includes(\"/var/run/docker.sock\"))).toBe(true);\n\n\t\t// Should have config bind mount\n\t\texpect(volumes.some((v: string) => v.includes(\"sandbox.toml\"))).toBe(true);\n\t});\n\n\tit(\"generates OPEN_SANDBOX_API_KEY with min 32 bytes via env quirk\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\tconst match = result.envFile.match(/OPEN_SANDBOX_API_KEY=([^\\n]+)/);\n\t\texpect(match).not.toBeNull();\n\t\t// 32 bytes base64url ≈ 43 chars\n\t\texpect(match?.[1].length).toBeGreaterThanOrEqual(43);\n\t});\n\n\tit(\"generates code-sandbox skill file and config patch\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\t// Skill config patch\n\t\texpect(result.openclawConfigPatch.skills.entries).toHaveProperty(\"code-sandbox\");\n\t\texpect(result.openclawConfigPatch.skills.entries[\"code-sandbox\"].enabled).toBe(true);\n\n\t\t// Skill file content should exist and contain key actions\n\t\tconst skillFile = Object.values(result.skillFiles).find((content) =>\n\t\t\tcontent.includes(\"code-sandbox\"),\n\t\t);\n\t\texpect(skillFile).toBeDefined();\n\t\texpect(skillFile).toContain(\"execute_code\");\n\t\texpect(skillFile).toContain(\"create_desktop\");\n\t\texpect(skillFile).toContain(\"get_preview_url\");\n\t});\n\n\tit(\"generates proxy route for opensandbox at /sandbox\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst route = result.proxyRoutes.find((r) => r.serviceId === \"opensandbox\");\n\t\texpect(route).toBeDefined();\n\t\texpect(route?.path).toBe(\"/sandbox\");\n\t\texpect(route?.port).toBe(8080);\n\t});\n\n\tit(\"generates sandbox.toml in additionalFiles\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.additionalFiles).toHaveProperty(\"sandbox.toml\");\n\t\tconst toml = result.additionalFiles[\"sandbox.toml\"];\n\t\texpect(toml).toContain(\"[server]\");\n\t\texpect(toml).toContain('api_key = \"${OPEN_SANDBOX_API_KEY}\"');\n\t\texpect(toml).toContain(\"[runtime]\");\n\t\texpect(toml).toContain(\"[docker]\");\n\t\texpect(toml).toContain(\"[secure_runtime]\");\n\t\texpect(toml).toContain('type = \"gvisor\"');\n\t});\n\n\tit(\"populates prePullImages with 8 images across 3 priority tiers\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst images = result.metadata.prePullImages;\n\t\texpect(images.length).toBe(8);\n\n\t\t// Priority 1: core + Homespace\n\t\tconst p1 = images.filter((i) => i.priority === 1);\n\t\texpect(p1.length).toBe(4);\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/server:v1.0.6\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/execd:v1.0.6\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/desktop:latest\");\n\t\texpect(p1.map((i) => i.image)).toContain(\"opensandbox/chrome:latest\");\n\n\t\t// Priority 2: common languages\n\t\tconst p2 = images.filter((i) => i.priority === 2);\n\t\texpect(p2.length).toBe(2);\n\t\texpect(p2.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:python\");\n\t\texpect(p2.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:node\");\n\n\t\t// Priority 3: optional\n\t\tconst p3 = images.filter((i) => i.priority === 3);\n\t\texpect(p3.length).toBe(2);\n\t\texpect(p3.map((i) => i.image)).toContain(\"opensandbox/code-interpreter:latest\");\n\t\texpect(p3.map((i) => i.image)).toContain(\"opensandbox/vscode:latest\");\n\t});\n\n\tit(\"resolves port conflict between opensandbox and searxng (both 8080)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\", \"searxng\"],\n\t\t});\n\n\t\texpect(result.metadata.resolvedServices).toContain(\"opensandbox\");\n\t\texpect(result.metadata.resolvedServices).toContain(\"searxng\");\n\n\t\t// Both should have port assignments, but they should differ\n\t\tconst assignments = result.metadata.portAssignments;\n\t\tconst opensandboxPort = assignments[\"opensandbox:8080\"];\n\t\tconst searxngPort = assignments[\"searxng:8080\"];\n\t\texpect(opensandboxPort).toBeDefined();\n\t\texpect(searxngPort).toBeDefined();\n\t\texpect(opensandboxPort).not.toBe(searxngPort);\n\t});\n\n\tit(\"does not include opensandbox in prePullImages when not selected\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\texpect(result.metadata.prePullImages.length).toBe(0);\n\t\texpect(result.additionalFiles).not.toHaveProperty(\"sandbox.toml\");\n\t});\n\n\tit(\"opensandbox does not require postgresql (no postgres-setup)\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).not.toHaveProperty(\"postgres-setup\");\n\t});\n\n\tit(\"accounts for 768MB memory in estimatedMemoryMB\", () => {\n\t\tconst result = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"opensandbox\"],\n\t\t});\n\n\t\texpect(result.metadata.estimatedMemoryMB).toBeGreaterThanOrEqual(768);\n\t});\n});\n\ndescribe(\"updateAddonStack\", () => {\n\tit(\"adds a service to an existing stack\", () => {\n\t\t// First generate a base stack\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\t// Then add meilisearch\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.metadata.added).toContain(\"meilisearch\");\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\t\texpect(composed.services).toHaveProperty(\"meilisearch\");\n\t});\n\n\tit(\"removes a service from an existing stack\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"meilisearch\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\tremoveServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.metadata.removed).toContain(\"meilisearch\");\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\n\t\tconst composed = parse(result.composeOverride);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\t\texpect(composed.services).not.toHaveProperty(\"meilisearch\");\n\t});\n\n\tit(\"preserves existing env values when adding a service\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// Simulate user editing an env value\n\t\tconst customEnv = base.envFile.replace(\n\t\t\t/QDRANT_API_KEY=[a-f0-9]+/,\n\t\t\t\"QDRANT_API_KEY=my_custom_key\",\n\t\t);\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: customEnv,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// If QDRANT_API_KEY was in the original env, it should be preserved\n\t\tif (customEnv.includes(\"QDRANT_API_KEY=my_custom_key\")) {\n\t\t\texpect(result.envFile).toContain(\"QDRANT_API_KEY=my_custom_key\");\n\t\t}\n\t});\n\n\tit(\"returns images to pull for added services\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t});\n\n\t\texpect(result.imagesToPull.length).toBeGreaterThan(0);\n\t\texpect(result.imagesToPull.some((img) => img.includes(\"meilisearch\"))).toBe(true);\n\t});\n\n\tit(\"handles empty update gracefully\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t});\n\n\t\texpect(result.metadata.added).toEqual([]);\n\t\texpect(result.metadata.removed).toEqual([]);\n\t\texpect(result.metadata.unchanged).toContain(\"qdrant\");\n\t});\n\n\tit(\"respects generateSecrets: false during update\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t\tgenerateSecrets: true,\n\t\t});\n\n\t\t// With generateSecrets: false, services requiring secrets (like meilisearch)\n\t\t// may be skipped as missing_credentials. Provide credentials explicitly.\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"meilisearch\"],\n\t\t\tgenerateSecrets: false,\n\t\t\tcredentials: {\n\t\t\t\tmeilisearch: { MEILI_MASTER_KEY: \"test-master-key-1234567890\" },\n\t\t\t},\n\t\t});\n\n\t\t// Meilisearch should be added since we provided the required credential\n\t\texpect(result.metadata.added).toContain(\"meilisearch\");\n\t});\n\n\tit(\"forwards portOverrides during update\", () => {\n\t\tconst base = generateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\n\t\tconst result = updateAddonStack({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tcurrentCompose: base.composeOverride,\n\t\t\tcurrentEnv: base.envFile,\n\t\t\taddServices: [\"n8n\"],\n\t\t\tportOverrides: { n8n: { \"5678\": 9999 } },\n\t\t});\n\n\t\t// n8n should be present with the port override\n\t\texpect(result.metadata.added).toContain(\"n8n\");\n\t});\n});\n\ndescribe(\"AddonStackInputSchema\", () => {\n\tit(\"accepts valid input\", () => {\n\t\tconst result = AddonStackInputSchema.safeParse({\n\t\t\tinstanceId: \"test-instance\",\n\t\t\tservices: [\"qdrant\", \"n8n\"],\n\t\t});\n\t\texpect(result.success).toBe(true);\n\t});\n\n\tit(\"requires instanceId\", () => {\n\t\tconst result = AddonStackInputSchema.safeParse({\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\t\texpect(result.success).toBe(false);\n\t});\n\n\tit(\"defaults empty arrays and booleans\", () => {\n\t\tconst result = AddonStackInputSchema.parse({\n\t\t\tinstanceId: \"test\",\n\t\t\tservices: [\"qdrant\"],\n\t\t});\n\t\texpect(result.skillPacks).toEqual([]);\n\t\texpect(result.reservedPorts).toEqual([]);\n\t\texpect(result.generateSecrets).toBe(true);\n\t\texpect(result.platform).toBe(\"linux/amd64\");\n\t});\n});\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { NativePlatform, Platform, ResolvedService, ResolverOutput } from "./types.cjs";
1
+ import { B as ResolverOutput, D as NativePlatform, L as ResolvedService, M as Platform } from "./types-Bsn0XzSP.cjs";
2
2
 
3
3
  //#region src/bare-metal-partition.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { NativePlatform, Platform, ResolvedService, ResolverOutput } from "./types.mjs";
1
+ import { B as ResolverOutput, D as NativePlatform, L as ResolvedService, M as Platform } from "./types-DwTKyCZ9.mjs";
2
2
 
3
3
  //#region src/bare-metal-partition.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- const require_test_CTcmp4Su = require("./test.CTcmp4Su-DlzTarwH.cjs");
1
+ const require_test_CTcmp4Su = require("./test.CTcmp4Su-BWSPM8ZQ.cjs");
2
2
  const require_resolver = require("./resolver.cjs");
3
3
  const require_bare_metal_partition = require("./bare-metal-partition.cjs");
4
4
  //#region src/bare-metal-partition.test.ts
@@ -1,4 +1,4 @@
1
- import { n as describe, r as it, t as globalExpect } from "./test.CTcmp4Su-ClCHJ3FA.mjs";
1
+ import { a as describe, o as it, t as globalExpect } from "./test.CTcmp4Su-BRa7-bTj.mjs";
2
2
  import { resolve } from "./resolver.mjs";
3
3
  import { partitionBareMetal, platformToNativePlatform, resolvedWithOnlyServices } from "./bare-metal-partition.mjs";
4
4
  //#region src/bare-metal-partition.test.ts
@@ -0,0 +1,15 @@
1
+ import { n as LogSink, s as OperationsLogEntry } from "./types-BREUfzzq.mjs";
2
+
3
+ //#region src/logger/sinks/callback-sink.d.ts
4
+ /**
5
+ * Callback sink — delegates to a user-provided function.
6
+ * Useful for tests, Sentry breadcrumbs, WebSocket streaming, etc.
7
+ */
8
+ declare class CallbackSink implements LogSink {
9
+ private readonly onEntry;
10
+ constructor(onEntry: (entry: OperationsLogEntry) => void | Promise<void>);
11
+ write(entry: OperationsLogEntry): void | Promise<void>;
12
+ }
13
+ //#endregion
14
+ export { CallbackSink as t };
15
+ //# sourceMappingURL=callback-sink-CdMKYayY.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callback-sink-CdMKYayY.d.mts","names":[],"sources":["../src/logger/sinks/callback-sink.ts"],"mappings":";;;;;AAMA;;cAAa,YAAA,YAAwB,OAAA;EAAA,iBACP,OAAA;cAAA,OAAA,GAAU,KAAA,EAAO,kBAAA,YAA8B,OAAA;EAE5E,KAAA,CAAM,KAAA,EAAO,kBAAA,UAA4B,OAAA;AAAA"}
@@ -0,0 +1,15 @@
1
+ import { n as LogSink, s as OperationsLogEntry } from "./types-CyZ5mn6w.cjs";
2
+
3
+ //#region src/logger/sinks/callback-sink.d.ts
4
+ /**
5
+ * Callback sink — delegates to a user-provided function.
6
+ * Useful for tests, Sentry breadcrumbs, WebSocket streaming, etc.
7
+ */
8
+ declare class CallbackSink implements LogSink {
9
+ private readonly onEntry;
10
+ constructor(onEntry: (entry: OperationsLogEntry) => void | Promise<void>);
11
+ write(entry: OperationsLogEntry): void | Promise<void>;
12
+ }
13
+ //#endregion
14
+ export { CallbackSink as t };
15
+ //# sourceMappingURL=callback-sink-iNThWRnQ.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callback-sink-iNThWRnQ.d.cts","names":[],"sources":["../src/logger/sinks/callback-sink.ts"],"mappings":";;;;;AAMA;;cAAa,YAAA,YAAwB,OAAA;EAAA,iBACP,OAAA;cAAA,OAAA,GAAU,KAAA,EAAO,kBAAA,YAA8B,OAAA;EAE5E,KAAA,CAAM,KAAA,EAAO,kBAAA,UAA4B,OAAA;AAAA"}
@@ -0,0 +1,7 @@
1
+ import { t as AgentFrameworkDefinition } from "./types-DbXajvYq.mjs";
2
+
3
+ //#region src/frameworks/claude-code-fw.d.ts
4
+ declare const claudeCodeFramework: AgentFrameworkDefinition;
5
+ //#endregion
6
+ export { claudeCodeFramework as t };
7
+ //# sourceMappingURL=claude-code-fw-CrbAlzTM.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code-fw-CrbAlzTM.d.mts","names":[],"sources":["../src/frameworks/claude-code-fw.ts"],"mappings":";;;cAuBa,mBAAA,EAAqB,wBAAA"}
@@ -0,0 +1,7 @@
1
+ import { t as AgentFrameworkDefinition } from "./types-DIsPc-hb.cjs";
2
+
3
+ //#region src/frameworks/claude-code-fw.d.ts
4
+ declare const claudeCodeFramework: AgentFrameworkDefinition;
5
+ //#endregion
6
+ export { claudeCodeFramework as t };
7
+ //# sourceMappingURL=claude-code-fw-dKYH2prH.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code-fw-dKYH2prH.d.cts","names":[],"sources":["../src/frameworks/claude-code-fw.ts"],"mappings":";;;cAuBa,mBAAA,EAAqB,wBAAA"}
@@ -0,0 +1,7 @@
1
+ import { t as AgentFrameworkDefinition } from "./types-DbXajvYq.mjs";
2
+
3
+ //#region src/frameworks/codex-fw.d.ts
4
+ declare const codexFramework: AgentFrameworkDefinition;
5
+ //#endregion
6
+ export { codexFramework as t };
7
+ //# sourceMappingURL=codex-fw-Ct8q4pzo.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-fw-Ct8q4pzo.d.mts","names":[],"sources":["../src/frameworks/codex-fw.ts"],"mappings":";;;cAiBa,cAAA,EAAgB,wBAAA"}
@@ -0,0 +1,7 @@
1
+ import { t as AgentFrameworkDefinition } from "./types-DIsPc-hb.cjs";
2
+
3
+ //#region src/frameworks/codex-fw.d.ts
4
+ declare const codexFramework: AgentFrameworkDefinition;
5
+ //#endregion
6
+ export { codexFramework as t };
7
+ //# sourceMappingURL=codex-fw-l7Ku8ZI8.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-fw-l7Ku8ZI8.d.cts","names":[],"sources":["../src/frameworks/codex-fw.ts"],"mappings":";;;cAiBa,cAAA,EAAgB,wBAAA"}
@@ -1,4 +1,4 @@
1
- const require_skills = require("./skills-BSF7iNa4.cjs");
1
+ const require_skills = require("./skills-uPxJVmKk.cjs");
2
2
  const require_generate = require("./generate.cjs");
3
3
  const require_presets_registry = require("./presets/registry.cjs");
4
4
  let yaml = require("yaml");