@better-openclaw/core 1.0.24 → 1.0.26

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 (226) hide show
  1. package/dist/addon-stack.cjs +725 -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 +723 -0
  8. package/dist/addon-stack.mjs.map +1 -0
  9. package/dist/addon-stack.test.cjs +461 -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 +461 -0
  14. package/dist/addon-stack.test.mjs.map +1 -0
  15. package/dist/bare-metal-partition.test.cjs +20 -20
  16. package/dist/bare-metal-partition.test.cjs.map +1 -1
  17. package/dist/bare-metal-partition.test.mjs +2 -2
  18. package/dist/compose-validation.test.cjs +1 -1
  19. package/dist/composer.cjs +5 -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 +1 -1
  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 +54 -54
  31. package/dist/composer.test.cjs.map +1 -1
  32. package/dist/composer.test.mjs +2 -2
  33. package/dist/deployers/strip-host-ports.cjs +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 +3 -3
  38. package/dist/generate.mjs +3 -3
  39. package/dist/generate.test.cjs +56 -56
  40. package/dist/generate.test.cjs.map +1 -1
  41. package/dist/generate.test.mjs +1 -1
  42. package/dist/generators/bare-metal-install.test.cjs +18 -18
  43. package/dist/generators/bare-metal-install.test.cjs.map +1 -1
  44. package/dist/generators/bare-metal-install.test.mjs +1 -1
  45. package/dist/generators/caddy.test.cjs +13 -13
  46. package/dist/generators/caddy.test.cjs.map +1 -1
  47. package/dist/generators/caddy.test.mjs +1 -1
  48. package/dist/generators/clone-repos.test.cjs +27 -27
  49. package/dist/generators/clone-repos.test.cjs.map +1 -1
  50. package/dist/generators/clone-repos.test.mjs +1 -1
  51. package/dist/generators/env.cjs +1 -1
  52. package/dist/generators/env.test.cjs +17 -17
  53. package/dist/generators/env.test.cjs.map +1 -1
  54. package/dist/generators/env.test.mjs +1 -1
  55. package/dist/generators/health-check.test.cjs +39 -39
  56. package/dist/generators/health-check.test.cjs.map +1 -1
  57. package/dist/generators/health-check.test.mjs +1 -1
  58. package/dist/generators/postgres-init.cjs +5 -0
  59. package/dist/generators/postgres-init.cjs.map +1 -1
  60. package/dist/generators/postgres-init.d.cts.map +1 -1
  61. package/dist/generators/postgres-init.d.mts.map +1 -1
  62. package/dist/generators/postgres-init.mjs +5 -0
  63. package/dist/generators/postgres-init.mjs.map +1 -1
  64. package/dist/generators/scripts.test.cjs +39 -39
  65. package/dist/generators/scripts.test.cjs.map +1 -1
  66. package/dist/generators/scripts.test.mjs +1 -1
  67. package/dist/generators/skills.cjs +1 -1
  68. package/dist/generators/skills.d.cts.map +1 -1
  69. package/dist/generators/skills.d.mts.map +1 -1
  70. package/dist/generators/skills.mjs +141 -0
  71. package/dist/generators/skills.mjs.map +1 -1
  72. package/dist/generators/traefik.test.cjs +32 -32
  73. package/dist/generators/traefik.test.cjs.map +1 -1
  74. package/dist/generators/traefik.test.mjs +1 -1
  75. package/dist/index.cjs +21 -5
  76. package/dist/index.d.cts +5 -4
  77. package/dist/index.d.mts +5 -4
  78. package/dist/index.mjs +7 -6
  79. package/dist/migrations.test.cjs +16 -16
  80. package/dist/migrations.test.cjs.map +1 -1
  81. package/dist/migrations.test.mjs +1 -1
  82. package/dist/presets/presets.test.cjs +1 -1
  83. package/dist/presets/registry.test.cjs +14 -14
  84. package/dist/presets/registry.test.cjs.map +1 -1
  85. package/dist/presets/registry.test.mjs +1 -1
  86. package/dist/resolver.test.cjs +95 -95
  87. package/dist/resolver.test.cjs.map +1 -1
  88. package/dist/resolver.test.mjs +1 -1
  89. package/dist/{schema-eX44HhRp.d.mts → schema-BQnZrcw8.d.cts} +300 -2
  90. package/dist/schema-BQnZrcw8.d.cts.map +1 -0
  91. package/dist/{schema-tn5RK8CM.d.cts → schema-SBpL0bdI.d.mts} +300 -2
  92. package/dist/schema-SBpL0bdI.d.mts.map +1 -0
  93. package/dist/schema.cjs +148 -2
  94. package/dist/schema.cjs.map +1 -1
  95. package/dist/schema.d.cts +2 -2
  96. package/dist/schema.d.mts +2 -2
  97. package/dist/schema.mjs +139 -2
  98. package/dist/schema.mjs.map +1 -1
  99. package/dist/schema.test.cjs +86 -86
  100. package/dist/schema.test.cjs.map +1 -1
  101. package/dist/schema.test.mjs +1 -1
  102. package/dist/services/definitions/browserless.cjs +4 -1
  103. package/dist/services/definitions/browserless.cjs.map +1 -1
  104. package/dist/services/definitions/browserless.mjs +4 -1
  105. package/dist/services/definitions/browserless.mjs.map +1 -1
  106. package/dist/services/definitions/burnlink.cjs +142 -0
  107. package/dist/services/definitions/burnlink.cjs.map +1 -0
  108. package/dist/services/definitions/burnlink.d.cts +7 -0
  109. package/dist/services/definitions/burnlink.d.cts.map +1 -0
  110. package/dist/services/definitions/burnlink.d.mts +7 -0
  111. package/dist/services/definitions/burnlink.d.mts.map +1 -0
  112. package/dist/services/definitions/burnlink.mjs +141 -0
  113. package/dist/services/definitions/burnlink.mjs.map +1 -0
  114. package/dist/services/definitions/convex.cjs +43 -1
  115. package/dist/services/definitions/convex.cjs.map +1 -1
  116. package/dist/services/definitions/convex.mjs +43 -1
  117. package/dist/services/definitions/convex.mjs.map +1 -1
  118. package/dist/services/definitions/grafana.cjs +11 -1
  119. package/dist/services/definitions/grafana.cjs.map +1 -1
  120. package/dist/services/definitions/grafana.mjs +11 -1
  121. package/dist/services/definitions/grafana.mjs.map +1 -1
  122. package/dist/services/definitions/hindsight.cjs +130 -0
  123. package/dist/services/definitions/hindsight.cjs.map +1 -0
  124. package/dist/services/definitions/hindsight.d.cts +7 -0
  125. package/dist/services/definitions/hindsight.d.cts.map +1 -0
  126. package/dist/services/definitions/hindsight.d.mts +7 -0
  127. package/dist/services/definitions/hindsight.d.mts.map +1 -0
  128. package/dist/services/definitions/hindsight.mjs +129 -0
  129. package/dist/services/definitions/hindsight.mjs.map +1 -0
  130. package/dist/services/definitions/index.cjs +9 -0
  131. package/dist/services/definitions/index.cjs.map +1 -1
  132. package/dist/services/definitions/index.d.cts +4 -1
  133. package/dist/services/definitions/index.d.cts.map +1 -1
  134. package/dist/services/definitions/index.d.mts +4 -1
  135. package/dist/services/definitions/index.d.mts.map +1 -1
  136. package/dist/services/definitions/index.mjs +7 -1
  137. package/dist/services/definitions/index.mjs.map +1 -1
  138. package/dist/services/definitions/meilisearch.cjs +11 -1
  139. package/dist/services/definitions/meilisearch.cjs.map +1 -1
  140. package/dist/services/definitions/meilisearch.mjs +11 -1
  141. package/dist/services/definitions/meilisearch.mjs.map +1 -1
  142. package/dist/services/definitions/minio.cjs +3 -1
  143. package/dist/services/definitions/minio.cjs.map +1 -1
  144. package/dist/services/definitions/minio.mjs +3 -1
  145. package/dist/services/definitions/minio.mjs.map +1 -1
  146. package/dist/services/definitions/n8n.cjs +11 -1
  147. package/dist/services/definitions/n8n.cjs.map +1 -1
  148. package/dist/services/definitions/n8n.mjs +11 -1
  149. package/dist/services/definitions/n8n.mjs.map +1 -1
  150. package/dist/services/definitions/ollama.cjs +3 -1
  151. package/dist/services/definitions/ollama.cjs.map +1 -1
  152. package/dist/services/definitions/ollama.mjs +3 -1
  153. package/dist/services/definitions/ollama.mjs.map +1 -1
  154. package/dist/services/definitions/opensandbox.cjs +149 -0
  155. package/dist/services/definitions/opensandbox.cjs.map +1 -0
  156. package/dist/services/definitions/opensandbox.d.cts +7 -0
  157. package/dist/services/definitions/opensandbox.d.cts.map +1 -0
  158. package/dist/services/definitions/opensandbox.d.mts +7 -0
  159. package/dist/services/definitions/opensandbox.d.mts.map +1 -0
  160. package/dist/services/definitions/opensandbox.mjs +148 -0
  161. package/dist/services/definitions/opensandbox.mjs.map +1 -0
  162. package/dist/services/definitions/qdrant.cjs +3 -1
  163. package/dist/services/definitions/qdrant.cjs.map +1 -1
  164. package/dist/services/definitions/qdrant.mjs +3 -1
  165. package/dist/services/definitions/qdrant.mjs.map +1 -1
  166. package/dist/services/definitions/searxng.cjs +8 -1
  167. package/dist/services/definitions/searxng.cjs.map +1 -1
  168. package/dist/services/definitions/searxng.mjs +8 -1
  169. package/dist/services/definitions/searxng.mjs.map +1 -1
  170. package/dist/services/definitions/uptime-kuma.cjs +8 -1
  171. package/dist/services/definitions/uptime-kuma.cjs.map +1 -1
  172. package/dist/services/definitions/uptime-kuma.mjs +8 -1
  173. package/dist/services/definitions/uptime-kuma.mjs.map +1 -1
  174. package/dist/services/registry.test.cjs +36 -36
  175. package/dist/services/registry.test.cjs.map +1 -1
  176. package/dist/services/registry.test.mjs +1 -1
  177. package/dist/{skills-BlzpHmpH.cjs → skills-BSF7iNa4.cjs} +142 -1
  178. package/dist/{skills-BlzpHmpH.cjs.map → skills-BSF7iNa4.cjs.map} +1 -1
  179. package/dist/{vi.2VT5v0um-C_jmO7m2.mjs → test.CTcmp4Su-ClCHJ3FA.mjs} +6793 -6403
  180. package/dist/test.CTcmp4Su-ClCHJ3FA.mjs.map +1 -0
  181. package/dist/{vi.2VT5v0um-iVBt6Fyq.cjs → test.CTcmp4Su-DlzTarwH.cjs} +6793 -6403
  182. package/dist/test.CTcmp4Su-DlzTarwH.cjs.map +1 -0
  183. package/dist/track-analytics.test.cjs +28 -28
  184. package/dist/track-analytics.test.cjs.map +1 -1
  185. package/dist/track-analytics.test.mjs +1 -1
  186. package/dist/types.cjs.map +1 -1
  187. package/dist/types.d.cts +10 -2
  188. package/dist/types.d.cts.map +1 -1
  189. package/dist/types.d.mts +10 -2
  190. package/dist/types.d.mts.map +1 -1
  191. package/dist/types.mjs.map +1 -1
  192. package/dist/validator.cjs +1 -1
  193. package/dist/validator.test.cjs +15 -15
  194. package/dist/validator.test.cjs.map +1 -1
  195. package/dist/validator.test.mjs +2 -2
  196. package/dist/version-manager.test.cjs +37 -37
  197. package/dist/version-manager.test.cjs.map +1 -1
  198. package/dist/version-manager.test.mjs +1 -1
  199. package/package.json +4 -4
  200. package/src/__snapshots__/composer.snapshot.test.ts.snap +5 -0
  201. package/src/addon-stack.test.ts +648 -0
  202. package/src/addon-stack.ts +1046 -0
  203. package/src/composer.ts +4 -4
  204. package/src/generators/postgres-init.ts +2 -0
  205. package/src/generators/skills.ts +142 -0
  206. package/src/index.ts +20 -2
  207. package/src/schema.ts +190 -0
  208. package/src/services/definitions/browserless.ts +3 -0
  209. package/src/services/definitions/burnlink.ts +142 -0
  210. package/src/services/definitions/convex.ts +31 -0
  211. package/src/services/definitions/grafana.ts +9 -0
  212. package/src/services/definitions/hindsight.ts +131 -0
  213. package/src/services/definitions/index.ts +10 -0
  214. package/src/services/definitions/meilisearch.ts +9 -0
  215. package/src/services/definitions/minio.ts +2 -0
  216. package/src/services/definitions/n8n.ts +9 -0
  217. package/src/services/definitions/ollama.ts +2 -0
  218. package/src/services/definitions/opensandbox.ts +156 -0
  219. package/src/services/definitions/qdrant.ts +2 -0
  220. package/src/services/definitions/searxng.ts +3 -0
  221. package/src/services/definitions/uptime-kuma.ts +3 -0
  222. package/src/types.ts +18 -0
  223. package/dist/schema-eX44HhRp.d.mts.map +0 -1
  224. package/dist/schema-tn5RK8CM.d.cts.map +0 -1
  225. package/dist/vi.2VT5v0um-C_jmO7m2.mjs.map +0 -1
  226. package/dist/vi.2VT5v0um-iVBt6Fyq.cjs.map +0 -1
package/src/composer.ts CHANGED
@@ -23,7 +23,7 @@ function getOpenclawImage(variant: OpenclawImageVariant, version: string): strin
23
23
  }
24
24
 
25
25
  /** Creates a YAML scalar that is always quoted — avoids YAML 1.1 bare `no` → false. */
26
- function quotedStr(value: string): Scalar {
26
+ export function quotedStr(value: string): Scalar {
27
27
  const s = new Scalar(value);
28
28
  s.type = Scalar.QUOTE_DOUBLE;
29
29
  return s;
@@ -53,7 +53,7 @@ const CATEGORY_PROFILE_MAP: Partial<Record<ServiceCategory, { file: string; prof
53
53
  "saas-boilerplate": { file: "docker-compose.saas.yml", profile: "saas" },
54
54
  };
55
55
 
56
- const YAML_OPTIONS = { lineWidth: 120, nullStr: "" };
56
+ export const YAML_OPTIONS = { lineWidth: 120, nullStr: "" };
57
57
 
58
58
  // ── Shared Gateway Builder ──────────────────────────────────────────────────
59
59
 
@@ -222,7 +222,7 @@ function buildGatewayServices(
222
222
 
223
223
  // ── Shared Companion Service Builder ────────────────────────────────────────
224
224
 
225
- function buildCompanionService(
225
+ export function buildCompanionService(
226
226
  def: ResolverOutput["services"][number]["definition"],
227
227
  resolved: ResolverOutput,
228
228
  options: ComposeOptions,
@@ -445,7 +445,7 @@ function buildCompanionService(
445
445
  *
446
446
  * Returns null when no setup is needed (no PostgreSQL or no DB requirements).
447
447
  */
448
- function buildPostgresSetup(resolved: ResolverOutput): Record<string, unknown> | null {
448
+ export function buildPostgresSetup(resolved: ResolverOutput): Record<string, unknown> | null {
449
449
  const hasPostgres = resolved.services.some((s) => s.definition.id === "postgresql");
450
450
  if (!hasPostgres) return null;
451
451
 
@@ -52,6 +52,8 @@ const DB_REQUIREMENTS: Record<string, Omit<DbRequirement, "serviceId" | "service
52
52
  openpanel: { dbName: "openpanel", dbUser: "openpanel", passwordEnvVar: "OPENPANEL_DB_PASSWORD" },
53
53
  usesend: { dbName: "usesend", dbUser: "usesend", passwordEnvVar: "USESEND_DB_PASSWORD" },
54
54
  nextcloud: { dbName: "nextcloud", dbUser: "nextcloud", passwordEnvVar: "NEXTCLOUD_DB_PASSWORD" },
55
+ // ── Agent Memory ────────────────────────────────────────────────────────
56
+ hindsight: { dbName: "hindsight", dbUser: "hindsight", passwordEnvVar: "HINDSIGHT_DB_PASSWORD" },
55
57
  // ── SaaS Boilerplates ────────────────────────────────────────────────────
56
58
  "open-saas": { dbName: "opensaas", dbUser: "opensaas", passwordEnvVar: "OPENSAAS_DB_PASSWORD" },
57
59
  "apptension-saas": {
@@ -535,6 +535,148 @@ curl -X POST http://{{STEEL_HOST}}:{{STEEL_PORT}}/v1/scrape \\
535
535
  - Proxy support and IP rotation
536
536
  - Auto CAPTCHA solving
537
537
  - Puppeteer/Playwright/Selenium compatible
538
+ `,
539
+
540
+ "code-sandbox": `---
541
+ name: code-sandbox
542
+ description: "Execute code safely in an isolated OpenSandbox container"
543
+ metadata:
544
+ openclaw:
545
+ emoji: "📦"
546
+ ---
547
+
548
+ # Code Sandbox
549
+
550
+ Execute code safely in an isolated OpenSandbox container.
551
+
552
+ ## Description
553
+
554
+ This skill provides secure, containerized code execution for AI agents. Code runs in ephemeral Docker containers with resource limits, network isolation, and automatic cleanup.
555
+
556
+ ## Connection Details
557
+
558
+ - **Host:** \`{{OPENSANDBOX_HOST}}\`
559
+ - **Port:** \`{{OPENSANDBOX_PORT}}\`
560
+ - **Auth:** API key (auto-configured)
561
+
562
+ ## Supported Languages
563
+
564
+ - Python 3.12
565
+ - JavaScript / TypeScript (Node.js 22)
566
+ - Java 21
567
+ - Go 1.24
568
+ - Bash
569
+
570
+ ## Available Actions
571
+
572
+ ### execute_code
573
+
574
+ Run a code snippet in a fresh sandbox.
575
+
576
+ **Parameters:**
577
+ - \`language\` (required): Programming language ("python", "javascript", "typescript", "java", "go", "bash")
578
+ - \`code\` (required): The code to execute
579
+ - \`timeout_seconds\` (optional): Max execution time (default: 60, max: 300)
580
+
581
+ **Returns:** stdout, stderr, exit_code, execution_time_ms
582
+
583
+ ### execute_shell
584
+
585
+ Run a shell command in an existing or new sandbox.
586
+
587
+ **Parameters:**
588
+ - \`command\` (required): Shell command to execute
589
+ - \`sandbox_id\` (optional): Reuse an existing sandbox (for multi-step workflows)
590
+ - \`background\` (optional): Run in background (default: false)
591
+
592
+ **Returns:** stdout, stderr, exit_code
593
+
594
+ ### upload_file
595
+
596
+ Upload a file to a sandbox for processing.
597
+
598
+ **Parameters:**
599
+ - \`sandbox_id\` (required): Target sandbox
600
+ - \`path\` (required): Destination path inside sandbox
601
+ - \`content\` (required): File content (text or base64 for binary)
602
+
603
+ ### download_file
604
+
605
+ Download a file from a sandbox.
606
+
607
+ **Parameters:**
608
+ - \`sandbox_id\` (required): Source sandbox
609
+ - \`path\` (required): File path inside sandbox
610
+
611
+ **Returns:** File content
612
+
613
+ ### list_sandboxes
614
+
615
+ List active sandboxes on this instance.
616
+
617
+ **Returns:** Array of { id, status, image, created_at, expires_at }
618
+
619
+ ### terminate_sandbox
620
+
621
+ Terminate a running sandbox immediately.
622
+
623
+ **Parameters:**
624
+ - \`sandbox_id\` (required): Sandbox to terminate
625
+
626
+ ### create_desktop
627
+
628
+ Create a GUI desktop sandbox with VNC access (for Homespace live preview).
629
+
630
+ **Parameters:**
631
+ - \`image\` (optional): Desktop image (default: "opensandbox/desktop:latest", also: "opensandbox/chrome:latest", "opensandbox/vscode:latest")
632
+ - \`resolution\` (optional): Screen resolution (default: "1280x800x24")
633
+
634
+ **Returns:** sandbox_id, vnc_endpoint (port 5900), novnc_url (port 6080 WebSocket), devtools_url (port 9222, chrome only)
635
+
636
+ ### get_preview_url
637
+
638
+ Get the browser-accessible noVNC URL for an existing desktop sandbox.
639
+
640
+ **Parameters:**
641
+ - \`sandbox_id\` (required): Desktop sandbox ID
642
+
643
+ **Returns:** novnc_url (embeddable in iframe), vnc_endpoint, status
644
+
645
+ ## Examples
646
+
647
+ ### Run Python code
648
+
649
+ \`\`\`bash
650
+ curl -X POST http://{{OPENSANDBOX_HOST}}:{{OPENSANDBOX_PORT}}/v1/sandboxes \\
651
+ -H "Authorization: Bearer $OPENSANDBOX_API_KEY" \\
652
+ -H "Content-Type: application/json" \\
653
+ -d '{"image": "opensandbox/code-interpreter:python"}'
654
+ \`\`\`
655
+
656
+ ### Execute code in a sandbox
657
+
658
+ \`\`\`bash
659
+ curl -X POST http://{{OPENSANDBOX_HOST}}:{{OPENSANDBOX_PORT}}/v1/sandboxes/{id}/code \\
660
+ -H "Authorization: Bearer $OPENSANDBOX_API_KEY" \\
661
+ -H "Content-Type: application/json" \\
662
+ -d '{"language": "python", "code": "print(42 * 42)"}'
663
+ \`\`\`
664
+
665
+ ## Configuration
666
+
667
+ - **Default timeout:** 60 seconds
668
+ - **Max concurrent sandboxes:** Determined by VPS RAM
669
+ - **Idle cleanup:** Sandboxes with no activity for 30 minutes are auto-terminated
670
+ - **Network:** Bridge mode (isolated from host services)
671
+ - **Security:** gVisor runtime, capability dropping, PID limits
672
+
673
+ ## Limitations
674
+
675
+ - No persistent storage between sandbox sessions (ephemeral by design)
676
+ - No GPU access (CPU-only execution)
677
+ - No outbound network access by default (egress blocked)
678
+ - Max 512 PIDs per sandbox (fork bomb protection)
679
+ - Memory capped per sandbox (default 512MB)
538
680
  `,
539
681
  };
540
682
 
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- // ─── Schemas ────────────────────────────────────────────────────────────────
1
+ // ─── Addon Stack (Clawexa) ──────────────────────────────────────────────────
2
+ export { generateAddonStack, updateAddonStack } from "./addon-stack.js";
2
3
 
3
4
  // ─── Core Engines ───────────────────────────────────────────────────────────
4
5
  export {
@@ -7,7 +8,7 @@ export {
7
8
  resolvedWithOnlyServices,
8
9
  } from "./bare-metal-partition.js";
9
10
  export type { ComposeResult } from "./composer.js";
10
- export { compose, composeMultiFile } from "./composer.js";
11
+ export { buildCompanionService, buildPostgresSetup, compose, composeMultiFile, quotedStr, YAML_OPTIONS } from "./composer.js";
11
12
  // ─── PaaS Deployers ─────────────────────────────────────────────────────────
12
13
  export type {
13
14
  DeployInput as PaasDeployInput,
@@ -71,6 +72,10 @@ export {
71
72
  } from "./presets/registry.js";
72
73
  export { resolve } from "./resolver.js";
73
74
  export {
75
+ AddonStackInputSchema,
76
+ AddonStackResultSchema,
77
+ AddonStackUpdateInputSchema,
78
+ AddonStackUpdateResultSchema,
74
79
  AddedDependencySchema,
75
80
  ApiErrorSchema,
76
81
  BuildContextSchema,
@@ -79,6 +84,8 @@ export {
79
84
  DeploymentTypeSchema,
80
85
  DeploySchema,
81
86
  DeployTargetSchema,
87
+ EnvQuirkFixSchema,
88
+ EnvQuirkSchema,
82
89
  EnvVariableSchema,
83
90
  ErrorSchema,
84
91
  GenerationInputSchema,
@@ -93,6 +100,7 @@ export {
93
100
  PlatformSchema,
94
101
  PortMappingSchema,
95
102
  PresetSchema,
103
+ ProxyRouteSchema,
96
104
  ProxyTypeSchema,
97
105
  ResolvedServiceSchema,
98
106
  ResolverOutputSchema,
@@ -102,6 +110,8 @@ export {
102
110
  ServiceDefinitionSchema,
103
111
  SkillBindingSchema,
104
112
  SkillPackSchema,
113
+ SkippedServiceReasonSchema,
114
+ SkippedServiceSchema,
105
115
  ValidateRequestSchema,
106
116
  ValidateResponseSchema,
107
117
  VolumeMappingSchema,
@@ -130,6 +140,10 @@ export {
130
140
  } from "./skills/skill-manifest.js";
131
141
  // ─── Types ──────────────────────────────────────────────────────────────────
132
142
  export type {
143
+ AddonStackInput,
144
+ AddonStackResult,
145
+ AddonStackUpdateInput,
146
+ AddonStackUpdateResult,
133
147
  AddedDependency,
134
148
  AiProvider,
135
149
  ApiError,
@@ -140,6 +154,8 @@ export type {
140
154
  DeploymentTarget,
141
155
  DeploymentType,
142
156
  DeployTarget,
157
+ EnvQuirk,
158
+ EnvQuirkFix,
143
159
  EnvVariable,
144
160
  GeneratedFiles,
145
161
  GenerationInput,
@@ -157,6 +173,7 @@ export type {
157
173
  Platform,
158
174
  PortMapping,
159
175
  Preset,
176
+ ProxyRoute,
160
177
  ProxyType,
161
178
  ResolvedService,
162
179
  ResolverError,
@@ -168,6 +185,7 @@ export type {
168
185
  ServiceDefinition,
169
186
  SkillBinding,
170
187
  SkillPack,
188
+ SkippedService,
171
189
  ValidateRequest,
172
190
  ValidateResponse,
173
191
  VolumeMapping,
package/src/schema.ts CHANGED
@@ -178,6 +178,24 @@ export const BuildContextSchema = z.object({
178
178
  target: z.string().optional(),
179
179
  });
180
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
+
181
199
  // ─── Service Definition ─────────────────────────────────────────────────────
182
200
 
183
201
  export const ServiceDefinitionSchema = z.object({
@@ -236,6 +254,18 @@ export const ServiceDefinitionSchema = z.object({
236
254
  // Bare-metal native (install/run on host when no Docker)
237
255
  nativeSupported: z.boolean().optional(),
238
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(),
239
269
  });
240
270
 
241
271
  // ─── Skill Pack ─────────────────────────────────────────────────────────────
@@ -364,6 +394,166 @@ export const ComposeOptionsSchema = z.object({
364
394
  openclawInstallMethod: OpenclawInstallMethodSchema.default("docker"),
365
395
  });
366
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
+ /** Additional files to write alongside compose (e.g. sandbox.toml). Keyed by filename. */
479
+ additionalFiles: z.record(z.string(), z.string()).default({}),
480
+ /** Metadata. */
481
+ metadata: z.object({
482
+ serviceCount: z.number(),
483
+ skillCount: z.number(),
484
+ estimatedMemoryMB: z.number(),
485
+ resolvedServices: z.array(z.string()),
486
+ skippedServices: z.array(SkippedServiceSchema),
487
+ generatedSecretKeys: z.array(z.string()),
488
+ portAssignments: z.record(z.string(), z.number()),
489
+ /** Docker images to pre-pull during cloud-init, grouped by priority. */
490
+ prePullImages: z.array(z.object({
491
+ image: z.string(),
492
+ priority: z.union([z.literal(1), z.literal(2), z.literal(3)]),
493
+ })).default([]),
494
+ }),
495
+ /** Warnings (non-fatal issues). */
496
+ warnings: z.array(z.string()),
497
+ });
498
+
499
+ export const AddonStackUpdateInputSchema = z.object({
500
+ instanceId: z.string().min(1),
501
+ /** Current compose override YAML (from the running instance). */
502
+ currentCompose: z.string(),
503
+ /** Current .env content. */
504
+ currentEnv: z.string(),
505
+ /** Services to add. */
506
+ addServices: z.array(z.string()).default([]),
507
+ /** Services to remove. */
508
+ removeServices: z.array(z.string()).default([]),
509
+ /** Whether to generate secrets for newly added services (default: true). */
510
+ generateSecrets: z.boolean().default(true),
511
+ /** User-provided credentials for new services. */
512
+ credentials: z.record(z.string(), z.record(z.string(), z.string())).default({}),
513
+ /** Port overrides for specific services. */
514
+ portOverrides: z.record(z.string(), z.record(z.string(), z.number().int().min(1).max(65535))).optional(),
515
+ /** Reserved ports. */
516
+ reservedPorts: z.array(z.number().int()).default([]),
517
+ platform: PlatformSchema.default("linux/amd64"),
518
+ openclawVersion: z.string().default("latest"),
519
+ aiProviders: z.array(AiProviderSchema).default([]),
520
+ prebuiltImages: z.record(z.string(), z.string()).default({}),
521
+ });
522
+
523
+ export const AddonStackUpdateResultSchema = z.object({
524
+ /** Updated compose override (merged with existing). */
525
+ composeOverride: z.string(),
526
+ /** Updated .env (existing values preserved, new ones added). */
527
+ envFile: z.string(),
528
+ /** Only NEW skill files to deploy. */
529
+ newSkillFiles: z.record(z.string(), z.string()),
530
+ /** Skill slugs to remove from openclaw.json. */
531
+ removedSkillSlugs: z.array(z.string()),
532
+ /** Config patch to apply. */
533
+ openclawConfigPatch: z.object({
534
+ skills: z.object({
535
+ add: z.record(z.string(), z.object({ enabled: z.boolean() })),
536
+ remove: z.array(z.string()),
537
+ }),
538
+ }),
539
+ /** New proxy routes to add. */
540
+ addProxyRoutes: z.array(ProxyRouteSchema),
541
+ /** Proxy routes to remove (service IDs). */
542
+ removeProxyRoutes: z.array(z.string()),
543
+ /** Services that need their images pulled. */
544
+ imagesToPull: z.array(z.string()),
545
+ /** Services to restart (due to dependency changes). */
546
+ restartRequired: z.array(z.string()),
547
+ /** Metadata. */
548
+ metadata: z.object({
549
+ added: z.array(z.string()),
550
+ removed: z.array(z.string()),
551
+ unchanged: z.array(z.string()),
552
+ estimatedMemoryDelta: z.number(),
553
+ }),
554
+ warnings: z.array(z.string()),
555
+ });
556
+
367
557
  // ─── API Request/Response ───────────────────────────────────────────────────
368
558
 
369
559
  export const ValidateRequestSchema = z.object({
@@ -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,142 @@
1
+ import type { ServiceDefinition } from "../../types.js";
2
+
3
+ export const burnlinkDefinition: ServiceDefinition = {
4
+ id: "burnlink",
5
+ name: "BurnLink",
6
+ description:
7
+ "Privacy-first, zero-knowledge file sharing with end-to-end browser-side AES-256-GCM encryption, one-time downloads, view-once mode, and brute-force protection.",
8
+ category: "storage",
9
+ icon: "🔥",
10
+
11
+ image: "diopisemou/burnlink",
12
+ imageTag: "latest",
13
+ ports: [
14
+ {
15
+ host: 3250,
16
+ container: 3000,
17
+ description: "BurnLink web interface",
18
+ exposed: true,
19
+ },
20
+ ],
21
+ volumes: [],
22
+ environment: [
23
+ {
24
+ key: "PORT",
25
+ defaultValue: "3000",
26
+ secret: false,
27
+ description: "Server listen port",
28
+ required: false,
29
+ },
30
+ {
31
+ key: "SUPABASE_URL",
32
+ defaultValue: "",
33
+ secret: false,
34
+ description: "Supabase project URL for metadata storage",
35
+ required: true,
36
+ },
37
+ {
38
+ key: "SUPABASE_SERVICE_ROLE_KEY",
39
+ defaultValue: "",
40
+ secret: true,
41
+ description: "Supabase service role key",
42
+ required: true,
43
+ },
44
+ {
45
+ key: "R2_ACCOUNT_ID",
46
+ defaultValue: "",
47
+ secret: false,
48
+ description: "Cloudflare R2 account ID (or MinIO endpoint for self-hosted)",
49
+ required: true,
50
+ },
51
+ {
52
+ key: "R2_ACCESS_KEY_ID",
53
+ defaultValue: "",
54
+ secret: true,
55
+ description: "S3-compatible storage access key",
56
+ required: true,
57
+ },
58
+ {
59
+ key: "R2_SECRET_ACCESS_KEY",
60
+ defaultValue: "",
61
+ secret: true,
62
+ description: "S3-compatible storage secret key",
63
+ required: true,
64
+ },
65
+ {
66
+ key: "R2_BUCKET_NAME",
67
+ defaultValue: "burnlink",
68
+ secret: false,
69
+ description: "Storage bucket name for encrypted files",
70
+ required: true,
71
+ },
72
+ {
73
+ key: "CANONICAL_BASE_URL",
74
+ defaultValue: "",
75
+ secret: false,
76
+ description: "Public URL for share links (e.g., https://burn.example.com)",
77
+ required: false,
78
+ },
79
+ {
80
+ key: "MAX_UPLOAD_BYTES",
81
+ defaultValue: "1073741824",
82
+ secret: false,
83
+ description: "Max file upload size in bytes (default 1 GB)",
84
+ required: false,
85
+ },
86
+ {
87
+ key: "NODE_ENV",
88
+ defaultValue: "production",
89
+ secret: false,
90
+ description: "Node environment (production enables rate limiting)",
91
+ required: false,
92
+ },
93
+ ],
94
+ healthcheck: {
95
+ test: "wget -q --spider http://localhost:3000/ || exit 1",
96
+ interval: "30s",
97
+ timeout: "10s",
98
+ retries: 3,
99
+ startPeriod: "15s",
100
+ },
101
+ dependsOn: [],
102
+ restartPolicy: "unless-stopped",
103
+ networks: ["openclaw-network"],
104
+
105
+ skills: [],
106
+ openclawEnvVars: [
107
+ {
108
+ key: "BURNLINK_HOST",
109
+ defaultValue: "burnlink",
110
+ secret: false,
111
+ description: "BurnLink hostname",
112
+ required: false,
113
+ },
114
+ {
115
+ key: "BURNLINK_PORT",
116
+ defaultValue: "3000",
117
+ secret: false,
118
+ description: "BurnLink internal port",
119
+ required: false,
120
+ },
121
+ ],
122
+
123
+ docsUrl: "https://github.com/diopisemou/BurnLink",
124
+ tags: ["file-sharing", "encryption", "privacy", "zero-knowledge", "self-destruct"],
125
+ maturity: "beta",
126
+
127
+ requires: [],
128
+ recommends: ["supabase", "minio"],
129
+ conflictsWith: [],
130
+
131
+ minMemoryMB: 128,
132
+ gpuRequired: false,
133
+ capDropCompatible: true,
134
+ proxyPath: "/burnlink",
135
+ envQuirks: [
136
+ {
137
+ key: "R2_SECRET_ACCESS_KEY",
138
+ issue: "min_length" as const,
139
+ fix: { type: "generate_base64url" as const, minBytes: 24 },
140
+ },
141
+ ],
142
+ };
@@ -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
  };
@@ -80,4 +80,13 @@ export const grafanaDefinition: ServiceDefinition = {
80
80
 
81
81
  minMemoryMB: 256,
82
82
  gpuRequired: false,
83
+ capDropCompatible: true,
84
+ proxyPath: "/grafana",
85
+ envQuirks: [
86
+ {
87
+ key: "GF_SECURITY_ADMIN_PASSWORD",
88
+ issue: "min_length" as const,
89
+ fix: { type: "generate_base64url" as const, minBytes: 16 },
90
+ },
91
+ ],
83
92
  };