@better-openclaw/core 1.0.23 → 1.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/dist/addon-stack.cjs +673 -0
  2. package/dist/addon-stack.cjs.map +1 -0
  3. package/dist/addon-stack.d.cts +23 -0
  4. package/dist/addon-stack.d.cts.map +1 -0
  5. package/dist/addon-stack.d.mts +23 -0
  6. package/dist/addon-stack.d.mts.map +1 -0
  7. package/dist/addon-stack.mjs +671 -0
  8. package/dist/addon-stack.mjs.map +1 -0
  9. package/dist/addon-stack.test.cjs +349 -0
  10. package/dist/addon-stack.test.cjs.map +1 -0
  11. package/dist/addon-stack.test.d.cts +1 -0
  12. package/dist/addon-stack.test.d.mts +1 -0
  13. package/dist/addon-stack.test.mjs +349 -0
  14. package/dist/addon-stack.test.mjs.map +1 -0
  15. package/dist/bare-metal-partition.test.cjs +20 -21
  16. package/dist/bare-metal-partition.test.cjs.map +1 -1
  17. package/dist/bare-metal-partition.test.mjs +4 -5
  18. package/dist/bare-metal-partition.test.mjs.map +1 -1
  19. package/dist/composer.cjs +17 -1
  20. package/dist/composer.cjs.map +1 -1
  21. package/dist/composer.d.cts +24 -1
  22. package/dist/composer.d.cts.map +1 -1
  23. package/dist/composer.d.mts +24 -1
  24. package/dist/composer.d.mts.map +1 -1
  25. package/dist/composer.mjs +14 -2
  26. package/dist/composer.mjs.map +1 -1
  27. package/dist/composer.snapshot.test.cjs +20 -20
  28. package/dist/composer.snapshot.test.cjs.map +1 -1
  29. package/dist/composer.snapshot.test.mjs +2 -2
  30. package/dist/composer.test.cjs +53 -52
  31. package/dist/composer.test.cjs.map +1 -1
  32. package/dist/composer.test.mjs +4 -3
  33. package/dist/composer.test.mjs.map +1 -1
  34. package/dist/deployers/strip-host-ports.test.cjs +26 -26
  35. package/dist/deployers/strip-host-ports.test.cjs.map +1 -1
  36. package/dist/deployers/strip-host-ports.test.mjs +1 -1
  37. package/dist/generate.cjs +8 -4
  38. package/dist/generate.cjs.map +1 -1
  39. package/dist/generate.d.cts.map +1 -1
  40. package/dist/generate.d.mts.map +1 -1
  41. package/dist/generate.mjs +9 -5
  42. package/dist/generate.mjs.map +1 -1
  43. package/dist/generate.test.cjs +55 -55
  44. package/dist/generate.test.cjs.map +1 -1
  45. package/dist/generate.test.mjs +2 -2
  46. package/dist/generate.test.mjs.map +1 -1
  47. package/dist/generators/bare-metal-install.test.cjs +18 -18
  48. package/dist/generators/bare-metal-install.test.cjs.map +1 -1
  49. package/dist/generators/bare-metal-install.test.mjs +1 -1
  50. package/dist/generators/caddy.test.cjs +13 -13
  51. package/dist/generators/caddy.test.cjs.map +1 -1
  52. package/dist/generators/caddy.test.mjs +1 -1
  53. package/dist/generators/clone-repos.cjs +140 -0
  54. package/dist/generators/clone-repos.cjs.map +1 -0
  55. package/dist/generators/clone-repos.d.cts +11 -0
  56. package/dist/generators/clone-repos.d.cts.map +1 -0
  57. package/dist/generators/clone-repos.d.mts +11 -0
  58. package/dist/generators/clone-repos.d.mts.map +1 -0
  59. package/dist/generators/clone-repos.mjs +139 -0
  60. package/dist/generators/clone-repos.mjs.map +1 -0
  61. package/dist/generators/clone-repos.test.cjs +140 -0
  62. package/dist/generators/clone-repos.test.cjs.map +1 -0
  63. package/dist/generators/clone-repos.test.d.cts +1 -0
  64. package/dist/generators/clone-repos.test.d.mts +1 -0
  65. package/dist/generators/clone-repos.test.mjs +141 -0
  66. package/dist/generators/clone-repos.test.mjs.map +1 -0
  67. package/dist/generators/env.test.cjs +17 -17
  68. package/dist/generators/env.test.cjs.map +1 -1
  69. package/dist/generators/env.test.mjs +1 -1
  70. package/dist/generators/health-check.test.cjs +39 -39
  71. package/dist/generators/health-check.test.cjs.map +1 -1
  72. package/dist/generators/health-check.test.mjs +1 -1
  73. package/dist/generators/postgres-init.cjs +20 -0
  74. package/dist/generators/postgres-init.cjs.map +1 -1
  75. package/dist/generators/postgres-init.d.cts.map +1 -1
  76. package/dist/generators/postgres-init.d.mts.map +1 -1
  77. package/dist/generators/postgres-init.mjs +20 -0
  78. package/dist/generators/postgres-init.mjs.map +1 -1
  79. package/dist/generators/scripts.cjs +332 -3
  80. package/dist/generators/scripts.cjs.map +1 -1
  81. package/dist/generators/scripts.d.cts +3 -1
  82. package/dist/generators/scripts.d.cts.map +1 -1
  83. package/dist/generators/scripts.d.mts +3 -1
  84. package/dist/generators/scripts.d.mts.map +1 -1
  85. package/dist/generators/scripts.mjs +332 -3
  86. package/dist/generators/scripts.mjs.map +1 -1
  87. package/dist/generators/scripts.test.cjs +57 -23
  88. package/dist/generators/scripts.test.cjs.map +1 -1
  89. package/dist/generators/scripts.test.mjs +39 -5
  90. package/dist/generators/scripts.test.mjs.map +1 -1
  91. package/dist/generators/stack-manifest.cjs +1 -0
  92. package/dist/generators/stack-manifest.cjs.map +1 -1
  93. package/dist/generators/stack-manifest.d.cts +3 -2
  94. package/dist/generators/stack-manifest.d.cts.map +1 -1
  95. package/dist/generators/stack-manifest.d.mts +3 -2
  96. package/dist/generators/stack-manifest.d.mts.map +1 -1
  97. package/dist/generators/stack-manifest.mjs +1 -0
  98. package/dist/generators/stack-manifest.mjs.map +1 -1
  99. package/dist/generators/traefik.test.cjs +32 -32
  100. package/dist/generators/traefik.test.cjs.map +1 -1
  101. package/dist/generators/traefik.test.mjs +1 -1
  102. package/dist/index.cjs +28 -5
  103. package/dist/index.d.cts +7 -4
  104. package/dist/index.d.mts +7 -4
  105. package/dist/index.mjs +10 -7
  106. package/dist/migrations.test.cjs +16 -16
  107. package/dist/migrations.test.cjs.map +1 -1
  108. package/dist/migrations.test.mjs +1 -1
  109. package/dist/presets/registry.cjs.map +1 -1
  110. package/dist/presets/registry.d.cts.map +1 -1
  111. package/dist/presets/registry.d.mts.map +1 -1
  112. package/dist/presets/registry.mjs.map +1 -1
  113. package/dist/presets/registry.test.cjs +14 -14
  114. package/dist/presets/registry.test.cjs.map +1 -1
  115. package/dist/presets/registry.test.mjs +1 -1
  116. package/dist/resolver.cjs +8 -0
  117. package/dist/resolver.cjs.map +1 -1
  118. package/dist/resolver.mjs +9 -1
  119. package/dist/resolver.mjs.map +1 -1
  120. package/dist/resolver.test.cjs +125 -90
  121. package/dist/resolver.test.cjs.map +1 -1
  122. package/dist/resolver.test.mjs +47 -12
  123. package/dist/resolver.test.mjs.map +1 -1
  124. package/dist/{schema-B4c64P8N.d.cts → schema-CKBRu-Rt.d.cts} +355 -8
  125. package/dist/schema-CKBRu-Rt.d.cts.map +1 -0
  126. package/dist/{schema-CXNhYci1.d.mts → schema-Dn-_Jpb6.d.mts} +355 -8
  127. package/dist/schema-Dn-_Jpb6.d.mts.map +1 -0
  128. package/dist/schema.cjs +160 -5
  129. package/dist/schema.cjs.map +1 -1
  130. package/dist/schema.d.cts +2 -2
  131. package/dist/schema.d.mts +2 -2
  132. package/dist/schema.mjs +150 -6
  133. package/dist/schema.mjs.map +1 -1
  134. package/dist/schema.test.cjs +86 -86
  135. package/dist/schema.test.cjs.map +1 -1
  136. package/dist/schema.test.mjs +1 -1
  137. package/dist/services/definitions/apptension-saas.cjs +87 -0
  138. package/dist/services/definitions/apptension-saas.cjs.map +1 -0
  139. package/dist/services/definitions/apptension-saas.d.cts +7 -0
  140. package/dist/services/definitions/apptension-saas.d.cts.map +1 -0
  141. package/dist/services/definitions/apptension-saas.d.mts +7 -0
  142. package/dist/services/definitions/apptension-saas.d.mts.map +1 -0
  143. package/dist/services/definitions/apptension-saas.mjs +86 -0
  144. package/dist/services/definitions/apptension-saas.mjs.map +1 -0
  145. package/dist/services/definitions/boxyhq-saas.cjs +88 -0
  146. package/dist/services/definitions/boxyhq-saas.cjs.map +1 -0
  147. package/dist/services/definitions/boxyhq-saas.d.cts +7 -0
  148. package/dist/services/definitions/boxyhq-saas.d.cts.map +1 -0
  149. package/dist/services/definitions/boxyhq-saas.d.mts +7 -0
  150. package/dist/services/definitions/boxyhq-saas.d.mts.map +1 -0
  151. package/dist/services/definitions/boxyhq-saas.mjs +87 -0
  152. package/dist/services/definitions/boxyhq-saas.mjs.map +1 -0
  153. package/dist/services/definitions/browserless.cjs +4 -1
  154. package/dist/services/definitions/browserless.cjs.map +1 -1
  155. package/dist/services/definitions/browserless.mjs +4 -1
  156. package/dist/services/definitions/browserless.mjs.map +1 -1
  157. package/dist/services/definitions/cmsaas-starter.cjs +86 -0
  158. package/dist/services/definitions/cmsaas-starter.cjs.map +1 -0
  159. package/dist/services/definitions/cmsaas-starter.d.cts +7 -0
  160. package/dist/services/definitions/cmsaas-starter.d.cts.map +1 -0
  161. package/dist/services/definitions/cmsaas-starter.d.mts +7 -0
  162. package/dist/services/definitions/cmsaas-starter.d.mts.map +1 -0
  163. package/dist/services/definitions/cmsaas-starter.mjs +85 -0
  164. package/dist/services/definitions/cmsaas-starter.mjs.map +1 -0
  165. package/dist/services/definitions/convex.cjs +43 -1
  166. package/dist/services/definitions/convex.cjs.map +1 -1
  167. package/dist/services/definitions/convex.mjs +43 -1
  168. package/dist/services/definitions/convex.mjs.map +1 -1
  169. package/dist/services/definitions/grafana.cjs +11 -1
  170. package/dist/services/definitions/grafana.cjs.map +1 -1
  171. package/dist/services/definitions/grafana.mjs +11 -1
  172. package/dist/services/definitions/grafana.mjs.map +1 -1
  173. package/dist/services/definitions/index.cjs +51 -36
  174. package/dist/services/definitions/index.cjs.map +1 -1
  175. package/dist/services/definitions/index.d.cts +30 -25
  176. package/dist/services/definitions/index.d.cts.map +1 -1
  177. package/dist/services/definitions/index.d.mts +30 -25
  178. package/dist/services/definitions/index.d.mts.map +1 -1
  179. package/dist/services/definitions/index.mjs +47 -37
  180. package/dist/services/definitions/index.mjs.map +1 -1
  181. package/dist/services/definitions/ixartz-saas.cjs +88 -0
  182. package/dist/services/definitions/ixartz-saas.cjs.map +1 -0
  183. package/dist/services/definitions/ixartz-saas.d.cts +7 -0
  184. package/dist/services/definitions/ixartz-saas.d.cts.map +1 -0
  185. package/dist/services/definitions/ixartz-saas.d.mts +7 -0
  186. package/dist/services/definitions/ixartz-saas.d.mts.map +1 -0
  187. package/dist/services/definitions/ixartz-saas.mjs +87 -0
  188. package/dist/services/definitions/ixartz-saas.mjs.map +1 -0
  189. package/dist/services/definitions/meilisearch.cjs +11 -1
  190. package/dist/services/definitions/meilisearch.cjs.map +1 -1
  191. package/dist/services/definitions/meilisearch.mjs +11 -1
  192. package/dist/services/definitions/meilisearch.mjs.map +1 -1
  193. package/dist/services/definitions/minio.cjs +3 -1
  194. package/dist/services/definitions/minio.cjs.map +1 -1
  195. package/dist/services/definitions/minio.mjs +3 -1
  196. package/dist/services/definitions/minio.mjs.map +1 -1
  197. package/dist/services/definitions/mission-control.cjs +16 -2
  198. package/dist/services/definitions/mission-control.cjs.map +1 -1
  199. package/dist/services/definitions/mission-control.mjs +16 -2
  200. package/dist/services/definitions/mission-control.mjs.map +1 -1
  201. package/dist/services/definitions/n8n.cjs +11 -1
  202. package/dist/services/definitions/n8n.cjs.map +1 -1
  203. package/dist/services/definitions/n8n.mjs +11 -1
  204. package/dist/services/definitions/n8n.mjs.map +1 -1
  205. package/dist/services/definitions/ollama.cjs +3 -1
  206. package/dist/services/definitions/ollama.cjs.map +1 -1
  207. package/dist/services/definitions/ollama.mjs +3 -1
  208. package/dist/services/definitions/ollama.mjs.map +1 -1
  209. package/dist/services/definitions/open-saas.cjs +81 -0
  210. package/dist/services/definitions/open-saas.cjs.map +1 -0
  211. package/dist/services/definitions/open-saas.d.cts +7 -0
  212. package/dist/services/definitions/open-saas.d.cts.map +1 -0
  213. package/dist/services/definitions/open-saas.d.mts +7 -0
  214. package/dist/services/definitions/open-saas.d.mts.map +1 -0
  215. package/dist/services/definitions/open-saas.mjs +80 -0
  216. package/dist/services/definitions/open-saas.mjs.map +1 -0
  217. package/dist/services/definitions/qdrant.cjs +3 -1
  218. package/dist/services/definitions/qdrant.cjs.map +1 -1
  219. package/dist/services/definitions/qdrant.mjs +3 -1
  220. package/dist/services/definitions/qdrant.mjs.map +1 -1
  221. package/dist/services/definitions/searxng.cjs +8 -1
  222. package/dist/services/definitions/searxng.cjs.map +1 -1
  223. package/dist/services/definitions/searxng.mjs +8 -1
  224. package/dist/services/definitions/searxng.mjs.map +1 -1
  225. package/dist/services/definitions/uptime-kuma.cjs +8 -1
  226. package/dist/services/definitions/uptime-kuma.cjs.map +1 -1
  227. package/dist/services/definitions/uptime-kuma.mjs +8 -1
  228. package/dist/services/definitions/uptime-kuma.mjs.map +1 -1
  229. package/dist/services/registry.cjs +3 -0
  230. package/dist/services/registry.cjs.map +1 -1
  231. package/dist/services/registry.d.cts.map +1 -1
  232. package/dist/services/registry.d.mts.map +1 -1
  233. package/dist/services/registry.mjs +3 -0
  234. package/dist/services/registry.mjs.map +1 -1
  235. package/dist/services/registry.test.cjs +40 -33
  236. package/dist/services/registry.test.cjs.map +1 -1
  237. package/dist/services/registry.test.mjs +8 -1
  238. package/dist/services/registry.test.mjs.map +1 -1
  239. package/dist/{skill-manifest-BVUXU0__.mjs → skill-manifest-6XhrhWsG.mjs} +49 -1
  240. package/dist/{skill-manifest--IgY9REK.cjs.map → skill-manifest-6XhrhWsG.mjs.map} +1 -1
  241. package/dist/{skill-manifest--IgY9REK.cjs → skill-manifest-B8znSsym.cjs} +49 -1
  242. package/dist/{skill-manifest-BVUXU0__.mjs.map → skill-manifest-B8znSsym.cjs.map} +1 -1
  243. package/dist/skills/registry.cjs +3 -3
  244. package/dist/skills/registry.cjs.map +1 -1
  245. package/dist/skills/registry.mjs +3 -3
  246. package/dist/skills/registry.mjs.map +1 -1
  247. package/dist/skills/skill-manifest.cjs +1 -1
  248. package/dist/skills/skill-manifest.mjs +1 -1
  249. package/dist/{vi.2VT5v0um-DvC3SVNc.mjs → test.CTcmp4Su-ClCHJ3FA.mjs} +6793 -6403
  250. package/dist/test.CTcmp4Su-ClCHJ3FA.mjs.map +1 -0
  251. package/dist/{vi.2VT5v0um-CRqXre87.cjs → test.CTcmp4Su-DlzTarwH.cjs} +6793 -6403
  252. package/dist/test.CTcmp4Su-DlzTarwH.cjs.map +1 -0
  253. package/dist/track-analytics.cjs +50 -0
  254. package/dist/track-analytics.cjs.map +1 -0
  255. package/dist/track-analytics.d.cts +34 -0
  256. package/dist/track-analytics.d.cts.map +1 -0
  257. package/dist/track-analytics.d.mts +34 -0
  258. package/dist/track-analytics.d.mts.map +1 -0
  259. package/dist/track-analytics.mjs +48 -0
  260. package/dist/track-analytics.mjs.map +1 -0
  261. package/dist/track-analytics.test.cjs +91 -0
  262. package/dist/track-analytics.test.cjs.map +1 -0
  263. package/dist/track-analytics.test.d.cts +1 -0
  264. package/dist/track-analytics.test.d.mts +1 -0
  265. package/dist/track-analytics.test.mjs +92 -0
  266. package/dist/track-analytics.test.mjs.map +1 -0
  267. package/dist/types.cjs +7 -0
  268. package/dist/types.cjs.map +1 -1
  269. package/dist/types.d.cts +12 -2
  270. package/dist/types.d.cts.map +1 -1
  271. package/dist/types.d.mts +12 -2
  272. package/dist/types.d.mts.map +1 -1
  273. package/dist/types.mjs +7 -0
  274. package/dist/types.mjs.map +1 -1
  275. package/dist/validator.test.cjs +15 -15
  276. package/dist/validator.test.cjs.map +1 -1
  277. package/dist/validator.test.mjs +2 -2
  278. package/dist/version-manager.cjs +1 -1
  279. package/dist/version-manager.cjs.map +1 -1
  280. package/dist/version-manager.mjs +1 -1
  281. package/dist/version-manager.mjs.map +1 -1
  282. package/dist/version-manager.test.cjs +40 -38
  283. package/dist/version-manager.test.cjs.map +1 -1
  284. package/dist/version-manager.test.mjs +7 -5
  285. package/dist/version-manager.test.mjs.map +1 -1
  286. package/package.json +4 -4
  287. package/src/__snapshots__/composer.snapshot.test.ts.snap +160 -0
  288. package/src/addon-stack.test.ts +490 -0
  289. package/src/addon-stack.ts +998 -0
  290. package/src/bare-metal-partition.test.ts +4 -3
  291. package/src/composer.test.ts +4 -2
  292. package/src/composer.ts +24 -5
  293. package/src/generate.test.ts +2 -1
  294. package/src/generate.ts +10 -1
  295. package/src/generators/clone-repos.test.ts +154 -0
  296. package/src/generators/clone-repos.ts +159 -0
  297. package/src/generators/postgres-init.ts +17 -0
  298. package/src/generators/scripts.test.ts +52 -4
  299. package/src/generators/scripts.ts +351 -3
  300. package/src/generators/stack-manifest.ts +4 -2
  301. package/src/index.ts +28 -2
  302. package/src/presets/registry.ts +241 -329
  303. package/src/resolver.test.ts +53 -15
  304. package/src/resolver.ts +13 -1
  305. package/src/schema.ts +216 -4
  306. package/src/services/definitions/apptension-saas.ts +84 -0
  307. package/src/services/definitions/boxyhq-saas.ts +84 -0
  308. package/src/services/definitions/browserless.ts +3 -0
  309. package/src/services/definitions/cmsaas-starter.ts +84 -0
  310. package/src/services/definitions/convex.ts +31 -0
  311. package/src/services/definitions/grafana.ts +9 -0
  312. package/src/services/definitions/index.ts +90 -70
  313. package/src/services/definitions/ixartz-saas.ts +84 -0
  314. package/src/services/definitions/meilisearch.ts +9 -0
  315. package/src/services/definitions/minio.ts +2 -0
  316. package/src/services/definitions/mission-control.ts +19 -2
  317. package/src/services/definitions/n8n.ts +9 -0
  318. package/src/services/definitions/ollama.ts +2 -0
  319. package/src/services/definitions/open-saas.ts +79 -0
  320. package/src/services/definitions/qdrant.ts +2 -0
  321. package/src/services/definitions/searxng.ts +3 -0
  322. package/src/services/definitions/uptime-kuma.ts +3 -0
  323. package/src/services/registry.test.ts +8 -0
  324. package/src/services/registry.ts +7 -0
  325. package/src/skills/manifest.json +64 -0
  326. package/src/skills/registry.ts +3 -3
  327. package/src/track-analytics.test.ts +82 -0
  328. package/src/track-analytics.ts +76 -0
  329. package/src/types.ts +29 -0
  330. package/src/version-manager.test.ts +10 -5
  331. package/src/version-manager.ts +1 -1
  332. package/dist/schema-B4c64P8N.d.cts.map +0 -1
  333. package/dist/schema-CXNhYci1.d.mts.map +0 -1
  334. package/dist/vi.2VT5v0um-CRqXre87.cjs.map +0 -1
  335. package/dist/vi.2VT5v0um-DvC3SVNc.mjs.map +0 -1
@@ -1,105 +1,113 @@
1
- const require_vi_2VT5v0um = require("./vi.2VT5v0um-CRqXre87.cjs");
1
+ const require_test_CTcmp4Su = require("./test.CTcmp4Su-DlzTarwH.cjs");
2
2
  const require_resolver = require("./resolver.cjs");
3
3
  //#region src/resolver.test.ts
4
- require_vi_2VT5v0um.describe("resolve", () => {
5
- require_vi_2VT5v0um.it("returns empty services for empty input", () => {
4
+ const MANDATORY_IDS = [
5
+ "convex",
6
+ "mission-control",
7
+ "tailscale"
8
+ ];
9
+ const MANDATORY_MEMORY = 448;
10
+ require_test_CTcmp4Su.describe("resolve", () => {
11
+ require_test_CTcmp4Su.it("returns only mandatory services for empty input", () => {
6
12
  const result = require_resolver.resolve({
7
13
  services: [],
8
14
  skillPacks: []
9
15
  });
10
- require_vi_2VT5v0um.globalExpect(result.services).toHaveLength(0);
11
- require_vi_2VT5v0um.globalExpect(result.isValid).toBe(true);
12
- require_vi_2VT5v0um.globalExpect(result.estimatedMemoryMB).toBe(512);
16
+ const ids = result.services.map((s) => s.definition.id);
17
+ for (const id of MANDATORY_IDS) require_test_CTcmp4Su.globalExpect(ids).toContain(id);
18
+ require_test_CTcmp4Su.globalExpect(result.isValid).toBe(true);
19
+ require_test_CTcmp4Su.globalExpect(result.estimatedMemoryMB).toBe(512 + MANDATORY_MEMORY);
13
20
  });
14
- require_vi_2VT5v0um.it("resolves a single service with no dependencies", () => {
21
+ require_test_CTcmp4Su.it("resolves a single service alongside mandatory services", () => {
15
22
  const result = require_resolver.resolve({
16
23
  services: ["redis"],
17
24
  skillPacks: []
18
25
  });
19
- require_vi_2VT5v0um.globalExpect(result.services).toHaveLength(1);
20
- require_vi_2VT5v0um.globalExpect(result.services[0].definition.id).toBe("redis");
21
- require_vi_2VT5v0um.globalExpect(result.isValid).toBe(true);
26
+ const ids = result.services.map((s) => s.definition.id);
27
+ require_test_CTcmp4Su.globalExpect(ids).toContain("redis");
28
+ for (const id of MANDATORY_IDS) require_test_CTcmp4Su.globalExpect(ids).toContain(id);
29
+ require_test_CTcmp4Su.globalExpect(result.isValid).toBe(true);
22
30
  });
23
- require_vi_2VT5v0um.it("auto-adds PostgreSQL when n8n is selected", () => {
31
+ require_test_CTcmp4Su.it("auto-adds PostgreSQL when n8n is selected", () => {
24
32
  const result = require_resolver.resolve({
25
33
  services: ["n8n"],
26
34
  skillPacks: []
27
35
  });
28
36
  const ids = result.services.map((s) => s.definition.id);
29
- require_vi_2VT5v0um.globalExpect(ids).toContain("n8n");
30
- require_vi_2VT5v0um.globalExpect(ids).toContain("postgresql");
31
- require_vi_2VT5v0um.globalExpect(result.addedDependencies).toContainEqual(require_vi_2VT5v0um.globalExpect.objectContaining({ service: "postgresql" }));
32
- require_vi_2VT5v0um.globalExpect(result.isValid).toBe(true);
37
+ require_test_CTcmp4Su.globalExpect(ids).toContain("n8n");
38
+ require_test_CTcmp4Su.globalExpect(ids).toContain("postgresql");
39
+ require_test_CTcmp4Su.globalExpect(result.addedDependencies).toContainEqual(require_test_CTcmp4Su.globalExpect.objectContaining({ service: "postgresql" }));
40
+ require_test_CTcmp4Su.globalExpect(result.isValid).toBe(true);
33
41
  });
34
- require_vi_2VT5v0um.it("auto-adds Prometheus when Grafana is selected", () => {
42
+ require_test_CTcmp4Su.it("auto-adds Prometheus when Grafana is selected", () => {
35
43
  const ids = require_resolver.resolve({
36
44
  services: ["grafana"],
37
45
  skillPacks: []
38
46
  }).services.map((s) => s.definition.id);
39
- require_vi_2VT5v0um.globalExpect(ids).toContain("grafana");
40
- require_vi_2VT5v0um.globalExpect(ids).toContain("prometheus");
47
+ require_test_CTcmp4Su.globalExpect(ids).toContain("grafana");
48
+ require_test_CTcmp4Su.globalExpect(ids).toContain("prometheus");
41
49
  });
42
- require_vi_2VT5v0um.it("detects Redis + Valkey conflict", () => {
50
+ require_test_CTcmp4Su.it("detects Redis + Valkey conflict", () => {
43
51
  const result = require_resolver.resolve({
44
52
  services: ["redis", "valkey"],
45
53
  skillPacks: []
46
54
  });
47
- require_vi_2VT5v0um.globalExpect(result.isValid).toBe(false);
48
- require_vi_2VT5v0um.globalExpect(result.errors).toContainEqual(require_vi_2VT5v0um.globalExpect.objectContaining({ type: "conflict" }));
55
+ require_test_CTcmp4Su.globalExpect(result.isValid).toBe(false);
56
+ require_test_CTcmp4Su.globalExpect(result.errors).toContainEqual(require_test_CTcmp4Su.globalExpect.objectContaining({ type: "conflict" }));
49
57
  });
50
- require_vi_2VT5v0um.it("detects Caddy + Traefik conflict", () => {
58
+ require_test_CTcmp4Su.it("detects Caddy + Traefik conflict", () => {
51
59
  const result = require_resolver.resolve({
52
60
  services: ["caddy", "traefik"],
53
61
  skillPacks: []
54
62
  });
55
- require_vi_2VT5v0um.globalExpect(result.isValid).toBe(false);
56
- require_vi_2VT5v0um.globalExpect(result.errors).toContainEqual(require_vi_2VT5v0um.globalExpect.objectContaining({ type: "conflict" }));
63
+ require_test_CTcmp4Su.globalExpect(result.isValid).toBe(false);
64
+ require_test_CTcmp4Su.globalExpect(result.errors).toContainEqual(require_test_CTcmp4Su.globalExpect.objectContaining({ type: "conflict" }));
57
65
  });
58
- require_vi_2VT5v0um.it("expands research-agent skill pack", () => {
66
+ require_test_CTcmp4Su.it("expands research-agent skill pack", () => {
59
67
  const result = require_resolver.resolve({
60
68
  services: [],
61
69
  skillPacks: ["research-agent"]
62
70
  });
63
71
  const ids = result.services.map((s) => s.definition.id);
64
- require_vi_2VT5v0um.globalExpect(ids).toContain("qdrant");
65
- require_vi_2VT5v0um.globalExpect(ids).toContain("searxng");
66
- require_vi_2VT5v0um.globalExpect(ids).toContain("browserless");
67
- require_vi_2VT5v0um.globalExpect(result.isValid).toBe(true);
72
+ require_test_CTcmp4Su.globalExpect(ids).toContain("qdrant");
73
+ require_test_CTcmp4Su.globalExpect(ids).toContain("searxng");
74
+ require_test_CTcmp4Su.globalExpect(ids).toContain("browserless");
75
+ require_test_CTcmp4Su.globalExpect(result.isValid).toBe(true);
68
76
  });
69
- require_vi_2VT5v0um.it("expands video-creator skill pack", () => {
77
+ require_test_CTcmp4Su.it("expands video-creator skill pack", () => {
70
78
  const ids = require_resolver.resolve({
71
79
  services: [],
72
80
  skillPacks: ["video-creator"]
73
81
  }).services.map((s) => s.definition.id);
74
- require_vi_2VT5v0um.globalExpect(ids).toContain("ffmpeg");
75
- require_vi_2VT5v0um.globalExpect(ids).toContain("remotion");
76
- require_vi_2VT5v0um.globalExpect(ids).toContain("minio");
82
+ require_test_CTcmp4Su.globalExpect(ids).toContain("ffmpeg");
83
+ require_test_CTcmp4Su.globalExpect(ids).toContain("remotion");
84
+ require_test_CTcmp4Su.globalExpect(ids).toContain("minio");
77
85
  });
78
- require_vi_2VT5v0um.it("expands dev-ops skill pack with transitive deps", () => {
86
+ require_test_CTcmp4Su.it("expands dev-ops skill pack with transitive deps", () => {
79
87
  const ids = require_resolver.resolve({
80
88
  services: [],
81
89
  skillPacks: ["dev-ops"]
82
90
  }).services.map((s) => s.definition.id);
83
- require_vi_2VT5v0um.globalExpect(ids).toContain("n8n");
84
- require_vi_2VT5v0um.globalExpect(ids).toContain("redis");
85
- require_vi_2VT5v0um.globalExpect(ids).toContain("uptime-kuma");
86
- require_vi_2VT5v0um.globalExpect(ids).toContain("grafana");
87
- require_vi_2VT5v0um.globalExpect(ids).toContain("prometheus");
88
- require_vi_2VT5v0um.globalExpect(ids).toContain("postgresql");
89
- });
90
- require_vi_2VT5v0um.it("reports unknown service IDs", () => {
91
- require_vi_2VT5v0um.globalExpect(require_resolver.resolve({
91
+ require_test_CTcmp4Su.globalExpect(ids).toContain("n8n");
92
+ require_test_CTcmp4Su.globalExpect(ids).toContain("redis");
93
+ require_test_CTcmp4Su.globalExpect(ids).toContain("uptime-kuma");
94
+ require_test_CTcmp4Su.globalExpect(ids).toContain("grafana");
95
+ require_test_CTcmp4Su.globalExpect(ids).toContain("prometheus");
96
+ require_test_CTcmp4Su.globalExpect(ids).toContain("postgresql");
97
+ });
98
+ require_test_CTcmp4Su.it("reports unknown service IDs", () => {
99
+ require_test_CTcmp4Su.globalExpect(require_resolver.resolve({
92
100
  services: ["nonexistent"],
93
101
  skillPacks: []
94
- }).errors).toContainEqual(require_vi_2VT5v0um.globalExpect.objectContaining({ type: "unknown_service" }));
102
+ }).errors).toContainEqual(require_test_CTcmp4Su.globalExpect.objectContaining({ type: "unknown_service" }));
95
103
  });
96
- require_vi_2VT5v0um.it("reports unknown skill pack IDs", () => {
97
- require_vi_2VT5v0um.globalExpect(require_resolver.resolve({
104
+ require_test_CTcmp4Su.it("reports unknown skill pack IDs", () => {
105
+ require_test_CTcmp4Su.globalExpect(require_resolver.resolve({
98
106
  services: [],
99
107
  skillPacks: ["nonexistent-pack"]
100
- }).errors).toContainEqual(require_vi_2VT5v0um.globalExpect.objectContaining({ type: "unknown_skill_pack" }));
108
+ }).errors).toContainEqual(require_test_CTcmp4Su.globalExpect.objectContaining({ type: "unknown_skill_pack" }));
101
109
  });
102
- require_vi_2VT5v0um.it("does not duplicate services already selected by user", () => {
110
+ require_test_CTcmp4Su.it("does not duplicate services already selected by user", () => {
103
111
  const qdrantCount = require_resolver.resolve({
104
112
  services: [
105
113
  "qdrant",
@@ -108,32 +116,32 @@ require_vi_2VT5v0um.describe("resolve", () => {
108
116
  ],
109
117
  skillPacks: ["research-agent"]
110
118
  }).services.map((s) => s.definition.id).filter((id) => id === "qdrant").length;
111
- require_vi_2VT5v0um.globalExpect(qdrantCount).toBe(1);
119
+ require_test_CTcmp4Su.globalExpect(qdrantCount).toBe(1);
112
120
  });
113
- require_vi_2VT5v0um.it("estimates memory correctly", () => {
114
- require_vi_2VT5v0um.globalExpect(require_resolver.resolve({
121
+ require_test_CTcmp4Su.it("estimates memory correctly", () => {
122
+ require_test_CTcmp4Su.globalExpect(require_resolver.resolve({
115
123
  services: ["redis"],
116
124
  skillPacks: []
117
- }).estimatedMemoryMB).toBe(640);
125
+ }).estimatedMemoryMB).toBe(640 + MANDATORY_MEMORY);
118
126
  });
119
- require_vi_2VT5v0um.it("adds proxy service when specified", () => {
120
- require_vi_2VT5v0um.globalExpect(require_resolver.resolve({
127
+ require_test_CTcmp4Su.it("adds proxy service when specified", () => {
128
+ require_test_CTcmp4Su.globalExpect(require_resolver.resolve({
121
129
  services: ["redis"],
122
130
  skillPacks: [],
123
131
  proxy: "caddy"
124
132
  }).services.map((s) => s.definition.id)).toContain("caddy");
125
133
  });
126
- require_vi_2VT5v0um.it("adds monitoring stack when flag is set", () => {
134
+ require_test_CTcmp4Su.it("adds monitoring stack when flag is set", () => {
127
135
  const ids = require_resolver.resolve({
128
136
  services: [],
129
137
  skillPacks: [],
130
138
  monitoring: true
131
139
  }).services.map((s) => s.definition.id);
132
- require_vi_2VT5v0um.globalExpect(ids).toContain("uptime-kuma");
133
- require_vi_2VT5v0um.globalExpect(ids).toContain("grafana");
134
- require_vi_2VT5v0um.globalExpect(ids).toContain("prometheus");
140
+ require_test_CTcmp4Su.globalExpect(ids).toContain("uptime-kuma");
141
+ require_test_CTcmp4Su.globalExpect(ids).toContain("grafana");
142
+ require_test_CTcmp4Su.globalExpect(ids).toContain("prometheus");
135
143
  });
136
- require_vi_2VT5v0um.it("is deterministic - same input gives same output", () => {
144
+ require_test_CTcmp4Su.it("is deterministic - same input gives same output", () => {
137
145
  const input = {
138
146
  services: [
139
147
  "redis",
@@ -146,57 +154,56 @@ require_vi_2VT5v0um.describe("resolve", () => {
146
154
  const result2 = require_resolver.resolve(input);
147
155
  const ids1 = result1.services.map((s) => s.definition.id);
148
156
  const ids2 = result2.services.map((s) => s.definition.id);
149
- require_vi_2VT5v0um.globalExpect(ids1).toEqual(ids2);
157
+ require_test_CTcmp4Su.globalExpect(ids1).toEqual(ids2);
150
158
  });
151
- require_vi_2VT5v0um.it("topologically sorts dependencies before dependents", () => {
159
+ require_test_CTcmp4Su.it("topologically sorts dependencies before dependents", () => {
152
160
  const ids = require_resolver.resolve({
153
161
  services: ["n8n"],
154
162
  skillPacks: []
155
163
  }).services.map((s) => s.definition.id);
156
164
  const pgIndex = ids.indexOf("postgresql");
157
165
  const n8nIndex = ids.indexOf("n8n");
158
- require_vi_2VT5v0um.globalExpect(pgIndex).toBeLessThan(n8nIndex);
166
+ require_test_CTcmp4Su.globalExpect(pgIndex).toBeLessThan(n8nIndex);
159
167
  });
160
- require_vi_2VT5v0um.it("warns about GPU when AI services selected without gpu flag", () => {
161
- require_vi_2VT5v0um.globalExpect(require_resolver.resolve({
168
+ require_test_CTcmp4Su.it("warns about GPU when AI services selected without gpu flag", () => {
169
+ require_test_CTcmp4Su.globalExpect(require_resolver.resolve({
162
170
  services: ["redis"],
163
171
  skillPacks: [],
164
172
  gpu: false
165
173
  }).warnings.filter((w) => w.type === "gpu")).toHaveLength(0);
166
174
  });
167
- require_vi_2VT5v0um.it("resolves tailscale as single service with no dependencies", () => {
175
+ require_test_CTcmp4Su.it("resolves tailscale (mandatory, always present)", () => {
168
176
  const result = require_resolver.resolve({
169
- services: ["tailscale"],
177
+ services: [],
170
178
  skillPacks: []
171
179
  });
172
- require_vi_2VT5v0um.globalExpect(result.services).toHaveLength(1);
173
- require_vi_2VT5v0um.globalExpect(result.services[0].definition.id).toBe("tailscale");
174
- require_vi_2VT5v0um.globalExpect(result.isValid).toBe(true);
180
+ require_test_CTcmp4Su.globalExpect(result.services.map((s) => s.definition.id)).toContain("tailscale");
181
+ require_test_CTcmp4Su.globalExpect(result.isValid).toBe(true);
175
182
  });
176
- require_vi_2VT5v0um.it("resolves coolify and dokploy as single services", () => {
183
+ require_test_CTcmp4Su.it("resolves coolify and dokploy as single services", () => {
177
184
  const result = require_resolver.resolve({
178
185
  services: ["coolify", "dokploy"],
179
186
  skillPacks: []
180
187
  });
181
188
  const ids = result.services.map((s) => s.definition.id);
182
- require_vi_2VT5v0um.globalExpect(ids).toContain("coolify");
183
- require_vi_2VT5v0um.globalExpect(ids).toContain("dokploy");
184
- require_vi_2VT5v0um.globalExpect(result.isValid).toBe(true);
189
+ require_test_CTcmp4Su.globalExpect(ids).toContain("coolify");
190
+ require_test_CTcmp4Su.globalExpect(ids).toContain("dokploy");
191
+ require_test_CTcmp4Su.globalExpect(result.isValid).toBe(true);
185
192
  });
186
- require_vi_2VT5v0um.it("auto-adds postgresql, redis, and livekit when lasuite-meet-backend is selected", () => {
193
+ require_test_CTcmp4Su.it("auto-adds postgresql, redis, and livekit when lasuite-meet-backend is selected", () => {
187
194
  const result = require_resolver.resolve({
188
195
  services: ["lasuite-meet-backend"],
189
196
  skillPacks: []
190
197
  });
191
198
  const ids = result.services.map((s) => s.definition.id);
192
- require_vi_2VT5v0um.globalExpect(ids).toContain("lasuite-meet-backend");
193
- require_vi_2VT5v0um.globalExpect(ids).toContain("postgresql");
194
- require_vi_2VT5v0um.globalExpect(ids).toContain("redis");
195
- require_vi_2VT5v0um.globalExpect(ids).toContain("livekit");
196
- require_vi_2VT5v0um.globalExpect(result.addedDependencies.some((a) => a.service === "postgresql")).toBe(true);
197
- require_vi_2VT5v0um.globalExpect(result.isValid).toBe(true);
198
- });
199
- require_vi_2VT5v0um.it("resolves La Suite Meet preset (postgresql, redis, livekit, backend, frontend, agents)", () => {
199
+ require_test_CTcmp4Su.globalExpect(ids).toContain("lasuite-meet-backend");
200
+ require_test_CTcmp4Su.globalExpect(ids).toContain("postgresql");
201
+ require_test_CTcmp4Su.globalExpect(ids).toContain("redis");
202
+ require_test_CTcmp4Su.globalExpect(ids).toContain("livekit");
203
+ require_test_CTcmp4Su.globalExpect(result.addedDependencies.some((a) => a.service === "postgresql")).toBe(true);
204
+ require_test_CTcmp4Su.globalExpect(result.isValid).toBe(true);
205
+ });
206
+ require_test_CTcmp4Su.it("resolves La Suite Meet preset (postgresql, redis, livekit, backend, frontend, agents)", () => {
200
207
  const lasuiteMeetServices = [
201
208
  "postgresql",
202
209
  "redis",
@@ -210,10 +217,10 @@ require_vi_2VT5v0um.describe("resolve", () => {
210
217
  skillPacks: []
211
218
  });
212
219
  const ids = result.services.map((s) => s.definition.id);
213
- for (const id of lasuiteMeetServices) require_vi_2VT5v0um.globalExpect(ids).toContain(id);
214
- require_vi_2VT5v0um.globalExpect(result.isValid).toBe(true);
220
+ for (const id of lasuiteMeetServices) require_test_CTcmp4Su.globalExpect(ids).toContain(id);
221
+ require_test_CTcmp4Su.globalExpect(result.isValid).toBe(true);
215
222
  });
216
- require_vi_2VT5v0um.it("orders lasuite-meet-backend after postgresql, redis, and livekit", () => {
223
+ require_test_CTcmp4Su.it("orders lasuite-meet-backend after postgresql, redis, and livekit", () => {
217
224
  const ids = require_resolver.resolve({
218
225
  services: ["lasuite-meet-backend", "lasuite-meet-frontend"],
219
226
  skillPacks: []
@@ -223,11 +230,39 @@ require_vi_2VT5v0um.describe("resolve", () => {
223
230
  const livekitIdx = ids.indexOf("livekit");
224
231
  const backendIdx = ids.indexOf("lasuite-meet-backend");
225
232
  const frontendIdx = ids.indexOf("lasuite-meet-frontend");
226
- require_vi_2VT5v0um.globalExpect(pgIdx).toBeGreaterThanOrEqual(0);
227
- require_vi_2VT5v0um.globalExpect(backendIdx).toBeGreaterThan(pgIdx);
228
- require_vi_2VT5v0um.globalExpect(backendIdx).toBeGreaterThan(redisIdx);
229
- require_vi_2VT5v0um.globalExpect(backendIdx).toBeGreaterThan(livekitIdx);
230
- require_vi_2VT5v0um.globalExpect(frontendIdx).toBeGreaterThan(backendIdx);
233
+ require_test_CTcmp4Su.globalExpect(pgIdx).toBeGreaterThanOrEqual(0);
234
+ require_test_CTcmp4Su.globalExpect(backendIdx).toBeGreaterThan(pgIdx);
235
+ require_test_CTcmp4Su.globalExpect(backendIdx).toBeGreaterThan(redisIdx);
236
+ require_test_CTcmp4Su.globalExpect(backendIdx).toBeGreaterThan(livekitIdx);
237
+ require_test_CTcmp4Su.globalExpect(frontendIdx).toBeGreaterThan(backendIdx);
238
+ });
239
+ require_test_CTcmp4Su.it("auto-includes mandatory services even when not selected", () => {
240
+ const result = require_resolver.resolve({
241
+ services: ["redis"],
242
+ skillPacks: []
243
+ });
244
+ const ids = result.services.map((s) => s.definition.id);
245
+ require_test_CTcmp4Su.globalExpect(ids).toContain("mission-control");
246
+ require_test_CTcmp4Su.globalExpect(ids).toContain("convex");
247
+ require_test_CTcmp4Su.globalExpect(ids).toContain("tailscale");
248
+ require_test_CTcmp4Su.globalExpect(result.services.find((s) => s.definition.id === "mission-control").addedBy).toBe("mandatory");
249
+ });
250
+ require_test_CTcmp4Su.it("marks user-selected mandatory services as 'user' not 'mandatory'", () => {
251
+ const result = require_resolver.resolve({
252
+ services: ["mission-control"],
253
+ skillPacks: []
254
+ });
255
+ require_test_CTcmp4Su.globalExpect(result.services.find((s) => s.definition.id === "mission-control").addedBy).toBe("user");
256
+ require_test_CTcmp4Su.globalExpect(result.services.find((s) => s.definition.id === "convex")).toBeDefined();
257
+ });
258
+ require_test_CTcmp4Su.it("orders convex before mission-control (dependency ordering)", () => {
259
+ const ids = require_resolver.resolve({
260
+ services: ["redis"],
261
+ skillPacks: []
262
+ }).services.map((s) => s.definition.id);
263
+ const convexIdx = ids.indexOf("convex");
264
+ const mcIdx = ids.indexOf("mission-control");
265
+ require_test_CTcmp4Su.globalExpect(convexIdx).toBeLessThan(mcIdx);
231
266
  });
232
267
  });
233
268
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.test.cjs","names":["describe","resolve","expect"],"sources":["../src/resolver.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { resolve } from \"./resolver.js\";\n\ndescribe(\"resolve\", () => {\n\tit(\"returns empty services for empty input\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [] });\n\t\texpect(result.services).toHaveLength(0);\n\t\texpect(result.isValid).toBe(true);\n\t\texpect(result.estimatedMemoryMB).toBe(512); // base OpenClaw only\n\t});\n\n\tit(\"resolves a single service with no dependencies\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\texpect(result.services).toHaveLength(1);\n\t\texpect(result.services[0]!.definition.id).toBe(\"redis\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds PostgreSQL when n8n is selected\", () => {\n\t\tconst result = resolve({ services: [\"n8n\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"n8n\");\n\t\texpect(ids).toContain(\"postgresql\");\n\t\texpect(result.addedDependencies).toContainEqual(\n\t\t\texpect.objectContaining({ service: \"postgresql\" }),\n\t\t);\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds Prometheus when Grafana is selected\", () => {\n\t\tconst result = resolve({ services: [\"grafana\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t});\n\n\tit(\"detects Redis + Valkey conflict\", () => {\n\t\tconst result = resolve({ services: [\"redis\", \"valkey\"], skillPacks: [] });\n\t\texpect(result.isValid).toBe(false);\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"conflict\" }));\n\t});\n\n\tit(\"detects Caddy + Traefik conflict\", () => {\n\t\tconst result = resolve({ services: [\"caddy\", \"traefik\"], skillPacks: [] });\n\t\texpect(result.isValid).toBe(false);\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"conflict\" }));\n\t});\n\n\tit(\"expands research-agent skill pack\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"research-agent\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"qdrant\");\n\t\texpect(ids).toContain(\"searxng\");\n\t\texpect(ids).toContain(\"browserless\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"expands video-creator skill pack\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"video-creator\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"ffmpeg\");\n\t\texpect(ids).toContain(\"remotion\");\n\t\texpect(ids).toContain(\"minio\");\n\t});\n\n\tit(\"expands dev-ops skill pack with transitive deps\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"dev-ops\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"n8n\");\n\t\texpect(ids).toContain(\"redis\");\n\t\texpect(ids).toContain(\"uptime-kuma\");\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t\t// n8n requires postgresql (transitive)\n\t\texpect(ids).toContain(\"postgresql\");\n\t});\n\n\tit(\"reports unknown service IDs\", () => {\n\t\tconst result = resolve({ services: [\"nonexistent\"], skillPacks: [] });\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"unknown_service\" }));\n\t});\n\n\tit(\"reports unknown skill pack IDs\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"nonexistent-pack\"] });\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"unknown_skill_pack\" }));\n\t});\n\n\tit(\"does not duplicate services already selected by user\", () => {\n\t\tconst result = resolve({\n\t\t\tservices: [\"qdrant\", \"searxng\", \"browserless\"],\n\t\t\tskillPacks: [\"research-agent\"],\n\t\t});\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst qdrantCount = ids.filter((id) => id === \"qdrant\").length;\n\t\texpect(qdrantCount).toBe(1);\n\t});\n\n\tit(\"estimates memory correctly\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\t// 512 (base) + 128 (redis) = 640\n\t\texpect(result.estimatedMemoryMB).toBe(640);\n\t});\n\n\tit(\"adds proxy service when specified\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [], proxy: \"caddy\" });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"caddy\");\n\t});\n\n\tit(\"adds monitoring stack when flag is set\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [], monitoring: true });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"uptime-kuma\");\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t});\n\n\tit(\"is deterministic - same input gives same output\", () => {\n\t\tconst input = { services: [\"redis\", \"qdrant\", \"n8n\"], skillPacks: [\"research-agent\"] };\n\t\tconst result1 = resolve(input);\n\t\tconst result2 = resolve(input);\n\t\tconst ids1 = result1.services.map((s) => s.definition.id);\n\t\tconst ids2 = result2.services.map((s) => s.definition.id);\n\t\texpect(ids1).toEqual(ids2);\n\t});\n\n\tit(\"topologically sorts dependencies before dependents\", () => {\n\t\tconst result = resolve({ services: [\"n8n\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst pgIndex = ids.indexOf(\"postgresql\");\n\t\tconst n8nIndex = ids.indexOf(\"n8n\");\n\t\texpect(pgIndex).toBeLessThan(n8nIndex);\n\t});\n\n\tit(\"warns about GPU when AI services selected without gpu flag\", () => {\n\t\t// Ollama doesn't have gpuRequired=true (it's recommended not required)\n\t\t// but we should still check GPU warning logic works\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [], gpu: false });\n\t\t// Redis doesn't need GPU, so no GPU warnings\n\t\tconst gpuWarnings = result.warnings.filter((w) => w.type === \"gpu\");\n\t\texpect(gpuWarnings).toHaveLength(0);\n\t});\n\n\tit(\"resolves tailscale as single service with no dependencies\", () => {\n\t\tconst result = resolve({ services: [\"tailscale\"], skillPacks: [] });\n\t\texpect(result.services).toHaveLength(1);\n\t\texpect(result.services[0]!.definition.id).toBe(\"tailscale\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"resolves coolify and dokploy as single services\", () => {\n\t\tconst result = resolve({ services: [\"coolify\", \"dokploy\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"coolify\");\n\t\texpect(ids).toContain(\"dokploy\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds postgresql, redis, and livekit when lasuite-meet-backend is selected\", () => {\n\t\tconst result = resolve({ services: [\"lasuite-meet-backend\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"lasuite-meet-backend\");\n\t\texpect(ids).toContain(\"postgresql\");\n\t\texpect(ids).toContain(\"redis\");\n\t\texpect(ids).toContain(\"livekit\");\n\t\texpect(result.addedDependencies.some((a) => a.service === \"postgresql\")).toBe(true);\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"resolves La Suite Meet preset (postgresql, redis, livekit, backend, frontend, agents)\", () => {\n\t\tconst lasuiteMeetServices = [\n\t\t\t\"postgresql\",\n\t\t\t\"redis\",\n\t\t\t\"livekit\",\n\t\t\t\"lasuite-meet-backend\",\n\t\t\t\"lasuite-meet-frontend\",\n\t\t\t\"lasuite-meet-agents\",\n\t\t];\n\t\tconst result = resolve({ services: lasuiteMeetServices, skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tfor (const id of lasuiteMeetServices) {\n\t\t\texpect(ids).toContain(id);\n\t\t}\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"orders lasuite-meet-backend after postgresql, redis, and livekit\", () => {\n\t\tconst result = resolve({\n\t\t\tservices: [\"lasuite-meet-backend\", \"lasuite-meet-frontend\"],\n\t\t\tskillPacks: [],\n\t\t});\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst pgIdx = ids.indexOf(\"postgresql\");\n\t\tconst redisIdx = ids.indexOf(\"redis\");\n\t\tconst livekitIdx = ids.indexOf(\"livekit\");\n\t\tconst backendIdx = ids.indexOf(\"lasuite-meet-backend\");\n\t\tconst frontendIdx = ids.indexOf(\"lasuite-meet-frontend\");\n\t\texpect(pgIdx).toBeGreaterThanOrEqual(0);\n\t\texpect(backendIdx).toBeGreaterThan(pgIdx);\n\t\texpect(backendIdx).toBeGreaterThan(redisIdx);\n\t\texpect(backendIdx).toBeGreaterThan(livekitIdx);\n\t\texpect(frontendIdx).toBeGreaterThan(backendIdx);\n\t});\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"resolver.test.cjs","names":["describe","resolve","expect"],"sources":["../src/resolver.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { resolve } from \"./resolver.js\";\n\n// Mandatory services (convex, mission-control, tailscale) are always auto-included.\n// These IDs and their combined memory are accounted for in all tests.\nconst MANDATORY_IDS = [\"convex\", \"mission-control\", \"tailscale\"];\nconst MANDATORY_MEMORY = 256 + 128 + 64; // convex + mission-control + tailscale\n\ndescribe(\"resolve\", () => {\n\tit(\"returns only mandatory services for empty input\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tfor (const id of MANDATORY_IDS) {\n\t\t\texpect(ids).toContain(id);\n\t\t}\n\t\texpect(result.isValid).toBe(true);\n\t\t// 512 (base) + mandatory services memory\n\t\texpect(result.estimatedMemoryMB).toBe(512 + MANDATORY_MEMORY);\n\t});\n\n\tit(\"resolves a single service alongside mandatory services\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"redis\");\n\t\tfor (const id of MANDATORY_IDS) {\n\t\t\texpect(ids).toContain(id);\n\t\t}\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds PostgreSQL when n8n is selected\", () => {\n\t\tconst result = resolve({ services: [\"n8n\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"n8n\");\n\t\texpect(ids).toContain(\"postgresql\");\n\t\texpect(result.addedDependencies).toContainEqual(\n\t\t\texpect.objectContaining({ service: \"postgresql\" }),\n\t\t);\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds Prometheus when Grafana is selected\", () => {\n\t\tconst result = resolve({ services: [\"grafana\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t});\n\n\tit(\"detects Redis + Valkey conflict\", () => {\n\t\tconst result = resolve({ services: [\"redis\", \"valkey\"], skillPacks: [] });\n\t\texpect(result.isValid).toBe(false);\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"conflict\" }));\n\t});\n\n\tit(\"detects Caddy + Traefik conflict\", () => {\n\t\tconst result = resolve({ services: [\"caddy\", \"traefik\"], skillPacks: [] });\n\t\texpect(result.isValid).toBe(false);\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"conflict\" }));\n\t});\n\n\tit(\"expands research-agent skill pack\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"research-agent\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"qdrant\");\n\t\texpect(ids).toContain(\"searxng\");\n\t\texpect(ids).toContain(\"browserless\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"expands video-creator skill pack\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"video-creator\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"ffmpeg\");\n\t\texpect(ids).toContain(\"remotion\");\n\t\texpect(ids).toContain(\"minio\");\n\t});\n\n\tit(\"expands dev-ops skill pack with transitive deps\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"dev-ops\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"n8n\");\n\t\texpect(ids).toContain(\"redis\");\n\t\texpect(ids).toContain(\"uptime-kuma\");\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t\t// n8n requires postgresql (transitive)\n\t\texpect(ids).toContain(\"postgresql\");\n\t});\n\n\tit(\"reports unknown service IDs\", () => {\n\t\tconst result = resolve({ services: [\"nonexistent\"], skillPacks: [] });\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"unknown_service\" }));\n\t});\n\n\tit(\"reports unknown skill pack IDs\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"nonexistent-pack\"] });\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"unknown_skill_pack\" }));\n\t});\n\n\tit(\"does not duplicate services already selected by user\", () => {\n\t\tconst result = resolve({\n\t\t\tservices: [\"qdrant\", \"searxng\", \"browserless\"],\n\t\t\tskillPacks: [\"research-agent\"],\n\t\t});\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst qdrantCount = ids.filter((id) => id === \"qdrant\").length;\n\t\texpect(qdrantCount).toBe(1);\n\t});\n\n\tit(\"estimates memory correctly\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\t// 512 (base) + 128 (redis) + mandatory services\n\t\texpect(result.estimatedMemoryMB).toBe(512 + 128 + MANDATORY_MEMORY);\n\t});\n\n\tit(\"adds proxy service when specified\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [], proxy: \"caddy\" });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"caddy\");\n\t});\n\n\tit(\"adds monitoring stack when flag is set\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [], monitoring: true });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"uptime-kuma\");\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t});\n\n\tit(\"is deterministic - same input gives same output\", () => {\n\t\tconst input = { services: [\"redis\", \"qdrant\", \"n8n\"], skillPacks: [\"research-agent\"] };\n\t\tconst result1 = resolve(input);\n\t\tconst result2 = resolve(input);\n\t\tconst ids1 = result1.services.map((s) => s.definition.id);\n\t\tconst ids2 = result2.services.map((s) => s.definition.id);\n\t\texpect(ids1).toEqual(ids2);\n\t});\n\n\tit(\"topologically sorts dependencies before dependents\", () => {\n\t\tconst result = resolve({ services: [\"n8n\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst pgIndex = ids.indexOf(\"postgresql\");\n\t\tconst n8nIndex = ids.indexOf(\"n8n\");\n\t\texpect(pgIndex).toBeLessThan(n8nIndex);\n\t});\n\n\tit(\"warns about GPU when AI services selected without gpu flag\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [], gpu: false });\n\t\tconst gpuWarnings = result.warnings.filter((w) => w.type === \"gpu\");\n\t\texpect(gpuWarnings).toHaveLength(0);\n\t});\n\n\tit(\"resolves tailscale (mandatory, always present)\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"tailscale\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"resolves coolify and dokploy as single services\", () => {\n\t\tconst result = resolve({ services: [\"coolify\", \"dokploy\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"coolify\");\n\t\texpect(ids).toContain(\"dokploy\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds postgresql, redis, and livekit when lasuite-meet-backend is selected\", () => {\n\t\tconst result = resolve({ services: [\"lasuite-meet-backend\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"lasuite-meet-backend\");\n\t\texpect(ids).toContain(\"postgresql\");\n\t\texpect(ids).toContain(\"redis\");\n\t\texpect(ids).toContain(\"livekit\");\n\t\texpect(result.addedDependencies.some((a) => a.service === \"postgresql\")).toBe(true);\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"resolves La Suite Meet preset (postgresql, redis, livekit, backend, frontend, agents)\", () => {\n\t\tconst lasuiteMeetServices = [\n\t\t\t\"postgresql\",\n\t\t\t\"redis\",\n\t\t\t\"livekit\",\n\t\t\t\"lasuite-meet-backend\",\n\t\t\t\"lasuite-meet-frontend\",\n\t\t\t\"lasuite-meet-agents\",\n\t\t];\n\t\tconst result = resolve({ services: lasuiteMeetServices, skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tfor (const id of lasuiteMeetServices) {\n\t\t\texpect(ids).toContain(id);\n\t\t}\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"orders lasuite-meet-backend after postgresql, redis, and livekit\", () => {\n\t\tconst result = resolve({\n\t\t\tservices: [\"lasuite-meet-backend\", \"lasuite-meet-frontend\"],\n\t\t\tskillPacks: [],\n\t\t});\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst pgIdx = ids.indexOf(\"postgresql\");\n\t\tconst redisIdx = ids.indexOf(\"redis\");\n\t\tconst livekitIdx = ids.indexOf(\"livekit\");\n\t\tconst backendIdx = ids.indexOf(\"lasuite-meet-backend\");\n\t\tconst frontendIdx = ids.indexOf(\"lasuite-meet-frontend\");\n\t\texpect(pgIdx).toBeGreaterThanOrEqual(0);\n\t\texpect(backendIdx).toBeGreaterThan(pgIdx);\n\t\texpect(backendIdx).toBeGreaterThan(redisIdx);\n\t\texpect(backendIdx).toBeGreaterThan(livekitIdx);\n\t\texpect(frontendIdx).toBeGreaterThan(backendIdx);\n\t});\n\n\t// ── Mandatory services enforcement ──────────────────────────────────────\n\n\tit(\"auto-includes mandatory services even when not selected\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"mission-control\");\n\t\texpect(ids).toContain(\"convex\");\n\t\texpect(ids).toContain(\"tailscale\");\n\t\tconst mc = result.services.find((s) => s.definition.id === \"mission-control\");\n\t\texpect(mc!.addedBy).toBe(\"mandatory\");\n\t});\n\n\tit(\"marks user-selected mandatory services as 'user' not 'mandatory'\", () => {\n\t\tconst result = resolve({ services: [\"mission-control\"], skillPacks: [] });\n\t\tconst mc = result.services.find((s) => s.definition.id === \"mission-control\");\n\t\texpect(mc!.addedBy).toBe(\"user\");\n\t\t// convex should still be auto-added as dependency of mission-control\n\t\tconst convex = result.services.find((s) => s.definition.id === \"convex\");\n\t\texpect(convex).toBeDefined();\n\t});\n\n\tit(\"orders convex before mission-control (dependency ordering)\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst convexIdx = ids.indexOf(\"convex\");\n\t\tconst mcIdx = ids.indexOf(\"mission-control\");\n\t\texpect(convexIdx).toBeLessThan(mcIdx);\n\t});\n});\n"],"mappings":"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"}
@@ -1,23 +1,31 @@
1
- import { n as describe, r as it, t as globalExpect } from "./vi.2VT5v0um-DvC3SVNc.mjs";
1
+ import { n as describe, r as it, t as globalExpect } from "./test.CTcmp4Su-ClCHJ3FA.mjs";
2
2
  import { resolve } from "./resolver.mjs";
3
3
  //#region src/resolver.test.ts
4
+ const MANDATORY_IDS = [
5
+ "convex",
6
+ "mission-control",
7
+ "tailscale"
8
+ ];
9
+ const MANDATORY_MEMORY = 448;
4
10
  describe("resolve", () => {
5
- it("returns empty services for empty input", () => {
11
+ it("returns only mandatory services for empty input", () => {
6
12
  const result = resolve({
7
13
  services: [],
8
14
  skillPacks: []
9
15
  });
10
- globalExpect(result.services).toHaveLength(0);
16
+ const ids = result.services.map((s) => s.definition.id);
17
+ for (const id of MANDATORY_IDS) globalExpect(ids).toContain(id);
11
18
  globalExpect(result.isValid).toBe(true);
12
- globalExpect(result.estimatedMemoryMB).toBe(512);
19
+ globalExpect(result.estimatedMemoryMB).toBe(512 + MANDATORY_MEMORY);
13
20
  });
14
- it("resolves a single service with no dependencies", () => {
21
+ it("resolves a single service alongside mandatory services", () => {
15
22
  const result = resolve({
16
23
  services: ["redis"],
17
24
  skillPacks: []
18
25
  });
19
- globalExpect(result.services).toHaveLength(1);
20
- globalExpect(result.services[0].definition.id).toBe("redis");
26
+ const ids = result.services.map((s) => s.definition.id);
27
+ globalExpect(ids).toContain("redis");
28
+ for (const id of MANDATORY_IDS) globalExpect(ids).toContain(id);
21
29
  globalExpect(result.isValid).toBe(true);
22
30
  });
23
31
  it("auto-adds PostgreSQL when n8n is selected", () => {
@@ -114,7 +122,7 @@ describe("resolve", () => {
114
122
  globalExpect(resolve({
115
123
  services: ["redis"],
116
124
  skillPacks: []
117
- }).estimatedMemoryMB).toBe(640);
125
+ }).estimatedMemoryMB).toBe(640 + MANDATORY_MEMORY);
118
126
  });
119
127
  it("adds proxy service when specified", () => {
120
128
  globalExpect(resolve({
@@ -164,13 +172,12 @@ describe("resolve", () => {
164
172
  gpu: false
165
173
  }).warnings.filter((w) => w.type === "gpu")).toHaveLength(0);
166
174
  });
167
- it("resolves tailscale as single service with no dependencies", () => {
175
+ it("resolves tailscale (mandatory, always present)", () => {
168
176
  const result = resolve({
169
- services: ["tailscale"],
177
+ services: [],
170
178
  skillPacks: []
171
179
  });
172
- globalExpect(result.services).toHaveLength(1);
173
- globalExpect(result.services[0].definition.id).toBe("tailscale");
180
+ globalExpect(result.services.map((s) => s.definition.id)).toContain("tailscale");
174
181
  globalExpect(result.isValid).toBe(true);
175
182
  });
176
183
  it("resolves coolify and dokploy as single services", () => {
@@ -229,6 +236,34 @@ describe("resolve", () => {
229
236
  globalExpect(backendIdx).toBeGreaterThan(livekitIdx);
230
237
  globalExpect(frontendIdx).toBeGreaterThan(backendIdx);
231
238
  });
239
+ it("auto-includes mandatory services even when not selected", () => {
240
+ const result = resolve({
241
+ services: ["redis"],
242
+ skillPacks: []
243
+ });
244
+ const ids = result.services.map((s) => s.definition.id);
245
+ globalExpect(ids).toContain("mission-control");
246
+ globalExpect(ids).toContain("convex");
247
+ globalExpect(ids).toContain("tailscale");
248
+ globalExpect(result.services.find((s) => s.definition.id === "mission-control").addedBy).toBe("mandatory");
249
+ });
250
+ it("marks user-selected mandatory services as 'user' not 'mandatory'", () => {
251
+ const result = resolve({
252
+ services: ["mission-control"],
253
+ skillPacks: []
254
+ });
255
+ globalExpect(result.services.find((s) => s.definition.id === "mission-control").addedBy).toBe("user");
256
+ globalExpect(result.services.find((s) => s.definition.id === "convex")).toBeDefined();
257
+ });
258
+ it("orders convex before mission-control (dependency ordering)", () => {
259
+ const ids = resolve({
260
+ services: ["redis"],
261
+ skillPacks: []
262
+ }).services.map((s) => s.definition.id);
263
+ const convexIdx = ids.indexOf("convex");
264
+ const mcIdx = ids.indexOf("mission-control");
265
+ globalExpect(convexIdx).toBeLessThan(mcIdx);
266
+ });
232
267
  });
233
268
  //#endregion
234
269
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.test.mjs","names":["expect"],"sources":["../src/resolver.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { resolve } from \"./resolver.js\";\n\ndescribe(\"resolve\", () => {\n\tit(\"returns empty services for empty input\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [] });\n\t\texpect(result.services).toHaveLength(0);\n\t\texpect(result.isValid).toBe(true);\n\t\texpect(result.estimatedMemoryMB).toBe(512); // base OpenClaw only\n\t});\n\n\tit(\"resolves a single service with no dependencies\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\texpect(result.services).toHaveLength(1);\n\t\texpect(result.services[0]!.definition.id).toBe(\"redis\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds PostgreSQL when n8n is selected\", () => {\n\t\tconst result = resolve({ services: [\"n8n\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"n8n\");\n\t\texpect(ids).toContain(\"postgresql\");\n\t\texpect(result.addedDependencies).toContainEqual(\n\t\t\texpect.objectContaining({ service: \"postgresql\" }),\n\t\t);\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds Prometheus when Grafana is selected\", () => {\n\t\tconst result = resolve({ services: [\"grafana\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t});\n\n\tit(\"detects Redis + Valkey conflict\", () => {\n\t\tconst result = resolve({ services: [\"redis\", \"valkey\"], skillPacks: [] });\n\t\texpect(result.isValid).toBe(false);\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"conflict\" }));\n\t});\n\n\tit(\"detects Caddy + Traefik conflict\", () => {\n\t\tconst result = resolve({ services: [\"caddy\", \"traefik\"], skillPacks: [] });\n\t\texpect(result.isValid).toBe(false);\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"conflict\" }));\n\t});\n\n\tit(\"expands research-agent skill pack\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"research-agent\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"qdrant\");\n\t\texpect(ids).toContain(\"searxng\");\n\t\texpect(ids).toContain(\"browserless\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"expands video-creator skill pack\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"video-creator\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"ffmpeg\");\n\t\texpect(ids).toContain(\"remotion\");\n\t\texpect(ids).toContain(\"minio\");\n\t});\n\n\tit(\"expands dev-ops skill pack with transitive deps\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"dev-ops\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"n8n\");\n\t\texpect(ids).toContain(\"redis\");\n\t\texpect(ids).toContain(\"uptime-kuma\");\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t\t// n8n requires postgresql (transitive)\n\t\texpect(ids).toContain(\"postgresql\");\n\t});\n\n\tit(\"reports unknown service IDs\", () => {\n\t\tconst result = resolve({ services: [\"nonexistent\"], skillPacks: [] });\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"unknown_service\" }));\n\t});\n\n\tit(\"reports unknown skill pack IDs\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"nonexistent-pack\"] });\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"unknown_skill_pack\" }));\n\t});\n\n\tit(\"does not duplicate services already selected by user\", () => {\n\t\tconst result = resolve({\n\t\t\tservices: [\"qdrant\", \"searxng\", \"browserless\"],\n\t\t\tskillPacks: [\"research-agent\"],\n\t\t});\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst qdrantCount = ids.filter((id) => id === \"qdrant\").length;\n\t\texpect(qdrantCount).toBe(1);\n\t});\n\n\tit(\"estimates memory correctly\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\t// 512 (base) + 128 (redis) = 640\n\t\texpect(result.estimatedMemoryMB).toBe(640);\n\t});\n\n\tit(\"adds proxy service when specified\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [], proxy: \"caddy\" });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"caddy\");\n\t});\n\n\tit(\"adds monitoring stack when flag is set\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [], monitoring: true });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"uptime-kuma\");\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t});\n\n\tit(\"is deterministic - same input gives same output\", () => {\n\t\tconst input = { services: [\"redis\", \"qdrant\", \"n8n\"], skillPacks: [\"research-agent\"] };\n\t\tconst result1 = resolve(input);\n\t\tconst result2 = resolve(input);\n\t\tconst ids1 = result1.services.map((s) => s.definition.id);\n\t\tconst ids2 = result2.services.map((s) => s.definition.id);\n\t\texpect(ids1).toEqual(ids2);\n\t});\n\n\tit(\"topologically sorts dependencies before dependents\", () => {\n\t\tconst result = resolve({ services: [\"n8n\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst pgIndex = ids.indexOf(\"postgresql\");\n\t\tconst n8nIndex = ids.indexOf(\"n8n\");\n\t\texpect(pgIndex).toBeLessThan(n8nIndex);\n\t});\n\n\tit(\"warns about GPU when AI services selected without gpu flag\", () => {\n\t\t// Ollama doesn't have gpuRequired=true (it's recommended not required)\n\t\t// but we should still check GPU warning logic works\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [], gpu: false });\n\t\t// Redis doesn't need GPU, so no GPU warnings\n\t\tconst gpuWarnings = result.warnings.filter((w) => w.type === \"gpu\");\n\t\texpect(gpuWarnings).toHaveLength(0);\n\t});\n\n\tit(\"resolves tailscale as single service with no dependencies\", () => {\n\t\tconst result = resolve({ services: [\"tailscale\"], skillPacks: [] });\n\t\texpect(result.services).toHaveLength(1);\n\t\texpect(result.services[0]!.definition.id).toBe(\"tailscale\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"resolves coolify and dokploy as single services\", () => {\n\t\tconst result = resolve({ services: [\"coolify\", \"dokploy\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"coolify\");\n\t\texpect(ids).toContain(\"dokploy\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds postgresql, redis, and livekit when lasuite-meet-backend is selected\", () => {\n\t\tconst result = resolve({ services: [\"lasuite-meet-backend\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"lasuite-meet-backend\");\n\t\texpect(ids).toContain(\"postgresql\");\n\t\texpect(ids).toContain(\"redis\");\n\t\texpect(ids).toContain(\"livekit\");\n\t\texpect(result.addedDependencies.some((a) => a.service === \"postgresql\")).toBe(true);\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"resolves La Suite Meet preset (postgresql, redis, livekit, backend, frontend, agents)\", () => {\n\t\tconst lasuiteMeetServices = [\n\t\t\t\"postgresql\",\n\t\t\t\"redis\",\n\t\t\t\"livekit\",\n\t\t\t\"lasuite-meet-backend\",\n\t\t\t\"lasuite-meet-frontend\",\n\t\t\t\"lasuite-meet-agents\",\n\t\t];\n\t\tconst result = resolve({ services: lasuiteMeetServices, skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tfor (const id of lasuiteMeetServices) {\n\t\t\texpect(ids).toContain(id);\n\t\t}\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"orders lasuite-meet-backend after postgresql, redis, and livekit\", () => {\n\t\tconst result = resolve({\n\t\t\tservices: [\"lasuite-meet-backend\", \"lasuite-meet-frontend\"],\n\t\t\tskillPacks: [],\n\t\t});\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst pgIdx = ids.indexOf(\"postgresql\");\n\t\tconst redisIdx = ids.indexOf(\"redis\");\n\t\tconst livekitIdx = ids.indexOf(\"livekit\");\n\t\tconst backendIdx = ids.indexOf(\"lasuite-meet-backend\");\n\t\tconst frontendIdx = ids.indexOf(\"lasuite-meet-frontend\");\n\t\texpect(pgIdx).toBeGreaterThanOrEqual(0);\n\t\texpect(backendIdx).toBeGreaterThan(pgIdx);\n\t\texpect(backendIdx).toBeGreaterThan(redisIdx);\n\t\texpect(backendIdx).toBeGreaterThan(livekitIdx);\n\t\texpect(frontendIdx).toBeGreaterThan(backendIdx);\n\t});\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"resolver.test.mjs","names":["expect"],"sources":["../src/resolver.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { resolve } from \"./resolver.js\";\n\n// Mandatory services (convex, mission-control, tailscale) are always auto-included.\n// These IDs and their combined memory are accounted for in all tests.\nconst MANDATORY_IDS = [\"convex\", \"mission-control\", \"tailscale\"];\nconst MANDATORY_MEMORY = 256 + 128 + 64; // convex + mission-control + tailscale\n\ndescribe(\"resolve\", () => {\n\tit(\"returns only mandatory services for empty input\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tfor (const id of MANDATORY_IDS) {\n\t\t\texpect(ids).toContain(id);\n\t\t}\n\t\texpect(result.isValid).toBe(true);\n\t\t// 512 (base) + mandatory services memory\n\t\texpect(result.estimatedMemoryMB).toBe(512 + MANDATORY_MEMORY);\n\t});\n\n\tit(\"resolves a single service alongside mandatory services\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"redis\");\n\t\tfor (const id of MANDATORY_IDS) {\n\t\t\texpect(ids).toContain(id);\n\t\t}\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds PostgreSQL when n8n is selected\", () => {\n\t\tconst result = resolve({ services: [\"n8n\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"n8n\");\n\t\texpect(ids).toContain(\"postgresql\");\n\t\texpect(result.addedDependencies).toContainEqual(\n\t\t\texpect.objectContaining({ service: \"postgresql\" }),\n\t\t);\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds Prometheus when Grafana is selected\", () => {\n\t\tconst result = resolve({ services: [\"grafana\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t});\n\n\tit(\"detects Redis + Valkey conflict\", () => {\n\t\tconst result = resolve({ services: [\"redis\", \"valkey\"], skillPacks: [] });\n\t\texpect(result.isValid).toBe(false);\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"conflict\" }));\n\t});\n\n\tit(\"detects Caddy + Traefik conflict\", () => {\n\t\tconst result = resolve({ services: [\"caddy\", \"traefik\"], skillPacks: [] });\n\t\texpect(result.isValid).toBe(false);\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"conflict\" }));\n\t});\n\n\tit(\"expands research-agent skill pack\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"research-agent\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"qdrant\");\n\t\texpect(ids).toContain(\"searxng\");\n\t\texpect(ids).toContain(\"browserless\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"expands video-creator skill pack\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"video-creator\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"ffmpeg\");\n\t\texpect(ids).toContain(\"remotion\");\n\t\texpect(ids).toContain(\"minio\");\n\t});\n\n\tit(\"expands dev-ops skill pack with transitive deps\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"dev-ops\"] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"n8n\");\n\t\texpect(ids).toContain(\"redis\");\n\t\texpect(ids).toContain(\"uptime-kuma\");\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t\t// n8n requires postgresql (transitive)\n\t\texpect(ids).toContain(\"postgresql\");\n\t});\n\n\tit(\"reports unknown service IDs\", () => {\n\t\tconst result = resolve({ services: [\"nonexistent\"], skillPacks: [] });\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"unknown_service\" }));\n\t});\n\n\tit(\"reports unknown skill pack IDs\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [\"nonexistent-pack\"] });\n\t\texpect(result.errors).toContainEqual(expect.objectContaining({ type: \"unknown_skill_pack\" }));\n\t});\n\n\tit(\"does not duplicate services already selected by user\", () => {\n\t\tconst result = resolve({\n\t\t\tservices: [\"qdrant\", \"searxng\", \"browserless\"],\n\t\t\tskillPacks: [\"research-agent\"],\n\t\t});\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst qdrantCount = ids.filter((id) => id === \"qdrant\").length;\n\t\texpect(qdrantCount).toBe(1);\n\t});\n\n\tit(\"estimates memory correctly\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\t// 512 (base) + 128 (redis) + mandatory services\n\t\texpect(result.estimatedMemoryMB).toBe(512 + 128 + MANDATORY_MEMORY);\n\t});\n\n\tit(\"adds proxy service when specified\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [], proxy: \"caddy\" });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"caddy\");\n\t});\n\n\tit(\"adds monitoring stack when flag is set\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [], monitoring: true });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"uptime-kuma\");\n\t\texpect(ids).toContain(\"grafana\");\n\t\texpect(ids).toContain(\"prometheus\");\n\t});\n\n\tit(\"is deterministic - same input gives same output\", () => {\n\t\tconst input = { services: [\"redis\", \"qdrant\", \"n8n\"], skillPacks: [\"research-agent\"] };\n\t\tconst result1 = resolve(input);\n\t\tconst result2 = resolve(input);\n\t\tconst ids1 = result1.services.map((s) => s.definition.id);\n\t\tconst ids2 = result2.services.map((s) => s.definition.id);\n\t\texpect(ids1).toEqual(ids2);\n\t});\n\n\tit(\"topologically sorts dependencies before dependents\", () => {\n\t\tconst result = resolve({ services: [\"n8n\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst pgIndex = ids.indexOf(\"postgresql\");\n\t\tconst n8nIndex = ids.indexOf(\"n8n\");\n\t\texpect(pgIndex).toBeLessThan(n8nIndex);\n\t});\n\n\tit(\"warns about GPU when AI services selected without gpu flag\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [], gpu: false });\n\t\tconst gpuWarnings = result.warnings.filter((w) => w.type === \"gpu\");\n\t\texpect(gpuWarnings).toHaveLength(0);\n\t});\n\n\tit(\"resolves tailscale (mandatory, always present)\", () => {\n\t\tconst result = resolve({ services: [], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"tailscale\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"resolves coolify and dokploy as single services\", () => {\n\t\tconst result = resolve({ services: [\"coolify\", \"dokploy\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"coolify\");\n\t\texpect(ids).toContain(\"dokploy\");\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"auto-adds postgresql, redis, and livekit when lasuite-meet-backend is selected\", () => {\n\t\tconst result = resolve({ services: [\"lasuite-meet-backend\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"lasuite-meet-backend\");\n\t\texpect(ids).toContain(\"postgresql\");\n\t\texpect(ids).toContain(\"redis\");\n\t\texpect(ids).toContain(\"livekit\");\n\t\texpect(result.addedDependencies.some((a) => a.service === \"postgresql\")).toBe(true);\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"resolves La Suite Meet preset (postgresql, redis, livekit, backend, frontend, agents)\", () => {\n\t\tconst lasuiteMeetServices = [\n\t\t\t\"postgresql\",\n\t\t\t\"redis\",\n\t\t\t\"livekit\",\n\t\t\t\"lasuite-meet-backend\",\n\t\t\t\"lasuite-meet-frontend\",\n\t\t\t\"lasuite-meet-agents\",\n\t\t];\n\t\tconst result = resolve({ services: lasuiteMeetServices, skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tfor (const id of lasuiteMeetServices) {\n\t\t\texpect(ids).toContain(id);\n\t\t}\n\t\texpect(result.isValid).toBe(true);\n\t});\n\n\tit(\"orders lasuite-meet-backend after postgresql, redis, and livekit\", () => {\n\t\tconst result = resolve({\n\t\t\tservices: [\"lasuite-meet-backend\", \"lasuite-meet-frontend\"],\n\t\t\tskillPacks: [],\n\t\t});\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst pgIdx = ids.indexOf(\"postgresql\");\n\t\tconst redisIdx = ids.indexOf(\"redis\");\n\t\tconst livekitIdx = ids.indexOf(\"livekit\");\n\t\tconst backendIdx = ids.indexOf(\"lasuite-meet-backend\");\n\t\tconst frontendIdx = ids.indexOf(\"lasuite-meet-frontend\");\n\t\texpect(pgIdx).toBeGreaterThanOrEqual(0);\n\t\texpect(backendIdx).toBeGreaterThan(pgIdx);\n\t\texpect(backendIdx).toBeGreaterThan(redisIdx);\n\t\texpect(backendIdx).toBeGreaterThan(livekitIdx);\n\t\texpect(frontendIdx).toBeGreaterThan(backendIdx);\n\t});\n\n\t// ── Mandatory services enforcement ──────────────────────────────────────\n\n\tit(\"auto-includes mandatory services even when not selected\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\texpect(ids).toContain(\"mission-control\");\n\t\texpect(ids).toContain(\"convex\");\n\t\texpect(ids).toContain(\"tailscale\");\n\t\tconst mc = result.services.find((s) => s.definition.id === \"mission-control\");\n\t\texpect(mc!.addedBy).toBe(\"mandatory\");\n\t});\n\n\tit(\"marks user-selected mandatory services as 'user' not 'mandatory'\", () => {\n\t\tconst result = resolve({ services: [\"mission-control\"], skillPacks: [] });\n\t\tconst mc = result.services.find((s) => s.definition.id === \"mission-control\");\n\t\texpect(mc!.addedBy).toBe(\"user\");\n\t\t// convex should still be auto-added as dependency of mission-control\n\t\tconst convex = result.services.find((s) => s.definition.id === \"convex\");\n\t\texpect(convex).toBeDefined();\n\t});\n\n\tit(\"orders convex before mission-control (dependency ordering)\", () => {\n\t\tconst result = resolve({ services: [\"redis\"], skillPacks: [] });\n\t\tconst ids = result.services.map((s) => s.definition.id);\n\t\tconst convexIdx = ids.indexOf(\"convex\");\n\t\tconst mcIdx = ids.indexOf(\"mission-control\");\n\t\texpect(convexIdx).toBeLessThan(mcIdx);\n\t});\n});\n"],"mappings":"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"}