@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,18 +1,30 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { resolve } from "./resolver.js";
3
3
 
4
+ // Mandatory services (convex, mission-control, tailscale) are always auto-included.
5
+ // These IDs and their combined memory are accounted for in all tests.
6
+ const MANDATORY_IDS = ["convex", "mission-control", "tailscale"];
7
+ const MANDATORY_MEMORY = 256 + 128 + 64; // convex + mission-control + tailscale
8
+
4
9
  describe("resolve", () => {
5
- it("returns empty services for empty input", () => {
10
+ it("returns only mandatory services for empty input", () => {
6
11
  const result = resolve({ services: [], skillPacks: [] });
7
- expect(result.services).toHaveLength(0);
12
+ const ids = result.services.map((s) => s.definition.id);
13
+ for (const id of MANDATORY_IDS) {
14
+ expect(ids).toContain(id);
15
+ }
8
16
  expect(result.isValid).toBe(true);
9
- expect(result.estimatedMemoryMB).toBe(512); // base OpenClaw only
17
+ // 512 (base) + mandatory services memory
18
+ expect(result.estimatedMemoryMB).toBe(512 + MANDATORY_MEMORY);
10
19
  });
11
20
 
12
- it("resolves a single service with no dependencies", () => {
21
+ it("resolves a single service alongside mandatory services", () => {
13
22
  const result = resolve({ services: ["redis"], skillPacks: [] });
14
- expect(result.services).toHaveLength(1);
15
- expect(result.services[0]!.definition.id).toBe("redis");
23
+ const ids = result.services.map((s) => s.definition.id);
24
+ expect(ids).toContain("redis");
25
+ for (const id of MANDATORY_IDS) {
26
+ expect(ids).toContain(id);
27
+ }
16
28
  expect(result.isValid).toBe(true);
17
29
  });
18
30
 
@@ -97,8 +109,8 @@ describe("resolve", () => {
97
109
 
98
110
  it("estimates memory correctly", () => {
99
111
  const result = resolve({ services: ["redis"], skillPacks: [] });
100
- // 512 (base) + 128 (redis) = 640
101
- expect(result.estimatedMemoryMB).toBe(640);
112
+ // 512 (base) + 128 (redis) + mandatory services
113
+ expect(result.estimatedMemoryMB).toBe(512 + 128 + MANDATORY_MEMORY);
102
114
  });
103
115
 
104
116
  it("adds proxy service when specified", () => {
@@ -133,18 +145,15 @@ describe("resolve", () => {
133
145
  });
134
146
 
135
147
  it("warns about GPU when AI services selected without gpu flag", () => {
136
- // Ollama doesn't have gpuRequired=true (it's recommended not required)
137
- // but we should still check GPU warning logic works
138
148
  const result = resolve({ services: ["redis"], skillPacks: [], gpu: false });
139
- // Redis doesn't need GPU, so no GPU warnings
140
149
  const gpuWarnings = result.warnings.filter((w) => w.type === "gpu");
141
150
  expect(gpuWarnings).toHaveLength(0);
142
151
  });
143
152
 
144
- it("resolves tailscale as single service with no dependencies", () => {
145
- const result = resolve({ services: ["tailscale"], skillPacks: [] });
146
- expect(result.services).toHaveLength(1);
147
- expect(result.services[0]!.definition.id).toBe("tailscale");
153
+ it("resolves tailscale (mandatory, always present)", () => {
154
+ const result = resolve({ services: [], skillPacks: [] });
155
+ const ids = result.services.map((s) => s.definition.id);
156
+ expect(ids).toContain("tailscale");
148
157
  expect(result.isValid).toBe(true);
149
158
  });
150
159
 
@@ -201,4 +210,33 @@ describe("resolve", () => {
201
210
  expect(backendIdx).toBeGreaterThan(livekitIdx);
202
211
  expect(frontendIdx).toBeGreaterThan(backendIdx);
203
212
  });
213
+
214
+ // ── Mandatory services enforcement ──────────────────────────────────────
215
+
216
+ it("auto-includes mandatory services even when not selected", () => {
217
+ const result = resolve({ services: ["redis"], skillPacks: [] });
218
+ const ids = result.services.map((s) => s.definition.id);
219
+ expect(ids).toContain("mission-control");
220
+ expect(ids).toContain("convex");
221
+ expect(ids).toContain("tailscale");
222
+ const mc = result.services.find((s) => s.definition.id === "mission-control");
223
+ expect(mc!.addedBy).toBe("mandatory");
224
+ });
225
+
226
+ it("marks user-selected mandatory services as 'user' not 'mandatory'", () => {
227
+ const result = resolve({ services: ["mission-control"], skillPacks: [] });
228
+ const mc = result.services.find((s) => s.definition.id === "mission-control");
229
+ expect(mc!.addedBy).toBe("user");
230
+ // convex should still be auto-added as dependency of mission-control
231
+ const convex = result.services.find((s) => s.definition.id === "convex");
232
+ expect(convex).toBeDefined();
233
+ });
234
+
235
+ it("orders convex before mission-control (dependency ordering)", () => {
236
+ const result = resolve({ services: ["redis"], skillPacks: [] });
237
+ const ids = result.services.map((s) => s.definition.id);
238
+ const convexIdx = ids.indexOf("convex");
239
+ const mcIdx = ids.indexOf("mission-control");
240
+ expect(convexIdx).toBeLessThan(mcIdx);
241
+ });
204
242
  });
package/src/resolver.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { getServiceById } from "./services/registry.js";
1
+ import { getAllServices, getServiceById } from "./services/registry.js";
2
2
  import { getSkillPackById } from "./skills/registry.js";
3
3
  import type {
4
4
  AddedDependency,
@@ -99,6 +99,18 @@ export function resolve(input: ResolverInput): ResolverOutput {
99
99
  }
100
100
  }
101
101
 
102
+ // Add mandatory platform services (mission-control, convex, tailscale, etc.)
103
+ for (const def of getAllServices()) {
104
+ if (def.mandatory && !serviceIds.has(def.id)) {
105
+ serviceIds.add(def.id);
106
+ serviceAddedBy.set(def.id, "mandatory");
107
+ addedDependencies.push({
108
+ service: def.id,
109
+ reason: "Mandatory OpenClaw platform service",
110
+ });
111
+ }
112
+ }
113
+
102
114
  // Validate all service IDs exist
103
115
  const unknownIds: string[] = [];
104
116
  for (const id of serviceIds) {
package/src/schema.ts CHANGED
@@ -38,6 +38,7 @@ export const ServiceCategorySchema = z.enum([
38
38
  "business-intelligence",
39
39
  "dns-networking",
40
40
  "iot",
41
+ "saas-boilerplate",
41
42
  ]);
42
43
 
43
44
  export const MaturitySchema = z.enum(["stable", "beta", "experimental"]);
@@ -153,6 +154,48 @@ export const NativeRecipeSchema = z.object({
153
154
  systemdUnit: z.string().optional(),
154
155
  });
155
156
 
157
+ // ─── Git Source / Build Context (for repo-based services) ───────────────────
158
+
159
+ export const GitSourceSchema = z.object({
160
+ /** Git clone URL, e.g. "https://github.com/wasp-lang/open-saas.git" */
161
+ repoUrl: z.string().url(),
162
+ /** Branch or tag to clone. Defaults to repo's default branch. */
163
+ branch: z.string().optional(),
164
+ /** Subdirectory within the cloned repo to use as build context root. */
165
+ subdirectory: z.string().optional(),
166
+ /** Commands to run after cloning (e.g. "cp .env.example .env"). */
167
+ postCloneCommands: z.array(z.string()).default([]),
168
+ });
169
+
170
+ export const BuildContextSchema = z.object({
171
+ /** Path to Dockerfile relative to the context root. Defaults to "Dockerfile". */
172
+ dockerfile: z.string().optional(),
173
+ /** Build context path relative to the subdirectory root. Defaults to ".". */
174
+ context: z.string().default("."),
175
+ /** Docker build args to pass at build time. */
176
+ args: z.record(z.string(), z.string()).optional(),
177
+ /** Target stage in a multi-stage Dockerfile. */
178
+ target: z.string().optional(),
179
+ });
180
+
181
+ // ─── Env Quirks (for addon stack generation) ────────────────────────────────
182
+
183
+ export const EnvQuirkFixSchema = z.object({
184
+ type: z.enum(["set_value", "generate_hex", "generate_base64url", "sync_with"]),
185
+ /** Fixed value to set (for set_value type). */
186
+ value: z.string().optional(),
187
+ /** Minimum bytes for generated secrets (for generate_hex/generate_base64url). */
188
+ minBytes: z.number().int().min(1).optional(),
189
+ /** Key to synchronize with (for sync_with type). */
190
+ syncKey: z.string().optional(),
191
+ });
192
+
193
+ export const EnvQuirkSchema = z.object({
194
+ key: z.string(),
195
+ issue: z.enum(["empty_string_crashes", "min_length", "must_sync"]),
196
+ fix: EnvQuirkFixSchema,
197
+ });
198
+
156
199
  // ─── Service Definition ─────────────────────────────────────────────────────
157
200
 
158
201
  export const ServiceDefinitionSchema = z.object({
@@ -166,9 +209,13 @@ export const ServiceDefinitionSchema = z.object({
166
209
  category: ServiceCategorySchema,
167
210
  icon: z.string(),
168
211
 
169
- // Docker
170
- image: z.string().min(1),
171
- imageTag: z.string().min(1),
212
+ // Docker (required for image-based services, omitted for git-based)
213
+ image: z.string().min(1).optional(),
214
+ imageTag: z.string().min(1).optional(),
215
+
216
+ // Git source (for services built from cloned repositories)
217
+ gitSource: GitSourceSchema.optional(),
218
+ buildContext: BuildContextSchema.optional(),
172
219
  ports: z.array(PortMappingSchema).default([]),
173
220
  volumes: z.array(VolumeMappingSchema).default([]),
174
221
  environment: z.array(EnvVariableSchema).default([]),
@@ -207,6 +254,18 @@ export const ServiceDefinitionSchema = z.object({
207
254
  // Bare-metal native (install/run on host when no Docker)
208
255
  nativeSupported: z.boolean().optional(),
209
256
  nativeRecipes: z.array(NativeRecipeSchema).optional(),
257
+
258
+ // Clawexa addon metadata
259
+ /** Whether the service works with cap_drop: ALL (default: undefined = not audited). */
260
+ capDropCompatible: z.boolean().optional(),
261
+ /** Pre-built Docker image for cloud-init deployment (replaces build: directives). */
262
+ prebuiltImage: z.string().optional(),
263
+ /** Default reverse proxy path (e.g. "/n8n", "/grafana"). */
264
+ proxyPath: z.string().optional(),
265
+ /** Linux capabilities needed at first boot (e.g. ["CHOWN", "SETGID"]). */
266
+ firstBootCapabilities: z.array(z.string()).optional(),
267
+ /** Known environment variable quirks that need special handling. */
268
+ envQuirks: z.array(EnvQuirkSchema).optional(),
210
269
  });
211
270
 
212
271
  // ─── Skill Pack ─────────────────────────────────────────────────────────────
@@ -278,7 +337,7 @@ export const GenerationInputSchema = z.object({
278
337
 
279
338
  export const ResolvedServiceSchema = z.object({
280
339
  definition: ServiceDefinitionSchema,
281
- addedBy: z.enum(["user", "dependency", "skill-pack", "proxy", "monitoring"]).default("user"),
340
+ addedBy: z.enum(["user", "dependency", "skill-pack", "proxy", "monitoring", "mandatory"]).default("user"),
282
341
  });
283
342
 
284
343
  export const AddedDependencySchema = z.object({
@@ -335,6 +394,159 @@ export const ComposeOptionsSchema = z.object({
335
394
  openclawInstallMethod: OpenclawInstallMethodSchema.default("docker"),
336
395
  });
337
396
 
397
+ // ─── Addon Stack (Clawexa) ───────────────────────────────────────────────────
398
+
399
+ export const SkippedServiceReasonSchema = z.enum([
400
+ "missing_credentials",
401
+ "no_image",
402
+ "platform_unsupported",
403
+ "conflict",
404
+ "gpu_required",
405
+ "unknown_service",
406
+ "resolution_error",
407
+ ]);
408
+
409
+ export const SkippedServiceSchema = z.object({
410
+ serviceId: z.string(),
411
+ reason: SkippedServiceReasonSchema,
412
+ details: z.string(),
413
+ requiredCredentials: z.array(z.string()).optional(),
414
+ });
415
+
416
+ export const ProxyRouteSchema = z.object({
417
+ serviceId: z.string(),
418
+ path: z.string(),
419
+ port: z.number().int(),
420
+ protocol: z.enum(["http", "https", "ws"]).default("http"),
421
+ stripPrefix: z.boolean().default(true),
422
+ });
423
+
424
+ export const AddonStackInputSchema = z.object({
425
+ /** Instance identifier (used for project name sanitization). */
426
+ instanceId: z.string().min(1),
427
+ /** Addon service IDs to deploy. */
428
+ services: z.array(z.string()),
429
+ /** Optional skill packs. */
430
+ skillPacks: z.array(z.string()).default([]),
431
+ /** Platform architecture. */
432
+ platform: PlatformSchema.default("linux/amd64"),
433
+ /** OpenClaw version running on the instance. */
434
+ openclawVersion: z.string().default("latest"),
435
+ /** Services already running on the instance (to detect conflicts). */
436
+ existingServices: z.array(z.string()).default([]),
437
+ /** Ports already in use on the host. */
438
+ reservedPorts: z.array(z.number().int()).default([]),
439
+ /** Whether to generate secrets (default: true). */
440
+ generateSecrets: z.boolean().default(true),
441
+ /** User-provided credentials for services that need them. */
442
+ credentials: z.record(z.string(), z.record(z.string(), z.string())).default({}),
443
+ /** Port overrides for specific services. */
444
+ portOverrides: z.record(z.string(), z.record(z.string(), z.number().int().min(1).max(65535))).optional(),
445
+ /** Whether the host has GPU support. */
446
+ gpu: z.boolean().default(false),
447
+ /** AI providers configured on this instance. */
448
+ aiProviders: z.array(AiProviderSchema).default([]),
449
+ /** Pre-built image overrides (serviceId → image:tag). */
450
+ prebuiltImages: z.record(z.string(), z.string()).default({}),
451
+ });
452
+
453
+ export const AddonStackResultSchema = z.object({
454
+ /** Single docker-compose.override.yml content. */
455
+ composeOverride: z.string(),
456
+ /** Complete .env file with all secrets generated. */
457
+ envFile: z.string(),
458
+ /** Structured env vars for UI display / credential management. */
459
+ envVars: z.array(z.object({
460
+ serviceName: z.string(),
461
+ vars: z.array(z.object({
462
+ key: z.string(),
463
+ description: z.string(),
464
+ value: z.string(),
465
+ secret: z.boolean(),
466
+ })),
467
+ })),
468
+ /** SKILL.md files keyed by slug. */
469
+ skillFiles: z.record(z.string(), z.string()),
470
+ /** OpenClaw config additions (skills.entries to merge). */
471
+ openclawConfigPatch: z.object({
472
+ skills: z.object({
473
+ entries: z.record(z.string(), z.object({ enabled: z.boolean() })),
474
+ }),
475
+ }),
476
+ /** Port mapping for reverse proxy configuration. */
477
+ proxyRoutes: z.array(ProxyRouteSchema),
478
+ /** Metadata. */
479
+ metadata: z.object({
480
+ serviceCount: z.number(),
481
+ skillCount: z.number(),
482
+ estimatedMemoryMB: z.number(),
483
+ resolvedServices: z.array(z.string()),
484
+ skippedServices: z.array(SkippedServiceSchema),
485
+ generatedSecretKeys: z.array(z.string()),
486
+ portAssignments: z.record(z.string(), z.number()),
487
+ }),
488
+ /** Warnings (non-fatal issues). */
489
+ warnings: z.array(z.string()),
490
+ });
491
+
492
+ export const AddonStackUpdateInputSchema = z.object({
493
+ instanceId: z.string().min(1),
494
+ /** Current compose override YAML (from the running instance). */
495
+ currentCompose: z.string(),
496
+ /** Current .env content. */
497
+ currentEnv: z.string(),
498
+ /** Services to add. */
499
+ addServices: z.array(z.string()).default([]),
500
+ /** Services to remove. */
501
+ removeServices: z.array(z.string()).default([]),
502
+ /** Whether to generate secrets for newly added services (default: true). */
503
+ generateSecrets: z.boolean().default(true),
504
+ /** User-provided credentials for new services. */
505
+ credentials: z.record(z.string(), z.record(z.string(), z.string())).default({}),
506
+ /** Port overrides for specific services. */
507
+ portOverrides: z.record(z.string(), z.record(z.string(), z.number().int().min(1).max(65535))).optional(),
508
+ /** Reserved ports. */
509
+ reservedPorts: z.array(z.number().int()).default([]),
510
+ platform: PlatformSchema.default("linux/amd64"),
511
+ openclawVersion: z.string().default("latest"),
512
+ aiProviders: z.array(AiProviderSchema).default([]),
513
+ prebuiltImages: z.record(z.string(), z.string()).default({}),
514
+ });
515
+
516
+ export const AddonStackUpdateResultSchema = z.object({
517
+ /** Updated compose override (merged with existing). */
518
+ composeOverride: z.string(),
519
+ /** Updated .env (existing values preserved, new ones added). */
520
+ envFile: z.string(),
521
+ /** Only NEW skill files to deploy. */
522
+ newSkillFiles: z.record(z.string(), z.string()),
523
+ /** Skill slugs to remove from openclaw.json. */
524
+ removedSkillSlugs: z.array(z.string()),
525
+ /** Config patch to apply. */
526
+ openclawConfigPatch: z.object({
527
+ skills: z.object({
528
+ add: z.record(z.string(), z.object({ enabled: z.boolean() })),
529
+ remove: z.array(z.string()),
530
+ }),
531
+ }),
532
+ /** New proxy routes to add. */
533
+ addProxyRoutes: z.array(ProxyRouteSchema),
534
+ /** Proxy routes to remove (service IDs). */
535
+ removeProxyRoutes: z.array(z.string()),
536
+ /** Services that need their images pulled. */
537
+ imagesToPull: z.array(z.string()),
538
+ /** Services to restart (due to dependency changes). */
539
+ restartRequired: z.array(z.string()),
540
+ /** Metadata. */
541
+ metadata: z.object({
542
+ added: z.array(z.string()),
543
+ removed: z.array(z.string()),
544
+ unchanged: z.array(z.string()),
545
+ estimatedMemoryDelta: z.number(),
546
+ }),
547
+ warnings: z.array(z.string()),
548
+ });
549
+
338
550
  // ─── API Request/Response ───────────────────────────────────────────────────
339
551
 
340
552
  export const ValidateRequestSchema = z.object({
@@ -0,0 +1,84 @@
1
+ import type { ServiceDefinition } from "../../types.js";
2
+
3
+ export const apptensionSaasDefinition: ServiceDefinition = {
4
+ id: "apptension-saas",
5
+ name: "SaaS Boilerplate (Apptension)",
6
+ description:
7
+ "Production-ready SaaS boilerplate with React frontend, Django backend, GraphQL API, PostgreSQL, async workers, email, Stripe payments, and Docker Compose support.",
8
+ category: "saas-boilerplate",
9
+ icon: "🚀",
10
+
11
+ gitSource: {
12
+ repoUrl: "https://github.com/apptension/saas-boilerplate.git",
13
+ branch: "master",
14
+ postCloneCommands: [],
15
+ },
16
+ buildContext: {
17
+ dockerfile: "Dockerfile",
18
+ context: ".",
19
+ },
20
+
21
+ ports: [
22
+ {
23
+ host: 3101,
24
+ container: 80,
25
+ description: "Apptension SaaS web application",
26
+ exposed: true,
27
+ },
28
+ ],
29
+ volumes: [],
30
+ environment: [
31
+ {
32
+ key: "APPTENSION_SAAS_DB_PASSWORD",
33
+ defaultValue: "",
34
+ secret: true,
35
+ description: "Database password for Apptension SaaS",
36
+ required: true,
37
+ },
38
+ {
39
+ key: "DB_CONNECTION",
40
+ defaultValue: "postgresql://apptensionsaas:${APPTENSION_SAAS_DB_PASSWORD}@postgresql:5432/apptensionsaas",
41
+ secret: false,
42
+ description: "PostgreSQL connection string",
43
+ required: true,
44
+ },
45
+ {
46
+ key: "DJANGO_SECRET_KEY",
47
+ defaultValue: "",
48
+ secret: true,
49
+ description: "Django secret key",
50
+ required: true,
51
+ },
52
+ {
53
+ key: "REDIS_URL",
54
+ defaultValue: "redis://redis:6379/0",
55
+ secret: false,
56
+ description: "Redis connection URL for caching and async workers",
57
+ required: true,
58
+ },
59
+ ],
60
+ healthcheck: {
61
+ test: "wget -q --spider http://localhost:80/ || exit 1",
62
+ interval: "30s",
63
+ timeout: "10s",
64
+ retries: 3,
65
+ startPeriod: "60s",
66
+ },
67
+ dependsOn: ["postgresql", "redis"],
68
+ restartPolicy: "unless-stopped",
69
+ networks: ["openclaw-network"],
70
+
71
+ skills: [],
72
+ openclawEnvVars: [],
73
+
74
+ docsUrl: "https://docs.demo.saas.apptension.com",
75
+ tags: ["saas", "boilerplate", "react", "django", "graphql", "stripe", "enterprise"],
76
+ maturity: "beta",
77
+
78
+ requires: ["postgresql", "redis"],
79
+ recommends: [],
80
+ conflictsWith: [],
81
+
82
+ minMemoryMB: 768,
83
+ gpuRequired: false,
84
+ };
@@ -0,0 +1,84 @@
1
+ import type { ServiceDefinition } from "../../types.js";
2
+
3
+ export const boxyhqSaasDefinition: ServiceDefinition = {
4
+ id: "boxyhq-saas",
5
+ name: "BoxyHQ Enterprise SaaS Starter",
6
+ description:
7
+ "Enterprise-grade Next.js SaaS starter kit with SSO (SAML/OIDC), directory sync, audit logs, team management, Prisma ORM, Stripe billing, and PostgreSQL.",
8
+ category: "saas-boilerplate",
9
+ icon: "🚀",
10
+
11
+ gitSource: {
12
+ repoUrl: "https://github.com/boxyhq/saas-starter-kit.git",
13
+ branch: "main",
14
+ postCloneCommands: ["cp .env.example .env"],
15
+ },
16
+ buildContext: {
17
+ dockerfile: "Dockerfile",
18
+ context: ".",
19
+ },
20
+
21
+ ports: [
22
+ {
23
+ host: 3102,
24
+ container: 3000,
25
+ description: "BoxyHQ SaaS web application",
26
+ exposed: true,
27
+ },
28
+ ],
29
+ volumes: [],
30
+ environment: [
31
+ {
32
+ key: "BOXYHQ_SAAS_DB_PASSWORD",
33
+ defaultValue: "",
34
+ secret: true,
35
+ description: "Database password for BoxyHQ SaaS",
36
+ required: true,
37
+ },
38
+ {
39
+ key: "DATABASE_URL",
40
+ defaultValue: "postgresql://boxyhqsaas:${BOXYHQ_SAAS_DB_PASSWORD}@postgresql:5432/boxyhqsaas",
41
+ secret: false,
42
+ description: "PostgreSQL connection string",
43
+ required: true,
44
+ },
45
+ {
46
+ key: "NEXTAUTH_SECRET",
47
+ defaultValue: "",
48
+ secret: true,
49
+ description: "NextAuth.js session encryption secret",
50
+ required: true,
51
+ },
52
+ {
53
+ key: "NEXTAUTH_URL",
54
+ defaultValue: "http://localhost:3102",
55
+ secret: false,
56
+ description: "NextAuth.js callback URL",
57
+ required: true,
58
+ },
59
+ ],
60
+ healthcheck: {
61
+ test: "wget -q --spider http://localhost:3000/ || exit 1",
62
+ interval: "30s",
63
+ timeout: "10s",
64
+ retries: 3,
65
+ startPeriod: "60s",
66
+ },
67
+ dependsOn: ["postgresql"],
68
+ restartPolicy: "unless-stopped",
69
+ networks: ["openclaw-network"],
70
+
71
+ skills: [],
72
+ openclawEnvVars: [],
73
+
74
+ docsUrl: "https://github.com/boxyhq/saas-starter-kit#readme",
75
+ tags: ["saas", "boilerplate", "nextjs", "enterprise", "sso", "saml", "stripe", "prisma"],
76
+ maturity: "beta",
77
+
78
+ requires: ["postgresql"],
79
+ recommends: [],
80
+ conflictsWith: [],
81
+
82
+ minMemoryMB: 512,
83
+ gpuRequired: false,
84
+ };
@@ -81,4 +81,7 @@ export const browserlessDefinition: ServiceDefinition = {
81
81
 
82
82
  minMemoryMB: 512,
83
83
  gpuRequired: false,
84
+ capDropCompatible: false,
85
+ proxyPath: "/browserless",
86
+ firstBootCapabilities: ["SYS_ADMIN"],
84
87
  };
@@ -0,0 +1,84 @@
1
+ import type { ServiceDefinition } from "../../types.js";
2
+
3
+ export const cmsaasStarterDefinition: ServiceDefinition = {
4
+ id: "cmsaas-starter",
5
+ name: "CMSaasStarter",
6
+ description:
7
+ "SvelteKit SaaS starter with Supabase backend, Stripe payments, user management, blog engine, SEO optimization, and edge deployment support.",
8
+ category: "saas-boilerplate",
9
+ icon: "🚀",
10
+
11
+ gitSource: {
12
+ repoUrl: "https://github.com/CriticalMoments/CMSaasStarter.git",
13
+ branch: "main",
14
+ postCloneCommands: [],
15
+ },
16
+ buildContext: {
17
+ dockerfile: "Dockerfile",
18
+ context: ".",
19
+ },
20
+
21
+ ports: [
22
+ {
23
+ host: 3104,
24
+ container: 3000,
25
+ description: "CMSaasStarter web application",
26
+ exposed: true,
27
+ },
28
+ ],
29
+ volumes: [],
30
+ environment: [
31
+ {
32
+ key: "PUBLIC_SUPABASE_URL",
33
+ defaultValue: "",
34
+ secret: false,
35
+ description: "Supabase project URL",
36
+ required: true,
37
+ },
38
+ {
39
+ key: "PUBLIC_SUPABASE_ANON_KEY",
40
+ defaultValue: "",
41
+ secret: false,
42
+ description: "Supabase anonymous/public key",
43
+ required: true,
44
+ },
45
+ {
46
+ key: "PRIVATE_SUPABASE_SERVICE_ROLE",
47
+ defaultValue: "",
48
+ secret: true,
49
+ description: "Supabase service role key (server-side only)",
50
+ required: true,
51
+ },
52
+ {
53
+ key: "PRIVATE_STRIPE_API_KEY",
54
+ defaultValue: "",
55
+ secret: true,
56
+ description: "Stripe secret API key",
57
+ required: true,
58
+ },
59
+ ],
60
+ healthcheck: {
61
+ test: "wget -q --spider http://localhost:3000/ || exit 1",
62
+ interval: "30s",
63
+ timeout: "10s",
64
+ retries: 3,
65
+ startPeriod: "60s",
66
+ },
67
+ dependsOn: [],
68
+ restartPolicy: "unless-stopped",
69
+ networks: ["openclaw-network"],
70
+
71
+ skills: [],
72
+ openclawEnvVars: [],
73
+
74
+ docsUrl: "https://github.com/CriticalMoments/CMSaasStarter#readme",
75
+ tags: ["saas", "boilerplate", "sveltekit", "supabase", "stripe", "seo"],
76
+ maturity: "beta",
77
+
78
+ requires: [],
79
+ recommends: ["postgresql"],
80
+ conflictsWith: [],
81
+
82
+ minMemoryMB: 256,
83
+ gpuRequired: false,
84
+ };
@@ -89,6 +89,14 @@ export const convexDefinition: ServiceDefinition = {
89
89
  "Admin key for CLI access. Generate with: docker compose exec convex ./generate_admin_key.sh",
90
90
  required: false,
91
91
  },
92
+ {
93
+ key: "CONVEX_INSTANCE_SECRET",
94
+ defaultValue: "${INSTANCE_SECRET}",
95
+ secret: true,
96
+ description:
97
+ "Convex instance secret used by Mission Control (synced with INSTANCE_SECRET)",
98
+ required: false,
99
+ },
92
100
  ],
93
101
  healthcheck: {
94
102
  test: "curl -f http://localhost:3210/version",
@@ -118,4 +126,27 @@ export const convexDefinition: ServiceDefinition = {
118
126
 
119
127
  minMemoryMB: 256,
120
128
  gpuRequired: false,
129
+ capDropCompatible: false,
130
+ envQuirks: [
131
+ {
132
+ key: "DISABLE_BEACON",
133
+ issue: "empty_string_crashes" as const,
134
+ fix: { type: "set_value" as const, value: "true" },
135
+ },
136
+ {
137
+ key: "INSTANCE_SECRET",
138
+ issue: "min_length" as const,
139
+ fix: { type: "generate_hex" as const, minBytes: 32 },
140
+ },
141
+ {
142
+ key: "CONVEX_SELF_HOSTED_ADMIN_KEY",
143
+ issue: "min_length" as const,
144
+ fix: { type: "generate_hex" as const, minBytes: 32 },
145
+ },
146
+ {
147
+ key: "INSTANCE_SECRET",
148
+ issue: "must_sync" as const,
149
+ fix: { type: "sync_with" as const, syncKey: "CONVEX_INSTANCE_SECRET" },
150
+ },
151
+ ],
121
152
  };