@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.
- package/dist/bare-metal-partition.test.cjs +3 -4
- package/dist/bare-metal-partition.test.cjs.map +1 -1
- package/dist/bare-metal-partition.test.mjs +3 -4
- package/dist/bare-metal-partition.test.mjs.map +1 -1
- package/dist/composer.cjs +13 -1
- package/dist/composer.cjs.map +1 -1
- package/dist/composer.d.cts.map +1 -1
- package/dist/composer.d.mts.map +1 -1
- package/dist/composer.mjs +13 -1
- package/dist/composer.mjs.map +1 -1
- package/dist/composer.snapshot.test.cjs +1 -1
- package/dist/composer.snapshot.test.mjs +1 -1
- package/dist/composer.test.cjs +3 -2
- package/dist/composer.test.cjs.map +1 -1
- package/dist/composer.test.mjs +3 -2
- package/dist/composer.test.mjs.map +1 -1
- package/dist/deployers/strip-host-ports.test.cjs +1 -1
- package/dist/deployers/strip-host-ports.test.mjs +1 -1
- package/dist/generate.cjs +6 -2
- package/dist/generate.cjs.map +1 -1
- package/dist/generate.d.cts.map +1 -1
- package/dist/generate.d.mts.map +1 -1
- package/dist/generate.mjs +6 -2
- package/dist/generate.mjs.map +1 -1
- package/dist/generate.test.cjs +2 -2
- package/dist/generate.test.cjs.map +1 -1
- package/dist/generate.test.mjs +2 -2
- package/dist/generate.test.mjs.map +1 -1
- package/dist/generators/bare-metal-install.test.cjs +1 -1
- package/dist/generators/bare-metal-install.test.mjs +1 -1
- package/dist/generators/caddy.test.cjs +1 -1
- package/dist/generators/caddy.test.mjs +1 -1
- package/dist/generators/clone-repos.cjs +140 -0
- package/dist/generators/clone-repos.cjs.map +1 -0
- package/dist/generators/clone-repos.d.cts +11 -0
- package/dist/generators/clone-repos.d.cts.map +1 -0
- package/dist/generators/clone-repos.d.mts +11 -0
- package/dist/generators/clone-repos.d.mts.map +1 -0
- package/dist/generators/clone-repos.mjs +139 -0
- package/dist/generators/clone-repos.mjs.map +1 -0
- package/dist/generators/clone-repos.test.cjs +140 -0
- package/dist/generators/clone-repos.test.cjs.map +1 -0
- package/dist/generators/clone-repos.test.d.cts +1 -0
- package/dist/generators/clone-repos.test.d.mts +1 -0
- package/dist/generators/clone-repos.test.mjs +141 -0
- package/dist/generators/clone-repos.test.mjs.map +1 -0
- package/dist/generators/env.test.cjs +1 -1
- package/dist/generators/env.test.mjs +1 -1
- package/dist/generators/health-check.test.cjs +1 -1
- package/dist/generators/health-check.test.mjs +1 -1
- package/dist/generators/postgres-init.cjs +20 -0
- package/dist/generators/postgres-init.cjs.map +1 -1
- package/dist/generators/postgres-init.d.cts.map +1 -1
- package/dist/generators/postgres-init.d.mts.map +1 -1
- package/dist/generators/postgres-init.mjs +20 -0
- package/dist/generators/postgres-init.mjs.map +1 -1
- package/dist/generators/scripts.cjs +332 -3
- package/dist/generators/scripts.cjs.map +1 -1
- package/dist/generators/scripts.d.cts +3 -1
- package/dist/generators/scripts.d.cts.map +1 -1
- package/dist/generators/scripts.d.mts +3 -1
- package/dist/generators/scripts.d.mts.map +1 -1
- package/dist/generators/scripts.mjs +332 -3
- package/dist/generators/scripts.mjs.map +1 -1
- package/dist/generators/scripts.test.cjs +39 -5
- package/dist/generators/scripts.test.cjs.map +1 -1
- package/dist/generators/scripts.test.mjs +39 -5
- package/dist/generators/scripts.test.mjs.map +1 -1
- package/dist/generators/stack-manifest.cjs +1 -0
- package/dist/generators/stack-manifest.cjs.map +1 -1
- package/dist/generators/stack-manifest.d.cts +3 -2
- package/dist/generators/stack-manifest.d.cts.map +1 -1
- package/dist/generators/stack-manifest.d.mts +3 -2
- package/dist/generators/stack-manifest.d.mts.map +1 -1
- package/dist/generators/stack-manifest.mjs +1 -0
- package/dist/generators/stack-manifest.mjs.map +1 -1
- package/dist/generators/traefik.test.cjs +1 -1
- package/dist/generators/traefik.test.mjs +1 -1
- package/dist/index.cjs +8 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.mts +5 -3
- package/dist/index.mjs +5 -3
- package/dist/migrations.test.cjs +1 -1
- package/dist/migrations.test.mjs +1 -1
- package/dist/presets/registry.cjs.map +1 -1
- package/dist/presets/registry.d.cts.map +1 -1
- package/dist/presets/registry.d.mts.map +1 -1
- package/dist/presets/registry.mjs.map +1 -1
- package/dist/presets/registry.test.cjs +1 -1
- package/dist/presets/registry.test.mjs +1 -1
- package/dist/resolver.cjs +8 -0
- package/dist/resolver.cjs.map +1 -1
- package/dist/resolver.mjs +9 -1
- package/dist/resolver.mjs.map +1 -1
- package/dist/resolver.test.cjs +47 -12
- package/dist/resolver.test.cjs.map +1 -1
- package/dist/resolver.test.mjs +47 -12
- package/dist/resolver.test.mjs.map +1 -1
- package/dist/{schema-B4c64P8N.d.cts → schema-eX44HhRp.d.mts} +62 -8
- package/dist/schema-eX44HhRp.d.mts.map +1 -0
- package/dist/{schema-CXNhYci1.d.mts → schema-tn5RK8CM.d.cts} +62 -8
- package/dist/schema-tn5RK8CM.d.cts.map +1 -0
- package/dist/schema.cjs +22 -4
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +2 -2
- package/dist/schema.d.mts +2 -2
- package/dist/schema.mjs +21 -5
- package/dist/schema.mjs.map +1 -1
- package/dist/schema.test.cjs +1 -1
- package/dist/schema.test.mjs +1 -1
- package/dist/services/definitions/apptension-saas.cjs +87 -0
- package/dist/services/definitions/apptension-saas.cjs.map +1 -0
- package/dist/services/definitions/apptension-saas.d.cts +7 -0
- package/dist/services/definitions/apptension-saas.d.cts.map +1 -0
- package/dist/services/definitions/apptension-saas.d.mts +7 -0
- package/dist/services/definitions/apptension-saas.d.mts.map +1 -0
- package/dist/services/definitions/apptension-saas.mjs +86 -0
- package/dist/services/definitions/apptension-saas.mjs.map +1 -0
- package/dist/services/definitions/boxyhq-saas.cjs +88 -0
- package/dist/services/definitions/boxyhq-saas.cjs.map +1 -0
- package/dist/services/definitions/boxyhq-saas.d.cts +7 -0
- package/dist/services/definitions/boxyhq-saas.d.cts.map +1 -0
- package/dist/services/definitions/boxyhq-saas.d.mts +7 -0
- package/dist/services/definitions/boxyhq-saas.d.mts.map +1 -0
- package/dist/services/definitions/boxyhq-saas.mjs +87 -0
- package/dist/services/definitions/boxyhq-saas.mjs.map +1 -0
- package/dist/services/definitions/cmsaas-starter.cjs +86 -0
- package/dist/services/definitions/cmsaas-starter.cjs.map +1 -0
- package/dist/services/definitions/cmsaas-starter.d.cts +7 -0
- package/dist/services/definitions/cmsaas-starter.d.cts.map +1 -0
- package/dist/services/definitions/cmsaas-starter.d.mts +7 -0
- package/dist/services/definitions/cmsaas-starter.d.mts.map +1 -0
- package/dist/services/definitions/cmsaas-starter.mjs +85 -0
- package/dist/services/definitions/cmsaas-starter.mjs.map +1 -0
- package/dist/services/definitions/index.cjs +51 -36
- package/dist/services/definitions/index.cjs.map +1 -1
- package/dist/services/definitions/index.d.cts +30 -25
- package/dist/services/definitions/index.d.cts.map +1 -1
- package/dist/services/definitions/index.d.mts +30 -25
- package/dist/services/definitions/index.d.mts.map +1 -1
- package/dist/services/definitions/index.mjs +47 -37
- package/dist/services/definitions/index.mjs.map +1 -1
- package/dist/services/definitions/ixartz-saas.cjs +88 -0
- package/dist/services/definitions/ixartz-saas.cjs.map +1 -0
- package/dist/services/definitions/ixartz-saas.d.cts +7 -0
- package/dist/services/definitions/ixartz-saas.d.cts.map +1 -0
- package/dist/services/definitions/ixartz-saas.d.mts +7 -0
- package/dist/services/definitions/ixartz-saas.d.mts.map +1 -0
- package/dist/services/definitions/ixartz-saas.mjs +87 -0
- package/dist/services/definitions/ixartz-saas.mjs.map +1 -0
- package/dist/services/definitions/mission-control.cjs +16 -2
- package/dist/services/definitions/mission-control.cjs.map +1 -1
- package/dist/services/definitions/mission-control.mjs +16 -2
- package/dist/services/definitions/mission-control.mjs.map +1 -1
- package/dist/services/definitions/open-saas.cjs +81 -0
- package/dist/services/definitions/open-saas.cjs.map +1 -0
- package/dist/services/definitions/open-saas.d.cts +7 -0
- package/dist/services/definitions/open-saas.d.cts.map +1 -0
- package/dist/services/definitions/open-saas.d.mts +7 -0
- package/dist/services/definitions/open-saas.d.mts.map +1 -0
- package/dist/services/definitions/open-saas.mjs +80 -0
- package/dist/services/definitions/open-saas.mjs.map +1 -0
- package/dist/services/registry.cjs +3 -0
- package/dist/services/registry.cjs.map +1 -1
- package/dist/services/registry.d.cts.map +1 -1
- package/dist/services/registry.d.mts.map +1 -1
- package/dist/services/registry.mjs +3 -0
- package/dist/services/registry.mjs.map +1 -1
- package/dist/services/registry.test.cjs +8 -1
- package/dist/services/registry.test.cjs.map +1 -1
- package/dist/services/registry.test.mjs +8 -1
- package/dist/services/registry.test.mjs.map +1 -1
- package/dist/{skill-manifest-BVUXU0__.mjs → skill-manifest-6XhrhWsG.mjs} +49 -1
- package/dist/{skill-manifest--IgY9REK.cjs.map → skill-manifest-6XhrhWsG.mjs.map} +1 -1
- package/dist/{skill-manifest--IgY9REK.cjs → skill-manifest-B8znSsym.cjs} +49 -1
- package/dist/{skill-manifest-BVUXU0__.mjs.map → skill-manifest-B8znSsym.cjs.map} +1 -1
- package/dist/skills/registry.cjs +3 -3
- package/dist/skills/registry.cjs.map +1 -1
- package/dist/skills/registry.mjs +3 -3
- package/dist/skills/registry.mjs.map +1 -1
- package/dist/skills/skill-manifest.cjs +1 -1
- package/dist/skills/skill-manifest.mjs +1 -1
- package/dist/track-analytics.cjs +50 -0
- package/dist/track-analytics.cjs.map +1 -0
- package/dist/track-analytics.d.cts +34 -0
- package/dist/track-analytics.d.cts.map +1 -0
- package/dist/track-analytics.d.mts +34 -0
- package/dist/track-analytics.d.mts.map +1 -0
- package/dist/track-analytics.mjs +48 -0
- package/dist/track-analytics.mjs.map +1 -0
- package/dist/track-analytics.test.cjs +91 -0
- package/dist/track-analytics.test.cjs.map +1 -0
- package/dist/track-analytics.test.d.cts +1 -0
- package/dist/track-analytics.test.d.mts +1 -0
- package/dist/track-analytics.test.mjs +92 -0
- package/dist/track-analytics.test.mjs.map +1 -0
- package/dist/types.cjs +7 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +4 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +4 -2
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +7 -0
- package/dist/types.mjs.map +1 -1
- package/dist/validator.test.cjs +1 -1
- package/dist/validator.test.mjs +1 -1
- package/dist/version-manager.cjs +1 -1
- package/dist/version-manager.cjs.map +1 -1
- package/dist/version-manager.mjs +1 -1
- package/dist/version-manager.mjs.map +1 -1
- package/dist/version-manager.test.cjs +7 -5
- package/dist/version-manager.test.cjs.map +1 -1
- package/dist/version-manager.test.mjs +7 -5
- package/dist/version-manager.test.mjs.map +1 -1
- package/dist/{vi.2VT5v0um-DvC3SVNc.mjs → vi.2VT5v0um-C_jmO7m2.mjs} +5 -5
- package/dist/{vi.2VT5v0um-DvC3SVNc.mjs.map → vi.2VT5v0um-C_jmO7m2.mjs.map} +1 -1
- package/dist/{vi.2VT5v0um-CRqXre87.cjs → vi.2VT5v0um-iVBt6Fyq.cjs} +5 -5
- package/dist/{vi.2VT5v0um-CRqXre87.cjs.map → vi.2VT5v0um-iVBt6Fyq.cjs.map} +1 -1
- package/package.json +1 -1
- package/src/__snapshots__/composer.snapshot.test.ts.snap +155 -0
- package/src/bare-metal-partition.test.ts +4 -3
- package/src/composer.test.ts +4 -2
- package/src/composer.ts +20 -1
- package/src/generate.test.ts +2 -1
- package/src/generate.ts +10 -1
- package/src/generators/clone-repos.test.ts +154 -0
- package/src/generators/clone-repos.ts +159 -0
- package/src/generators/postgres-init.ts +17 -0
- package/src/generators/scripts.test.ts +52 -4
- package/src/generators/scripts.ts +351 -3
- package/src/generators/stack-manifest.ts +4 -2
- package/src/index.ts +8 -0
- package/src/presets/registry.ts +241 -329
- package/src/resolver.test.ts +53 -15
- package/src/resolver.ts +13 -1
- package/src/schema.ts +33 -4
- package/src/services/definitions/apptension-saas.ts +84 -0
- package/src/services/definitions/boxyhq-saas.ts +84 -0
- package/src/services/definitions/cmsaas-starter.ts +84 -0
- package/src/services/definitions/index.ts +90 -70
- package/src/services/definitions/ixartz-saas.ts +84 -0
- package/src/services/definitions/mission-control.ts +19 -2
- package/src/services/definitions/open-saas.ts +79 -0
- package/src/services/registry.test.ts +8 -0
- package/src/services/registry.ts +7 -0
- package/src/skills/manifest.json +64 -0
- package/src/skills/registry.ts +3 -3
- package/src/track-analytics.test.ts +82 -0
- package/src/track-analytics.ts +76 -0
- package/src/types.ts +11 -0
- package/src/version-manager.test.ts +10 -5
- package/src/version-manager.ts +1 -1
- package/dist/schema-B4c64P8N.d.cts.map +0 -1
- package/dist/schema-CXNhYci1.d.mts.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-openclaw/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.24",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Core logic for better-openclaw: schemas, service registry, resolver, composer, validators and generators",
|
|
6
6
|
"author": "bidew.io <bachir@bidew.io>",
|
|
@@ -63,10 +63,39 @@ exports[`compose snapshot tests > creator preset (ffmpeg + remotion + minio + re
|
|
|
63
63
|
retries: 5
|
|
64
64
|
start_period: 20s
|
|
65
65
|
depends_on:
|
|
66
|
+
convex:
|
|
67
|
+
condition: service_healthy
|
|
66
68
|
minio:
|
|
67
69
|
condition: service_healthy
|
|
68
70
|
redis:
|
|
69
71
|
condition: service_healthy
|
|
72
|
+
convex:
|
|
73
|
+
image: ghcr.io/get-convex/convex-backend:latest
|
|
74
|
+
environment:
|
|
75
|
+
CONVEX_CLOUD_ORIGIN: http://127.0.0.1:3210
|
|
76
|
+
CONVEX_SITE_ORIGIN: http://127.0.0.1:3211
|
|
77
|
+
INSTANCE_NAME: openclaw-convex
|
|
78
|
+
INSTANCE_SECRET: \${INSTANCE_SECRET}
|
|
79
|
+
DATABASE_URL: \${DATABASE_URL}
|
|
80
|
+
RUST_LOG: info
|
|
81
|
+
DISABLE_BEACON: ""
|
|
82
|
+
CONVEX_SELF_HOSTED_ADMIN_KEY: \${CONVEX_SELF_HOSTED_ADMIN_KEY}
|
|
83
|
+
ports:
|
|
84
|
+
- \${CONVEX_EXTERNAL_PORT_0:-3210}:3210
|
|
85
|
+
- \${CONVEX_EXTERNAL_PORT_1:-3211}:3211
|
|
86
|
+
volumes:
|
|
87
|
+
- convex-data:/convex/data
|
|
88
|
+
healthcheck:
|
|
89
|
+
test:
|
|
90
|
+
- CMD-SHELL
|
|
91
|
+
- curl -f http://localhost:3210/version
|
|
92
|
+
interval: 5s
|
|
93
|
+
timeout: 5s
|
|
94
|
+
retries: 5
|
|
95
|
+
start_period: 10s
|
|
96
|
+
restart: unless-stopped
|
|
97
|
+
networks:
|
|
98
|
+
- openclaw-network
|
|
70
99
|
minio:
|
|
71
100
|
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
|
72
101
|
environment:
|
|
@@ -146,9 +175,11 @@ exports[`compose snapshot tests > creator preset (ffmpeg + remotion + minio + re
|
|
|
146
175
|
depends_on:
|
|
147
176
|
- openclaw-gateway
|
|
148
177
|
volumes:
|
|
178
|
+
convex-data:
|
|
149
179
|
ffmpeg-shared:
|
|
150
180
|
minio-data:
|
|
151
181
|
redis-data:
|
|
182
|
+
tailscale-state:
|
|
152
183
|
networks:
|
|
153
184
|
openclaw-network:
|
|
154
185
|
driver: bridge
|
|
@@ -246,12 +277,41 @@ exports[`compose snapshot tests > devops preset (n8n + postgresql + redis + moni
|
|
|
246
277
|
retries: 5
|
|
247
278
|
start_period: 20s
|
|
248
279
|
depends_on:
|
|
280
|
+
convex:
|
|
281
|
+
condition: service_healthy
|
|
249
282
|
postgresql:
|
|
250
283
|
condition: service_healthy
|
|
251
284
|
redis:
|
|
252
285
|
condition: service_healthy
|
|
253
286
|
n8n:
|
|
254
287
|
condition: service_healthy
|
|
288
|
+
convex:
|
|
289
|
+
image: ghcr.io/get-convex/convex-backend:latest
|
|
290
|
+
environment:
|
|
291
|
+
CONVEX_CLOUD_ORIGIN: http://127.0.0.1:3210
|
|
292
|
+
CONVEX_SITE_ORIGIN: http://127.0.0.1:3211
|
|
293
|
+
INSTANCE_NAME: openclaw-convex
|
|
294
|
+
INSTANCE_SECRET: \${INSTANCE_SECRET}
|
|
295
|
+
DATABASE_URL: \${DATABASE_URL}
|
|
296
|
+
RUST_LOG: info
|
|
297
|
+
DISABLE_BEACON: ""
|
|
298
|
+
CONVEX_SELF_HOSTED_ADMIN_KEY: \${CONVEX_SELF_HOSTED_ADMIN_KEY}
|
|
299
|
+
ports:
|
|
300
|
+
- \${CONVEX_EXTERNAL_PORT_0:-3210}:3210
|
|
301
|
+
- \${CONVEX_EXTERNAL_PORT_1:-3211}:3211
|
|
302
|
+
volumes:
|
|
303
|
+
- convex-data:/convex/data
|
|
304
|
+
healthcheck:
|
|
305
|
+
test:
|
|
306
|
+
- CMD-SHELL
|
|
307
|
+
- curl -f http://localhost:3210/version
|
|
308
|
+
interval: 5s
|
|
309
|
+
timeout: 5s
|
|
310
|
+
retries: 5
|
|
311
|
+
start_period: 10s
|
|
312
|
+
restart: unless-stopped
|
|
313
|
+
networks:
|
|
314
|
+
- openclaw-network
|
|
255
315
|
postgresql:
|
|
256
316
|
image: postgres:17-alpine
|
|
257
317
|
environment:
|
|
@@ -403,11 +463,13 @@ exports[`compose snapshot tests > devops preset (n8n + postgresql + redis + moni
|
|
|
403
463
|
depends_on:
|
|
404
464
|
- openclaw-gateway
|
|
405
465
|
volumes:
|
|
466
|
+
convex-data:
|
|
406
467
|
grafana-data:
|
|
407
468
|
n8n-data:
|
|
408
469
|
postgres-data:
|
|
409
470
|
prometheus-data:
|
|
410
471
|
redis-data:
|
|
472
|
+
tailscale-state:
|
|
411
473
|
uptime-kuma-data:
|
|
412
474
|
networks:
|
|
413
475
|
openclaw-network:
|
|
@@ -580,6 +642,8 @@ exports[`compose snapshot tests > full preset (many services) 1`] = `
|
|
|
580
642
|
condition: service_healthy
|
|
581
643
|
caddy:
|
|
582
644
|
condition: service_healthy
|
|
645
|
+
convex:
|
|
646
|
+
condition: service_healthy
|
|
583
647
|
meilisearch:
|
|
584
648
|
condition: service_healthy
|
|
585
649
|
minio:
|
|
@@ -631,6 +695,33 @@ exports[`compose snapshot tests > full preset (many services) 1`] = `
|
|
|
631
695
|
restart: unless-stopped
|
|
632
696
|
networks:
|
|
633
697
|
- openclaw-network
|
|
698
|
+
convex:
|
|
699
|
+
image: ghcr.io/get-convex/convex-backend:latest
|
|
700
|
+
environment:
|
|
701
|
+
CONVEX_CLOUD_ORIGIN: http://127.0.0.1:3210
|
|
702
|
+
CONVEX_SITE_ORIGIN: http://127.0.0.1:3211
|
|
703
|
+
INSTANCE_NAME: openclaw-convex
|
|
704
|
+
INSTANCE_SECRET: \${INSTANCE_SECRET}
|
|
705
|
+
DATABASE_URL: \${DATABASE_URL}
|
|
706
|
+
RUST_LOG: info
|
|
707
|
+
DISABLE_BEACON: ""
|
|
708
|
+
CONVEX_SELF_HOSTED_ADMIN_KEY: \${CONVEX_SELF_HOSTED_ADMIN_KEY}
|
|
709
|
+
ports:
|
|
710
|
+
- \${CONVEX_EXTERNAL_PORT_0:-3210}:3210
|
|
711
|
+
- \${CONVEX_EXTERNAL_PORT_1:-3211}:3211
|
|
712
|
+
volumes:
|
|
713
|
+
- convex-data:/convex/data
|
|
714
|
+
healthcheck:
|
|
715
|
+
test:
|
|
716
|
+
- CMD-SHELL
|
|
717
|
+
- curl -f http://localhost:3210/version
|
|
718
|
+
interval: 5s
|
|
719
|
+
timeout: 5s
|
|
720
|
+
retries: 5
|
|
721
|
+
start_period: 10s
|
|
722
|
+
restart: unless-stopped
|
|
723
|
+
networks:
|
|
724
|
+
- openclaw-network
|
|
634
725
|
meilisearch:
|
|
635
726
|
image: getmeili/meilisearch:v1.35.1
|
|
636
727
|
environment:
|
|
@@ -861,6 +952,7 @@ exports[`compose snapshot tests > full preset (many services) 1`] = `
|
|
|
861
952
|
volumes:
|
|
862
953
|
caddy-config:
|
|
863
954
|
caddy-data:
|
|
955
|
+
convex-data:
|
|
864
956
|
ffmpeg-shared:
|
|
865
957
|
gotify-data:
|
|
866
958
|
grafana-data:
|
|
@@ -873,6 +965,7 @@ volumes:
|
|
|
873
965
|
qdrant-data:
|
|
874
966
|
redis-data:
|
|
875
967
|
searxng-data:
|
|
968
|
+
tailscale-state:
|
|
876
969
|
uptime-kuma-data:
|
|
877
970
|
networks:
|
|
878
971
|
openclaw-network:
|
|
@@ -935,8 +1028,37 @@ exports[`compose snapshot tests > minimal preset (redis only) 1`] = `
|
|
|
935
1028
|
retries: 5
|
|
936
1029
|
start_period: 20s
|
|
937
1030
|
depends_on:
|
|
1031
|
+
convex:
|
|
1032
|
+
condition: service_healthy
|
|
938
1033
|
redis:
|
|
939
1034
|
condition: service_healthy
|
|
1035
|
+
convex:
|
|
1036
|
+
image: ghcr.io/get-convex/convex-backend:latest
|
|
1037
|
+
environment:
|
|
1038
|
+
CONVEX_CLOUD_ORIGIN: http://127.0.0.1:3210
|
|
1039
|
+
CONVEX_SITE_ORIGIN: http://127.0.0.1:3211
|
|
1040
|
+
INSTANCE_NAME: openclaw-convex
|
|
1041
|
+
INSTANCE_SECRET: \${INSTANCE_SECRET}
|
|
1042
|
+
DATABASE_URL: \${DATABASE_URL}
|
|
1043
|
+
RUST_LOG: info
|
|
1044
|
+
DISABLE_BEACON: ""
|
|
1045
|
+
CONVEX_SELF_HOSTED_ADMIN_KEY: \${CONVEX_SELF_HOSTED_ADMIN_KEY}
|
|
1046
|
+
ports:
|
|
1047
|
+
- \${CONVEX_EXTERNAL_PORT_0:-3210}:3210
|
|
1048
|
+
- \${CONVEX_EXTERNAL_PORT_1:-3211}:3211
|
|
1049
|
+
volumes:
|
|
1050
|
+
- convex-data:/convex/data
|
|
1051
|
+
healthcheck:
|
|
1052
|
+
test:
|
|
1053
|
+
- CMD-SHELL
|
|
1054
|
+
- curl -f http://localhost:3210/version
|
|
1055
|
+
interval: 5s
|
|
1056
|
+
timeout: 5s
|
|
1057
|
+
retries: 5
|
|
1058
|
+
start_period: 10s
|
|
1059
|
+
restart: unless-stopped
|
|
1060
|
+
networks:
|
|
1061
|
+
- openclaw-network
|
|
940
1062
|
redis:
|
|
941
1063
|
image: redis:8-alpine
|
|
942
1064
|
environment:
|
|
@@ -994,7 +1116,9 @@ exports[`compose snapshot tests > minimal preset (redis only) 1`] = `
|
|
|
994
1116
|
depends_on:
|
|
995
1117
|
- openclaw-gateway
|
|
996
1118
|
volumes:
|
|
1119
|
+
convex-data:
|
|
997
1120
|
redis-data:
|
|
1121
|
+
tailscale-state:
|
|
998
1122
|
networks:
|
|
999
1123
|
openclaw-network:
|
|
1000
1124
|
driver: bridge
|
|
@@ -1065,6 +1189,8 @@ exports[`compose snapshot tests > researcher preset (qdrant + searxng + browserl
|
|
|
1065
1189
|
depends_on:
|
|
1066
1190
|
browserless:
|
|
1067
1191
|
condition: service_healthy
|
|
1192
|
+
convex:
|
|
1193
|
+
condition: service_healthy
|
|
1068
1194
|
qdrant:
|
|
1069
1195
|
condition: service_healthy
|
|
1070
1196
|
redis:
|
|
@@ -1089,6 +1215,33 @@ exports[`compose snapshot tests > researcher preset (qdrant + searxng + browserl
|
|
|
1089
1215
|
restart: unless-stopped
|
|
1090
1216
|
networks:
|
|
1091
1217
|
- openclaw-network
|
|
1218
|
+
convex:
|
|
1219
|
+
image: ghcr.io/get-convex/convex-backend:latest
|
|
1220
|
+
environment:
|
|
1221
|
+
CONVEX_CLOUD_ORIGIN: http://127.0.0.1:3210
|
|
1222
|
+
CONVEX_SITE_ORIGIN: http://127.0.0.1:3211
|
|
1223
|
+
INSTANCE_NAME: openclaw-convex
|
|
1224
|
+
INSTANCE_SECRET: \${INSTANCE_SECRET}
|
|
1225
|
+
DATABASE_URL: \${DATABASE_URL}
|
|
1226
|
+
RUST_LOG: info
|
|
1227
|
+
DISABLE_BEACON: ""
|
|
1228
|
+
CONVEX_SELF_HOSTED_ADMIN_KEY: \${CONVEX_SELF_HOSTED_ADMIN_KEY}
|
|
1229
|
+
ports:
|
|
1230
|
+
- \${CONVEX_EXTERNAL_PORT_0:-3210}:3210
|
|
1231
|
+
- \${CONVEX_EXTERNAL_PORT_1:-3211}:3211
|
|
1232
|
+
volumes:
|
|
1233
|
+
- convex-data:/convex/data
|
|
1234
|
+
healthcheck:
|
|
1235
|
+
test:
|
|
1236
|
+
- CMD-SHELL
|
|
1237
|
+
- curl -f http://localhost:3210/version
|
|
1238
|
+
interval: 5s
|
|
1239
|
+
timeout: 5s
|
|
1240
|
+
retries: 5
|
|
1241
|
+
start_period: 10s
|
|
1242
|
+
restart: unless-stopped
|
|
1243
|
+
networks:
|
|
1244
|
+
- openclaw-network
|
|
1092
1245
|
qdrant:
|
|
1093
1246
|
image: qdrant/qdrant:v1.12.1
|
|
1094
1247
|
environment:
|
|
@@ -1182,9 +1335,11 @@ exports[`compose snapshot tests > researcher preset (qdrant + searxng + browserl
|
|
|
1182
1335
|
depends_on:
|
|
1183
1336
|
- openclaw-gateway
|
|
1184
1337
|
volumes:
|
|
1338
|
+
convex-data:
|
|
1185
1339
|
qdrant-data:
|
|
1186
1340
|
redis-data:
|
|
1187
1341
|
searxng-data:
|
|
1342
|
+
tailscale-state:
|
|
1188
1343
|
networks:
|
|
1189
1344
|
openclaw-network:
|
|
1190
1345
|
driver: bridge
|
|
@@ -25,9 +25,10 @@ describe("bare-metal partition", () => {
|
|
|
25
25
|
});
|
|
26
26
|
const result = partitionBareMetal(resolved, "linux/amd64");
|
|
27
27
|
expect(result.nativeIds.has("redis")).toBe(true);
|
|
28
|
-
|
|
29
|
-
expect(
|
|
30
|
-
|
|
28
|
+
const redisNative = result.nativeServices.find((s) => s.definition.id === "redis");
|
|
29
|
+
expect(redisNative).toBeDefined();
|
|
30
|
+
// Mandatory services (convex, mission-control, tailscale) are Docker-only
|
|
31
|
+
expect(result.dockerOnlyServices.length).toBeGreaterThanOrEqual(0);
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
it("partitions n8n as Docker-only (no native recipe)", () => {
|
package/src/composer.test.ts
CHANGED
|
@@ -38,8 +38,10 @@ describe("compose", () => {
|
|
|
38
38
|
// Should have CLI companion service
|
|
39
39
|
expect(parsed.services).toHaveProperty("openclaw-cli");
|
|
40
40
|
|
|
41
|
-
// Gateway should have
|
|
42
|
-
|
|
41
|
+
// Gateway should have depends_on for mandatory services (convex, mission-control, tailscale)
|
|
42
|
+
// Even with no user-selected services, mandatory services are always present
|
|
43
|
+
expect(parsed.services).toHaveProperty("convex");
|
|
44
|
+
expect(parsed.services).toHaveProperty("mission-control");
|
|
43
45
|
|
|
44
46
|
// Gateway should have restart policy
|
|
45
47
|
expect(parsed.services["openclaw-gateway"].restart).toBe("unless-stopped");
|
package/src/composer.ts
CHANGED
|
@@ -50,6 +50,7 @@ const CATEGORY_PROFILE_MAP: Partial<Record<ServiceCategory, { file: string; prof
|
|
|
50
50
|
"social-media": { file: "docker-compose.social.yml", profile: "social" },
|
|
51
51
|
knowledge: { file: "docker-compose.knowledge.yml", profile: "knowledge" },
|
|
52
52
|
communication: { file: "docker-compose.communication.yml", profile: "communication" },
|
|
53
|
+
"saas-boilerplate": { file: "docker-compose.saas.yml", profile: "saas" },
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
const YAML_OPTIONS = { lineWidth: 120, nullStr: "" };
|
|
@@ -230,7 +231,25 @@ function buildCompanionService(
|
|
|
230
231
|
const svc: Record<string, unknown> = {};
|
|
231
232
|
const volumeNames: string[] = [];
|
|
232
233
|
|
|
233
|
-
|
|
234
|
+
// Git-based services use build: context; image-based services use image:
|
|
235
|
+
if (def.gitSource && def.buildContext) {
|
|
236
|
+
const subdir = def.gitSource.subdirectory || ".";
|
|
237
|
+
const ctxPath = def.buildContext.context || ".";
|
|
238
|
+
const contextFull = subdir === "." ? `./repos/${def.id}/${ctxPath}` : `./repos/${def.id}/${subdir}/${ctxPath}`;
|
|
239
|
+
const buildBlock: Record<string, unknown> = { context: contextFull };
|
|
240
|
+
if (def.buildContext.dockerfile) {
|
|
241
|
+
buildBlock.dockerfile = def.buildContext.dockerfile;
|
|
242
|
+
}
|
|
243
|
+
if (def.buildContext.args && Object.keys(def.buildContext.args).length > 0) {
|
|
244
|
+
buildBlock.args = def.buildContext.args;
|
|
245
|
+
}
|
|
246
|
+
if (def.buildContext.target) {
|
|
247
|
+
buildBlock.target = def.buildContext.target;
|
|
248
|
+
}
|
|
249
|
+
svc.build = buildBlock;
|
|
250
|
+
} else {
|
|
251
|
+
svc.image = `${def.image}:${def.imageTag}`;
|
|
252
|
+
}
|
|
234
253
|
|
|
235
254
|
if (def.environment.length > 0) {
|
|
236
255
|
const env: Record<string, string> = {};
|
package/src/generate.test.ts
CHANGED
|
@@ -187,7 +187,8 @@ describe("generate (end-to-end)", () => {
|
|
|
187
187
|
for (const id of lasuiteMeetServices) {
|
|
188
188
|
expect(allServiceIds.has(id), `missing service ${id}`).toBe(true);
|
|
189
189
|
}
|
|
190
|
-
|
|
190
|
+
// Service count includes user services + mandatory platform services (convex, mission-control, tailscale)
|
|
191
|
+
expect(result.metadata.serviceCount).toBeGreaterThanOrEqual(lasuiteMeetServices.length);
|
|
191
192
|
});
|
|
192
193
|
|
|
193
194
|
it("generates bare-metal installer for Windows (install.ps1)", () => {
|
package/src/generate.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { composeMultiFile } from "./composer.js";
|
|
|
7
7
|
import { StackConfigError, ValidationError } from "./errors.js";
|
|
8
8
|
import { generateBareMetalInstall } from "./generators/bare-metal-install.js";
|
|
9
9
|
import { generateCaddyfile } from "./generators/caddy.js";
|
|
10
|
+
import { generateCloneScripts } from "./generators/clone-repos.js";
|
|
10
11
|
import { generateCloudInit } from "./generators/cloud-init.js";
|
|
11
12
|
import { generateEnvFiles } from "./generators/env.js";
|
|
12
13
|
import { generateGsdScripts } from "./generators/get-shit-done.js";
|
|
@@ -149,6 +150,7 @@ export function generate(rawInput: GenerationInput): GenerationResult {
|
|
|
149
150
|
".env.*.local",
|
|
150
151
|
"*.log",
|
|
151
152
|
"docker-compose.override.yml",
|
|
153
|
+
"repos/",
|
|
152
154
|
].join("\n");
|
|
153
155
|
|
|
154
156
|
// Stack manifest (consumed by Mission Control)
|
|
@@ -181,11 +183,18 @@ export function generate(rawInput: GenerationInput): GenerationResult {
|
|
|
181
183
|
});
|
|
182
184
|
|
|
183
185
|
// Scripts
|
|
184
|
-
const
|
|
186
|
+
const hasGitServices = resolvedForCompose.services.some((s) => s.definition.gitSource);
|
|
187
|
+
const scripts = generateScripts({ hasGitServices });
|
|
185
188
|
for (const [path, content] of Object.entries(scripts)) {
|
|
186
189
|
files[path] = content;
|
|
187
190
|
}
|
|
188
191
|
|
|
192
|
+
// Clone scripts for git-based services (SaaS boilerplates)
|
|
193
|
+
const cloneScripts = generateCloneScripts(resolvedForCompose);
|
|
194
|
+
for (const [path, content] of Object.entries(cloneScripts)) {
|
|
195
|
+
files[path] = content;
|
|
196
|
+
}
|
|
197
|
+
|
|
189
198
|
// Health check scripts (dynamic, stack-specific)
|
|
190
199
|
const healthCheckFiles = generateHealthCheck(resolved, {
|
|
191
200
|
projectName: input.projectName,
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { generateCloneScripts } from "./clone-repos.js";
|
|
3
|
+
import type { ResolverOutput } from "../types.js";
|
|
4
|
+
|
|
5
|
+
/** Minimal resolved output with no services */
|
|
6
|
+
function emptyResolved(): ResolverOutput {
|
|
7
|
+
return {
|
|
8
|
+
services: [],
|
|
9
|
+
addedDependencies: [],
|
|
10
|
+
removedConflicts: [],
|
|
11
|
+
warnings: [],
|
|
12
|
+
errors: [],
|
|
13
|
+
isValid: true,
|
|
14
|
+
estimatedMemoryMB: 0,
|
|
15
|
+
aiProviders: [],
|
|
16
|
+
gsdRuntimes: [],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Create a resolved output with image-based services only */
|
|
21
|
+
function imageOnlyResolved(): ResolverOutput {
|
|
22
|
+
return {
|
|
23
|
+
...emptyResolved(),
|
|
24
|
+
services: [
|
|
25
|
+
{
|
|
26
|
+
definition: {
|
|
27
|
+
id: "redis",
|
|
28
|
+
name: "Redis",
|
|
29
|
+
description: "In-memory cache",
|
|
30
|
+
category: "database",
|
|
31
|
+
icon: "🔴",
|
|
32
|
+
image: "redis",
|
|
33
|
+
imageTag: "7-alpine",
|
|
34
|
+
ports: [],
|
|
35
|
+
volumes: [],
|
|
36
|
+
environment: [],
|
|
37
|
+
dependsOn: [],
|
|
38
|
+
restartPolicy: "unless-stopped",
|
|
39
|
+
networks: ["openclaw-network"],
|
|
40
|
+
skills: [],
|
|
41
|
+
openclawEnvVars: [],
|
|
42
|
+
docsUrl: "https://redis.io",
|
|
43
|
+
tags: [],
|
|
44
|
+
maturity: "stable",
|
|
45
|
+
requires: [],
|
|
46
|
+
recommends: [],
|
|
47
|
+
conflictsWith: [],
|
|
48
|
+
gpuRequired: false,
|
|
49
|
+
},
|
|
50
|
+
addedBy: "user",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Create a resolved output with a git-based service */
|
|
57
|
+
function gitServiceResolved(): ResolverOutput {
|
|
58
|
+
return {
|
|
59
|
+
...emptyResolved(),
|
|
60
|
+
services: [
|
|
61
|
+
{
|
|
62
|
+
definition: {
|
|
63
|
+
id: "open-saas",
|
|
64
|
+
name: "Open SaaS",
|
|
65
|
+
description: "SaaS boilerplate",
|
|
66
|
+
category: "saas-boilerplate",
|
|
67
|
+
icon: "🚀",
|
|
68
|
+
gitSource: {
|
|
69
|
+
repoUrl: "https://github.com/wasp-lang/open-saas.git",
|
|
70
|
+
branch: "main",
|
|
71
|
+
subdirectory: "template",
|
|
72
|
+
postCloneCommands: ["cp .env.example .env"],
|
|
73
|
+
},
|
|
74
|
+
buildContext: {
|
|
75
|
+
dockerfile: "Dockerfile",
|
|
76
|
+
context: ".",
|
|
77
|
+
},
|
|
78
|
+
ports: [{ host: 3100, container: 3000, description: "Web app", exposed: true }],
|
|
79
|
+
volumes: [],
|
|
80
|
+
environment: [],
|
|
81
|
+
dependsOn: [],
|
|
82
|
+
restartPolicy: "unless-stopped",
|
|
83
|
+
networks: ["openclaw-network"],
|
|
84
|
+
skills: [],
|
|
85
|
+
openclawEnvVars: [],
|
|
86
|
+
docsUrl: "https://opensaas.sh/docs",
|
|
87
|
+
tags: [],
|
|
88
|
+
maturity: "beta",
|
|
89
|
+
requires: [],
|
|
90
|
+
recommends: [],
|
|
91
|
+
conflictsWith: [],
|
|
92
|
+
gpuRequired: false,
|
|
93
|
+
},
|
|
94
|
+
addedBy: "user",
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
describe("generateCloneScripts", () => {
|
|
101
|
+
it("returns empty object when no git-based services exist", () => {
|
|
102
|
+
const result = generateCloneScripts(emptyResolved());
|
|
103
|
+
expect(result).toEqual({});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("returns empty object when only image-based services exist", () => {
|
|
107
|
+
const result = generateCloneScripts(imageOnlyResolved());
|
|
108
|
+
expect(result).toEqual({});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("generates bash and PowerShell scripts for git-based services", () => {
|
|
112
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
113
|
+
expect(Object.keys(result)).toHaveLength(2);
|
|
114
|
+
expect(result).toHaveProperty("scripts/clone-repos.sh");
|
|
115
|
+
expect(result).toHaveProperty("scripts/clone-repos.ps1");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("bash script contains clone command with repo URL", () => {
|
|
119
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
120
|
+
const bash = result["scripts/clone-repos.sh"];
|
|
121
|
+
expect(bash).toContain("https://github.com/wasp-lang/open-saas.git");
|
|
122
|
+
expect(bash).toContain('"open-saas"');
|
|
123
|
+
expect(bash).toContain('"main"');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("bash script includes postCloneCommands", () => {
|
|
127
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
128
|
+
const bash = result["scripts/clone-repos.sh"];
|
|
129
|
+
expect(bash).toContain("cp .env.example .env");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("bash script includes git check and idempotency logic", () => {
|
|
133
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
134
|
+
const bash = result["scripts/clone-repos.sh"];
|
|
135
|
+
expect(bash).toContain("command -v git");
|
|
136
|
+
expect(bash).toContain("clone_or_update");
|
|
137
|
+
expect(bash).toContain("pull --ff-only");
|
|
138
|
+
expect(bash).toContain("git clone --depth 1");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("PowerShell script contains clone command with repo URL", () => {
|
|
142
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
143
|
+
const ps = result["scripts/clone-repos.ps1"];
|
|
144
|
+
expect(ps).toContain("https://github.com/wasp-lang/open-saas.git");
|
|
145
|
+
expect(ps).toContain('"open-saas"');
|
|
146
|
+
expect(ps).toContain("Clone-OrUpdate");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("bash script is executable (starts with shebang)", () => {
|
|
150
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
151
|
+
const bash = result["scripts/clone-repos.sh"];
|
|
152
|
+
expect(bash).toMatch(/^#!/);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { ResolverOutput } from "../types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generates clone scripts for git-based services (SaaS boilerplates).
|
|
5
|
+
* Returns empty object if no git-based services exist in the resolved stack.
|
|
6
|
+
*/
|
|
7
|
+
export function generateCloneScripts(resolved: ResolverOutput): Record<string, string> {
|
|
8
|
+
const gitServices = resolved.services.filter(
|
|
9
|
+
(s) => s.definition.gitSource && s.definition.buildContext,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
if (gitServices.length === 0) return {};
|
|
13
|
+
|
|
14
|
+
const files: Record<string, string> = {};
|
|
15
|
+
|
|
16
|
+
// ── scripts/clone-repos.sh ─────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
const bashEntries = gitServices
|
|
19
|
+
.map((s) => {
|
|
20
|
+
const gs = s.definition.gitSource!;
|
|
21
|
+
const branchArg = gs.branch ? `"${gs.branch}"` : '""';
|
|
22
|
+
let block = `clone_or_update "${s.definition.id}" "${gs.repoUrl}" ${branchArg}`;
|
|
23
|
+
if (gs.postCloneCommands && gs.postCloneCommands.length > 0) {
|
|
24
|
+
const cmds = gs.postCloneCommands
|
|
25
|
+
.map((cmd) => ` (cd "$REPOS_DIR/${s.definition.id}${gs.subdirectory ? `/${gs.subdirectory}` : ""}" && ${cmd})`)
|
|
26
|
+
.join("\n");
|
|
27
|
+
block += `\n${cmds}`;
|
|
28
|
+
}
|
|
29
|
+
return block;
|
|
30
|
+
})
|
|
31
|
+
.join("\n\n");
|
|
32
|
+
|
|
33
|
+
files["scripts/clone-repos.sh"] = `#!/usr/bin/env bash
|
|
34
|
+
set -euo pipefail
|
|
35
|
+
|
|
36
|
+
# ─── Clone/Update Git-Based Service Repositories ────────────────────────────
|
|
37
|
+
# Idempotent: clones if missing, pulls if already present.
|
|
38
|
+
|
|
39
|
+
SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
40
|
+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
41
|
+
REPOS_DIR="$PROJECT_DIR/repos"
|
|
42
|
+
|
|
43
|
+
# ── Colour helpers ──────────────────────────────────────────────────────────
|
|
44
|
+
if [ -t 1 ]; then
|
|
45
|
+
GREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; CYAN='\\033[0;36m'; RED='\\033[0;31m'; NC='\\033[0m'
|
|
46
|
+
else
|
|
47
|
+
GREEN=''; YELLOW=''; CYAN=''; RED=''; NC=''
|
|
48
|
+
fi
|
|
49
|
+
info() { echo -e "\${CYAN}i $*\${NC}"; }
|
|
50
|
+
ok() { echo -e "\${GREEN}✓ $*\${NC}"; }
|
|
51
|
+
warn() { echo -e "\${YELLOW}⚠ $*\${NC}"; }
|
|
52
|
+
err() { echo -e "\${RED}✗ $*\${NC}" >&2; }
|
|
53
|
+
|
|
54
|
+
# ── Check git ───────────────────────────────────────────────────────────────
|
|
55
|
+
if ! command -v git &> /dev/null; then
|
|
56
|
+
err "git is not installed. Please install git first."
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
mkdir -p "$REPOS_DIR"
|
|
61
|
+
|
|
62
|
+
clone_or_update() {
|
|
63
|
+
local name="$1" url="$2" branch="\${3:-}"
|
|
64
|
+
local dir="$REPOS_DIR/$name"
|
|
65
|
+
|
|
66
|
+
if [ -d "$dir/.git" ]; then
|
|
67
|
+
info "Updating $name..."
|
|
68
|
+
git -C "$dir" pull --ff-only 2>/dev/null || warn "Could not fast-forward $name (you may have local changes)"
|
|
69
|
+
else
|
|
70
|
+
info "Cloning $name..."
|
|
71
|
+
if [ -n "$branch" ]; then
|
|
72
|
+
git clone --depth 1 --branch "$branch" "$url" "$dir"
|
|
73
|
+
else
|
|
74
|
+
git clone --depth 1 "$url" "$dir"
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
echo ""
|
|
80
|
+
info "Cloning/updating SaaS boilerplate repositories..."
|
|
81
|
+
echo ""
|
|
82
|
+
|
|
83
|
+
${bashEntries}
|
|
84
|
+
|
|
85
|
+
echo ""
|
|
86
|
+
ok "All repositories ready."
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
// ── scripts/clone-repos.ps1 ────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
const psEntries = gitServices
|
|
92
|
+
.map((s) => {
|
|
93
|
+
const gs = s.definition.gitSource!;
|
|
94
|
+
const branchArg = gs.branch ? ` -Branch "${gs.branch}"` : "";
|
|
95
|
+
let block = `Clone-OrUpdate -Name "${s.definition.id}" -Url "${gs.repoUrl}"${branchArg}`;
|
|
96
|
+
if (gs.postCloneCommands && gs.postCloneCommands.length > 0) {
|
|
97
|
+
const subdir = gs.subdirectory ? `/${gs.subdirectory}` : "";
|
|
98
|
+
const cmds = gs.postCloneCommands
|
|
99
|
+
.map((cmd) => `Push-Location "$ReposDir/${s.definition.id}${subdir}"; ${cmd}; Pop-Location`)
|
|
100
|
+
.join("\n");
|
|
101
|
+
block += `\n${cmds}`;
|
|
102
|
+
}
|
|
103
|
+
return block;
|
|
104
|
+
})
|
|
105
|
+
.join("\n\n");
|
|
106
|
+
|
|
107
|
+
files["scripts/clone-repos.ps1"] = `#Requires -Version 5.1
|
|
108
|
+
<#
|
|
109
|
+
.SYNOPSIS
|
|
110
|
+
Clone/update git-based service repositories.
|
|
111
|
+
Idempotent: clones if missing, pulls if already present.
|
|
112
|
+
#>
|
|
113
|
+
$ErrorActionPreference = "Stop"
|
|
114
|
+
|
|
115
|
+
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
116
|
+
$ProjectDir = Split-Path -Parent $ScriptDir
|
|
117
|
+
$ReposDir = Join-Path $ProjectDir "repos"
|
|
118
|
+
|
|
119
|
+
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
|
120
|
+
Write-Error "git is not installed. Please install git first."
|
|
121
|
+
exit 1
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (-not (Test-Path $ReposDir)) { New-Item -ItemType Directory -Path $ReposDir -Force | Out-Null }
|
|
125
|
+
|
|
126
|
+
function Clone-OrUpdate {
|
|
127
|
+
param(
|
|
128
|
+
[string]$Name,
|
|
129
|
+
[string]$Url,
|
|
130
|
+
[string]$Branch = ""
|
|
131
|
+
)
|
|
132
|
+
$dir = Join-Path $ReposDir $Name
|
|
133
|
+
|
|
134
|
+
if (Test-Path (Join-Path $dir ".git")) {
|
|
135
|
+
Write-Host " Updating $Name..." -ForegroundColor Cyan
|
|
136
|
+
git -C $dir pull --ff-only 2>$null
|
|
137
|
+
if ($LASTEXITCODE -ne 0) { Write-Warning "Could not fast-forward $Name" }
|
|
138
|
+
} else {
|
|
139
|
+
Write-Host " Cloning $Name..." -ForegroundColor Cyan
|
|
140
|
+
if ($Branch) {
|
|
141
|
+
git clone --depth 1 --branch $Branch $Url $dir
|
|
142
|
+
} else {
|
|
143
|
+
git clone --depth 1 $Url $dir
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
Write-Host ""
|
|
149
|
+
Write-Host "Cloning/updating SaaS boilerplate repositories..." -ForegroundColor Cyan
|
|
150
|
+
Write-Host ""
|
|
151
|
+
|
|
152
|
+
${psEntries}
|
|
153
|
+
|
|
154
|
+
Write-Host ""
|
|
155
|
+
Write-Host "All repositories ready." -ForegroundColor Green
|
|
156
|
+
`;
|
|
157
|
+
|
|
158
|
+
return files;
|
|
159
|
+
}
|