wizards 0.1.9

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 (308) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +170 -0
  3. package/apps/runtime/dist/agents/architect.js +453 -0
  4. package/apps/runtime/dist/agents/project-assistant.js +354 -0
  5. package/apps/runtime/dist/agents/subscription.js +206 -0
  6. package/apps/runtime/dist/agents/thought.js +21 -0
  7. package/apps/runtime/dist/agents/website.js +197 -0
  8. package/apps/runtime/dist/app.js +153 -0
  9. package/apps/runtime/dist/auth/account.js +121 -0
  10. package/apps/runtime/dist/auth/index.js +329 -0
  11. package/apps/runtime/dist/auth/keys.js +55 -0
  12. package/apps/runtime/dist/auth/local-ticket.js +23 -0
  13. package/apps/runtime/dist/authoring/example.js +73 -0
  14. package/apps/runtime/dist/authoring/guide.js +259 -0
  15. package/apps/runtime/dist/authoring/ops.js +117 -0
  16. package/apps/runtime/dist/cli/autostart.js +134 -0
  17. package/apps/runtime/dist/cli/clients.js +44 -0
  18. package/apps/runtime/dist/cli/environment.js +81 -0
  19. package/apps/runtime/dist/cli/exec.js +45 -0
  20. package/apps/runtime/dist/cli/home.js +74 -0
  21. package/apps/runtime/dist/cli/machine.js +145 -0
  22. package/apps/runtime/dist/cli/main.js +125 -0
  23. package/apps/runtime/dist/cli/setup.js +273 -0
  24. package/apps/runtime/dist/cli/start.js +190 -0
  25. package/apps/runtime/dist/cli/status.js +120 -0
  26. package/apps/runtime/dist/cli/ui.js +12 -0
  27. package/apps/runtime/dist/cli/update.js +58 -0
  28. package/apps/runtime/dist/connectors/builtin.js +70 -0
  29. package/apps/runtime/dist/connectors/external.js +400 -0
  30. package/apps/runtime/dist/connectors/gmail.js +107 -0
  31. package/apps/runtime/dist/connectors/imap.js +217 -0
  32. package/apps/runtime/dist/connectors/index.js +266 -0
  33. package/apps/runtime/dist/connectors/mail.js +72 -0
  34. package/apps/runtime/dist/connectors/outlook.js +90 -0
  35. package/apps/runtime/dist/credits/credits.js +103 -0
  36. package/apps/runtime/dist/credits/estimate.js +132 -0
  37. package/apps/runtime/dist/db/adopt.js +149 -0
  38. package/apps/runtime/dist/db/client.js +146 -0
  39. package/apps/runtime/dist/db/control-schema.js +70 -0
  40. package/apps/runtime/dist/db/migrations/control/0000_last_absorbing_man.sql +47 -0
  41. package/apps/runtime/dist/db/migrations/control/0001_gorgeous_nicolaos.sql +41 -0
  42. package/apps/runtime/dist/db/migrations/control/0002_secret_penance.sql +1 -0
  43. package/apps/runtime/dist/db/migrations/control/0003_nasty_mordo.sql +3 -0
  44. package/apps/runtime/dist/db/migrations/control/0004_breezy_eddie_brock.sql +6 -0
  45. package/apps/runtime/dist/db/migrations/control/meta/0000_snapshot.json +311 -0
  46. package/apps/runtime/dist/db/migrations/control/meta/0001_snapshot.json +599 -0
  47. package/apps/runtime/dist/db/migrations/control/meta/0002_snapshot.json +607 -0
  48. package/apps/runtime/dist/db/migrations/control/meta/0003_snapshot.json +311 -0
  49. package/apps/runtime/dist/db/migrations/control/meta/0004_snapshot.json +350 -0
  50. package/apps/runtime/dist/db/migrations/control/meta/_journal.json +41 -0
  51. package/apps/runtime/dist/db/migrations/tenant/0000_eminent_freak.sql +188 -0
  52. package/apps/runtime/dist/db/migrations/tenant/0001_salty_master_chief.sql +1 -0
  53. package/apps/runtime/dist/db/migrations/tenant/0002_cloudy_firebird.sql +1 -0
  54. package/apps/runtime/dist/db/migrations/tenant/0003_brown_warbound.sql +52 -0
  55. package/apps/runtime/dist/db/migrations/tenant/0004_romantic_nocturne.sql +6 -0
  56. package/apps/runtime/dist/db/migrations/tenant/meta/0000_snapshot.json +1323 -0
  57. package/apps/runtime/dist/db/migrations/tenant/meta/0001_snapshot.json +1330 -0
  58. package/apps/runtime/dist/db/migrations/tenant/meta/0002_snapshot.json +1337 -0
  59. package/apps/runtime/dist/db/migrations/tenant/meta/0003_snapshot.json +1605 -0
  60. package/apps/runtime/dist/db/migrations/tenant/meta/0004_snapshot.json +1645 -0
  61. package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +41 -0
  62. package/apps/runtime/dist/db/schema.js +278 -0
  63. package/apps/runtime/dist/documents/parse.js +230 -0
  64. package/apps/runtime/dist/engenty/connections-external/build-connector.js +193 -0
  65. package/apps/runtime/dist/engenty/connections-external/errors.js +11 -0
  66. package/apps/runtime/dist/engenty/connections-external/import-service.js +270 -0
  67. package/apps/runtime/dist/engenty/connections-external/importer/classify.js +34 -0
  68. package/apps/runtime/dist/engenty/connections-external/importer/map-auth.js +185 -0
  69. package/apps/runtime/dist/engenty/connections-external/importer/normalize-mcp.js +63 -0
  70. package/apps/runtime/dist/engenty/connections-external/importer/normalize-openapi.js +130 -0
  71. package/apps/runtime/dist/engenty/connections-external/invoke/http-invoker.js +143 -0
  72. package/apps/runtime/dist/engenty/connections-external/invoke/mcp-client.js +143 -0
  73. package/apps/runtime/dist/engenty/connections-external/net/guarded-fetch.js +119 -0
  74. package/apps/runtime/dist/engenty/connections-external/oauth-dcr.js +88 -0
  75. package/apps/runtime/dist/engenty/connections-external/registry-client.js +336 -0
  76. package/apps/runtime/dist/engenty/connections-external/registry-source.js +82 -0
  77. package/apps/runtime/dist/engenty/connections-external/types.js +7 -0
  78. package/apps/runtime/dist/engenty/connections-github/connector.js +209 -0
  79. package/apps/runtime/dist/engenty/connections-google/connectors/calendar.js +359 -0
  80. package/apps/runtime/dist/engenty/connections-google/connectors/contacts.js +94 -0
  81. package/apps/runtime/dist/engenty/connections-google/connectors/drive.js +290 -0
  82. package/apps/runtime/dist/engenty/connections-google/connectors/gmail.js +567 -0
  83. package/apps/runtime/dist/engenty/connections-google/definitions.js +11 -0
  84. package/apps/runtime/dist/engenty/connections-google/shared.js +73 -0
  85. package/apps/runtime/dist/engenty/connections-hubspot/connector.js +140 -0
  86. package/apps/runtime/dist/engenty/connections-microsoft/action.js +17 -0
  87. package/apps/runtime/dist/engenty/connections-microsoft/graph.js +114 -0
  88. package/apps/runtime/dist/engenty/connections-microsoft/onedrive.js +286 -0
  89. package/apps/runtime/dist/engenty/connections-microsoft/outlook.js +464 -0
  90. package/apps/runtime/dist/engenty/connections-s3/s3.js +328 -0
  91. package/apps/runtime/dist/engenty/connections-sdk/files-capability.js +86 -0
  92. package/apps/runtime/dist/engenty/connections-sdk/oauth2.js +150 -0
  93. package/apps/runtime/dist/engenty/connections-sdk/registry.js +89 -0
  94. package/apps/runtime/dist/engenty/connections-sdk/storage-capability.js +84 -0
  95. package/apps/runtime/dist/engenty/connections-sdk/types.js +30 -0
  96. package/apps/runtime/dist/engenty/connections-slack/connector.js +360 -0
  97. package/apps/runtime/dist/engenty/csv-import/csv-matrix.js +178 -0
  98. package/apps/runtime/dist/engenty/csv-import/import-sources.js +87 -0
  99. package/apps/runtime/dist/engenty/csv-import/parse-csv.js +129 -0
  100. package/apps/runtime/dist/engenty/csv-import/serialize-csv.js +20 -0
  101. package/apps/runtime/dist/engenty/csv-import/types.js +1 -0
  102. package/apps/runtime/dist/engenty/csv-import/xlsx-workbook.js +159 -0
  103. package/apps/runtime/dist/engenty/doc-converter/interface.js +9 -0
  104. package/apps/runtime/dist/engenty/doc-converter/page-break.js +92 -0
  105. package/apps/runtime/dist/engenty/doc-converter/providers/local/index.js +72 -0
  106. package/apps/runtime/dist/engenty/document-scanner/schemas/classifier.js +30 -0
  107. package/apps/runtime/dist/engenty/document-scanner/schemas/index.js +4 -0
  108. package/apps/runtime/dist/engenty/document-scanner/schemas/invoice.js +73 -0
  109. package/apps/runtime/dist/engenty/document-scanner/schemas/receipt.js +63 -0
  110. package/apps/runtime/dist/engenty/document-scanner/schemas/shared.js +40 -0
  111. package/apps/runtime/dist/engenty/search-index/chunks.js +164 -0
  112. package/apps/runtime/dist/engenty/search-index/contracts.js +9 -0
  113. package/apps/runtime/dist/engenty/shims/connections-sdk.js +39 -0
  114. package/apps/runtime/dist/engenty/shims/plugin-sdk.js +1 -0
  115. package/apps/runtime/dist/engenty/shims/web-ingest.js +3 -0
  116. package/apps/runtime/dist/engenty/web-ingest/ssrf.js +277 -0
  117. package/apps/runtime/dist/engine/asks.js +60 -0
  118. package/apps/runtime/dist/engine/events.js +46 -0
  119. package/apps/runtime/dist/engine/input.js +170 -0
  120. package/apps/runtime/dist/engine/prepare.js +51 -0
  121. package/apps/runtime/dist/engine/requirements.js +124 -0
  122. package/apps/runtime/dist/engine/resources.js +89 -0
  123. package/apps/runtime/dist/engine/runner.js +560 -0
  124. package/apps/runtime/dist/engine/steps.js +598 -0
  125. package/apps/runtime/dist/engine/template.js +183 -0
  126. package/apps/runtime/dist/engine/types.js +3 -0
  127. package/apps/runtime/dist/env.js +224 -0
  128. package/apps/runtime/dist/files/blobs.js +22 -0
  129. package/apps/runtime/dist/files/objects.js +86 -0
  130. package/apps/runtime/dist/files/storage.js +95 -0
  131. package/apps/runtime/dist/geocode.js +58 -0
  132. package/apps/runtime/dist/harness/claude.js +138 -0
  133. package/apps/runtime/dist/harness/codex.js +228 -0
  134. package/apps/runtime/dist/harness/cursor.js +93 -0
  135. package/apps/runtime/dist/harness/env.js +124 -0
  136. package/apps/runtime/dist/harness/gemini.js +126 -0
  137. package/apps/runtime/dist/harness/index.js +99 -0
  138. package/apps/runtime/dist/harness/model.js +267 -0
  139. package/apps/runtime/dist/harness/prompt.js +121 -0
  140. package/apps/runtime/dist/harness/terminal.js +193 -0
  141. package/apps/runtime/dist/harness/types.js +7 -0
  142. package/apps/runtime/dist/index.js +76 -0
  143. package/apps/runtime/dist/limits.js +57 -0
  144. package/apps/runtime/dist/link-preview.js +59 -0
  145. package/apps/runtime/dist/manage.js +151 -0
  146. package/apps/runtime/dist/mcp/auth.js +102 -0
  147. package/apps/runtime/dist/mcp/bridge.js +59 -0
  148. package/apps/runtime/dist/mcp/handler.js +42 -0
  149. package/apps/runtime/dist/mcp/scopes.js +5 -0
  150. package/apps/runtime/dist/mcp/tools.js +401 -0
  151. package/apps/runtime/dist/media/ffmpeg.js +121 -0
  152. package/apps/runtime/dist/media/generate.js +120 -0
  153. package/apps/runtime/dist/media/marking.js +121 -0
  154. package/apps/runtime/dist/media/transcribe.js +30 -0
  155. package/apps/runtime/dist/model-errors.js +3 -0
  156. package/apps/runtime/dist/models.js +489 -0
  157. package/apps/runtime/dist/plugin.js +61 -0
  158. package/apps/runtime/dist/render/chromium.js +85 -0
  159. package/apps/runtime/dist/render/convert.js +161 -0
  160. package/apps/runtime/dist/render/downloads.js +277 -0
  161. package/apps/runtime/dist/render/guard.js +25 -0
  162. package/apps/runtime/dist/routes/api.js +74 -0
  163. package/apps/runtime/dist/routes/delivery.js +52 -0
  164. package/apps/runtime/dist/routes/internal.js +49 -0
  165. package/apps/runtime/dist/routes/marketplace.js +36 -0
  166. package/apps/runtime/dist/routes/runs.js +805 -0
  167. package/apps/runtime/dist/routes/studio.js +702 -0
  168. package/apps/runtime/dist/routes/wizard-stream.js +30 -0
  169. package/apps/runtime/dist/running.js +39 -0
  170. package/apps/runtime/dist/sandbox/agentos.js +191 -0
  171. package/apps/runtime/dist/sandbox/docker.js +77 -0
  172. package/apps/runtime/dist/sandbox/index.js +30 -0
  173. package/apps/runtime/dist/sandbox/types.js +1 -0
  174. package/apps/runtime/dist/secrets/crypto.js +28 -0
  175. package/apps/runtime/dist/secrets/signing.js +34 -0
  176. package/apps/runtime/dist/secrets/vault.js +59 -0
  177. package/apps/runtime/dist/services/architect.js +92 -0
  178. package/apps/runtime/dist/services/brand.js +20 -0
  179. package/apps/runtime/dist/services/cloud.js +52 -0
  180. package/apps/runtime/dist/services/draft-events.js +27 -0
  181. package/apps/runtime/dist/services/errors.js +24 -0
  182. package/apps/runtime/dist/services/files.js +129 -0
  183. package/apps/runtime/dist/services/links.js +16 -0
  184. package/apps/runtime/dist/services/marketplace.js +292 -0
  185. package/apps/runtime/dist/services/package.js +202 -0
  186. package/apps/runtime/dist/services/profile.js +23 -0
  187. package/apps/runtime/dist/services/project-files.js +266 -0
  188. package/apps/runtime/dist/services/project-index.js +109 -0
  189. package/apps/runtime/dist/services/projects.js +181 -0
  190. package/apps/runtime/dist/services/runs.js +224 -0
  191. package/apps/runtime/dist/services/shares.js +129 -0
  192. package/apps/runtime/dist/services/widgets.js +34 -0
  193. package/apps/runtime/dist/services/wizards.js +342 -0
  194. package/apps/runtime/dist/settings.js +16 -0
  195. package/apps/runtime/dist/store/index.js +279 -0
  196. package/apps/runtime/dist/tenants/control.js +106 -0
  197. package/apps/runtime/dist/tenants/tenant.js +26 -0
  198. package/apps/runtime/dist/tools/browser.js +380 -0
  199. package/apps/runtime/dist/tools/connector.js +204 -0
  200. package/apps/runtime/dist/tools/index.js +234 -0
  201. package/apps/runtime/dist/tools/mail.js +104 -0
  202. package/apps/runtime/dist/tools/net-guard.js +59 -0
  203. package/apps/runtime/dist/tools/project.js +53 -0
  204. package/apps/runtime/dist/tools/shared.js +42 -0
  205. package/apps/runtime/dist/tools/store.js +238 -0
  206. package/apps/runtime/dist/update.js +118 -0
  207. package/apps/runtime/dist/widgets/bundle.js +99 -0
  208. package/apps/runtime/dist/widgets/render.js +312 -0
  209. package/apps/runtime/dist/widgets/runtime.js +84 -0
  210. package/apps/runtime/dist/widgets/script.js +30 -0
  211. package/apps/runtime/dist/widgets/step.js +192 -0
  212. package/apps/web/dist/ai-labels/ai-black.svg +20 -0
  213. package/apps/web/dist/ai-labels/ai-generated-black.svg +31 -0
  214. package/apps/web/dist/ai-labels/ai-generated-white.svg +32 -0
  215. package/apps/web/dist/ai-labels/ai-modified-black.svg +30 -0
  216. package/apps/web/dist/ai-labels/ai-modified-white.svg +31 -0
  217. package/apps/web/dist/ai-labels/ai-white.svg +21 -0
  218. package/apps/web/dist/assets/ChangelogDialog-O00vEnbm.js +1 -0
  219. package/apps/web/dist/assets/ChatPanel-CH9ry0Hp.js +1 -0
  220. package/apps/web/dist/assets/CreditsDialog-DRzecwUw.js +1 -0
  221. package/apps/web/dist/assets/EditorPage-BRnBf02Q.js +2 -0
  222. package/apps/web/dist/assets/FlowPreview-DDOj8IAH.js +1 -0
  223. package/apps/web/dist/assets/HomePage-CAGTTG3N.js +1 -0
  224. package/apps/web/dist/assets/ImportWizard-0RRrAnX3.js +1 -0
  225. package/apps/web/dist/assets/LocalRuntime-BrP-ENHg.css +1 -0
  226. package/apps/web/dist/assets/LocalRuntime-C-TBYrzs.js +16 -0
  227. package/apps/web/dist/assets/NewWizardPage-kI90h3_9.js +2 -0
  228. package/apps/web/dist/assets/PublicRunner-CKRDeIVd.js +1 -0
  229. package/apps/web/dist/assets/RunnerView-Rc4H8wYH.js +2 -0
  230. package/apps/web/dist/assets/SettingsPage-CESMuZxq.js +2 -0
  231. package/apps/web/dist/assets/SetupPage-D1rrYU14.js +1 -0
  232. package/apps/web/dist/assets/SharePage-COTAmOgh.js +1 -0
  233. package/apps/web/dist/assets/api-BY28cjGY.js +10 -0
  234. package/apps/web/dist/assets/arrow-left-UpjOBfQp.js +1 -0
  235. package/apps/web/dist/assets/arrow-right-CucQycEy.js +1 -0
  236. package/apps/web/dist/assets/changelog-p3wsaDDS.js +1 -0
  237. package/apps/web/dist/assets/check-Bjx41xbB.js +1 -0
  238. package/apps/web/dist/assets/createLucideIcon-DBu2Ovxj.js +1 -0
  239. package/apps/web/dist/assets/definition-cy7uvY5L.js +55 -0
  240. package/apps/web/dist/assets/engenty-CWIFpgKq.js +1 -0
  241. package/apps/web/dist/assets/esm-D1OEhjTK.js +7 -0
  242. package/apps/web/dist/assets/external-link-CRg6DVdO.js +1 -0
  243. package/apps/web/dist/assets/fluffy-engenty-CJ3wCQgT.js +456 -0
  244. package/apps/web/dist/assets/index-CQ_q9iMO.css +1 -0
  245. package/apps/web/dist/assets/index-D0gxh90t.js +10 -0
  246. package/apps/web/dist/assets/jsx-runtime-Dk72oS4N.js +1 -0
  247. package/apps/web/dist/assets/meta-BbSdtTUa.js +1 -0
  248. package/apps/web/dist/assets/mic-CeqQ89Dj.js +1 -0
  249. package/apps/web/dist/assets/oss-credits-cPuZUFHD.js +1 -0
  250. package/apps/web/dist/assets/outputs-D6pxH6ve.js +69 -0
  251. package/apps/web/dist/assets/plus-HA210wXA.js +1 -0
  252. package/apps/web/dist/assets/preload-helper-CNEI7N0N.js +1 -0
  253. package/apps/web/dist/assets/qr-scanner-worker.min-CJaXo3yu.js +98 -0
  254. package/apps/web/dist/assets/qr-scanner.min-C_UI0gH-.js +1 -0
  255. package/apps/web/dist/assets/type-BzYgVWKL.js +1 -0
  256. package/apps/web/dist/assets/ui-Ddwl35A6.js +1 -0
  257. package/apps/web/dist/assets/upload-DsgLfQ3U.js +1 -0
  258. package/apps/web/dist/assets/video-Dk1fuTDl.js +1 -0
  259. package/apps/web/dist/assets/wand-sparkles-Bf97fs8T.js +1 -0
  260. package/apps/web/dist/embed.js +102 -0
  261. package/apps/web/dist/favicon.svg +1 -0
  262. package/apps/web/dist/icons/apple-touch-icon.png +0 -0
  263. package/apps/web/dist/icons/icon-192.png +0 -0
  264. package/apps/web/dist/icons/icon-512.png +0 -0
  265. package/apps/web/dist/icons/icon-maskable-512.png +0 -0
  266. package/apps/web/dist/index.html +46 -0
  267. package/apps/web/dist/install.sh +532 -0
  268. package/apps/web/dist/manifest.webmanifest +20 -0
  269. package/apps/web/dist/sw.js +15 -0
  270. package/bin/wizards.mjs +13 -0
  271. package/node_modules/@engenty-wizards/shared/dist/connectors.d.ts +77 -0
  272. package/node_modules/@engenty-wizards/shared/dist/connectors.js +2 -0
  273. package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +1415 -0
  274. package/node_modules/@engenty-wizards/shared/dist/definition.js +762 -0
  275. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.d.ts +5 -0
  276. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.js +132 -0
  277. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.d.ts +11 -0
  278. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.js +209 -0
  279. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.d.ts +532 -0
  280. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.js +216 -0
  281. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.d.ts +3 -0
  282. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.js +3 -0
  283. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.d.ts +14 -0
  284. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.js +166 -0
  285. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.d.ts +33 -0
  286. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.js +99 -0
  287. package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.d.ts +35 -0
  288. package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.js +86 -0
  289. package/node_modules/@engenty-wizards/shared/dist/marketplace-search.d.ts +20 -0
  290. package/node_modules/@engenty-wizards/shared/dist/marketplace-search.js +133 -0
  291. package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +298 -0
  292. package/node_modules/@engenty-wizards/shared/dist/marketplace.js +211 -0
  293. package/node_modules/@engenty-wizards/shared/dist/merge.d.ts +7 -0
  294. package/node_modules/@engenty-wizards/shared/dist/merge.js +40 -0
  295. package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +74 -0
  296. package/node_modules/@engenty-wizards/shared/dist/projects.js +96 -0
  297. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +219 -0
  298. package/node_modules/@engenty-wizards/shared/dist/run.js +1 -0
  299. package/node_modules/@engenty-wizards/shared/dist/store.d.ts +100 -0
  300. package/node_modules/@engenty-wizards/shared/dist/store.js +44 -0
  301. package/node_modules/@engenty-wizards/shared/dist/workspace.d.ts +16 -0
  302. package/node_modules/@engenty-wizards/shared/dist/workspace.js +51 -0
  303. package/node_modules/@engenty-wizards/shared/package.json +10 -0
  304. package/package.json +62 -0
  305. package/plugin/.claude-plugin/plugin.json +9 -0
  306. package/plugin/.mcp.json +8 -0
  307. package/plugin/commands/wizard.md +8 -0
  308. package/plugin/skills/build-wizard/SKILL.md +27 -0
@@ -0,0 +1,30 @@
1
+ import { Hono } from "hono";
2
+ import { streamSSE } from "hono/streaming";
3
+ import { subscribeDraft } from "../services/draft-events.js";
4
+ import { ownedWizard } from "../services/wizards.js";
5
+ /**
6
+ * Live changes to a wizard the editor has open — from an MCP client, the architect or another
7
+ * tab. The editor refetches on `change`; `hello` carries the revision to catch up after a gap.
8
+ */
9
+ export const wizardStream = new Hono().get("/:id/stream", async (c) => {
10
+ const w = await ownedWizard(c.get("user").id, c.req.param("id"));
11
+ return streamSSE(c, async (stream) => {
12
+ let closed = false;
13
+ const unsubscribe = subscribeDraft(w.id, (change) => {
14
+ if (!closed) {
15
+ void stream.writeSSE({ event: "change", data: JSON.stringify(change) });
16
+ }
17
+ });
18
+ stream.onAbort(() => {
19
+ closed = true;
20
+ unsubscribe();
21
+ });
22
+ await stream.writeSSE({ event: "hello", data: JSON.stringify({ revision: w.revision }) });
23
+ while (!closed) {
24
+ await stream.sleep(20_000);
25
+ if (!closed) {
26
+ await stream.writeSSE({ event: "ping", data: "" });
27
+ }
28
+ }
29
+ });
30
+ });
@@ -0,0 +1,39 @@
1
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ const FILE = "running.json";
4
+ export function writeRunning(dataDir, running) {
5
+ mkdirSync(dataDir, { recursive: true });
6
+ writeFileSync(join(dataDir, FILE), `${JSON.stringify(running, null, 2)}\n`, { mode: 0o600 });
7
+ }
8
+ function alive(pid) {
9
+ try {
10
+ process.kill(pid, 0);
11
+ return true;
12
+ }
13
+ catch (error) {
14
+ // EPERM: the process exists and belongs to somebody else.
15
+ return error.code === "EPERM";
16
+ }
17
+ }
18
+ /** The runtime that holds this data folder, if its process still exists. */
19
+ export function readRunning(dataDir) {
20
+ try {
21
+ const running = JSON.parse(readFileSync(join(dataDir, FILE), "utf8"));
22
+ return Number.isInteger(running.pid) && alive(running.pid) ? running : null;
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ }
28
+ /** Takes the note away, unless another runtime wrote it meanwhile. */
29
+ export function clearRunning(dataDir, pid = process.pid) {
30
+ try {
31
+ const running = JSON.parse(readFileSync(join(dataDir, FILE), "utf8"));
32
+ if (running.pid === pid) {
33
+ rmSync(join(dataDir, FILE), { force: true });
34
+ }
35
+ }
36
+ catch {
37
+ // nothing there
38
+ }
39
+ }
@@ -0,0 +1,191 @@
1
+ import { posix } from "node:path";
2
+ const WORKDIR = "/workspace";
3
+ const EXEC_TIMEOUT_MS = 120_000;
4
+ const INSTALL_TIMEOUT_MS = 300_000;
5
+ /** The only hosts the VM reaches: where npm and Python packages come from. */
6
+ const REGISTRIES = ["registry.npmjs.org", "pypi.org", "files.pythonhosted.org"];
7
+ /** python at the start of a command, after an operator, or behind VAR=value assignments. */
8
+ const PYTHON_IN_SHELL = /(?:^|[;&|(\n`]|\$\()\s*(?:\w+=\S*\s+)*python3?(?:\.\d+)?(?=\s|$)/;
9
+ /**
10
+ * What an agentOS VM runs: a WASM userland (a POSIX shell and coreutils, no native binaries),
11
+ * Node in a V8 isolate and Python as Pyodide. Python started from the shell never returns, so
12
+ * it is offered as its own tool; of the compiled wheels only numpy loads reliably.
13
+ */
14
+ export const AGENTOS_CAPABILITIES = {
15
+ commands: [
16
+ "sh",
17
+ "ls",
18
+ "cat",
19
+ "head",
20
+ "tail",
21
+ "sort",
22
+ "wc",
23
+ "grep",
24
+ "sed",
25
+ "awk",
26
+ "find",
27
+ "diff",
28
+ "tar",
29
+ "gzip",
30
+ "base64",
31
+ "node",
32
+ "npm",
33
+ ],
34
+ python: "tool",
35
+ pythonPackages: "Python 3.13 standard library; `packages` installs numpy and pure-Python packages from PyPI such as openpyxl or pypdf — no pandas, pillow or matplotlib",
36
+ network: "none, except the npm and PyPI registries",
37
+ uses: "calculations or file conversion",
38
+ workdir: WORKDIR,
39
+ };
40
+ let runtime = null;
41
+ /** One native sidecar process hosts the VM of every run; the first sandbox starts it. */
42
+ function agentOsRuntime() {
43
+ runtime ??= (async () => {
44
+ const [core, runtimeCore] = await Promise.all([
45
+ import("@rivet-dev/agentos-core"),
46
+ import("@rivet-dev/agentos-runtime-core"),
47
+ ]).catch((err) => {
48
+ throw new Error(`SANDBOX=agentos needs @rivet-dev/agentos-core in this build: ${err.message}`);
49
+ });
50
+ keepRuntimeCommands(runtimeCore.SidecarProcess);
51
+ return { AgentOs: core.AgentOs, sidecar: await core.AgentOs.createSidecar() };
52
+ })().catch((err) => {
53
+ runtime = null;
54
+ throw err;
55
+ });
56
+ return runtime;
57
+ }
58
+ const patched = new WeakSet();
59
+ /**
60
+ * agentOS registers `python` as a kernel command when it creates a VM and leaves it out when
61
+ * it configures that VM; naming the runtime commands there again is what makes Python start.
62
+ */
63
+ function keepRuntimeCommands(SidecarProcess) {
64
+ const proto = SidecarProcess.prototype;
65
+ if (patched.has(proto)) {
66
+ return;
67
+ }
68
+ patched.add(proto);
69
+ const configureVm = proto.configureVm;
70
+ proto.configureVm = function (session, vm, options) {
71
+ const commands = [
72
+ "node",
73
+ "npm",
74
+ "npx",
75
+ "python",
76
+ "python3",
77
+ ...(options?.bootstrapCommands ?? []),
78
+ ];
79
+ return configureVm.call(this, session, vm, {
80
+ ...options,
81
+ bootstrapCommands: [...new Set(commands)],
82
+ });
83
+ };
84
+ }
85
+ /** Stops the sidecar process. The next sandbox starts a new one. */
86
+ export async function stopAgentOs() {
87
+ const current = runtime;
88
+ runtime = null;
89
+ await (await current?.catch(() => null))?.sidecar.dispose();
90
+ }
91
+ function toResult(result, python = false) {
92
+ const stdout = result.stdout ?? "";
93
+ let stderr = result.stderr ?? "";
94
+ if (python) {
95
+ // A Python traceback arrives with the interpreter's own JavaScript stack behind it.
96
+ stderr = stderr.replace(/\n\s+at [\s\S]*$/, "\n");
97
+ }
98
+ if (result.outcome === "timed_out") {
99
+ stderr = `${stderr}\ntimed out`.trim();
100
+ }
101
+ else if (result.outcome !== "succeeded" && !stdout && !stderr) {
102
+ stderr = result.error.message;
103
+ }
104
+ return { exitCode: result.exitCode ?? (result.outcome === "succeeded" ? 0 : 1), stdout, stderr };
105
+ }
106
+ /** One agentOS VM per run, inside the server process's sidecar: no Docker, no container. */
107
+ export class AgentOsSandbox {
108
+ capabilities = AGENTOS_CAPABILITIES;
109
+ machine = null;
110
+ installed = new Set();
111
+ vm() {
112
+ this.machine ??= (async () => {
113
+ const { AgentOs, sidecar } = await agentOsRuntime();
114
+ return AgentOs.create({
115
+ sidecar: { kind: "explicit", handle: sidecar },
116
+ // Anything not listed is refused, the host's own loopback included.
117
+ permissions: {
118
+ fs: "allow",
119
+ childProcess: "allow",
120
+ process: "allow",
121
+ env: "allow",
122
+ network: {
123
+ default: "deny",
124
+ rules: [
125
+ {
126
+ mode: "allow",
127
+ operations: ["*"],
128
+ patterns: REGISTRIES.flatMap((host) => [`dns://${host}`, `tcp://${host}:443`]),
129
+ },
130
+ ],
131
+ },
132
+ },
133
+ });
134
+ })();
135
+ return this.machine;
136
+ }
137
+ async exec(command, options = {}) {
138
+ if (PYTHON_IN_SHELL.test(command)) {
139
+ return {
140
+ exitCode: 127,
141
+ stdout: "",
142
+ stderr: "python is not a shell command in this sandbox; run Python with run_python.",
143
+ };
144
+ }
145
+ const vm = await this.vm();
146
+ return toResult(await vm.process.exec(command, {
147
+ cwd: options.cwd ?? WORKDIR,
148
+ env: options.env,
149
+ timeoutMs: options.timeoutMs ?? EXEC_TIMEOUT_MS,
150
+ output: { capture: "all" },
151
+ }));
152
+ }
153
+ async runPython(source, options = {}) {
154
+ const vm = await this.vm();
155
+ const missing = (options.packages ?? []).filter((name) => !this.installed.has(name));
156
+ if (missing.length) {
157
+ const install = toResult(await vm.python.install(missing, {
158
+ timeoutMs: INSTALL_TIMEOUT_MS,
159
+ output: { capture: "all" },
160
+ }));
161
+ // A wheel that is listed but not shipped "installs" and then fails to load.
162
+ if (install.exitCode !== 0 || /Failed to load/.test(install.stdout)) {
163
+ return { ...install, exitCode: install.exitCode || 1 };
164
+ }
165
+ for (const name of missing) {
166
+ this.installed.add(name);
167
+ }
168
+ }
169
+ return toResult(await vm.python.execute(source, {
170
+ // Python starts in its HOME whatever cwd says, and only /workspace is shared with the shell.
171
+ env: { ...options.env, HOME: WORKDIR },
172
+ timeoutMs: options.timeoutMs ?? EXEC_TIMEOUT_MS,
173
+ output: { capture: "all" },
174
+ }), true);
175
+ }
176
+ async writeFile(path, data) {
177
+ const vm = await this.vm();
178
+ const target = posix.resolve(WORKDIR, path);
179
+ await vm.filesystem.mkdir(posix.dirname(target), { recursive: true });
180
+ await vm.filesystem.writeFile(target, data);
181
+ }
182
+ async readFile(path) {
183
+ const vm = await this.vm();
184
+ return vm.filesystem.readFile(posix.resolve(WORKDIR, path));
185
+ }
186
+ async destroy() {
187
+ const machine = this.machine;
188
+ this.machine = null;
189
+ await (await machine?.catch(() => null))?.dispose();
190
+ }
191
+ }
@@ -0,0 +1,77 @@
1
+ import { DockerSandbox } from "@mastra/docker";
2
+ /** The image's own working directory, the one its unprivileged user may write to. */
3
+ const WORKDIR = "/sandbox";
4
+ /** Bytes per append when a file is written through the shell; stays far below ARG_MAX. */
5
+ const WRITE_CHUNK = 48 * 1024;
6
+ /** What the engenty-sandbox image carries. The container sits on Docker's default bridge. */
7
+ export const DOCKER_CAPABILITIES = {
8
+ commands: ["bash", "python3", "pip", "uv", "node", "npm", "bun", "jq", "curl", "git", "unzip"],
9
+ python: "shell",
10
+ pythonPackages: "install what you need with pip or uv",
11
+ network: "open internet",
12
+ uses: "calculations, charts or file conversion",
13
+ workdir: WORKDIR,
14
+ };
15
+ const quote = (value) => `'${value.replaceAll("'", `'\\''`)}'`;
16
+ /** One container per run through the host's Docker. */
17
+ export class DockerRunSandbox {
18
+ runId;
19
+ image;
20
+ capabilities = DOCKER_CAPABILITIES;
21
+ container = null;
22
+ constructor(runId, image) {
23
+ this.runId = runId;
24
+ this.image = image;
25
+ }
26
+ start() {
27
+ this.container ??= (async () => {
28
+ const sandbox = new DockerSandbox({
29
+ id: `wizards-${this.runId.toLowerCase()}`,
30
+ image: this.image,
31
+ memory: 1024 * 1024 * 1024,
32
+ pidsLimit: 256,
33
+ capDrop: ["ALL"],
34
+ securityOpt: ["no-new-privileges"],
35
+ workingDir: WORKDIR,
36
+ labels: { "engenty-wizards.run": this.runId },
37
+ });
38
+ await sandbox.start();
39
+ return sandbox;
40
+ })();
41
+ return this.container;
42
+ }
43
+ async exec(command, options = {}) {
44
+ const sandbox = await this.start();
45
+ const result = await sandbox.executeCommand(command, [], {
46
+ timeout: options.timeoutMs,
47
+ cwd: options.cwd,
48
+ env: options.env,
49
+ });
50
+ return { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr };
51
+ }
52
+ async writeFile(path, data) {
53
+ const target = quote(path);
54
+ const bytes = typeof data === "string" ? Buffer.from(data, "utf8") : Buffer.from(data);
55
+ await this.must(`mkdir -p "$(dirname ${target})" && : > ${target}`);
56
+ for (let at = 0; at < bytes.length; at += WRITE_CHUNK) {
57
+ const chunk = bytes.subarray(at, at + WRITE_CHUNK).toString("base64");
58
+ await this.must(`printf %s ${chunk} | base64 -d >> ${target}`);
59
+ }
60
+ }
61
+ async readFile(path) {
62
+ const result = await this.must(`base64 -w0 ${quote(path)}`);
63
+ return Buffer.from(result.stdout.trim(), "base64");
64
+ }
65
+ async must(command) {
66
+ const result = await this.exec(command);
67
+ if (result.exitCode !== 0) {
68
+ throw new Error(result.stderr.trim() || `exit code ${result.exitCode}`);
69
+ }
70
+ return result;
71
+ }
72
+ async destroy() {
73
+ const container = this.container;
74
+ this.container = null;
75
+ await (await container?.catch(() => null))?.destroy();
76
+ }
77
+ }
@@ -0,0 +1,30 @@
1
+ import { env } from "../env.js";
2
+ import { AGENTOS_CAPABILITIES, AgentOsSandbox } from "./agentos.js";
3
+ import { DOCKER_CAPABILITIES, DockerRunSandbox } from "./docker.js";
4
+ /** What this server's sandbox runs, or null when it is switched off. */
5
+ export function sandboxCapabilities() {
6
+ if (env.sandbox === "off") {
7
+ return null;
8
+ }
9
+ return env.sandbox === "agentos" ? AGENTOS_CAPABILITIES : DOCKER_CAPABILITIES;
10
+ }
11
+ export function createSandbox(runId) {
12
+ if (env.sandbox === "off") {
13
+ throw new Error("The sandbox is switched off on this server.");
14
+ }
15
+ return env.sandbox === "agentos"
16
+ ? new AgentOsSandbox()
17
+ : new DockerRunSandbox(runId, env.sandboxImage);
18
+ }
19
+ export function shellToolDescription(caps) {
20
+ const python = caps.python === "tool" ? " Python is not a shell command here: use run_python." : "";
21
+ return `Run a shell command in a private Linux sandbox (${caps.commands.join(", ")}). Working directory ${caps.workdir}. Network: ${caps.network}.${python}`;
22
+ }
23
+ export function pythonToolDescription(caps) {
24
+ return `Run Python code in the sandbox (${caps.pythonPackages}). Working directory ${caps.workdir}, shared with run_command and export_file.`;
25
+ }
26
+ /** The authoring guide's line about the sandbox tool. */
27
+ export function sandboxGuideLine() {
28
+ const uses = (sandboxCapabilities() ?? DOCKER_CAPABILITIES).uses;
29
+ return `- The sandbox runs code (python/node) for ${uses}; export_file hands files to the person.`;
30
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import { createCipheriv, createDecipheriv, hkdfSync, randomBytes } from "node:crypto";
2
+ import { env } from "../env.js";
3
+ /** AES-256-GCM for what the store keeps secret (account tokens, passwords, browser sessions). */
4
+ const key = Buffer.from(hkdfSync("sha256", env.storeKey || env.authSecret, "engenty-wizards", "store-secrets", 32));
5
+ export function seal(value) {
6
+ const iv = randomBytes(12);
7
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
8
+ const body = Buffer.concat([cipher.update(JSON.stringify(value), "utf8"), cipher.final()]);
9
+ return `v1.${iv.toString("base64url")}.${cipher.getAuthTag().toString("base64url")}.${body.toString("base64url")}`;
10
+ }
11
+ export function unseal(sealed) {
12
+ const [version, iv, tag, body] = sealed.split(".");
13
+ if (version !== "v1" || !iv || !tag || !body) {
14
+ return null;
15
+ }
16
+ try {
17
+ const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(iv, "base64url"));
18
+ decipher.setAuthTag(Buffer.from(tag, "base64url"));
19
+ const plain = Buffer.concat([
20
+ decipher.update(Buffer.from(body, "base64url")),
21
+ decipher.final(),
22
+ ]);
23
+ return JSON.parse(plain.toString("utf8"));
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
@@ -0,0 +1,34 @@
1
+ import { createHmac, timingSafeEqual } from "node:crypto";
2
+ import { env } from "../env.js";
3
+ /** The query the signature covers: everything but exp/sig, sorted, so parameter order never matters. */
4
+ function payload(url, exp) {
5
+ const params = [...url.searchParams.entries()]
6
+ .filter(([k]) => k !== "exp" && k !== "sig")
7
+ .sort(([a], [b]) => a.localeCompare(b));
8
+ return `${url.pathname}?${new URLSearchParams(params)}|${exp}`;
9
+ }
10
+ function sign(data) {
11
+ return createHmac("sha256", env.authSecret).update(data).digest("base64url");
12
+ }
13
+ /**
14
+ * A link that opens one GET path without a session — for clients that have no studio cookie,
15
+ * such as an MCP client handing a download to the admin.
16
+ */
17
+ export function signedUrl(path, ttlSeconds = 3600) {
18
+ const url = new URL(path, env.appUrl);
19
+ const exp = String(Math.floor(Date.now() / 1000) + ttlSeconds);
20
+ url.searchParams.set("exp", exp);
21
+ url.searchParams.set("sig", sign(payload(url, exp)));
22
+ return url.toString();
23
+ }
24
+ export function verifySignedUrl(href) {
25
+ const url = new URL(href, env.appUrl);
26
+ const exp = url.searchParams.get("exp");
27
+ const sig = url.searchParams.get("sig");
28
+ if (!exp || !sig || Number(exp) * 1000 < Date.now()) {
29
+ return false;
30
+ }
31
+ const expected = Buffer.from(sign(payload(url, exp)));
32
+ const given = Buffer.from(sig);
33
+ return expected.length === given.length && timingSafeEqual(expected, given);
34
+ }
@@ -0,0 +1,59 @@
1
+ import { execFile } from "node:child_process";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { promisify } from "node:util";
5
+ import { env } from "../env.js";
6
+ import { seal, unseal } from "./crypto.js";
7
+ const run = promisify(execFile);
8
+ /**
9
+ * Secrets of a runtime that runs alone: the keys the person entered and the linked account's
10
+ * refresh token. In the desktop app (`SECRETS=keychain`) they live in the macOS Keychain, never
11
+ * in the database; elsewhere in an encrypted file in the data folder.
12
+ */
13
+ const SERVICE = "engenty-wizards";
14
+ const keychain = process.env.SECRETS === "keychain" && process.platform === "darwin";
15
+ const file = join(env.dataDir, "vault");
16
+ function readFileVault() {
17
+ if (!existsSync(file)) {
18
+ return {};
19
+ }
20
+ return unseal(readFileSync(file, "utf8").trim()) ?? {};
21
+ }
22
+ function writeFileVault(all) {
23
+ writeFileSync(file, seal(all), { mode: 0o600 });
24
+ }
25
+ export async function vaultGet(name) {
26
+ if (!keychain) {
27
+ return readFileVault()[name] ?? null;
28
+ }
29
+ try {
30
+ const { stdout } = await run("security", [
31
+ "find-generic-password",
32
+ "-s",
33
+ SERVICE,
34
+ "-a",
35
+ name,
36
+ "-w",
37
+ ]);
38
+ return stdout.replace(/\n$/, "") || null;
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ }
44
+ export async function vaultSet(name, value) {
45
+ if (!keychain) {
46
+ writeFileVault({ ...readFileVault(), [name]: value });
47
+ return;
48
+ }
49
+ await run("security", ["add-generic-password", "-U", "-s", SERVICE, "-a", name, "-w", value]);
50
+ }
51
+ export async function vaultDelete(name) {
52
+ if (!keychain) {
53
+ const all = readFileVault();
54
+ delete all[name];
55
+ writeFileVault(all);
56
+ return;
57
+ }
58
+ await run("security", ["delete-generic-password", "-s", SERVICE, "-a", name]).catch(() => undefined);
59
+ }
@@ -0,0 +1,92 @@
1
+ import { eq } from "drizzle-orm";
2
+ import { runArchitect } from "../agents/architect.js";
3
+ import { chatEngine, subscriptionTurn } from "../agents/subscription.js";
4
+ import { listConnectors } from "../connectors/external.js";
5
+ import { db, schema } from "../db/client.js";
6
+ import { managed } from "../manage.js";
7
+ import { hasTextModel } from "../models.js";
8
+ import { draftFiles, readFile } from "./files.js";
9
+ import { ownedProject } from "./projects.js";
10
+ import { addMessage, draftIssues, ownedWizard, wizardMessages } from "./wizards.js";
11
+ /** The formats a model can look at; an SVG it reads as text. */
12
+ const VISIBLE = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
13
+ /** A picture larger than this would pass the model's limit once base64-encoded. */
14
+ const VISIBLE_BYTES = 3_700_000;
15
+ /** The pictures a message attaches ("Angehängt: a.png, b.pdf"), read from the workspace. */
16
+ async function attachedImages(userId, wizardId, message) {
17
+ const names = message.match(/(?:^|\n\n)(?:Angehängt|Attached): ([^\n]+)$/)?.[1]?.split(", ");
18
+ const images = [];
19
+ for (const name of names?.slice(0, 8) ?? []) {
20
+ try {
21
+ const { file, data } = await readFile(userId, wizardId, name);
22
+ if (VISIBLE.has(file.mime) && data.byteLength <= VISIBLE_BYTES) {
23
+ images.push({ path: file.path, mime: file.mime, data });
24
+ }
25
+ }
26
+ catch {
27
+ // A name that is no workspace file stays text.
28
+ }
29
+ }
30
+ return images;
31
+ }
32
+ /** One studio chat turn: the architect reads the draft, its workspace and the thread, and changes them with tools. */
33
+ export async function architectTurn(userId, wizardId, message, io) {
34
+ const w = await ownedWizard(userId, wizardId);
35
+ const history = await wizardMessages(w.id);
36
+ const project = await ownedProject(userId, w.projectId);
37
+ const images = await attachedImages(userId, w.id, message);
38
+ const userMessageId = await addMessage(w.id, { role: "user", content: message });
39
+ try {
40
+ // A runtime that runs alone can answer the chat on the admin's own Claude subscription.
41
+ const engine = managed ? "models" : await chatEngine(await hasTextModel());
42
+ const result = engine === "claude"
43
+ ? {
44
+ ...(await subscriptionTurn({
45
+ wizardId: w.id,
46
+ message,
47
+ signal: io.signal,
48
+ onText: io.onText,
49
+ onActivity: io.onActivity,
50
+ onThought: io.onThought,
51
+ onBuilding: io.onBuilding,
52
+ images,
53
+ })),
54
+ unfinished: false,
55
+ }
56
+ : await runArchitect({
57
+ userId,
58
+ wizardId: w.id,
59
+ message,
60
+ history: history.map((m) => ({ role: m.role, content: m.content })),
61
+ mcpServers: project.mcpServers.map((s) => ({ id: s.id, name: s.name })),
62
+ connectors: await listConnectors(project.id),
63
+ signal: io.signal,
64
+ onText: io.onText,
65
+ onActivity: io.onActivity,
66
+ onThought: io.onThought,
67
+ onBuilding: io.onBuilding,
68
+ images,
69
+ });
70
+ const reply = result.unfinished
71
+ ? `${result.reply ? `${result.reply}\n\n` : ""}Ich bin noch nicht ganz fertig geworden. Schreib „weiter“, dann mache ich genau dort weiter.`
72
+ : result.reply ||
73
+ (result.changed
74
+ ? "Erledigt."
75
+ : "Das habe ich nicht verstanden – magst du es anders formulieren?");
76
+ await addMessage(w.id, { role: "assistant", content: reply, changed: result.changed });
77
+ const after = await ownedWizard(userId, wizardId);
78
+ return {
79
+ reply,
80
+ changed: result.changed,
81
+ draft: after.draft,
82
+ revision: after.revision,
83
+ files: await draftFiles(after.id),
84
+ issues: await draftIssues(after),
85
+ };
86
+ }
87
+ catch (err) {
88
+ // An unanswered turn would repeat in the next turn's history.
89
+ await db.delete(schema.wizardMessage).where(eq(schema.wizardMessage.id, userMessageId));
90
+ throw err;
91
+ }
92
+ }
@@ -0,0 +1,20 @@
1
+ import { and, asc, eq } from "drizzle-orm";
2
+ import { db, schema } from "../db/client.js";
3
+ /** The project's first logo: the one end users, documents and widgets get. */
4
+ export async function mainLogoId(projectId) {
5
+ const logo = await db.query.projectFile.findFirst({
6
+ where: and(eq(schema.projectFile.projectId, projectId), eq(schema.projectFile.kind, "logo")),
7
+ orderBy: [asc(schema.projectFile.position), asc(schema.projectFile.createdAt)],
8
+ });
9
+ return logo?.id ?? null;
10
+ }
11
+ /** What end users see of the project: its title, accent and logo — never the project name. */
12
+ export async function brandView(projectId) {
13
+ const p = await db.query.project.findFirst({ where: eq(schema.project.id, projectId) });
14
+ const logo = p ? await mainLogoId(p.id) : null;
15
+ return {
16
+ name: p?.brand.name ?? "",
17
+ accent: p?.brand.colors?.[0]?.value ?? null,
18
+ logoUrl: logo ? `/api/public/logos/${logo}` : null,
19
+ };
20
+ }
@@ -0,0 +1,52 @@
1
+ import { accountToken } from "../auth/account.js";
2
+ import { env } from "../env.js";
3
+ import { getBlob } from "../files/blobs.js";
4
+ import { readSetting, writeSetting } from "../settings.js";
5
+ import { ServiceError } from "./errors.js";
6
+ import { draftFiles } from "./files.js";
7
+ import { ownedWizard, requireClean } from "./wizards.js";
8
+ const key = (wizardId) => `cloud:${wizardId}`;
9
+ /** Where a local wizard was published to the cloud, if it was. */
10
+ export function cloudCopy(wizardId) {
11
+ return readSetting(key(wizardId));
12
+ }
13
+ /**
14
+ * Publishes a local wizard to the cloud runtime of the linked account: definition and workspace
15
+ * go through the cloud's import API, which validates them like any other write. Publishing
16
+ * again updates the same cloud wizard, so its link stays.
17
+ */
18
+ export async function publishToCloud(userId, wizardId) {
19
+ const w = await ownedWizard(userId, wizardId);
20
+ const { definition } = await requireClean(w);
21
+ const token = await accountToken();
22
+ if (!token) {
23
+ throw new ServiceError("refused", "Bitte zuerst in den Einstellungen ein Konto anmelden.");
24
+ }
25
+ const files = await Promise.all((await draftFiles(w.id)).map(async (f) => ({
26
+ path: f.path,
27
+ mime: f.mime,
28
+ data: (await getBlob(f.hash)).toString("base64"),
29
+ })));
30
+ const before = await cloudCopy(w.id);
31
+ const res = await fetch(`${env.local.cloudUrl}/api/v1/wizards/import`, {
32
+ method: "POST",
33
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
34
+ body: JSON.stringify({ wizardId: before?.wizardId, definition, files, publish: true }),
35
+ signal: AbortSignal.timeout(120_000),
36
+ }).catch(() => null);
37
+ if (!res) {
38
+ throw new ServiceError("refused", "Die Cloud ist gerade nicht erreichbar.");
39
+ }
40
+ const body = (await res.json().catch(() => ({})));
41
+ if (!res.ok || !body.wizardId || !body.shareUrl) {
42
+ throw new ServiceError(body.issues?.length ? "has_issues" : "refused", body.error ?? "Veröffentlichen in der Cloud hat nicht geklappt.", body.issues?.length ? { issues: body.issues } : {});
43
+ }
44
+ const copy = {
45
+ wizardId: body.wizardId,
46
+ shareUrl: body.shareUrl,
47
+ version: body.version ?? 1,
48
+ publishedAt: new Date().toISOString(),
49
+ };
50
+ await writeSetting(key(w.id), copy);
51
+ return copy;
52
+ }