@better-openclaw/core 1.0.23 → 1.0.24

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 (254) hide show
  1. package/dist/bare-metal-partition.test.cjs +3 -4
  2. package/dist/bare-metal-partition.test.cjs.map +1 -1
  3. package/dist/bare-metal-partition.test.mjs +3 -4
  4. package/dist/bare-metal-partition.test.mjs.map +1 -1
  5. package/dist/composer.cjs +13 -1
  6. package/dist/composer.cjs.map +1 -1
  7. package/dist/composer.d.cts.map +1 -1
  8. package/dist/composer.d.mts.map +1 -1
  9. package/dist/composer.mjs +13 -1
  10. package/dist/composer.mjs.map +1 -1
  11. package/dist/composer.snapshot.test.cjs +1 -1
  12. package/dist/composer.snapshot.test.mjs +1 -1
  13. package/dist/composer.test.cjs +3 -2
  14. package/dist/composer.test.cjs.map +1 -1
  15. package/dist/composer.test.mjs +3 -2
  16. package/dist/composer.test.mjs.map +1 -1
  17. package/dist/deployers/strip-host-ports.test.cjs +1 -1
  18. package/dist/deployers/strip-host-ports.test.mjs +1 -1
  19. package/dist/generate.cjs +6 -2
  20. package/dist/generate.cjs.map +1 -1
  21. package/dist/generate.d.cts.map +1 -1
  22. package/dist/generate.d.mts.map +1 -1
  23. package/dist/generate.mjs +6 -2
  24. package/dist/generate.mjs.map +1 -1
  25. package/dist/generate.test.cjs +2 -2
  26. package/dist/generate.test.cjs.map +1 -1
  27. package/dist/generate.test.mjs +2 -2
  28. package/dist/generate.test.mjs.map +1 -1
  29. package/dist/generators/bare-metal-install.test.cjs +1 -1
  30. package/dist/generators/bare-metal-install.test.mjs +1 -1
  31. package/dist/generators/caddy.test.cjs +1 -1
  32. package/dist/generators/caddy.test.mjs +1 -1
  33. package/dist/generators/clone-repos.cjs +140 -0
  34. package/dist/generators/clone-repos.cjs.map +1 -0
  35. package/dist/generators/clone-repos.d.cts +11 -0
  36. package/dist/generators/clone-repos.d.cts.map +1 -0
  37. package/dist/generators/clone-repos.d.mts +11 -0
  38. package/dist/generators/clone-repos.d.mts.map +1 -0
  39. package/dist/generators/clone-repos.mjs +139 -0
  40. package/dist/generators/clone-repos.mjs.map +1 -0
  41. package/dist/generators/clone-repos.test.cjs +140 -0
  42. package/dist/generators/clone-repos.test.cjs.map +1 -0
  43. package/dist/generators/clone-repos.test.d.cts +1 -0
  44. package/dist/generators/clone-repos.test.d.mts +1 -0
  45. package/dist/generators/clone-repos.test.mjs +141 -0
  46. package/dist/generators/clone-repos.test.mjs.map +1 -0
  47. package/dist/generators/env.test.cjs +1 -1
  48. package/dist/generators/env.test.mjs +1 -1
  49. package/dist/generators/health-check.test.cjs +1 -1
  50. package/dist/generators/health-check.test.mjs +1 -1
  51. package/dist/generators/postgres-init.cjs +20 -0
  52. package/dist/generators/postgres-init.cjs.map +1 -1
  53. package/dist/generators/postgres-init.d.cts.map +1 -1
  54. package/dist/generators/postgres-init.d.mts.map +1 -1
  55. package/dist/generators/postgres-init.mjs +20 -0
  56. package/dist/generators/postgres-init.mjs.map +1 -1
  57. package/dist/generators/scripts.cjs +332 -3
  58. package/dist/generators/scripts.cjs.map +1 -1
  59. package/dist/generators/scripts.d.cts +3 -1
  60. package/dist/generators/scripts.d.cts.map +1 -1
  61. package/dist/generators/scripts.d.mts +3 -1
  62. package/dist/generators/scripts.d.mts.map +1 -1
  63. package/dist/generators/scripts.mjs +332 -3
  64. package/dist/generators/scripts.mjs.map +1 -1
  65. package/dist/generators/scripts.test.cjs +39 -5
  66. package/dist/generators/scripts.test.cjs.map +1 -1
  67. package/dist/generators/scripts.test.mjs +39 -5
  68. package/dist/generators/scripts.test.mjs.map +1 -1
  69. package/dist/generators/stack-manifest.cjs +1 -0
  70. package/dist/generators/stack-manifest.cjs.map +1 -1
  71. package/dist/generators/stack-manifest.d.cts +3 -2
  72. package/dist/generators/stack-manifest.d.cts.map +1 -1
  73. package/dist/generators/stack-manifest.d.mts +3 -2
  74. package/dist/generators/stack-manifest.d.mts.map +1 -1
  75. package/dist/generators/stack-manifest.mjs +1 -0
  76. package/dist/generators/stack-manifest.mjs.map +1 -1
  77. package/dist/generators/traefik.test.cjs +1 -1
  78. package/dist/generators/traefik.test.mjs +1 -1
  79. package/dist/index.cjs +8 -1
  80. package/dist/index.d.cts +5 -3
  81. package/dist/index.d.mts +5 -3
  82. package/dist/index.mjs +5 -3
  83. package/dist/migrations.test.cjs +1 -1
  84. package/dist/migrations.test.mjs +1 -1
  85. package/dist/presets/registry.cjs.map +1 -1
  86. package/dist/presets/registry.d.cts.map +1 -1
  87. package/dist/presets/registry.d.mts.map +1 -1
  88. package/dist/presets/registry.mjs.map +1 -1
  89. package/dist/presets/registry.test.cjs +1 -1
  90. package/dist/presets/registry.test.mjs +1 -1
  91. package/dist/resolver.cjs +8 -0
  92. package/dist/resolver.cjs.map +1 -1
  93. package/dist/resolver.mjs +9 -1
  94. package/dist/resolver.mjs.map +1 -1
  95. package/dist/resolver.test.cjs +47 -12
  96. package/dist/resolver.test.cjs.map +1 -1
  97. package/dist/resolver.test.mjs +47 -12
  98. package/dist/resolver.test.mjs.map +1 -1
  99. package/dist/{schema-B4c64P8N.d.cts → schema-eX44HhRp.d.mts} +62 -8
  100. package/dist/schema-eX44HhRp.d.mts.map +1 -0
  101. package/dist/{schema-CXNhYci1.d.mts → schema-tn5RK8CM.d.cts} +62 -8
  102. package/dist/schema-tn5RK8CM.d.cts.map +1 -0
  103. package/dist/schema.cjs +22 -4
  104. package/dist/schema.cjs.map +1 -1
  105. package/dist/schema.d.cts +2 -2
  106. package/dist/schema.d.mts +2 -2
  107. package/dist/schema.mjs +21 -5
  108. package/dist/schema.mjs.map +1 -1
  109. package/dist/schema.test.cjs +1 -1
  110. package/dist/schema.test.mjs +1 -1
  111. package/dist/services/definitions/apptension-saas.cjs +87 -0
  112. package/dist/services/definitions/apptension-saas.cjs.map +1 -0
  113. package/dist/services/definitions/apptension-saas.d.cts +7 -0
  114. package/dist/services/definitions/apptension-saas.d.cts.map +1 -0
  115. package/dist/services/definitions/apptension-saas.d.mts +7 -0
  116. package/dist/services/definitions/apptension-saas.d.mts.map +1 -0
  117. package/dist/services/definitions/apptension-saas.mjs +86 -0
  118. package/dist/services/definitions/apptension-saas.mjs.map +1 -0
  119. package/dist/services/definitions/boxyhq-saas.cjs +88 -0
  120. package/dist/services/definitions/boxyhq-saas.cjs.map +1 -0
  121. package/dist/services/definitions/boxyhq-saas.d.cts +7 -0
  122. package/dist/services/definitions/boxyhq-saas.d.cts.map +1 -0
  123. package/dist/services/definitions/boxyhq-saas.d.mts +7 -0
  124. package/dist/services/definitions/boxyhq-saas.d.mts.map +1 -0
  125. package/dist/services/definitions/boxyhq-saas.mjs +87 -0
  126. package/dist/services/definitions/boxyhq-saas.mjs.map +1 -0
  127. package/dist/services/definitions/cmsaas-starter.cjs +86 -0
  128. package/dist/services/definitions/cmsaas-starter.cjs.map +1 -0
  129. package/dist/services/definitions/cmsaas-starter.d.cts +7 -0
  130. package/dist/services/definitions/cmsaas-starter.d.cts.map +1 -0
  131. package/dist/services/definitions/cmsaas-starter.d.mts +7 -0
  132. package/dist/services/definitions/cmsaas-starter.d.mts.map +1 -0
  133. package/dist/services/definitions/cmsaas-starter.mjs +85 -0
  134. package/dist/services/definitions/cmsaas-starter.mjs.map +1 -0
  135. package/dist/services/definitions/index.cjs +51 -36
  136. package/dist/services/definitions/index.cjs.map +1 -1
  137. package/dist/services/definitions/index.d.cts +30 -25
  138. package/dist/services/definitions/index.d.cts.map +1 -1
  139. package/dist/services/definitions/index.d.mts +30 -25
  140. package/dist/services/definitions/index.d.mts.map +1 -1
  141. package/dist/services/definitions/index.mjs +47 -37
  142. package/dist/services/definitions/index.mjs.map +1 -1
  143. package/dist/services/definitions/ixartz-saas.cjs +88 -0
  144. package/dist/services/definitions/ixartz-saas.cjs.map +1 -0
  145. package/dist/services/definitions/ixartz-saas.d.cts +7 -0
  146. package/dist/services/definitions/ixartz-saas.d.cts.map +1 -0
  147. package/dist/services/definitions/ixartz-saas.d.mts +7 -0
  148. package/dist/services/definitions/ixartz-saas.d.mts.map +1 -0
  149. package/dist/services/definitions/ixartz-saas.mjs +87 -0
  150. package/dist/services/definitions/ixartz-saas.mjs.map +1 -0
  151. package/dist/services/definitions/mission-control.cjs +16 -2
  152. package/dist/services/definitions/mission-control.cjs.map +1 -1
  153. package/dist/services/definitions/mission-control.mjs +16 -2
  154. package/dist/services/definitions/mission-control.mjs.map +1 -1
  155. package/dist/services/definitions/open-saas.cjs +81 -0
  156. package/dist/services/definitions/open-saas.cjs.map +1 -0
  157. package/dist/services/definitions/open-saas.d.cts +7 -0
  158. package/dist/services/definitions/open-saas.d.cts.map +1 -0
  159. package/dist/services/definitions/open-saas.d.mts +7 -0
  160. package/dist/services/definitions/open-saas.d.mts.map +1 -0
  161. package/dist/services/definitions/open-saas.mjs +80 -0
  162. package/dist/services/definitions/open-saas.mjs.map +1 -0
  163. package/dist/services/registry.cjs +3 -0
  164. package/dist/services/registry.cjs.map +1 -1
  165. package/dist/services/registry.d.cts.map +1 -1
  166. package/dist/services/registry.d.mts.map +1 -1
  167. package/dist/services/registry.mjs +3 -0
  168. package/dist/services/registry.mjs.map +1 -1
  169. package/dist/services/registry.test.cjs +8 -1
  170. package/dist/services/registry.test.cjs.map +1 -1
  171. package/dist/services/registry.test.mjs +8 -1
  172. package/dist/services/registry.test.mjs.map +1 -1
  173. package/dist/{skill-manifest-BVUXU0__.mjs → skill-manifest-6XhrhWsG.mjs} +49 -1
  174. package/dist/{skill-manifest--IgY9REK.cjs.map → skill-manifest-6XhrhWsG.mjs.map} +1 -1
  175. package/dist/{skill-manifest--IgY9REK.cjs → skill-manifest-B8znSsym.cjs} +49 -1
  176. package/dist/{skill-manifest-BVUXU0__.mjs.map → skill-manifest-B8znSsym.cjs.map} +1 -1
  177. package/dist/skills/registry.cjs +3 -3
  178. package/dist/skills/registry.cjs.map +1 -1
  179. package/dist/skills/registry.mjs +3 -3
  180. package/dist/skills/registry.mjs.map +1 -1
  181. package/dist/skills/skill-manifest.cjs +1 -1
  182. package/dist/skills/skill-manifest.mjs +1 -1
  183. package/dist/track-analytics.cjs +50 -0
  184. package/dist/track-analytics.cjs.map +1 -0
  185. package/dist/track-analytics.d.cts +34 -0
  186. package/dist/track-analytics.d.cts.map +1 -0
  187. package/dist/track-analytics.d.mts +34 -0
  188. package/dist/track-analytics.d.mts.map +1 -0
  189. package/dist/track-analytics.mjs +48 -0
  190. package/dist/track-analytics.mjs.map +1 -0
  191. package/dist/track-analytics.test.cjs +91 -0
  192. package/dist/track-analytics.test.cjs.map +1 -0
  193. package/dist/track-analytics.test.d.cts +1 -0
  194. package/dist/track-analytics.test.d.mts +1 -0
  195. package/dist/track-analytics.test.mjs +92 -0
  196. package/dist/track-analytics.test.mjs.map +1 -0
  197. package/dist/types.cjs +7 -0
  198. package/dist/types.cjs.map +1 -1
  199. package/dist/types.d.cts +4 -2
  200. package/dist/types.d.cts.map +1 -1
  201. package/dist/types.d.mts +4 -2
  202. package/dist/types.d.mts.map +1 -1
  203. package/dist/types.mjs +7 -0
  204. package/dist/types.mjs.map +1 -1
  205. package/dist/validator.test.cjs +1 -1
  206. package/dist/validator.test.mjs +1 -1
  207. package/dist/version-manager.cjs +1 -1
  208. package/dist/version-manager.cjs.map +1 -1
  209. package/dist/version-manager.mjs +1 -1
  210. package/dist/version-manager.mjs.map +1 -1
  211. package/dist/version-manager.test.cjs +7 -5
  212. package/dist/version-manager.test.cjs.map +1 -1
  213. package/dist/version-manager.test.mjs +7 -5
  214. package/dist/version-manager.test.mjs.map +1 -1
  215. package/dist/{vi.2VT5v0um-DvC3SVNc.mjs → vi.2VT5v0um-C_jmO7m2.mjs} +5 -5
  216. package/dist/{vi.2VT5v0um-DvC3SVNc.mjs.map → vi.2VT5v0um-C_jmO7m2.mjs.map} +1 -1
  217. package/dist/{vi.2VT5v0um-CRqXre87.cjs → vi.2VT5v0um-iVBt6Fyq.cjs} +5 -5
  218. package/dist/{vi.2VT5v0um-CRqXre87.cjs.map → vi.2VT5v0um-iVBt6Fyq.cjs.map} +1 -1
  219. package/package.json +1 -1
  220. package/src/__snapshots__/composer.snapshot.test.ts.snap +155 -0
  221. package/src/bare-metal-partition.test.ts +4 -3
  222. package/src/composer.test.ts +4 -2
  223. package/src/composer.ts +20 -1
  224. package/src/generate.test.ts +2 -1
  225. package/src/generate.ts +10 -1
  226. package/src/generators/clone-repos.test.ts +154 -0
  227. package/src/generators/clone-repos.ts +159 -0
  228. package/src/generators/postgres-init.ts +17 -0
  229. package/src/generators/scripts.test.ts +52 -4
  230. package/src/generators/scripts.ts +351 -3
  231. package/src/generators/stack-manifest.ts +4 -2
  232. package/src/index.ts +8 -0
  233. package/src/presets/registry.ts +241 -329
  234. package/src/resolver.test.ts +53 -15
  235. package/src/resolver.ts +13 -1
  236. package/src/schema.ts +33 -4
  237. package/src/services/definitions/apptension-saas.ts +84 -0
  238. package/src/services/definitions/boxyhq-saas.ts +84 -0
  239. package/src/services/definitions/cmsaas-starter.ts +84 -0
  240. package/src/services/definitions/index.ts +90 -70
  241. package/src/services/definitions/ixartz-saas.ts +84 -0
  242. package/src/services/definitions/mission-control.ts +19 -2
  243. package/src/services/definitions/open-saas.ts +79 -0
  244. package/src/services/registry.test.ts +8 -0
  245. package/src/services/registry.ts +7 -0
  246. package/src/skills/manifest.json +64 -0
  247. package/src/skills/registry.ts +3 -3
  248. package/src/track-analytics.test.ts +82 -0
  249. package/src/track-analytics.ts +76 -0
  250. package/src/types.ts +11 -0
  251. package/src/version-manager.test.ts +10 -5
  252. package/src/version-manager.ts +1 -1
  253. package/dist/schema-B4c64P8N.d.cts.map +0 -1
  254. package/dist/schema-CXNhYci1.d.mts.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,30 @@ 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
+
156
181
  // ─── Service Definition ─────────────────────────────────────────────────────
157
182
 
158
183
  export const ServiceDefinitionSchema = z.object({
@@ -166,9 +191,13 @@ export const ServiceDefinitionSchema = z.object({
166
191
  category: ServiceCategorySchema,
167
192
  icon: z.string(),
168
193
 
169
- // Docker
170
- image: z.string().min(1),
171
- imageTag: z.string().min(1),
194
+ // Docker (required for image-based services, omitted for git-based)
195
+ image: z.string().min(1).optional(),
196
+ imageTag: z.string().min(1).optional(),
197
+
198
+ // Git source (for services built from cloned repositories)
199
+ gitSource: GitSourceSchema.optional(),
200
+ buildContext: BuildContextSchema.optional(),
172
201
  ports: z.array(PortMappingSchema).default([]),
173
202
  volumes: z.array(VolumeMappingSchema).default([]),
174
203
  environment: z.array(EnvVariableSchema).default([]),
@@ -278,7 +307,7 @@ export const GenerationInputSchema = z.object({
278
307
 
279
308
  export const ResolvedServiceSchema = z.object({
280
309
  definition: ServiceDefinitionSchema,
281
- addedBy: z.enum(["user", "dependency", "skill-pack", "proxy", "monitoring"]).default("user"),
310
+ addedBy: z.enum(["user", "dependency", "skill-pack", "proxy", "monitoring", "mandatory"]).default("user"),
282
311
  });
283
312
 
284
313
  export const AddedDependencySchema = 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
+ };
@@ -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
+ };