crewswarm 0.8.1-beta

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 (362) hide show
  1. package/.env.example +155 -0
  2. package/LICENSE +21 -0
  3. package/README.md +316 -0
  4. package/apps/dashboard/dist/assets/chat-core-BwSoInmZ.js +1 -0
  5. package/apps/dashboard/dist/assets/chat-core-BwSoInmZ.js.br +0 -0
  6. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js +1 -0
  7. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
  8. package/apps/dashboard/dist/assets/components-CSUb80ze.js +1 -0
  9. package/apps/dashboard/dist/assets/components-CSUb80ze.js.br +0 -0
  10. package/apps/dashboard/dist/assets/core-utils-CAVnDoe1.js +1 -0
  11. package/apps/dashboard/dist/assets/core-utils-CAVnDoe1.js.br +0 -0
  12. package/apps/dashboard/dist/assets/index-CF0aJRtC.css +1 -0
  13. package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
  14. package/apps/dashboard/dist/assets/index-Px49zu76.js +2 -0
  15. package/apps/dashboard/dist/assets/index-Px49zu76.js.br +0 -0
  16. package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js +1 -0
  17. package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
  18. package/apps/dashboard/dist/assets/setup-wizard-i3eEixlo.js +1 -0
  19. package/apps/dashboard/dist/assets/setup-wizard-i3eEixlo.js.br +0 -0
  20. package/apps/dashboard/dist/assets/tab-agents-tab-BThdsdJY.js +1 -0
  21. package/apps/dashboard/dist/assets/tab-agents-tab-BThdsdJY.js.br +0 -0
  22. package/apps/dashboard/dist/assets/tab-benchmarks-tab-DfCuAClu.js +1 -0
  23. package/apps/dashboard/dist/assets/tab-comms-tab-eHpOSBhG.js +1 -0
  24. package/apps/dashboard/dist/assets/tab-comms-tab-eHpOSBhG.js.br +0 -0
  25. package/apps/dashboard/dist/assets/tab-contacts-tab-yEegNyO4.js +1 -0
  26. package/apps/dashboard/dist/assets/tab-contacts-tab-yEegNyO4.js.br +0 -0
  27. package/apps/dashboard/dist/assets/tab-engines-tab-C3DYxTwy.js +1 -0
  28. package/apps/dashboard/dist/assets/tab-engines-tab-C3DYxTwy.js.br +0 -0
  29. package/apps/dashboard/dist/assets/tab-memory-tab-C59BYFQD.js +1 -0
  30. package/apps/dashboard/dist/assets/tab-memory-tab-C59BYFQD.js.br +0 -0
  31. package/apps/dashboard/dist/assets/tab-models-tab-9Ur7pXWA.js +1 -0
  32. package/apps/dashboard/dist/assets/tab-models-tab-9Ur7pXWA.js.br +0 -0
  33. package/apps/dashboard/dist/assets/tab-pm-loop-tab-D7mnDelU.js +1 -0
  34. package/apps/dashboard/dist/assets/tab-pm-loop-tab-D7mnDelU.js.br +0 -0
  35. package/apps/dashboard/dist/assets/tab-projects-tab-C6h2Mv1K.js +1 -0
  36. package/apps/dashboard/dist/assets/tab-projects-tab-C6h2Mv1K.js.br +0 -0
  37. package/apps/dashboard/dist/assets/tab-prompts-tab-C0wZvWK3.js +1 -0
  38. package/apps/dashboard/dist/assets/tab-prompts-tab-C0wZvWK3.js.br +0 -0
  39. package/apps/dashboard/dist/assets/tab-services-tab-DBj_w3bc.js +1 -0
  40. package/apps/dashboard/dist/assets/tab-services-tab-DBj_w3bc.js.br +0 -0
  41. package/apps/dashboard/dist/assets/tab-settings-tab-ezeqAjZk.js +1 -0
  42. package/apps/dashboard/dist/assets/tab-settings-tab-ezeqAjZk.js.br +0 -0
  43. package/apps/dashboard/dist/assets/tab-skills-tab-BYdU2whk.js +1 -0
  44. package/apps/dashboard/dist/assets/tab-skills-tab-BYdU2whk.js.br +0 -0
  45. package/apps/dashboard/dist/assets/tab-spending-tab-Bg6w9t_p.js +1 -0
  46. package/apps/dashboard/dist/assets/tab-spending-tab-Bg6w9t_p.js.br +0 -0
  47. package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BBV9HB2X.js +1 -0
  48. package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BBV9HB2X.js.br +0 -0
  49. package/apps/dashboard/dist/assets/tab-swarm-tab-ChqLlEVs.js +1 -0
  50. package/apps/dashboard/dist/assets/tab-swarm-tab-ChqLlEVs.js.br +0 -0
  51. package/apps/dashboard/dist/assets/tab-usage-tab-B2UWXenJ.js +1 -0
  52. package/apps/dashboard/dist/assets/tab-usage-tab-B2UWXenJ.js.br +0 -0
  53. package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js +1 -0
  54. package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
  55. package/apps/dashboard/dist/assets/tab-workflows-tab-6QSXLJ0i.js +1 -0
  56. package/apps/dashboard/dist/assets/tab-workflows-tab-6QSXLJ0i.js.br +0 -0
  57. package/apps/dashboard/dist/favicon.png +0 -0
  58. package/apps/dashboard/dist/index.html +6466 -0
  59. package/apps/dashboard/dist/index.html.br +0 -0
  60. package/apps/dashboard/dist/index.html.gz +0 -0
  61. package/apps/dashboard/dist/signup.html +446 -0
  62. package/apps/dashboard/index.html +6442 -0
  63. package/apps/dashboard/package.json +15 -0
  64. package/apps/dashboard/src/app.js +2823 -0
  65. package/apps/dashboard/src/app.js.br +0 -0
  66. package/apps/dashboard/src/app.js.gz +0 -0
  67. package/apps/dashboard/src/chat/chat-actions.js +1847 -0
  68. package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
  69. package/apps/dashboard/src/chat/unified-messages.js +327 -0
  70. package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
  71. package/apps/dashboard/src/cli-process.js +208 -0
  72. package/apps/dashboard/src/cli-process.js.br +0 -0
  73. package/apps/dashboard/src/cli-process.js.gz +0 -0
  74. package/apps/dashboard/src/components/active-tasks-panel.js +175 -0
  75. package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
  76. package/apps/dashboard/src/core/api.js +18 -0
  77. package/apps/dashboard/src/core/api.js.br +0 -0
  78. package/apps/dashboard/src/core/dom.js +220 -0
  79. package/apps/dashboard/src/core/dom.js.br +0 -0
  80. package/apps/dashboard/src/core/state.js +91 -0
  81. package/apps/dashboard/src/core/state.js.br +0 -0
  82. package/apps/dashboard/src/core/task-manager.js +134 -0
  83. package/apps/dashboard/src/core/task-manager.js.br +0 -0
  84. package/apps/dashboard/src/orchestration-status.js +127 -0
  85. package/apps/dashboard/src/orchestration-status.js.br +0 -0
  86. package/apps/dashboard/src/setup-wizard.js +555 -0
  87. package/apps/dashboard/src/setup-wizard.js.br +0 -0
  88. package/apps/dashboard/src/styles.css +2085 -0
  89. package/apps/dashboard/src/styles.css.br +0 -0
  90. package/apps/dashboard/src/styles.css.gz +0 -0
  91. package/apps/dashboard/src/tabs/agents-tab.js +2237 -0
  92. package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
  93. package/apps/dashboard/src/tabs/benchmarks-tab.js +229 -0
  94. package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
  95. package/apps/dashboard/src/tabs/comms-tab.js +955 -0
  96. package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
  97. package/apps/dashboard/src/tabs/contacts-tab.js +654 -0
  98. package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
  99. package/apps/dashboard/src/tabs/engines-tab.js +175 -0
  100. package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
  101. package/apps/dashboard/src/tabs/memory-tab.js +182 -0
  102. package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
  103. package/apps/dashboard/src/tabs/models-tab.js +441 -0
  104. package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
  105. package/apps/dashboard/src/tabs/pm-loop-tab.js +185 -0
  106. package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
  107. package/apps/dashboard/src/tabs/projects-tab.js +663 -0
  108. package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
  109. package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
  110. package/apps/dashboard/src/tabs/prompts-tab.js +160 -0
  111. package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
  112. package/apps/dashboard/src/tabs/services-tab.js +202 -0
  113. package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
  114. package/apps/dashboard/src/tabs/settings-tab.js +803 -0
  115. package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
  116. package/apps/dashboard/src/tabs/skills-tab.js +284 -0
  117. package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
  118. package/apps/dashboard/src/tabs/spending-tab.js +173 -0
  119. package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
  120. package/apps/dashboard/src/tabs/swarm-chat-tab.js +660 -0
  121. package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
  122. package/apps/dashboard/src/tabs/swarm-tab.js +538 -0
  123. package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
  124. package/apps/dashboard/src/tabs/usage-tab.js +390 -0
  125. package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
  126. package/apps/dashboard/src/tabs/waves-tab.js +238 -0
  127. package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
  128. package/apps/dashboard/src/tabs/workflows-tab.js +747 -0
  129. package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
  130. package/apps/vibe/.crew/agent-memory/pipeline.json +249 -0
  131. package/apps/vibe/.crew/cost.json +17 -0
  132. package/apps/vibe/.crew/json-parse-metrics.jsonl +22 -0
  133. package/apps/vibe/.crew/pipeline-metrics.jsonl +22 -0
  134. package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +5 -0
  135. package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +5 -0
  136. package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +1 -0
  137. package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +5 -0
  138. package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +5 -0
  139. package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +5 -0
  140. package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +5 -0
  141. package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +5 -0
  142. package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +5 -0
  143. package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +5 -0
  144. package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +5 -0
  145. package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +5 -0
  146. package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +5 -0
  147. package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +5 -0
  148. package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +5 -0
  149. package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +5 -0
  150. package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +5 -0
  151. package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +5 -0
  152. package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +2 -0
  153. package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +5 -0
  154. package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +5 -0
  155. package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +5 -0
  156. package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +5 -0
  157. package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +5 -0
  158. package/apps/vibe/.crew/sandbox.json +7 -0
  159. package/apps/vibe/.crew/session.json +285 -0
  160. package/apps/vibe/.crew/training-data.jsonl +0 -0
  161. package/apps/vibe/.github/workflows/studio-quality.yml +37 -0
  162. package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +12 -0
  163. package/apps/vibe/.studio-data/project-messages/general.jsonl +54 -0
  164. package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +10 -0
  165. package/apps/vibe/ARCHITECTURE.md +3393 -0
  166. package/apps/vibe/QUICK-REFERENCE.md +211 -0
  167. package/apps/vibe/README.md +76 -0
  168. package/apps/vibe/ROADMAP.md +41 -0
  169. package/apps/vibe/STUDIO-SETUP-COMPLETE.md +35 -0
  170. package/apps/vibe/VISUAL-GUIDE.md +378 -0
  171. package/apps/vibe/capture-demo.mjs +160 -0
  172. package/apps/vibe/capture-vibe-assets.mjs +71 -0
  173. package/apps/vibe/capture-vibe-video.mjs +260 -0
  174. package/apps/vibe/check-buttons.js +41 -0
  175. package/apps/vibe/diagnose.html +106 -0
  176. package/apps/vibe/fix-buttons.js +103 -0
  177. package/apps/vibe/index.html +3401 -0
  178. package/apps/vibe/package-lock.json +920 -0
  179. package/apps/vibe/package.json +31 -0
  180. package/apps/vibe/public/favicon.png +0 -0
  181. package/apps/vibe/scripts/studio-pty-host.py +117 -0
  182. package/apps/vibe/server.mjs +1835 -0
  183. package/apps/vibe/src/main.js +2846 -0
  184. package/apps/vibe/src/register-all-languages.js +98 -0
  185. package/apps/vibe/start-studio.sh +11 -0
  186. package/apps/vibe/test/accessibility-tests.js +77 -0
  187. package/apps/vibe/test/browser-performance-audit.mjs +205 -0
  188. package/apps/vibe/test/performance-tests.js +120 -0
  189. package/apps/vibe/test/security-tests.js +213 -0
  190. package/apps/vibe/tests/e2e.local.mjs +54 -0
  191. package/apps/vibe/tests/server.smoke.mjs +106 -0
  192. package/apps/vibe/update_website.mjs +74 -0
  193. package/apps/vibe/vite.config.js +19 -0
  194. package/apps/vibe/watch-server.mjs +108 -0
  195. package/contrib/openclaw-plugin/README.md +199 -0
  196. package/contrib/openclaw-plugin/index.ts +306 -0
  197. package/contrib/openclaw-plugin/openclaw.plugin.json +41 -0
  198. package/contrib/openclaw-plugin/package.json +27 -0
  199. package/contrib/openclaw-plugin/skills/crewswarm/SKILL.md +88 -0
  200. package/crew-lead.mjs +649 -0
  201. package/engines/claude-code.json +36 -0
  202. package/engines/codex.json +37 -0
  203. package/engines/crew-cli.json +42 -0
  204. package/engines/cursor.json +40 -0
  205. package/engines/docker-sandbox.json +38 -0
  206. package/engines/gemini-cli.json +75 -0
  207. package/engines/opencode.json +31 -0
  208. package/gateway-bridge.mjs +1575 -0
  209. package/install.sh +738 -0
  210. package/lib/agent-registry.mjs +232 -0
  211. package/lib/agents/daemon.mjs +121 -0
  212. package/lib/agents/dispatch.mjs +225 -0
  213. package/lib/agents/permissions.mjs +90 -0
  214. package/lib/agents/platform-formatting.mjs +102 -0
  215. package/lib/agents/registry.mjs +81 -0
  216. package/lib/agents/tool-instructions.mjs +257 -0
  217. package/lib/agents/validation.mjs +75 -0
  218. package/lib/approval/policy-manager.mjs +221 -0
  219. package/lib/autoharness/index.mjs +391 -0
  220. package/lib/bridges/cli-executor.mjs +332 -0
  221. package/lib/bridges/gateway-ws.mjs +345 -0
  222. package/lib/bridges/integration.mjs +229 -0
  223. package/lib/bridges/rag-helper.mjs +90 -0
  224. package/lib/browser/opencode-passthrough-filter.js +44 -0
  225. package/lib/browser/passthrough-stderr.js +109 -0
  226. package/lib/chat/autonomous-mentions.mjs +373 -0
  227. package/lib/chat/history.mjs +82 -0
  228. package/lib/chat/mention-routing-intent.mjs +136 -0
  229. package/lib/chat/participants.mjs +95 -0
  230. package/lib/chat/project-messages-rag.mjs +265 -0
  231. package/lib/chat/project-messages.mjs +479 -0
  232. package/lib/chat/shared-chat-prompt-overlay.mjs +52 -0
  233. package/lib/chat/thread-binding.mjs +34 -0
  234. package/lib/chat/unified-history.mjs +223 -0
  235. package/lib/chat/unified-wrapper.mjs +41 -0
  236. package/lib/cli-process-tracker.mjs +228 -0
  237. package/lib/collections/index.mjs +433 -0
  238. package/lib/contacts/identity-linker.mjs +248 -0
  239. package/lib/contacts/index.mjs +341 -0
  240. package/lib/crew-judge/PROMPT.md +93 -0
  241. package/lib/crew-judge/judge.mjs +260 -0
  242. package/lib/crew-lead/agent-manager.mjs +125 -0
  243. package/lib/crew-lead/background.mjs +270 -0
  244. package/lib/crew-lead/brain.mjs +110 -0
  245. package/lib/crew-lead/chat-handler.mjs +2603 -0
  246. package/lib/crew-lead/chat-handler.mjs.bak +1274 -0
  247. package/lib/crew-lead/classifier.mjs +83 -0
  248. package/lib/crew-lead/http-server.mjs +4824 -0
  249. package/lib/crew-lead/intent.mjs +102 -0
  250. package/lib/crew-lead/interval-manager.mjs +41 -0
  251. package/lib/crew-lead/llm-caller.mjs +544 -0
  252. package/lib/crew-lead/prompts.mjs +392 -0
  253. package/lib/crew-lead/retry-manager.mjs +118 -0
  254. package/lib/crew-lead/tools.mjs +318 -0
  255. package/lib/crew-lead/wave-dispatcher.mjs +798 -0
  256. package/lib/crew-lead/waves-config.json +73 -0
  257. package/lib/crew-lead/waves-loader.mjs +110 -0
  258. package/lib/crew-lead/ws-router.mjs +428 -0
  259. package/lib/dispatch/parsers.mjs +299 -0
  260. package/lib/domain-planning/detector.mjs +196 -0
  261. package/lib/domain-planning/prompts/crew-pm-cli.md +96 -0
  262. package/lib/domain-planning/prompts/crew-pm-core.md +122 -0
  263. package/lib/domain-planning/prompts/crew-pm-frontend.md +111 -0
  264. package/lib/engines/crew-cli-sandbox.mjs +422 -0
  265. package/lib/engines/crew-cli.mjs +155 -0
  266. package/lib/engines/cursor-launcher.mjs +110 -0
  267. package/lib/engines/engine-registry.mjs +253 -0
  268. package/lib/engines/llm-direct.mjs +184 -0
  269. package/lib/engines/opencode.mjs +256 -0
  270. package/lib/engines/ouroboros.mjs +114 -0
  271. package/lib/engines/rt-envelope.mjs +1643 -0
  272. package/lib/engines/rt-envelope.mjs.backup-current +870 -0
  273. package/lib/engines/runners.mjs +1367 -0
  274. package/lib/gemini-cli-passthrough-noise.mjs +37 -0
  275. package/lib/integrations/code-search.mjs +259 -0
  276. package/lib/integrations/greptile.mjs +148 -0
  277. package/lib/integrations/multimodal.mjs +313 -0
  278. package/lib/integrations/telegram-streaming.mjs +153 -0
  279. package/lib/integrations/tts.mjs +312 -0
  280. package/lib/integrations/twitter-links.mjs +294 -0
  281. package/lib/memory/shared-adapter.mjs +296 -0
  282. package/lib/pipeline/manager.mjs +539 -0
  283. package/lib/preferences/extractor.mjs +347 -0
  284. package/lib/project-dir.mjs +20 -0
  285. package/lib/runtime/config.mjs +388 -0
  286. package/lib/runtime/dlq.mjs +170 -0
  287. package/lib/runtime/log-rotation.mjs +82 -0
  288. package/lib/runtime/logger.mjs +58 -0
  289. package/lib/runtime/memory.mjs +421 -0
  290. package/lib/runtime/paths.mjs +76 -0
  291. package/lib/runtime/project-dir.mjs +127 -0
  292. package/lib/runtime/spending.mjs +204 -0
  293. package/lib/runtime/startup-guard.mjs +291 -0
  294. package/lib/runtime/task-lease.mjs +234 -0
  295. package/lib/runtime/telemetry-schema.mjs +208 -0
  296. package/lib/runtime/telemetry.mjs +101 -0
  297. package/lib/runtime/utils.mjs +64 -0
  298. package/lib/skills/index.mjs +265 -0
  299. package/lib/tools/browser.mjs +135 -0
  300. package/lib/tools/executor.mjs +913 -0
  301. package/lib/types.d.ts +57 -0
  302. package/package.json +106 -0
  303. package/pm-loop.mjs +1626 -0
  304. package/prompts/coder-back.md +27 -0
  305. package/prompts/coder-front.md +27 -0
  306. package/prompts/coder.md +28 -0
  307. package/prompts/copywriter.md +17 -0
  308. package/prompts/fixer.md +39 -0
  309. package/prompts/frontend.md +23 -0
  310. package/prompts/github.md +24 -0
  311. package/prompts/main.md +39 -0
  312. package/prompts/pm-cli.md +95 -0
  313. package/prompts/pm-core.md +121 -0
  314. package/prompts/pm-frontend.md +110 -0
  315. package/prompts/pm.md +234 -0
  316. package/prompts/qa.md +44 -0
  317. package/prompts/security.md +19 -0
  318. package/scripts/build-crew-chat.sh +28 -0
  319. package/scripts/build-llms-full.mjs +52 -0
  320. package/scripts/chatmock-login.sh +16 -0
  321. package/scripts/chatmock-serve.sh +16 -0
  322. package/scripts/check-dashboard.mjs +88 -0
  323. package/scripts/crew-scribe.mjs +326 -0
  324. package/scripts/dashboard-helpers.mjs +391 -0
  325. package/scripts/dashboard-validation.mjs +198 -0
  326. package/scripts/dashboard.mjs +9717 -0
  327. package/scripts/dlq-replay.mjs +61 -0
  328. package/scripts/doctor.mjs +196 -0
  329. package/scripts/file-lock.mjs +186 -0
  330. package/scripts/fresh-machine-smoke.sh +323 -0
  331. package/scripts/generate-changelog.mjs +227 -0
  332. package/scripts/generate-openapi.mjs +334 -0
  333. package/scripts/health-check.mjs +229 -0
  334. package/scripts/install-docker.sh +213 -0
  335. package/scripts/mcp-server.mjs +1625 -0
  336. package/scripts/opencrew-rt-daemon.mjs +568 -0
  337. package/scripts/openswitchctl +646 -0
  338. package/scripts/refactor-configs.mjs +39 -0
  339. package/scripts/release-check.sh +46 -0
  340. package/scripts/resolve-node-bin.sh +25 -0
  341. package/scripts/restart-all-from-repo.sh +329 -0
  342. package/scripts/restart-crew-lead.sh +98 -0
  343. package/scripts/restart-dashboard.sh +104 -0
  344. package/scripts/restart-service.sh +274 -0
  345. package/scripts/run-accessibility-audit.mjs +356 -0
  346. package/scripts/run-integration-bounded.mjs +188 -0
  347. package/scripts/run-scheduled-pipeline.mjs +230 -0
  348. package/scripts/run.mjs +41 -0
  349. package/scripts/scan-skills.mjs +79 -0
  350. package/scripts/setup-firewall.sh +128 -0
  351. package/scripts/smoke-dispatch.mjs +149 -0
  352. package/scripts/smoke.sh +163 -0
  353. package/scripts/start-crew.mjs +328 -0
  354. package/scripts/start.mjs +146 -0
  355. package/scripts/swiftbar-restart-service.sh +19 -0
  356. package/scripts/sync-agents.mjs +152 -0
  357. package/scripts/sync-prompts.mjs +79 -0
  358. package/scripts/validate-config.mjs +337 -0
  359. package/scripts/wow.mjs +89 -0
  360. package/telegram-bridge.mjs +2421 -0
  361. package/unified-orchestrator.mjs +519 -0
  362. package/whatsapp-bridge.mjs +1481 -0
@@ -0,0 +1,391 @@
1
+ /**
2
+ * Standardized error handlers and response helpers for Dashboard API
3
+ */
4
+
5
+ // ── Response Helpers ──────────────────────────────────────────────────────
6
+
7
+ /**
8
+ * Send a successful JSON response
9
+ */
10
+ export function jsonOk(res, data = {}) {
11
+ res.writeHead(200, { 'content-type': 'application/json' });
12
+ res.end(JSON.stringify({ ok: true, ...data }));
13
+ }
14
+
15
+ /**
16
+ * Send an error JSON response with appropriate HTTP status
17
+ */
18
+ export function jsonError(res, status, message, details = null) {
19
+ res.writeHead(status, { 'content-type': 'application/json' });
20
+ const payload = { ok: false, error: message };
21
+ if (details) payload.details = details;
22
+ res.end(JSON.stringify(payload));
23
+ }
24
+
25
+ /**
26
+ * Send a validation error (400)
27
+ */
28
+ export function validationError(res, message, details = null) {
29
+ jsonError(res, 400, message, details);
30
+ }
31
+
32
+ /**
33
+ * Send a not found error (404)
34
+ */
35
+ export function notFoundError(res, message = 'Resource not found') {
36
+ jsonError(res, 404, message);
37
+ }
38
+
39
+ /**
40
+ * Send an internal server error (500)
41
+ */
42
+ export function serverError(res, message = 'Internal server error', details = null) {
43
+ jsonError(res, 500, message, details);
44
+ }
45
+
46
+ // ── Request Helpers ───────────────────────────────────────────────────────
47
+
48
+ /**
49
+ * Parse JSON body from request with error handling
50
+ */
51
+ export async function parseJsonBody(req) {
52
+ let body = '';
53
+ for await (const chunk of req) body += chunk;
54
+
55
+ if (!body.trim()) {
56
+ return { ok: false, error: 'Empty request body' };
57
+ }
58
+
59
+ try {
60
+ const parsed = JSON.parse(body);
61
+ return { ok: true, data: parsed };
62
+ } catch (err) {
63
+ return { ok: false, error: 'Invalid JSON: ' + err.message };
64
+ }
65
+ }
66
+
67
+ // ── Process Helpers ───────────────────────────────────────────────────────
68
+
69
+ /**
70
+ * Spawn a child process with better error handling and timeout
71
+ * Replaces execSync for safer command execution
72
+ */
73
+ export async function spawnAsync(command, args = [], options = {}) {
74
+ return new Promise((resolve, reject) => {
75
+ import('node:child_process').then(({ spawn }) => {
76
+ const timeout = options.timeout || 10000;
77
+ const encoding = options.encoding || 'utf8';
78
+
79
+ const proc = spawn(command, args, {
80
+ ...options,
81
+ timeout: undefined, // We'll handle timeout manually
82
+ });
83
+
84
+ let stdout = '';
85
+ let stderr = '';
86
+ let timedOut = false;
87
+
88
+ const timer = setTimeout(() => {
89
+ timedOut = true;
90
+ proc.kill('SIGTERM');
91
+ setTimeout(() => proc.kill('SIGKILL'), 1000); // Force kill after 1s
92
+ }, timeout);
93
+
94
+ if (proc.stdout) {
95
+ proc.stdout.on('data', (data) => {
96
+ stdout += data.toString(encoding);
97
+ });
98
+ }
99
+
100
+ if (proc.stderr) {
101
+ proc.stderr.on('data', (data) => {
102
+ stderr += data.toString(encoding);
103
+ });
104
+ }
105
+
106
+ proc.on('error', (err) => {
107
+ clearTimeout(timer);
108
+ reject(new Error(`Failed to spawn ${command}: ${err.message}`));
109
+ });
110
+
111
+ proc.on('close', (code) => {
112
+ clearTimeout(timer);
113
+
114
+ if (timedOut) {
115
+ reject(new Error(`Command timed out after ${timeout}ms: ${command} ${args.join(' ')}`));
116
+ } else if (code === 0) {
117
+ resolve({ stdout, stderr, code });
118
+ } else {
119
+ reject(new Error(`Command exited with code ${code}: ${stderr || stdout}`));
120
+ }
121
+ });
122
+ });
123
+ });
124
+ }
125
+
126
+ /**
127
+ * Check if a process is running by pattern
128
+ */
129
+ export async function isProcessRunning(pattern) {
130
+ try {
131
+ const { stdout } = await spawnAsync('pgrep', ['-f', pattern], { timeout: 2000 });
132
+ return stdout.trim().length > 0;
133
+ } catch {
134
+ return false;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Get PID of a process by pattern
140
+ */
141
+ export async function getProcessPid(pattern) {
142
+ try {
143
+ const { stdout } = await spawnAsync('pgrep', ['-f', pattern], { timeout: 2000 });
144
+ const pids = stdout.trim().split('\n').filter(Boolean).map(p => parseInt(p, 10));
145
+ return pids.length > 0 ? pids[0] : null;
146
+ } catch {
147
+ return null;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Get all PIDs of a process by pattern
153
+ */
154
+ export async function getAllProcessPids(pattern) {
155
+ try {
156
+ const { stdout } = await spawnAsync('pgrep', ['-f', pattern], { timeout: 2000 });
157
+ return stdout.trim().split('\n').filter(Boolean).map(p => parseInt(p, 10));
158
+ } catch {
159
+ return [];
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Count processes matching pattern
165
+ */
166
+ export async function countProcesses(pattern) {
167
+ try {
168
+ const pids = await getAllProcessPids(pattern);
169
+ return pids.length;
170
+ } catch {
171
+ return 0;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Kill a process by pattern
177
+ */
178
+ export async function killProcess(pattern, signal = 'SIGTERM') {
179
+ try {
180
+ await spawnAsync('pkill', [signal === 'SIGKILL' ? '-9' : '', '-f', pattern].filter(Boolean), { timeout: 3000 });
181
+ return true;
182
+ } catch {
183
+ return false;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Check if a port is in use
189
+ */
190
+ export async function isPortInUse(port) {
191
+ try {
192
+ await spawnAsync('lsof', ['-ti', `:${port}`], { timeout: 2000 });
193
+ return true;
194
+ } catch {
195
+ return false;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Kill process using a specific port
201
+ */
202
+ export async function killProcessOnPort(port, force = false) {
203
+ try {
204
+ const { stdout } = await spawnAsync('lsof', ['-ti', `:${port}`], { timeout: 2000 });
205
+ const pids = stdout.trim().split('\n').filter(Boolean);
206
+ for (const pid of pids) {
207
+ try {
208
+ process.kill(parseInt(pid, 10), force ? 'SIGKILL' : 'SIGTERM');
209
+ } catch {}
210
+ }
211
+ return true;
212
+ } catch {
213
+ return false;
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Check if a command/binary exists
219
+ */
220
+ export async function commandExists(command) {
221
+ try {
222
+ await spawnAsync('which', [command], { timeout: 2000 });
223
+ return true;
224
+ } catch {
225
+ return false;
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Get process start time
231
+ */
232
+ export async function getProcessStartTime(pid) {
233
+ try {
234
+ const { stdout } = await spawnAsync('ps', ['-p', String(pid), '-o', 'lstart='], { timeout: 1500 });
235
+ return stdout.trim() ? new Date(stdout.trim()).getTime() : null;
236
+ } catch {
237
+ return null;
238
+ }
239
+ }
240
+
241
+ // ── File Locking Helpers ──────────────────────────────────────────────────
242
+
243
+ import lockfile from 'proper-lockfile';
244
+ import { promises as fsPromises } from 'node:fs';
245
+
246
+ /**
247
+ * Write to a config file with proper locking to prevent corruption
248
+ */
249
+ export async function writeConfigSafely(filePath, data) {
250
+ const lockOptions = {
251
+ retries: { retries: 5, minTimeout: 100, maxTimeout: 1000 },
252
+ stale: 5000,
253
+ realpath: false // Don't resolve symlinks
254
+ };
255
+
256
+ let release;
257
+ try {
258
+ // Ensure parent directory exists
259
+ const dir = filePath.substring(0, filePath.lastIndexOf('/'));
260
+ await fsPromises.mkdir(dir, { recursive: true });
261
+
262
+ // Create file if it doesn't exist
263
+ try {
264
+ await fsPromises.access(filePath);
265
+ } catch {
266
+ await fsPromises.writeFile(filePath, '{}');
267
+ }
268
+
269
+ // Acquire lock
270
+ release = await lockfile.lock(filePath, lockOptions);
271
+
272
+ // Write data
273
+ const jsonData = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
274
+ await fsPromises.writeFile(filePath, jsonData, 'utf8');
275
+
276
+ return { ok: true };
277
+ } catch (err) {
278
+ return { ok: false, error: `Failed to write ${filePath}: ${err.message}` };
279
+ } finally {
280
+ if (release) {
281
+ try {
282
+ await release();
283
+ } catch {}
284
+ }
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Read from a config file with proper locking
290
+ */
291
+ export async function readConfigSafely(filePath) {
292
+ const lockOptions = {
293
+ retries: { retries: 3, minTimeout: 50, maxTimeout: 500 },
294
+ stale: 5000,
295
+ realpath: false
296
+ };
297
+
298
+ let release;
299
+ try {
300
+ // Check if file exists
301
+ try {
302
+ await fsPromises.access(filePath);
303
+ } catch {
304
+ return { ok: false, error: 'File does not exist' };
305
+ }
306
+
307
+ // Acquire shared lock for reading
308
+ release = await lockfile.lock(filePath, { ...lockOptions, shared: true });
309
+
310
+ // Read and parse
311
+ const content = await fsPromises.readFile(filePath, 'utf8');
312
+ const data = JSON.parse(content);
313
+
314
+ return { ok: true, data };
315
+ } catch (err) {
316
+ return { ok: false, error: `Failed to read ${filePath}: ${err.message}` };
317
+ } finally {
318
+ if (release) {
319
+ try {
320
+ await release();
321
+ } catch {}
322
+ }
323
+ }
324
+ }
325
+
326
+ // ── Error Handler Wrapper ─────────────────────────────────────────────────
327
+
328
+ /**
329
+ * Wrap an async route handler with error handling
330
+ */
331
+ export function asyncHandler(handler) {
332
+ return async (req, res, url) => {
333
+ try {
334
+ await handler(req, res, url);
335
+ } catch (err) {
336
+ console.error('[dashboard] Unhandled error:', err);
337
+ if (!res.headersSent) {
338
+ serverError(res, err.message, process.env.NODE_ENV === 'development' ? err.stack : undefined);
339
+ }
340
+ }
341
+ };
342
+ }
343
+
344
+ // ── Path Sanitization ─────────────────────────────────────────────────────
345
+
346
+ import { resolve, normalize } from 'path';
347
+ import { homedir } from 'os';
348
+
349
+ /**
350
+ * Validate that a path is safe (prevents path traversal attacks)
351
+ * Safe paths must be within the workspace or user's home directory
352
+ * @param {string} targetPath - Path to validate
353
+ * @param {string} workspaceRoot - Root of the workspace
354
+ * @returns {{ok: boolean, error?: string, sanitized?: string}}
355
+ */
356
+ export function sanitizePath(targetPath, workspaceRoot) {
357
+ try {
358
+ if (!targetPath || typeof targetPath !== 'string') {
359
+ return { ok: false, error: 'Invalid path: must be a non-empty string' };
360
+ }
361
+
362
+ // Normalize and resolve the path
363
+ const sanitized = normalize(resolve(targetPath));
364
+ const wsRoot = normalize(resolve(workspaceRoot));
365
+ const home = normalize(homedir());
366
+
367
+ // Check if path is within workspace or home directory
368
+ const inWorkspace = sanitized.startsWith(wsRoot + '/') || sanitized === wsRoot;
369
+ const inHome = sanitized.startsWith(home + '/') || sanitized === home;
370
+
371
+ if (!inWorkspace && !inHome) {
372
+ return {
373
+ ok: false,
374
+ error: `Path must be within workspace (${wsRoot}) or home directory (${home})`
375
+ };
376
+ }
377
+
378
+ // Block dangerous path components
379
+ const dangerous = ['..', '.git', 'node_modules/.bin'];
380
+ const parts = sanitized.split('/');
381
+ for (const danger of dangerous) {
382
+ if (parts.includes(danger)) {
383
+ return { ok: false, error: `Path contains dangerous component: ${danger}` };
384
+ }
385
+ }
386
+
387
+ return { ok: true, sanitized };
388
+ } catch (err) {
389
+ return { ok: false, error: `Path validation failed: ${err.message}` };
390
+ }
391
+ }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Zod validation schemas for Dashboard API endpoints
3
+ */
4
+ import { z } from 'zod';
5
+
6
+ // ── Common Schemas ────────────────────────────────────────────────────────
7
+ export const AgentIdSchema = z.string().min(1).max(50).regex(/^[a-z0-9-]+$/);
8
+ export const ProjectIdSchema = z.string().min(1).max(100);
9
+ export const ModelNameSchema = z.string().min(1).max(100);
10
+
11
+ // ── Agent Management ──────────────────────────────────────────────────────
12
+ export const SendMessageSchema = z.object({
13
+ to: AgentIdSchema,
14
+ message: z.string().min(1).max(10000),
15
+ });
16
+
17
+ export const UpdateAgentConfigSchema = z.object({
18
+ agentId: AgentIdSchema,
19
+ model: ModelNameSchema.optional(),
20
+ fallbackModel: ModelNameSchema.optional(),
21
+ systemPrompt: z.string().max(50000).optional(),
22
+ name: z.string().max(100).optional(),
23
+ emoji: z.string().max(10).optional(),
24
+ theme: z.string().max(200).optional(),
25
+ toolProfile: z.enum(['crewswarm', 'basic', 'custom']).optional(),
26
+ alsoAllow: z.array(z.string()).optional(),
27
+ useOpenCode: z.boolean().optional(),
28
+ opencodeModel: z.string().optional(),
29
+ opencodeFallbackModel: z.string().optional(),
30
+ useCursorCli: z.boolean().optional(),
31
+ cursorCliModel: z.string().optional(),
32
+ useClaudeCode: z.boolean().optional(),
33
+ claudeCodeModel: z.string().optional(),
34
+ useCodex: z.boolean().optional(),
35
+ useGeminiCli: z.boolean().optional(),
36
+ geminiCliModel: z.string().optional(),
37
+ role: z.string().optional(),
38
+ opencodeLoop: z.boolean().optional(),
39
+ opencodeLoopMaxRounds: z.number().int().min(1).max(100).optional(),
40
+ workspace: z.string().optional(),
41
+ });
42
+
43
+ export const CreateAgentSchema = z.object({
44
+ id: AgentIdSchema,
45
+ model: ModelNameSchema,
46
+ name: z.string().max(100).optional(),
47
+ emoji: z.string().max(10).optional(),
48
+ theme: z.string().max(200).optional(),
49
+ systemPrompt: z.string().max(50000).optional(),
50
+ alsoAllow: z.array(z.string()).optional(),
51
+ });
52
+
53
+ // ── Project Management ────────────────────────────────────────────────────
54
+ export const CreateProjectSchema = z.object({
55
+ name: z.string().min(1).max(100),
56
+ description: z.string().max(1000).optional(),
57
+ outputDir: z.string().min(1).max(500).refine(
58
+ (p) => {
59
+ // Path sanitization: prevent traversal attacks
60
+ const { resolve } = require('node:path');
61
+ const { homedir } = require('node:os');
62
+ const resolved = resolve(p);
63
+ const cwd = resolve(process.cwd());
64
+ const home = resolve(homedir());
65
+ // Allow paths under workspace root OR user's home directory
66
+ return resolved.startsWith(cwd) || resolved.startsWith(home);
67
+ },
68
+ { message: "outputDir must be under workspace root or home directory" }
69
+ ),
70
+ featuresDoc: z.string().max(500).optional(),
71
+ });
72
+
73
+ export const UpdateProjectSchema = z.object({
74
+ projectId: ProjectIdSchema,
75
+ autoAdvance: z.boolean().optional(),
76
+ name: z.string().min(1).max(100).optional(),
77
+ description: z.string().max(1000).optional(),
78
+ outputDir: z.string().max(500).optional(),
79
+ });
80
+
81
+ export const DeleteProjectSchema = z.object({
82
+ projectId: ProjectIdSchema,
83
+ });
84
+
85
+ // ── Build Operations ──────────────────────────────────────────────────────
86
+ export const StartBuildSchema = z.object({
87
+ requirement: z.string().min(1).max(10000),
88
+ projectId: ProjectIdSchema.optional(),
89
+ });
90
+
91
+ export const StopBuildSchema = z.object({
92
+ projectId: ProjectIdSchema.optional(),
93
+ });
94
+
95
+ // ── PM Loop ───────────────────────────────────────────────────────────────
96
+ export const StartPMLoopSchema = z.object({
97
+ dryRun: z.boolean().optional(),
98
+ projectId: ProjectIdSchema.optional(),
99
+ pmOptions: z.object({
100
+ autoAdvance: z.boolean().optional(),
101
+ maxIterations: z.number().int().min(1).max(1000).optional(),
102
+ useSecurity: z.boolean().optional(),
103
+ useQA: z.boolean().optional(),
104
+ }).optional(),
105
+ });
106
+
107
+ export const StopPMLoopSchema = z.object({
108
+ projectId: ProjectIdSchema.optional(),
109
+ });
110
+
111
+ // ── Skills ────────────────────────────────────────────────────────────────
112
+ export const CreateSkillSchema = z.object({
113
+ name: z.string().min(1).max(100).regex(/^[a-zA-Z0-9_-]+$/),
114
+ url: z.string().url().max(1000),
115
+ method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']).optional(),
116
+ description: z.string().max(1000).optional(),
117
+ auth: z.object({
118
+ type: z.enum(['bearer', 'header', 'basic']).optional(),
119
+ keyFrom: z.string().optional(),
120
+ token: z.string().optional(),
121
+ header: z.string().optional(),
122
+ }).optional(),
123
+ defaultParams: z.record(z.any()).optional(),
124
+ requiresApproval: z.boolean().optional(),
125
+ });
126
+
127
+ export const DeleteSkillSchema = z.object({
128
+ name: z.string().min(1).max(100),
129
+ });
130
+
131
+ export const RunSkillSchema = z.object({
132
+ name: z.string().min(1).max(100),
133
+ params: z.record(z.any()).optional(),
134
+ });
135
+
136
+ export const ImportSkillSchema = z.object({
137
+ url: z.string().url().min(1).max(2000),
138
+ });
139
+
140
+ // ── Services ──────────────────────────────────────────────────────────────
141
+ export const ServiceActionSchema = z.object({
142
+ id: z.enum([
143
+ 'rt-bus',
144
+ 'agents',
145
+ 'crew-lead',
146
+ 'telegram',
147
+ 'whatsapp',
148
+ 'opencode',
149
+ 'mcp',
150
+ 'openclaw-gateway',
151
+ 'dashboard',
152
+ ]),
153
+ });
154
+
155
+ // ── Memory ────────────────────────────────────────────────────────────────
156
+ export const SearchMemorySchema = z.object({
157
+ query: z.string().min(1).max(500),
158
+ maxResults: z.number().int().min(1).max(100).optional(),
159
+ });
160
+
161
+ // ── Benchmarks ────────────────────────────────────────────────────────────
162
+ export const RunBenchmarkSchema = z.object({
163
+ benchmarkId: z.string().min(1).max(100),
164
+ taskId: z.string().min(1).max(100),
165
+ model: ModelNameSchema.optional(),
166
+ });
167
+
168
+ // ── DLQ ───────────────────────────────────────────────────────────────────
169
+ export const ReplayDLQSchema = z.object({
170
+ key: z.string().min(1).max(200),
171
+ });
172
+
173
+ // ── Config ────────────────────────────────────────────────────────────────
174
+ export const UpdateConfigSchema = z.object({
175
+ rtToken: z.string().optional(),
176
+ telegramToken: z.string().optional(),
177
+ telegramChatIds: z.array(z.string()).optional(),
178
+ whatsappEnabled: z.boolean().optional(),
179
+ bgConsciousnessModel: z.string().optional(),
180
+ opencodeProject: z.string().optional(),
181
+ opencodeFallbackModel: z.string().optional(),
182
+ cursorCli: z.boolean().optional(),
183
+ cursorCliModel: z.string().optional(),
184
+ claudeCode: z.boolean().optional(),
185
+ claudeCodeModel: z.string().optional(),
186
+ geminiCli: z.boolean().optional(),
187
+ geminiCliModel: z.string().optional(),
188
+ });
189
+
190
+ // ── Validation Helper ─────────────────────────────────────────────────────
191
+ export function validate(schema, data) {
192
+ try {
193
+ return { ok: true, data: schema.parse(data) };
194
+ } catch (err) {
195
+ const msg = err.issues?.[0]?.message || err.errors?.[0]?.message || err.message;
196
+ return { ok: false, error: msg };
197
+ }
198
+ }