@engenty/wizards-runtime 0.0.0-stage → 0.7.6

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 (523) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +214 -2
  3. package/apps/runtime/dist/agents/architect.js +444 -0
  4. package/apps/runtime/dist/agents/studio-assistant.js +883 -0
  5. package/apps/runtime/dist/agents/subscription.js +207 -0
  6. package/apps/runtime/dist/agents/thought.js +21 -0
  7. package/apps/runtime/dist/agents/website.js +480 -0
  8. package/apps/runtime/dist/app.js +359 -0
  9. package/apps/runtime/dist/apple/wizards-apple +0 -0
  10. package/apps/runtime/dist/assistant/browser.js +26 -0
  11. package/apps/runtime/dist/assistant/docs.js +158 -0
  12. package/apps/runtime/dist/assistant/knows.js +353 -0
  13. package/apps/runtime/dist/assistant/primer.js +26 -0
  14. package/apps/runtime/dist/assistant/runs.js +24 -0
  15. package/apps/runtime/dist/auth/account.js +175 -0
  16. package/apps/runtime/dist/auth/device.js +112 -0
  17. package/apps/runtime/dist/auth/index.js +358 -0
  18. package/apps/runtime/dist/auth/keys.js +55 -0
  19. package/apps/runtime/dist/auth/local-ticket.js +26 -0
  20. package/apps/runtime/dist/authoring/example.js +74 -0
  21. package/apps/runtime/dist/authoring/guide.js +433 -0
  22. package/apps/runtime/dist/authoring/ops.js +117 -0
  23. package/apps/runtime/dist/browser/fast/action-space.js +160 -0
  24. package/apps/runtime/dist/browser/fast/classifier.js +91 -0
  25. package/apps/runtime/dist/browser/fast/decide.js +171 -0
  26. package/apps/runtime/dist/browser/fast/driver.js +141 -0
  27. package/apps/runtime/dist/browser/fast/instructions.js +33 -0
  28. package/apps/runtime/dist/browser/fast/run.js +249 -0
  29. package/apps/runtime/dist/browser/fast/span-picker.js +153 -0
  30. package/apps/runtime/dist/browser/fast/text-helper.js +178 -0
  31. package/apps/runtime/dist/browser/snapshot.js +335 -0
  32. package/apps/runtime/dist/cli/account.js +385 -0
  33. package/apps/runtime/dist/cli/autostart.js +182 -0
  34. package/apps/runtime/dist/cli/autoupdate.js +221 -0
  35. package/apps/runtime/dist/cli/clients.js +44 -0
  36. package/apps/runtime/dist/cli/connect.js +540 -0
  37. package/apps/runtime/dist/cli/environment.js +95 -0
  38. package/apps/runtime/dist/cli/exec.js +45 -0
  39. package/apps/runtime/dist/cli/features.js +50 -0
  40. package/apps/runtime/dist/cli/home.js +88 -0
  41. package/apps/runtime/dist/cli/install-env.js +33 -0
  42. package/apps/runtime/dist/cli/machine.js +107 -0
  43. package/apps/runtime/dist/cli/main.js +231 -0
  44. package/apps/runtime/dist/cli/mcp.js +136 -0
  45. package/apps/runtime/dist/cli/menu.js +202 -0
  46. package/apps/runtime/dist/cli/processes.js +72 -0
  47. package/apps/runtime/dist/cli/report.js +66 -0
  48. package/apps/runtime/dist/cli/sandbox.js +77 -0
  49. package/apps/runtime/dist/cli/setup.js +335 -0
  50. package/apps/runtime/dist/cli/start.js +276 -0
  51. package/apps/runtime/dist/cli/status.js +212 -0
  52. package/apps/runtime/dist/cli/ui.js +59 -0
  53. package/apps/runtime/dist/cli/uninstall-main.js +20 -0
  54. package/apps/runtime/dist/cli/uninstall.js +231 -0
  55. package/apps/runtime/dist/cli/update.js +86 -0
  56. package/apps/runtime/dist/cli/validate.js +75 -0
  57. package/apps/runtime/dist/cli/versions.js +123 -0
  58. package/apps/runtime/dist/connectors/builtin.js +86 -0
  59. package/apps/runtime/dist/connectors/external.js +409 -0
  60. package/apps/runtime/dist/connectors/gmail.js +107 -0
  61. package/apps/runtime/dist/connectors/imap.js +261 -0
  62. package/apps/runtime/dist/connectors/index.js +325 -0
  63. package/apps/runtime/dist/connectors/mail.js +72 -0
  64. package/apps/runtime/dist/connectors/outlook.js +90 -0
  65. package/apps/runtime/dist/connectors/plugin-connections.js +343 -0
  66. package/apps/runtime/dist/connectors/via-account.js +65 -0
  67. package/apps/runtime/dist/credits/credits.js +100 -0
  68. package/apps/runtime/dist/credits/estimate.js +152 -0
  69. package/apps/runtime/dist/db/adopt.js +149 -0
  70. package/apps/runtime/dist/db/client.js +173 -0
  71. package/apps/runtime/dist/db/control-schema.js +133 -0
  72. package/apps/runtime/dist/db/migrations/control/0000_last_absorbing_man.sql +47 -0
  73. package/apps/runtime/dist/db/migrations/control/0001_gorgeous_nicolaos.sql +41 -0
  74. package/apps/runtime/dist/db/migrations/control/0002_secret_penance.sql +1 -0
  75. package/apps/runtime/dist/db/migrations/control/0003_nasty_mordo.sql +3 -0
  76. package/apps/runtime/dist/db/migrations/control/0004_breezy_eddie_brock.sql +6 -0
  77. package/apps/runtime/dist/db/migrations/control/0005_chilly_oracle.sql +1 -0
  78. package/apps/runtime/dist/db/migrations/control/0006_sleepy_mojo.sql +1 -0
  79. package/apps/runtime/dist/db/migrations/control/0007_common_the_hand.sql +15 -0
  80. package/apps/runtime/dist/db/migrations/control/0008_runner.sql +8 -0
  81. package/apps/runtime/dist/db/migrations/control/0009_plugin_claim.sql +9 -0
  82. package/apps/runtime/dist/db/migrations/control/0010_tan_snowbird.sql +6 -0
  83. package/apps/runtime/dist/db/migrations/control/meta/0000_snapshot.json +311 -0
  84. package/apps/runtime/dist/db/migrations/control/meta/0001_snapshot.json +599 -0
  85. package/apps/runtime/dist/db/migrations/control/meta/0002_snapshot.json +607 -0
  86. package/apps/runtime/dist/db/migrations/control/meta/0003_snapshot.json +311 -0
  87. package/apps/runtime/dist/db/migrations/control/meta/0004_snapshot.json +350 -0
  88. package/apps/runtime/dist/db/migrations/control/meta/0005_snapshot.json +358 -0
  89. package/apps/runtime/dist/db/migrations/control/meta/0006_snapshot.json +365 -0
  90. package/apps/runtime/dist/db/migrations/control/meta/0007_snapshot.json +460 -0
  91. package/apps/runtime/dist/db/migrations/control/meta/0008_snapshot.json +505 -0
  92. package/apps/runtime/dist/db/migrations/control/meta/0009_snapshot.json +561 -0
  93. package/apps/runtime/dist/db/migrations/control/meta/0010_snapshot.json +600 -0
  94. package/apps/runtime/dist/db/migrations/control/meta/_journal.json +83 -0
  95. package/apps/runtime/dist/db/migrations/tenant/0000_eminent_freak.sql +188 -0
  96. package/apps/runtime/dist/db/migrations/tenant/0001_salty_master_chief.sql +1 -0
  97. package/apps/runtime/dist/db/migrations/tenant/0002_cloudy_firebird.sql +1 -0
  98. package/apps/runtime/dist/db/migrations/tenant/0003_brown_warbound.sql +52 -0
  99. package/apps/runtime/dist/db/migrations/tenant/0004_romantic_nocturne.sql +6 -0
  100. package/apps/runtime/dist/db/migrations/tenant/0005_reflective_forgotten_one.sql +2 -0
  101. package/apps/runtime/dist/db/migrations/tenant/0006_hard_war_machine.sql +30 -0
  102. package/apps/runtime/dist/db/migrations/tenant/0007_space_data.sql +43 -0
  103. package/apps/runtime/dist/db/migrations/tenant/0008_run_event_note.sql +1 -0
  104. package/apps/runtime/dist/db/migrations/tenant/0009_knowledge.sql +104 -0
  105. package/apps/runtime/dist/db/migrations/tenant/0010_plugin_connection.sql +15 -0
  106. package/apps/runtime/dist/db/migrations/tenant/0011_runners.sql +2 -0
  107. package/apps/runtime/dist/db/migrations/tenant/0012_assistant_chat.sql +40 -0
  108. package/apps/runtime/dist/db/migrations/tenant/0013_run_origin.sql +1 -0
  109. package/apps/runtime/dist/db/migrations/tenant/0014_run_answer.sql +2 -0
  110. package/apps/runtime/dist/db/migrations/tenant/0015_device_calls.sql +13 -0
  111. package/apps/runtime/dist/db/migrations/tenant/0016_even_trish_tilby.sql +12 -0
  112. package/apps/runtime/dist/db/migrations/tenant/meta/0000_snapshot.json +1323 -0
  113. package/apps/runtime/dist/db/migrations/tenant/meta/0001_snapshot.json +1330 -0
  114. package/apps/runtime/dist/db/migrations/tenant/meta/0002_snapshot.json +1337 -0
  115. package/apps/runtime/dist/db/migrations/tenant/meta/0003_snapshot.json +1605 -0
  116. package/apps/runtime/dist/db/migrations/tenant/meta/0004_snapshot.json +1645 -0
  117. package/apps/runtime/dist/db/migrations/tenant/meta/0005_snapshot.json +1659 -0
  118. package/apps/runtime/dist/db/migrations/tenant/meta/0006_snapshot.json +1696 -0
  119. package/apps/runtime/dist/db/migrations/tenant/meta/0007_snapshot.json +2012 -0
  120. package/apps/runtime/dist/db/migrations/tenant/meta/0008_snapshot.json +2019 -0
  121. package/apps/runtime/dist/db/migrations/tenant/meta/0009_snapshot.json +2837 -0
  122. package/apps/runtime/dist/db/migrations/tenant/meta/0010_snapshot.json +2942 -0
  123. package/apps/runtime/dist/db/migrations/tenant/meta/0011_snapshot.json +2956 -0
  124. package/apps/runtime/dist/db/migrations/tenant/meta/0012_snapshot.json +3132 -0
  125. package/apps/runtime/dist/db/migrations/tenant/meta/0013_snapshot.json +3139 -0
  126. package/apps/runtime/dist/db/migrations/tenant/meta/0014_snapshot.json +3154 -0
  127. package/apps/runtime/dist/db/migrations/tenant/meta/0015_snapshot.json +3243 -0
  128. package/apps/runtime/dist/db/migrations/tenant/meta/0016_snapshot.json +3326 -0
  129. package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +125 -0
  130. package/apps/runtime/dist/db/schema.js +602 -0
  131. package/apps/runtime/dist/debug.js +197 -0
  132. package/apps/runtime/dist/docs/dev/index.md +41 -0
  133. package/apps/runtime/dist/docs/dev/meta.json +7 -0
  134. package/apps/runtime/dist/docs/dev/plugins/connections.md +74 -0
  135. package/apps/runtime/dist/docs/dev/plugins/events.md +89 -0
  136. package/apps/runtime/dist/docs/dev/plugins/examples.md +101 -0
  137. package/apps/runtime/dist/docs/dev/plugins/index.md +78 -0
  138. package/apps/runtime/dist/docs/dev/plugins/layout.md +90 -0
  139. package/apps/runtime/dist/docs/dev/plugins/meta.json +28 -0
  140. package/apps/runtime/dist/docs/dev/plugins/quick-start.md +94 -0
  141. package/apps/runtime/dist/docs/dev/plugins/reference.md +348 -0
  142. package/apps/runtime/dist/docs/dev/plugins/reloading.md +76 -0
  143. package/apps/runtime/dist/docs/dev/plugins/routes.md +185 -0
  144. package/apps/runtime/dist/docs/dev/plugins/runners.md +230 -0
  145. package/apps/runtime/dist/docs/dev/plugins/server.md +281 -0
  146. package/apps/runtime/dist/docs/dev/plugins/shipping.md +79 -0
  147. package/apps/runtime/dist/docs/dev/plugins/space.md +297 -0
  148. package/apps/runtime/dist/docs/dev/plugins/starters.md +70 -0
  149. package/apps/runtime/dist/docs/dev/plugins/studio.md +363 -0
  150. package/apps/runtime/dist/docs/dev/plugins/styling.md +129 -0
  151. package/apps/runtime/dist/docs/dev/plugins/tables.md +104 -0
  152. package/apps/runtime/dist/docs/dev/plugins/tools.md +139 -0
  153. package/apps/runtime/dist/docs/dev/wizard-format.md +125 -0
  154. package/apps/runtime/dist/docs/meta.json +3 -0
  155. package/apps/runtime/dist/docs/user/ai-apps.md +112 -0
  156. package/apps/runtime/dist/docs/user/assistant.md +77 -0
  157. package/apps/runtime/dist/docs/user/build-a-wizard.md +116 -0
  158. package/apps/runtime/dist/docs/user/command-line.md +86 -0
  159. package/apps/runtime/dist/docs/user/connectors.md +90 -0
  160. package/apps/runtime/dist/docs/user/create-account.md +52 -0
  161. package/apps/runtime/dist/docs/user/data.md +66 -0
  162. package/apps/runtime/dist/docs/user/first-start.md +60 -0
  163. package/apps/runtime/dist/docs/user/index.md +49 -0
  164. package/apps/runtime/dist/docs/user/install.md +123 -0
  165. package/apps/runtime/dist/docs/user/local-only.md +42 -0
  166. package/apps/runtime/dist/docs/user/meta.json +32 -0
  167. package/apps/runtime/dist/docs/user/models.md +156 -0
  168. package/apps/runtime/dist/docs/user/plugins.md +71 -0
  169. package/apps/runtime/dist/docs/user/run.md +98 -0
  170. package/apps/runtime/dist/docs/user/server.md +52 -0
  171. package/apps/runtime/dist/docs/user/space.md +126 -0
  172. package/apps/runtime/dist/docs/user/steps.md +220 -0
  173. package/apps/runtime/dist/docs/user/test-and-share.md +224 -0
  174. package/apps/runtime/dist/docs/user/troubleshooting.md +67 -0
  175. package/apps/runtime/dist/docs/user/wordpress.md +121 -0
  176. package/apps/runtime/dist/documents/parse.js +246 -0
  177. package/apps/runtime/dist/engenty/connections-external/build-connector.js +193 -0
  178. package/apps/runtime/dist/engenty/connections-external/errors.js +11 -0
  179. package/apps/runtime/dist/engenty/connections-external/import-service.js +270 -0
  180. package/apps/runtime/dist/engenty/connections-external/importer/classify.js +34 -0
  181. package/apps/runtime/dist/engenty/connections-external/importer/map-auth.js +185 -0
  182. package/apps/runtime/dist/engenty/connections-external/importer/normalize-mcp.js +63 -0
  183. package/apps/runtime/dist/engenty/connections-external/importer/normalize-openapi.js +130 -0
  184. package/apps/runtime/dist/engenty/connections-external/invoke/http-invoker.js +143 -0
  185. package/apps/runtime/dist/engenty/connections-external/invoke/mcp-client.js +143 -0
  186. package/apps/runtime/dist/engenty/connections-external/net/guarded-fetch.js +119 -0
  187. package/apps/runtime/dist/engenty/connections-external/oauth-dcr.js +88 -0
  188. package/apps/runtime/dist/engenty/connections-external/registry-client.js +336 -0
  189. package/apps/runtime/dist/engenty/connections-external/registry-source.js +82 -0
  190. package/apps/runtime/dist/engenty/connections-external/types.js +7 -0
  191. package/apps/runtime/dist/engenty/connections-github/connector.js +209 -0
  192. package/apps/runtime/dist/engenty/connections-google/connectors/calendar.js +381 -0
  193. package/apps/runtime/dist/engenty/connections-google/connectors/contacts.js +94 -0
  194. package/apps/runtime/dist/engenty/connections-google/connectors/drive.js +290 -0
  195. package/apps/runtime/dist/engenty/connections-google/connectors/gmail.js +567 -0
  196. package/apps/runtime/dist/engenty/connections-google/definitions.js +11 -0
  197. package/apps/runtime/dist/engenty/connections-google/shared.js +73 -0
  198. package/apps/runtime/dist/engenty/connections-hubspot/connector.js +140 -0
  199. package/apps/runtime/dist/engenty/connections-microsoft/action.js +17 -0
  200. package/apps/runtime/dist/engenty/connections-microsoft/graph.js +114 -0
  201. package/apps/runtime/dist/engenty/connections-microsoft/onedrive.js +286 -0
  202. package/apps/runtime/dist/engenty/connections-microsoft/outlook.js +492 -0
  203. package/apps/runtime/dist/engenty/connections-s3/s3.js +328 -0
  204. package/apps/runtime/dist/engenty/connections-sdk/files-capability.js +86 -0
  205. package/apps/runtime/dist/engenty/connections-sdk/oauth2.js +150 -0
  206. package/apps/runtime/dist/engenty/connections-sdk/registry.js +89 -0
  207. package/apps/runtime/dist/engenty/connections-sdk/storage-capability.js +84 -0
  208. package/apps/runtime/dist/engenty/connections-sdk/types.js +30 -0
  209. package/apps/runtime/dist/engenty/connections-slack/connector.js +360 -0
  210. package/apps/runtime/dist/engenty/csv-import/csv-matrix.js +178 -0
  211. package/apps/runtime/dist/engenty/csv-import/import-sources.js +87 -0
  212. package/apps/runtime/dist/engenty/csv-import/parse-csv.js +129 -0
  213. package/apps/runtime/dist/engenty/csv-import/serialize-csv.js +20 -0
  214. package/apps/runtime/dist/engenty/csv-import/types.js +1 -0
  215. package/apps/runtime/dist/engenty/csv-import/xlsx-workbook.js +159 -0
  216. package/apps/runtime/dist/engenty/doc-converter/interface.js +9 -0
  217. package/apps/runtime/dist/engenty/doc-converter/page-break.js +92 -0
  218. package/apps/runtime/dist/engenty/doc-converter/providers/local/index.js +72 -0
  219. package/apps/runtime/dist/engenty/document-scanner/schemas/classifier.js +30 -0
  220. package/apps/runtime/dist/engenty/document-scanner/schemas/index.js +4 -0
  221. package/apps/runtime/dist/engenty/document-scanner/schemas/invoice.js +73 -0
  222. package/apps/runtime/dist/engenty/document-scanner/schemas/receipt.js +63 -0
  223. package/apps/runtime/dist/engenty/document-scanner/schemas/shared.js +40 -0
  224. package/apps/runtime/dist/engenty/search-index/chunks.js +164 -0
  225. package/apps/runtime/dist/engenty/search-index/contracts.js +9 -0
  226. package/apps/runtime/dist/engenty/shims/connections-sdk.js +39 -0
  227. package/apps/runtime/dist/engenty/shims/plugin-sdk.js +1 -0
  228. package/apps/runtime/dist/engenty/shims/web-ingest.js +19 -0
  229. package/apps/runtime/dist/engenty/web-ingest/ssrf.js +277 -0
  230. package/apps/runtime/dist/engine/asks.js +82 -0
  231. package/apps/runtime/dist/engine/decide.js +82 -0
  232. package/apps/runtime/dist/engine/device.js +194 -0
  233. package/apps/runtime/dist/engine/events.js +128 -0
  234. package/apps/runtime/dist/engine/input.js +208 -0
  235. package/apps/runtime/dist/engine/owner.js +32 -0
  236. package/apps/runtime/dist/engine/prepare.js +90 -0
  237. package/apps/runtime/dist/engine/push.js +58 -0
  238. package/apps/runtime/dist/engine/requirements.js +355 -0
  239. package/apps/runtime/dist/engine/resources.js +198 -0
  240. package/apps/runtime/dist/engine/runner.js +970 -0
  241. package/apps/runtime/dist/engine/steps.js +1000 -0
  242. package/apps/runtime/dist/engine/surface.js +91 -0
  243. package/apps/runtime/dist/engine/template.js +240 -0
  244. package/apps/runtime/dist/engine/types.js +4 -0
  245. package/apps/runtime/dist/env.js +279 -0
  246. package/apps/runtime/dist/features.js +123 -0
  247. package/apps/runtime/dist/files/blobs.js +22 -0
  248. package/apps/runtime/dist/files/objects.js +86 -0
  249. package/apps/runtime/dist/files/storage.js +120 -0
  250. package/apps/runtime/dist/files/web-images.js +101 -0
  251. package/apps/runtime/dist/film/project.js +257 -0
  252. package/apps/runtime/dist/film/skills.js +91 -0
  253. package/apps/runtime/dist/film/sounds.js +122 -0
  254. package/apps/runtime/dist/film/step.js +289 -0
  255. package/apps/runtime/dist/film/tools.js +99 -0
  256. package/apps/runtime/dist/geocode.js +58 -0
  257. package/apps/runtime/dist/harness/antigravity.js +129 -0
  258. package/apps/runtime/dist/harness/apple.js +270 -0
  259. package/apps/runtime/dist/harness/claude.js +147 -0
  260. package/apps/runtime/dist/harness/codex.js +229 -0
  261. package/apps/runtime/dist/harness/cursor.js +93 -0
  262. package/apps/runtime/dist/harness/env.js +124 -0
  263. package/apps/runtime/dist/harness/index.js +95 -0
  264. package/apps/runtime/dist/harness/model.js +279 -0
  265. package/apps/runtime/dist/harness/prompt.js +121 -0
  266. package/apps/runtime/dist/harness/terminal.js +194 -0
  267. package/apps/runtime/dist/harness/types.js +7 -0
  268. package/apps/runtime/dist/index.js +113 -0
  269. package/apps/runtime/dist/limits.js +81 -0
  270. package/apps/runtime/dist/link-preview.js +60 -0
  271. package/apps/runtime/dist/log.js +106 -0
  272. package/apps/runtime/dist/manage.js +249 -0
  273. package/apps/runtime/dist/mcp/auth.js +161 -0
  274. package/apps/runtime/dist/mcp/bridge.js +59 -0
  275. package/apps/runtime/dist/mcp/flow-app.js +97 -0
  276. package/apps/runtime/dist/mcp/handler.js +54 -0
  277. package/apps/runtime/dist/mcp/scopes.js +14 -0
  278. package/apps/runtime/dist/mcp/seen.js +65 -0
  279. package/apps/runtime/dist/mcp/tools.js +572 -0
  280. package/apps/runtime/dist/media/conversation.js +167 -0
  281. package/apps/runtime/dist/media/ffmpeg.js +121 -0
  282. package/apps/runtime/dist/media/generate.js +120 -0
  283. package/apps/runtime/dist/media/marking.js +121 -0
  284. package/apps/runtime/dist/media/transcribe.js +198 -0
  285. package/apps/runtime/dist/model-errors.js +42 -0
  286. package/apps/runtime/dist/models.js +1164 -0
  287. package/apps/runtime/dist/plugin.js +61 -0
  288. package/apps/runtime/dist/plugins/channels.js +78 -0
  289. package/apps/runtime/dist/plugins/claims.js +56 -0
  290. package/apps/runtime/dist/plugins/discovery.js +155 -0
  291. package/apps/runtime/dist/plugins/events.js +63 -0
  292. package/apps/runtime/dist/plugins/jobs.js +64 -0
  293. package/apps/runtime/dist/plugins/loader.js +508 -0
  294. package/apps/runtime/dist/plugins/migrations.js +69 -0
  295. package/apps/runtime/dist/plugins/pro-keys.js +34 -0
  296. package/apps/runtime/dist/plugins/pro-state.js +83 -0
  297. package/apps/runtime/dist/plugins/pro.js +310 -0
  298. package/apps/runtime/dist/plugins/public.js +44 -0
  299. package/apps/runtime/dist/plugins/registry.js +122 -0
  300. package/apps/runtime/dist/plugins/runs.js +210 -0
  301. package/apps/runtime/dist/render/browser-path.js +54 -0
  302. package/apps/runtime/dist/render/chromium.js +98 -0
  303. package/apps/runtime/dist/render/convert.js +158 -0
  304. package/apps/runtime/dist/render/downloads.js +299 -0
  305. package/apps/runtime/dist/render/guard.js +25 -0
  306. package/apps/runtime/dist/render/shaped.js +137 -0
  307. package/apps/runtime/dist/routes/api.js +440 -0
  308. package/apps/runtime/dist/routes/debug.js +42 -0
  309. package/apps/runtime/dist/routes/delivery.js +90 -0
  310. package/apps/runtime/dist/routes/internal.js +81 -0
  311. package/apps/runtime/dist/routes/lang.js +25 -0
  312. package/apps/runtime/dist/routes/marketplace.js +43 -0
  313. package/apps/runtime/dist/routes/plugin-public.js +113 -0
  314. package/apps/runtime/dist/routes/plugins.js +212 -0
  315. package/apps/runtime/dist/routes/runs.js +1422 -0
  316. package/apps/runtime/dist/routes/studio.js +1629 -0
  317. package/apps/runtime/dist/routes/wizard-stream.js +30 -0
  318. package/apps/runtime/dist/running.js +39 -0
  319. package/apps/runtime/dist/sandbox/agentos-package.js +73 -0
  320. package/apps/runtime/dist/sandbox/agentos.js +162 -0
  321. package/apps/runtime/dist/sandbox/docker.js +45 -0
  322. package/apps/runtime/dist/sandbox/index.js +52 -0
  323. package/apps/runtime/dist/sandbox/install.js +122 -0
  324. package/apps/runtime/dist/sandbox/mastra.js +71 -0
  325. package/apps/runtime/dist/sandbox/types.js +1 -0
  326. package/apps/runtime/dist/secrets/crypto.js +28 -0
  327. package/apps/runtime/dist/secrets/signing.js +52 -0
  328. package/apps/runtime/dist/secrets/vault.js +59 -0
  329. package/apps/runtime/dist/services/access.js +88 -0
  330. package/apps/runtime/dist/services/api.js +248 -0
  331. package/apps/runtime/dist/services/architect.js +95 -0
  332. package/apps/runtime/dist/services/assistant-chats.js +311 -0
  333. package/apps/runtime/dist/services/brand.js +85 -0
  334. package/apps/runtime/dist/services/cloud.js +456 -0
  335. package/apps/runtime/dist/services/draft-events.js +27 -0
  336. package/apps/runtime/dist/services/engenty-shapes.js +71 -0
  337. package/apps/runtime/dist/services/errors.js +29 -0
  338. package/apps/runtime/dist/services/files.js +178 -0
  339. package/apps/runtime/dist/services/knowledge-model.js +559 -0
  340. package/apps/runtime/dist/services/knowledge.js +721 -0
  341. package/apps/runtime/dist/services/links.js +16 -0
  342. package/apps/runtime/dist/services/marketplace.js +492 -0
  343. package/apps/runtime/dist/services/onboarding.js +152 -0
  344. package/apps/runtime/dist/services/package-file.js +82 -0
  345. package/apps/runtime/dist/services/package.js +170 -0
  346. package/apps/runtime/dist/services/profile.js +51 -0
  347. package/apps/runtime/dist/services/project-files.js +439 -0
  348. package/apps/runtime/dist/services/project-index.js +707 -0
  349. package/apps/runtime/dist/services/projects.js +387 -0
  350. package/apps/runtime/dist/services/relevance.js +58 -0
  351. package/apps/runtime/dist/services/runners.js +178 -0
  352. package/apps/runtime/dist/services/runs.js +524 -0
  353. package/apps/runtime/dist/services/shares.js +130 -0
  354. package/apps/runtime/dist/services/site.js +270 -0
  355. package/apps/runtime/dist/services/space-categories.js +861 -0
  356. package/apps/runtime/dist/services/space-data.js +792 -0
  357. package/apps/runtime/dist/services/space-origin.js +207 -0
  358. package/apps/runtime/dist/services/spaces.js +657 -0
  359. package/apps/runtime/dist/services/translations.js +306 -0
  360. package/apps/runtime/dist/services/widgets.js +34 -0
  361. package/apps/runtime/dist/services/wizard-icon.js +157 -0
  362. package/apps/runtime/dist/services/wizards.js +420 -0
  363. package/apps/runtime/dist/settings.js +16 -0
  364. package/apps/runtime/dist/store/errors.js +4 -0
  365. package/apps/runtime/dist/store/index.js +299 -0
  366. package/apps/runtime/dist/store/shared-lists.js +123 -0
  367. package/apps/runtime/dist/tenants/control.js +238 -0
  368. package/apps/runtime/dist/tenants/tenant.js +26 -0
  369. package/apps/runtime/dist/texts.js +207 -0
  370. package/apps/runtime/dist/tools/browser.js +703 -0
  371. package/apps/runtime/dist/tools/connector.js +204 -0
  372. package/apps/runtime/dist/tools/index.js +291 -0
  373. package/apps/runtime/dist/tools/mail.js +104 -0
  374. package/apps/runtime/dist/tools/net-guard.js +170 -0
  375. package/apps/runtime/dist/tools/pages.js +81 -0
  376. package/apps/runtime/dist/tools/plugin.js +180 -0
  377. package/apps/runtime/dist/tools/project.js +154 -0
  378. package/apps/runtime/dist/tools/shared.js +69 -0
  379. package/apps/runtime/dist/tools/store.js +238 -0
  380. package/apps/runtime/dist/update.js +113 -0
  381. package/apps/runtime/dist/widgets/bundle.js +99 -0
  382. package/apps/runtime/dist/widgets/render.js +329 -0
  383. package/apps/runtime/dist/widgets/runtime.js +108 -0
  384. package/apps/runtime/dist/widgets/script.js +30 -0
  385. package/apps/runtime/dist/widgets/step.js +223 -0
  386. package/apps/web/dist/ai-labels/ai-black.svg +20 -0
  387. package/apps/web/dist/ai-labels/ai-generated-black.svg +31 -0
  388. package/apps/web/dist/ai-labels/ai-generated-white.svg +32 -0
  389. package/apps/web/dist/ai-labels/ai-modified-black.svg +30 -0
  390. package/apps/web/dist/ai-labels/ai-modified-white.svg +31 -0
  391. package/apps/web/dist/ai-labels/ai-white.svg +21 -0
  392. package/apps/web/dist/assets/AboutDialog-DYgXPagX.js +1 -0
  393. package/apps/web/dist/assets/ChangelogDialog-BXqBW8j3.js +1 -0
  394. package/apps/web/dist/assets/ChatView-CK2--tsE.js +10 -0
  395. package/apps/web/dist/assets/EditorPage-jvcwyXbU.js +3 -0
  396. package/apps/web/dist/assets/FlowPreview-CusP7ci9.js +1 -0
  397. package/apps/web/dist/assets/Harness-BrP-ENHg.css +1 -0
  398. package/apps/web/dist/assets/Harness-CV9uYzbv.js +16 -0
  399. package/apps/web/dist/assets/NewWizardChannels-BakW5q6s.js +1 -0
  400. package/apps/web/dist/assets/NewWizardPage-BoA-LJuW.js +7 -0
  401. package/apps/web/dist/assets/OnboardingPage-DaD1XnOe.js +1 -0
  402. package/apps/web/dist/assets/ProjectPage-BFgk1DEv.js +1 -0
  403. package/apps/web/dist/assets/PublicRunner-C9MwEvxy.js +1 -0
  404. package/apps/web/dist/assets/ReadOnly-DC2Kp7l8.js +1 -0
  405. package/apps/web/dist/assets/RunDrawer-CY6NZSrI.js +4 -0
  406. package/apps/web/dist/assets/RunnerView-BT26XgcY.js +1 -0
  407. package/apps/web/dist/assets/SettingsPage-AFt9IDLt.js +1 -0
  408. package/apps/web/dist/assets/SetupPage-DtFYAbwJ.js +1 -0
  409. package/apps/web/dist/assets/SharePage-t2xwNS61.js +1 -0
  410. package/apps/web/dist/assets/changelog-CATQFxCQ.js +1 -0
  411. package/apps/web/dist/assets/engenty-eXZ-EAwd.js +1 -0
  412. package/apps/web/dist/assets/esm-DXeMu8Lb.js +7 -0
  413. package/apps/web/dist/assets/fluffy-engenty-ClRou7yu.js +456 -0
  414. package/apps/web/dist/assets/index-AfUa8xKD.css +1 -0
  415. package/apps/web/dist/assets/index-Drw7FMTQ.js +14 -0
  416. package/apps/web/dist/assets/jsx-runtime-BNakU3Ej.js +1 -0
  417. package/apps/web/dist/assets/lock-R_CfU0Xo.js +1 -0
  418. package/apps/web/dist/assets/oss-credits-cZ9NUaLZ.js +1 -0
  419. package/apps/web/dist/assets/pdfmake-Cx9scTbD.js +228 -0
  420. package/apps/web/dist/assets/phone-CgrIS4_-.js +1 -0
  421. package/apps/web/dist/assets/preload-helper-CNEI7N0N.js +1 -0
  422. package/apps/web/dist/assets/qr-scanner-worker.min-CJaXo3yu.js +98 -0
  423. package/apps/web/dist/assets/qr-scanner.min-C_UI0gH-.js +1 -0
  424. package/apps/web/dist/assets/rolldown-runtime-hePW80VL.js +1 -0
  425. package/apps/web/dist/assets/settings-menu-2IMOOlLl.js +1 -0
  426. package/apps/web/dist/assets/ui-dzGvc3xk.js +137 -0
  427. package/apps/web/dist/assets/upload-BSuTilFF.js +1 -0
  428. package/apps/web/dist/assets/vfs_fonts-DllDSYt1.js +1 -0
  429. package/apps/web/dist/embed.js +266 -0
  430. package/apps/web/dist/favicon.svg +1 -0
  431. package/apps/web/dist/icons/apple-touch-icon.png +0 -0
  432. package/apps/web/dist/icons/icon-192.png +0 -0
  433. package/apps/web/dist/icons/icon-512.png +0 -0
  434. package/apps/web/dist/icons/icon-maskable-512.png +0 -0
  435. package/apps/web/dist/index.html +48 -0
  436. package/apps/web/dist/install.sh +577 -0
  437. package/apps/web/dist/manifest.webmanifest +21 -0
  438. package/apps/web/dist/mcp/flow.html +980 -0
  439. package/apps/web/dist/sw.js +15 -0
  440. package/bin/wizards.mjs +288 -0
  441. package/modules/README.md +15 -0
  442. package/node_modules/@engenty/wizards/dist/plugin/index.d.ts +1150 -0
  443. package/node_modules/@engenty/wizards/dist/plugin/index.js +4 -0
  444. package/node_modules/@engenty/wizards/dist/plugin/studio.d.ts +322 -0
  445. package/node_modules/@engenty/wizards/dist/plugin/studio.js +4 -0
  446. package/node_modules/@engenty/wizards/dist/plugin/thread.d.ts +174 -0
  447. package/node_modules/@engenty/wizards/dist/plugin/thread.js +821 -0
  448. package/node_modules/@engenty/wizards/package.json +11 -0
  449. package/node_modules/@engenty-wizards/shared/dist/assistant.d.ts +174 -0
  450. package/node_modules/@engenty-wizards/shared/dist/assistant.js +65 -0
  451. package/node_modules/@engenty-wizards/shared/dist/changes.d.ts +22 -0
  452. package/node_modules/@engenty-wizards/shared/dist/changes.js +61 -0
  453. package/node_modules/@engenty-wizards/shared/dist/connectors.d.ts +77 -0
  454. package/node_modules/@engenty-wizards/shared/dist/connectors.js +2 -0
  455. package/node_modules/@engenty-wizards/shared/dist/decision.d.ts +76 -0
  456. package/node_modules/@engenty-wizards/shared/dist/decision.js +147 -0
  457. package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +2922 -0
  458. package/node_modules/@engenty-wizards/shared/dist/definition.js +1920 -0
  459. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.d.ts +5 -0
  460. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.js +132 -0
  461. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.d.ts +11 -0
  462. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.js +209 -0
  463. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.d.ts +532 -0
  464. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.js +217 -0
  465. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.d.ts +3 -0
  466. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.js +3 -0
  467. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.d.ts +14 -0
  468. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.js +166 -0
  469. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.d.ts +33 -0
  470. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.js +99 -0
  471. package/node_modules/@engenty-wizards/shared/dist/feature-flags.d.ts +78 -0
  472. package/node_modules/@engenty-wizards/shared/dist/feature-flags.js +70 -0
  473. package/node_modules/@engenty-wizards/shared/dist/features.d.ts +41 -0
  474. package/node_modules/@engenty-wizards/shared/dist/features.js +89 -0
  475. package/node_modules/@engenty-wizards/shared/dist/fit.d.ts +71 -0
  476. package/node_modules/@engenty-wizards/shared/dist/fit.js +382 -0
  477. package/node_modules/@engenty-wizards/shared/dist/json-schema.d.ts +14 -0
  478. package/node_modules/@engenty-wizards/shared/dist/json-schema.js +66 -0
  479. package/node_modules/@engenty-wizards/shared/dist/knowledge.d.ts +199 -0
  480. package/node_modules/@engenty-wizards/shared/dist/knowledge.js +68 -0
  481. package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.d.ts +35 -0
  482. package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.js +107 -0
  483. package/node_modules/@engenty-wizards/shared/dist/marketplace-search.d.ts +20 -0
  484. package/node_modules/@engenty-wizards/shared/dist/marketplace-search.js +136 -0
  485. package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +227 -0
  486. package/node_modules/@engenty-wizards/shared/dist/marketplace.js +354 -0
  487. package/node_modules/@engenty-wizards/shared/dist/merge.d.ts +7 -0
  488. package/node_modules/@engenty-wizards/shared/dist/merge.js +40 -0
  489. package/node_modules/@engenty-wizards/shared/dist/numbers.d.ts +24 -0
  490. package/node_modules/@engenty-wizards/shared/dist/numbers.js +54 -0
  491. package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +224 -0
  492. package/node_modules/@engenty-wizards/shared/dist/projects.js +319 -0
  493. package/node_modules/@engenty-wizards/shared/dist/providers.d.ts +101 -0
  494. package/node_modules/@engenty-wizards/shared/dist/providers.js +390 -0
  495. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +680 -0
  496. package/node_modules/@engenty-wizards/shared/dist/run.js +620 -0
  497. package/node_modules/@engenty-wizards/shared/dist/runner-texts.d.ts +546 -0
  498. package/node_modules/@engenty-wizards/shared/dist/runner-texts.js +523 -0
  499. package/node_modules/@engenty-wizards/shared/dist/runners.d.ts +93 -0
  500. package/node_modules/@engenty-wizards/shared/dist/runners.js +146 -0
  501. package/node_modules/@engenty-wizards/shared/dist/shape.d.ts +102 -0
  502. package/node_modules/@engenty-wizards/shared/dist/shape.js +318 -0
  503. package/node_modules/@engenty-wizards/shared/dist/space-data.d.ts +89 -0
  504. package/node_modules/@engenty-wizards/shared/dist/space-data.js +47 -0
  505. package/node_modules/@engenty-wizards/shared/dist/store.d.ts +117 -0
  506. package/node_modules/@engenty-wizards/shared/dist/store.js +81 -0
  507. package/node_modules/@engenty-wizards/shared/dist/surface.d.ts +115 -0
  508. package/node_modules/@engenty-wizards/shared/dist/surface.js +442 -0
  509. package/node_modules/@engenty-wizards/shared/dist/texts.d.ts +45 -0
  510. package/node_modules/@engenty-wizards/shared/dist/texts.js +54 -0
  511. package/node_modules/@engenty-wizards/shared/dist/translation.d.ts +23 -0
  512. package/node_modules/@engenty-wizards/shared/dist/translation.js +154 -0
  513. package/node_modules/@engenty-wizards/shared/dist/wizard-package.d.ts +48 -0
  514. package/node_modules/@engenty-wizards/shared/dist/wizard-package.js +49 -0
  515. package/node_modules/@engenty-wizards/shared/dist/workspace.d.ts +16 -0
  516. package/node_modules/@engenty-wizards/shared/dist/workspace.js +51 -0
  517. package/node_modules/@engenty-wizards/shared/package.json +10 -0
  518. package/package.json +77 -4
  519. package/plugin/.claude-plugin/plugin.json +9 -0
  520. package/plugin/.mcp.json +8 -0
  521. package/plugin/commands/wizard.md +8 -0
  522. package/plugin/skills/build-wizard/SKILL.md +27 -0
  523. package/plugin/skills/use-wizard/SKILL.md +18 -0
@@ -0,0 +1,1164 @@
1
+ import { createHash } from "node:crypto";
2
+ import { createAnthropic } from "@ai-sdk/anthropic";
3
+ import { createElevenLabs } from "@ai-sdk/elevenlabs";
4
+ import { createFal } from "@ai-sdk/fal";
5
+ import { createGateway } from "@ai-sdk/gateway";
6
+ import { createGoogle } from "@ai-sdk/google";
7
+ import { createOpenAI } from "@ai-sdk/openai";
8
+ import { createReplicate } from "@ai-sdk/replicate";
9
+ import { MODEL_CLASSES, TEXT_CLASSES, } from "@engenty-wizards/shared/definition";
10
+ import { CAPABILITY_CLASSES, creditsModelOf, isCreditsRef, PROVIDER_IDS, providerInfo, providerOfRef, } from "@engenty-wizards/shared/providers";
11
+ import { note, saidText } from "@engenty-wizards/shared/run";
12
+ import { generateSpeech, generateText, } from "ai";
13
+ import { accountToken, linkedAccount } from "./auth/account.js";
14
+ import { DeviceModel, deviceThinks } from "./engine/device.js";
15
+ import { env } from "./env.js";
16
+ import { appleModel, appleStatus, appleTranscriptionModel, appleUnavailableNote, } from "./harness/apple.js";
17
+ import { detectHarness, HarnessModel, harness, isHarnessVendor, notInstalled, signedOut, } from "./harness/index.js";
18
+ import { managed } from "./manage.js";
19
+ import { hasFfmpeg } from "./media/ffmpeg.js";
20
+ import { ModelUnavailableError, saidOf } from "./model-errors.js";
21
+ import { seal, unseal } from "./secrets/crypto.js";
22
+ import { vaultDelete, vaultGet, vaultSet } from "./secrets/vault.js";
23
+ import { readSetting, writeSetting } from "./settings.js";
24
+ import { currentTenant, currentTenantOrNull } from "./tenants/tenant.js";
25
+ import { serverText } from "./texts.js";
26
+ export { isHarnessVendor } from "./harness/index.js";
27
+ /**
28
+ * A step names a model class, never a model. Where the class runs is decided here, in this order:
29
+ * 1. its own way: an AI client installed on the machine (Claude Code, Codex, Antigravity, Cursor
30
+ * Agent — on its subscription), an API key the person brought (OpenAI, Anthropic, Google,
31
+ * fal.ai, ElevenLabs, Replicate, AI Gateway) or a local model;
32
+ * 2. credits: the model-gateway binds `wizards/<class>`, meters the call and books it — on the
33
+ * tenant of a Manage-App's runtime, or on the account a runtime that runs alone is linked to.
34
+ * A runtime of a Manage-App keeps a tenant's own keys (sealed) and runs on its credits where it
35
+ * has none. A binding is `[provider:]vendor/model`, e.g. `anthropic/claude-haiku-4.5` (AI
36
+ * Gateway), `openai:gpt-5.4-mini`, `fal:fal-ai/flux/schnell`, `ollama:qwen3`, `claude/sonnet`
37
+ * (an installed client), or `credits`.
38
+ */
39
+ export { ModelUnavailableError } from "./model-errors.js";
40
+ // --- configuration -----------------------------------------------------------------
41
+ export const LOCAL_KEYS = PROVIDER_IDS;
42
+ const local = { settings: { source: "own", bindings: {} }, keys: {} };
43
+ function envKey(name) {
44
+ const value = {
45
+ gateway: env.aiGatewayKey,
46
+ openai: env.openaiKey,
47
+ anthropic: env.anthropicKey,
48
+ google: env.googleKey,
49
+ fal: env.falKey,
50
+ elevenlabs: env.elevenlabsKey,
51
+ replicate: env.replicateKey,
52
+ }[name];
53
+ return value || undefined;
54
+ }
55
+ /** Reads the model settings and keys of a runtime that runs alone; called at start and after a change. */
56
+ export async function loadLocalModels() {
57
+ if (managed) {
58
+ return;
59
+ }
60
+ local.settings = (await readSetting("models")) ?? {
61
+ source: "own",
62
+ bindings: {},
63
+ };
64
+ const keys = {};
65
+ for (const name of LOCAL_KEYS) {
66
+ keys[name] = (await vaultGet(`key.${name}`)) ?? envKey(name);
67
+ }
68
+ local.keys = keys;
69
+ }
70
+ const tenantConfigs = new Map();
71
+ const tenantSetting = (tenant) => `models:${tenant}`;
72
+ /** A team thinks on its credits unless it brought a key: `own`, with the credits behind it. */
73
+ function teamSettings(bindings) {
74
+ return { source: "own", bindings, creditFallback: true };
75
+ }
76
+ async function tenantConfig(tenant) {
77
+ const hit = tenantConfigs.get(tenant);
78
+ if (hit) {
79
+ return hit;
80
+ }
81
+ const stored = await readSetting(tenantSetting(tenant));
82
+ const value = {
83
+ settings: teamSettings(stored?.bindings ?? {}),
84
+ keys: (stored?.keys ? unseal(stored.keys) : null) ?? {},
85
+ };
86
+ tenantConfigs.set(tenant, value);
87
+ return value;
88
+ }
89
+ async function config() {
90
+ if (!managed) {
91
+ return local;
92
+ }
93
+ const tenant = currentTenantOrNull();
94
+ return tenant ? tenantConfig(tenant) : { settings: teamSettings({}), keys: {} };
95
+ }
96
+ function mergeBindings(current, changes = {}) {
97
+ const bindings = { ...current };
98
+ for (const [cls, ref] of Object.entries(changes)) {
99
+ if (ref?.trim()) {
100
+ bindings[cls] = ref.trim();
101
+ }
102
+ else {
103
+ delete bindings[cls];
104
+ }
105
+ }
106
+ return bindings;
107
+ }
108
+ export function localModelSettings() {
109
+ return {
110
+ ...local.settings,
111
+ creditFallback: local.settings.creditFallback !== false,
112
+ bindings: Object.fromEntries(MODEL_CLASSES.map((c) => [c, ownRef(local, c) || local.settings.bindings[c] || ""])),
113
+ ollamaUrl: local.settings.ollamaUrl ?? env.ollamaUrl,
114
+ keys: Object.fromEntries(LOCAL_KEYS.map((k) => [k, Boolean(local.keys[k])])),
115
+ };
116
+ }
117
+ export async function saveLocalModels(input) {
118
+ await writeSetting("models", {
119
+ source: input.source ?? local.settings.source,
120
+ bindings: mergeBindings(local.settings.bindings, input.bindings),
121
+ ollamaUrl: input.ollamaUrl?.trim() || local.settings.ollamaUrl,
122
+ creditFallback: input.creditFallback ?? local.settings.creditFallback,
123
+ });
124
+ for (const [name, value] of Object.entries(input.keys ?? {})) {
125
+ if (value?.trim()) {
126
+ await vaultSet(`key.${name}`, value.trim());
127
+ }
128
+ else {
129
+ await vaultDelete(`key.${name}`);
130
+ }
131
+ }
132
+ await loadLocalModels();
133
+ }
134
+ /** A team's keys and bindings on a runtime of a Manage-App. No client and no local model there. */
135
+ async function saveTenantModels(tenant, input) {
136
+ const current = await tenantConfig(tenant);
137
+ const keys = { ...current.keys };
138
+ for (const [name, value] of Object.entries(input.keys ?? {})) {
139
+ if (value?.trim()) {
140
+ keys[name] = value.trim();
141
+ }
142
+ else {
143
+ delete keys[name];
144
+ }
145
+ }
146
+ await writeSetting(tenantSetting(tenant), {
147
+ bindings: mergeBindings(current.settings.bindings, input.bindings),
148
+ keys: Object.keys(keys).length ? seal(keys) : null,
149
+ });
150
+ tenantConfigs.delete(tenant);
151
+ }
152
+ /** Saves for this machine, or for the current tenant of a Manage-App's runtime. */
153
+ export async function saveModels(input) {
154
+ if (managed) {
155
+ await saveTenantModels(currentTenant(), input);
156
+ }
157
+ else {
158
+ await saveLocalModels(input);
159
+ }
160
+ }
161
+ /** A stored key, for a check of the key itself; never sent to the studio. */
162
+ export async function storedKey(name) {
163
+ return (await config()).keys[name];
164
+ }
165
+ // --- where a class runs -------------------------------------------------------------
166
+ const isText = (cls) => TEXT_CLASSES.includes(cls);
167
+ /**
168
+ * `fal:fal-ai/flux/schnell` → provider fal, id `fal-ai/flux/schnell`. Without a provider the
169
+ * binding is an AI Gateway id (`google/veo-…`) or an installed client (`codex/default`).
170
+ */
171
+ function parse(ref) {
172
+ const match = /^([a-z]+):(.*)$/s.exec(ref);
173
+ if (match) {
174
+ const [, provider, id] = match;
175
+ // Ollama and the gateway name a model as the gateway does; the others are their own vendor.
176
+ const vendor = provider === "ollama" || provider === "gateway" ? id.split("/")[0] : provider;
177
+ return { provider, id, vendor };
178
+ }
179
+ return { provider: null, id: ref, vendor: ref.split("/")[0] };
180
+ }
181
+ const vendorOf = (ref) => parse(ref).vendor;
182
+ /** A model on this machine: Ollama, or Apple Intelligence on a Mac (harness/apple.ts). */
183
+ const isLocalProvider = (provider) => provider === "ollama" || provider === "apple";
184
+ /** The part of `codex/default` after the client. */
185
+ const aliasOf = (id) => id.split("/").slice(1).join("/");
186
+ /** Whether this configuration can reach a binding without credits. */
187
+ function reachable(cfg, ref) {
188
+ if (!ref || isCreditsRef(ref)) {
189
+ return false;
190
+ }
191
+ const { provider, vendor } = parse(ref);
192
+ if ((provider === null && isHarnessVendor(vendor)) || isLocalProvider(provider)) {
193
+ // An installed client and a local model are of this machine.
194
+ return !managed;
195
+ }
196
+ if (provider === null || provider === "gateway") {
197
+ return (Boolean(cfg.keys.gateway) ||
198
+ (provider === null &&
199
+ (vendor === "openai" || vendor === "anthropic") &&
200
+ Boolean(cfg.keys[vendor])));
201
+ }
202
+ return Boolean(cfg.keys[provider]);
203
+ }
204
+ /** What a class runs on without credits: a client, an own key or a local model. "" = no way of its own. */
205
+ function ownRef(cfg, cls) {
206
+ const bound = cfg.settings.bindings[cls];
207
+ // A text class bound to Apple Intelligence (Classifier, Standard) thinks on this Mac whatever
208
+ // the source is: the others keep the installed client, the account's credits or the keys.
209
+ if (isText(cls) && bound && parse(bound).provider === "apple") {
210
+ return reachable(cfg, bound) ? bound : "";
211
+ }
212
+ const client = managed ? null : harness(cfg.settings.source);
213
+ if (isText(cls) && client) {
214
+ // Text thinks on the installed client; a binding counts only where it names that client.
215
+ return bound?.startsWith(`${client.id}/`)
216
+ ? bound
217
+ : `${client.id}/${client.classes[cls]}`;
218
+ }
219
+ // Text on the account's credits: every text class goes there.
220
+ if (isText(cls) && cfg.settings.source === "account") {
221
+ return "";
222
+ }
223
+ if (isCreditsRef(bound)) {
224
+ return "";
225
+ }
226
+ if (bound) {
227
+ return reachable(cfg, bound) ? bound : "";
228
+ }
229
+ // A client that makes images on the sign-in makes them too, before any key is paid for.
230
+ if (cls === "image" && client?.image) {
231
+ return `${client.id}/${client.image.alias}`;
232
+ }
233
+ // The defaults name gateway models; without a gateway key nothing reaches them.
234
+ return reachable(cfg, env.models[cls]) ? env.models[cls] : "";
235
+ }
236
+ /** What a class makes, to say that nothing makes it. */
237
+ const MAKES = {
238
+ classifier: "makesText",
239
+ standard: "makesText",
240
+ high: "makesText",
241
+ highest: "makesText",
242
+ image: "makesImages",
243
+ video: "makesVideos",
244
+ speech: "makesSpeech",
245
+ audio: "makesVoiceNotes",
246
+ };
247
+ /** Why a class has no way: the provider picked for it lacks its key, or nothing is set up. */
248
+ function missingText(cfg, cls) {
249
+ const bound = cfg.settings.bindings[cls];
250
+ if (isCreditsRef(bound)) {
251
+ return note("creditsNotSignedIn");
252
+ }
253
+ const provider = bound ? providerInfo(providerOfRef(bound) ?? "") : null;
254
+ if (provider) {
255
+ return note("providerKeyMissing", { provider: provider.name, makes: note(MAKES[cls]) });
256
+ }
257
+ return note("noModelFor", { makes: note(MAKES[cls]) });
258
+ }
259
+ /**
260
+ * The way to the credits. On a Manage-App's runtime always; alone, through the linked account —
261
+ * where text runs on it (`chosen`), or as the fallback for what has no way of its own.
262
+ */
263
+ async function creditAccess(cfg, chosen) {
264
+ if (managed) {
265
+ return {
266
+ baseUrl: env.manage.gatewayUrl,
267
+ token: env.manage.serviceKey,
268
+ tenant: currentTenantOrNull(),
269
+ };
270
+ }
271
+ if (!chosen && (cfg.settings.creditFallback === false || !(await linkedAccount()))) {
272
+ return null;
273
+ }
274
+ const token = await accountToken();
275
+ if (!token) {
276
+ if (chosen) {
277
+ throw new ModelUnavailableError(note("accountNoLongerSigned"));
278
+ }
279
+ return null;
280
+ }
281
+ return { baseUrl: env.local.gatewayUrl, token, tenant: null };
282
+ }
283
+ /** A class goes to the credits on purpose: text on the account, or a binding that says so. */
284
+ function creditsChosen(cfg, cls) {
285
+ return (isCreditsRef(cfg.settings.bindings[cls]) ||
286
+ (isText(cls) && cfg.settings.source === "account" && !managed));
287
+ }
288
+ async function routeOf(cls) {
289
+ const cfg = await config();
290
+ const ref = ownRef(cfg, cls);
291
+ if (ref) {
292
+ return { kind: "own", cfg, ref };
293
+ }
294
+ const access = await creditAccess(cfg, creditsChosen(cfg, cls));
295
+ if (access) {
296
+ return { kind: "credits", access, model: creditsModelOf(cfg.settings.bindings[cls]) };
297
+ }
298
+ throw new ModelUnavailableError(missingText(cfg, cls));
299
+ }
300
+ /** The key of a provider, or why the call cannot be made. */
301
+ function need(cfg, name) {
302
+ const key = cfg.keys[name];
303
+ if (!key) {
304
+ throw new ModelUnavailableError(note("noApiKeyFor", { provider: providerInfo(name)?.name ?? name }));
305
+ }
306
+ return key;
307
+ }
308
+ function gatewayClient(access, meta) {
309
+ return createGateway({
310
+ baseURL: `${access.baseUrl}/v4/ai`,
311
+ apiKey: access.token,
312
+ headers: {
313
+ ...(access.tenant ? { "x-wizards-tenant": access.tenant } : {}),
314
+ ...(meta.runId ? { "x-wizards-run": meta.runId } : {}),
315
+ ...(meta.stepId ? { "x-wizards-step": meta.stepId } : {}),
316
+ ...(meta.effort ? { "x-wizards-effort": meta.effort } : {}),
317
+ },
318
+ });
319
+ }
320
+ let gatewayCatalog = null;
321
+ /** What the gateway binds each class to, with prices in credits. Cached for five minutes. */
322
+ export async function classCatalog() {
323
+ const cfg = await config();
324
+ const access = await creditAccess(cfg, cfg.settings.source === "account").catch(() => null);
325
+ if (!access) {
326
+ return null;
327
+ }
328
+ if (gatewayCatalog &&
329
+ gatewayCatalog.base === access.baseUrl &&
330
+ Date.now() - gatewayCatalog.at < 300_000) {
331
+ return gatewayCatalog.value;
332
+ }
333
+ try {
334
+ const res = await fetch(`${access.baseUrl}/v1/models`, {
335
+ headers: {
336
+ authorization: `Bearer ${access.token}`,
337
+ ...(access.tenant ? { "x-wizards-tenant": access.tenant } : {}),
338
+ },
339
+ signal: AbortSignal.timeout(10_000),
340
+ });
341
+ if (!res.ok) {
342
+ return gatewayCatalog?.value ?? null;
343
+ }
344
+ const value = (await res.json());
345
+ gatewayCatalog = { at: Date.now(), base: access.baseUrl, value };
346
+ return value;
347
+ }
348
+ catch {
349
+ return gatewayCatalog?.value ?? null;
350
+ }
351
+ }
352
+ /** A call on the credits: the model a binding names, or the gateway's model of the class. */
353
+ async function viaCredits(route, cls, meta, pick) {
354
+ const bound = route.model ?? (await classCatalog())?.classes[cls]?.model ?? "";
355
+ return {
356
+ model: pick(gatewayClient(route.access, meta), route.model ?? `wizards/${cls}`),
357
+ ref: bound,
358
+ vendor: vendorOf(bound.replace(/^[a-z]+:/, "")),
359
+ gateway: true,
360
+ metered: true,
361
+ };
362
+ }
363
+ /** The installed client a binding names, checked to be there. */
364
+ async function installedClient(ref) {
365
+ const { provider, vendor } = parse(ref);
366
+ const client = provider === null ? harness(vendor) : null;
367
+ if (client && !(await detectHarness(client.id))?.version) {
368
+ throw new ModelUnavailableError(notInstalled(client));
369
+ }
370
+ return client;
371
+ }
372
+ async function ownLanguageModel(cfg, ref, meta) {
373
+ const { provider, id, vendor } = parse(ref);
374
+ const client = await installedClient(ref);
375
+ if (client) {
376
+ return { model: client.model(aliasOf(id), meta.effort), gateway: false };
377
+ }
378
+ switch (provider) {
379
+ case "ollama":
380
+ return {
381
+ model: createOpenAI({
382
+ baseURL: cfg.settings.ollamaUrl ?? env.ollamaUrl,
383
+ apiKey: "ollama",
384
+ }).chat(id),
385
+ gateway: false,
386
+ };
387
+ case "apple": {
388
+ const status = await appleStatus();
389
+ if (!status.available) {
390
+ throw new ModelUnavailableError(appleUnavailableNote(status));
391
+ }
392
+ return { model: appleModel(id), gateway: false };
393
+ }
394
+ case "openai":
395
+ return { model: createOpenAI({ apiKey: need(cfg, "openai") })(id), gateway: false };
396
+ case "anthropic":
397
+ return { model: createAnthropic({ apiKey: need(cfg, "anthropic") })(id), gateway: false };
398
+ case "google":
399
+ return { model: createGoogle({ apiKey: need(cfg, "google") })(id), gateway: false };
400
+ case null:
401
+ case "gateway":
402
+ if (cfg.keys.gateway) {
403
+ return { model: createGateway({ apiKey: cfg.keys.gateway })(id), gateway: true };
404
+ }
405
+ if (provider === null && vendor === "openai" && cfg.keys.openai) {
406
+ return { model: createOpenAI({ apiKey: cfg.keys.openai })(aliasOf(id)), gateway: false };
407
+ }
408
+ if (provider === null && vendor === "anthropic" && cfg.keys.anthropic) {
409
+ return {
410
+ model: createAnthropic({ apiKey: cfg.keys.anthropic })(aliasOf(id)),
411
+ gateway: false,
412
+ };
413
+ }
414
+ }
415
+ throw new ModelUnavailableError(note("modelNotUsedYet", { ref }));
416
+ }
417
+ /**
418
+ * The language model a text class runs on. A run watched from a device that offers the class
419
+ * (the app's Apple Intelligence, engine/device.ts) asks the device first and falls back to this.
420
+ */
421
+ export async function textModel(cls, meta = {}) {
422
+ const resolved = await ownTextModel(cls, meta);
423
+ const device = meta.runId && isText(cls) ? await deviceThinks(meta.runId, cls) : null;
424
+ return device
425
+ ? { ...resolved, model: new DeviceModel(meta.runId, resolved.model, device.maxChars) }
426
+ : resolved;
427
+ }
428
+ async function ownTextModel(cls, meta) {
429
+ const route = await routeOf(cls);
430
+ if (route.kind === "credits") {
431
+ return viaCredits(route, cls, meta, (client, id) => client(id));
432
+ }
433
+ const { model, gateway } = await ownLanguageModel(route.cfg, route.ref, meta);
434
+ return { model, ref: route.ref, vendor: vendorOf(route.ref), gateway, metered: false };
435
+ }
436
+ /**
437
+ * Hands the tools an agent will call to a model that runs them itself (an installed AI client).
438
+ * `ignorable`: the step declared no tools and has no uploads — the run's device may answer
439
+ * without the generic ones (engine/device.ts).
440
+ */
441
+ export function attachTools(resolved, tools, ignorable = false) {
442
+ if (resolved.model instanceof HarnessModel) {
443
+ resolved.model.attach(tools);
444
+ }
445
+ else if (resolved.model instanceof DeviceModel) {
446
+ resolved.model.attach(tools, ignorable);
447
+ }
448
+ }
449
+ /** Gemini image models of the gateway are chat models answering with image files. */
450
+ export function isChatImageModel(ref) {
451
+ return ref.includes("gemini") && ref.includes("image");
452
+ }
453
+ /** `chat`: the model answers through chat with an image file (`textModel("image")`). */
454
+ export async function imageModel(meta = {}) {
455
+ const route = await routeOf("image");
456
+ if (route.kind === "credits") {
457
+ const resolved = await viaCredits(route, "image", meta, (c, id) => c.imageModel(id));
458
+ return { ...resolved, chat: isChatImageModel(resolved.ref) };
459
+ }
460
+ const { cfg, ref } = route;
461
+ const { provider, id, vendor } = parse(ref);
462
+ const own = (model, gateway = false) => ({
463
+ model,
464
+ ref,
465
+ vendor,
466
+ gateway,
467
+ metered: false,
468
+ chat: false,
469
+ });
470
+ const client = await installedClient(ref);
471
+ if (client?.image) {
472
+ return own(client.image.model(aliasOf(id)));
473
+ }
474
+ switch (provider) {
475
+ case "openai":
476
+ return own(createOpenAI({ apiKey: need(cfg, "openai") }).image(id));
477
+ case "google":
478
+ return own(createGoogle({ apiKey: need(cfg, "google") }).image(id));
479
+ case "fal":
480
+ return own(createFal({ apiKey: need(cfg, "fal") }).image(id));
481
+ case "replicate":
482
+ return own(createReplicate({ apiToken: need(cfg, "replicate") }).image(id));
483
+ case null:
484
+ case "gateway":
485
+ if (cfg.keys.gateway) {
486
+ return {
487
+ ...own(createGateway({ apiKey: cfg.keys.gateway }).imageModel(id), true),
488
+ chat: isChatImageModel(id),
489
+ };
490
+ }
491
+ if (provider === null && vendor === "openai" && cfg.keys.openai) {
492
+ return own(createOpenAI({ apiKey: cfg.keys.openai }).image(aliasOf(id)));
493
+ }
494
+ }
495
+ throw new ModelUnavailableError(note("noModelForImages"));
496
+ }
497
+ export async function videoModel(meta = {}) {
498
+ const route = await routeOf("video");
499
+ if (route.kind === "credits") {
500
+ return viaCredits(route, "video", meta, (c, id) => c.videoModel(id));
501
+ }
502
+ const { cfg, ref } = route;
503
+ const { provider, id, vendor } = parse(ref);
504
+ const own = (model, gateway = false) => ({
505
+ model,
506
+ ref,
507
+ vendor,
508
+ gateway,
509
+ metered: false,
510
+ });
511
+ switch (provider) {
512
+ case "google":
513
+ return own(createGoogle({ apiKey: need(cfg, "google") }).video(id));
514
+ case "fal":
515
+ return own(createFal({ apiKey: need(cfg, "fal") }).video(id));
516
+ case "replicate":
517
+ return own(createReplicate({ apiToken: need(cfg, "replicate") }).video(id));
518
+ case null:
519
+ case "gateway":
520
+ if (cfg.keys.gateway) {
521
+ return own(createGateway({ apiKey: cfg.keys.gateway }).videoModel(id), true);
522
+ }
523
+ }
524
+ throw new ModelUnavailableError(note("noModelForVideos"));
525
+ }
526
+ /** The model that reads a text aloud. */
527
+ export async function speechModel(meta = {}) {
528
+ const route = await routeOf("speech");
529
+ if (route.kind === "credits") {
530
+ const resolved = await viaCredits(route, "speech", meta, (c, id) => c.speechModel(id));
531
+ if (!resolved.ref) {
532
+ throw new ModelUnavailableError(note("noModelForSpeech"));
533
+ }
534
+ return resolved;
535
+ }
536
+ const { cfg, ref } = route;
537
+ const { provider, id, vendor } = parse(ref);
538
+ const own = (model, gateway = false) => ({
539
+ model,
540
+ ref,
541
+ vendor,
542
+ gateway,
543
+ metered: false,
544
+ });
545
+ switch (provider) {
546
+ case "openai":
547
+ return own(createOpenAI({ apiKey: need(cfg, "openai") }).speech(id));
548
+ case "google":
549
+ return own(createGoogle({ apiKey: need(cfg, "google") }).speech(id));
550
+ case "fal":
551
+ return own(createFal({ apiKey: need(cfg, "fal") }).speech(id));
552
+ case "elevenlabs":
553
+ return own(createElevenLabs({ apiKey: need(cfg, "elevenlabs") }).speech(id));
554
+ case null:
555
+ case "gateway":
556
+ if (cfg.keys.gateway) {
557
+ return own(createGateway({ apiKey: cfg.keys.gateway }).speechModel(id), true);
558
+ }
559
+ if (provider === null && vendor === "openai" && cfg.keys.openai) {
560
+ return own(createOpenAI({ apiKey: cfg.keys.openai }).speech(aliasOf(id)));
561
+ }
562
+ }
563
+ throw new ModelUnavailableError(note("noModelForSpeech"));
564
+ }
565
+ /**
566
+ * What turns a voice note into text: a transcription model (ElevenLabs Scribe, Whisper), or a
567
+ * chat model that takes audio files (Gemini) — that one also on the credits.
568
+ */
569
+ export async function listenerModel(meta = {}) {
570
+ const route = await routeOf("audio");
571
+ if (route.kind === "credits") {
572
+ // On the credits a transcription model is called as one: named so, or bound to the class.
573
+ const catalog = await classCatalog();
574
+ const transcribes = route.model
575
+ ? catalog?.models?.find((m) => m.id === route.model)?.kind === "transcription"
576
+ : Boolean(catalog?.classes.audio?.transcribes);
577
+ if (transcribes) {
578
+ return {
579
+ kind: "transcription",
580
+ resolved: await viaCredits(route, "audio", meta, (c, id) => c.transcriptionModel(id)),
581
+ };
582
+ }
583
+ }
584
+ if (route.kind === "own") {
585
+ const { cfg, ref } = route;
586
+ const { provider, id, vendor } = parse(ref);
587
+ if (provider === "apple") {
588
+ // This Mac writes the note down itself (SpeechAnalyzer); nothing is paid.
589
+ return {
590
+ kind: "transcription",
591
+ resolved: {
592
+ model: appleTranscriptionModel(),
593
+ ref,
594
+ vendor,
595
+ gateway: false,
596
+ metered: false,
597
+ },
598
+ };
599
+ }
600
+ const transcriber = provider === "elevenlabs"
601
+ ? createElevenLabs({ apiKey: need(cfg, "elevenlabs") }).transcription(id)
602
+ : provider === "fal"
603
+ ? createFal({ apiKey: need(cfg, "fal") }).transcription(id)
604
+ : provider === "openai" && /transcribe|whisper/.test(id)
605
+ ? createOpenAI({ apiKey: need(cfg, "openai") }).transcription(id)
606
+ : null;
607
+ if (transcriber) {
608
+ return {
609
+ kind: "transcription",
610
+ resolved: { model: transcriber, ref, vendor, gateway: false, metered: false },
611
+ };
612
+ }
613
+ }
614
+ return { kind: "chat", resolved: await textModel("audio", meta) };
615
+ }
616
+ /**
617
+ * The model that turns passages and questions into vectors for a project's document index.
618
+ * Null where none is set up — an installed AI client has no such model — and the index then
619
+ * works on keywords alone.
620
+ */
621
+ export async function embeddingModel() {
622
+ const cfg = await config();
623
+ if (managed) {
624
+ const access = await creditAccess(cfg, true).catch(() => null);
625
+ const bound = access ? (await classCatalog())?.classes.embedding : null;
626
+ return access && bound
627
+ ? { model: gatewayClient(access, {}).embeddingModel("wizards/embedding"), ref: bound.model }
628
+ : null;
629
+ }
630
+ const ref = env.models.embedding;
631
+ const { provider, id, vendor } = parse(ref);
632
+ if (provider === "ollama") {
633
+ const ollama = createOpenAI({
634
+ baseURL: cfg.settings.ollamaUrl ?? env.ollamaUrl,
635
+ apiKey: "ollama",
636
+ });
637
+ return { model: ollama.embedding(id), ref };
638
+ }
639
+ if ((provider === null || provider === "gateway") && cfg.keys.gateway) {
640
+ return { model: createGateway({ apiKey: cfg.keys.gateway }).embeddingModel(id), ref };
641
+ }
642
+ if ((provider === "openai" || vendor === "openai") && cfg.keys.openai) {
643
+ return {
644
+ model: createOpenAI({ apiKey: cfg.keys.openai }).embedding(provider ? id : aliasOf(id)),
645
+ ref,
646
+ };
647
+ }
648
+ // No key of its own: the linked account's gateway, where its catalog has the class.
649
+ const access = await creditAccess(cfg, false).catch(() => null);
650
+ const bound = access ? (await classCatalog())?.classes.embedding : null;
651
+ return access && bound
652
+ ? { model: gatewayClient(access, {}).embeddingModel("wizards/embedding"), ref: bound.model }
653
+ : null;
654
+ }
655
+ /**
656
+ * The way to TypeSafe's Jev, a classifier that answers typed questions with probabilities and
657
+ * writes no text: the credits on a Manage-App's runtime; alone, an AI Gateway key, a TypeSafe
658
+ * key, or the linked account's credits. Null when there is none.
659
+ */
660
+ export async function systemOneAccess() {
661
+ const cfg = await config();
662
+ const viaCredits = (access) => ({
663
+ url: `${access.baseUrl}/v4/ai/systemone`,
664
+ headers: {
665
+ authorization: `Bearer ${access.token}`,
666
+ // Booked under the classifier class; its evaluation model answers.
667
+ "ai-model-id": "wizards/classifier",
668
+ ...(access.tenant ? { "x-wizards-tenant": access.tenant } : {}),
669
+ },
670
+ model: null,
671
+ });
672
+ if (managed) {
673
+ const access = await creditAccess(cfg, true).catch(() => null);
674
+ return access ? viaCredits(access) : null;
675
+ }
676
+ if (cfg.keys.gateway) {
677
+ return {
678
+ url: "https://ai-gateway.vercel.sh/typesafe/v1/systemone",
679
+ headers: { authorization: `Bearer ${cfg.keys.gateway}` },
680
+ model: "typesafe-ai/jev",
681
+ };
682
+ }
683
+ if (env.typesafeKey) {
684
+ return {
685
+ url: "https://api.typesafe.ai/v1/systemone",
686
+ headers: { authorization: `Bearer ${env.typesafeKey}` },
687
+ model: "jev-latest",
688
+ };
689
+ }
690
+ const access = await creditAccess(cfg, false).catch(() => null);
691
+ return access ? viaCredits(access) : null;
692
+ }
693
+ /** Search tools an AI Gateway runs itself; they travel in the request, so any gateway client names them. */
694
+ export const gatewayTools = createGateway({ apiKey: "unused" }).tools;
695
+ // --- An AI Gateway key on Vercel's free tier ------------------------------------------
696
+ /** Where Vercel tops an AI Gateway account up to paid credits. */
697
+ const GATEWAY_TOP_UP = "https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dtop-up";
698
+ /**
699
+ * The model Vercel refused because the key's account is on the free tier ("" where the answer
700
+ * names none); null where the error is something else.
701
+ */
702
+ export function freeTierRefusal(err) {
703
+ let text = "";
704
+ for (let e = err; e && text.length < 100_000; e = e.cause) {
705
+ text += ` ${e.message ?? ""} ${e.responseBody ?? ""}`;
706
+ }
707
+ if (!/RestrictedModelsError|Free tier users do not have access/i.test(text)) {
708
+ return null;
709
+ }
710
+ return /"originalModelId":"([^"]+)"/.exec(text)?.[1] ?? "";
711
+ }
712
+ /** What the person reads when Vercel's free tier leaves a model out, and how to fix it. */
713
+ export function freeTierText(model) {
714
+ return note("freeTier", {
715
+ model: model ? note("quoted", { text: model }) : note("thisModel"),
716
+ url: GATEWAY_TOP_UP,
717
+ });
718
+ }
719
+ /** What a key was answered for a model: refusals are asked again soon, as a top-up lifts them. */
720
+ const gatewayProbes = new Map();
721
+ const probeId = (key, id) => `${createHash("sha256").update(key).digest("hex").slice(0, 16)}|${id}`;
722
+ /** A call refused for the free tier: the next run's check knows before it starts. */
723
+ export async function noteFreeTierRefusal(err) {
724
+ const model = freeTierRefusal(err);
725
+ const key = model ? (await config()).keys.gateway : undefined;
726
+ if (model && key) {
727
+ gatewayProbes.set(probeId(key, model), { refused: true, at: Date.now() });
728
+ }
729
+ }
730
+ /**
731
+ * Whether the key's account may call the model. Vercel refuses before any provider is asked, so
732
+ * a refused probe costs nothing; an allowed one costs a token or a word. Images (but those a chat
733
+ * model makes) and videos cannot be asked that cheaply: only a refusal already seen counts.
734
+ */
735
+ async function gatewayRefuses(key, id, cls) {
736
+ const at = probeId(key, id);
737
+ const known = gatewayProbes.get(at);
738
+ if (known && Date.now() - known.at < (known.refused ? 300_000 : 86_400_000)) {
739
+ return known.refused;
740
+ }
741
+ if (cls === "video" || (cls === "image" && !isChatImageModel(id))) {
742
+ return known?.refused ?? false;
743
+ }
744
+ const gateway = createGateway({ apiKey: key });
745
+ const limits = { maxRetries: 0, abortSignal: AbortSignal.timeout(15_000) };
746
+ let refused = false;
747
+ try {
748
+ if (cls === "speech") {
749
+ await generateSpeech({ model: gateway.speechModel(id), text: "Ok.", ...limits });
750
+ }
751
+ else {
752
+ await generateText({ model: gateway(id), prompt: "Ok.", maxOutputTokens: 1, ...limits });
753
+ }
754
+ }
755
+ catch (err) {
756
+ // Anything else (a limit of the model, the network) is not the tier: the run will tell.
757
+ refused = freeTierRefusal(err) !== null;
758
+ }
759
+ gatewayProbes.set(at, { refused, at: Date.now() });
760
+ return refused;
761
+ }
762
+ /** The realtime speech model a live conversation runs on; a setting of the install may name another. */
763
+ const CONVERSATION_MODEL = env.conversationModel || "gpt-realtime";
764
+ /**
765
+ * What a live conversation with a wizard runs on: the tenant's own OpenAI key, until a class of
766
+ * its own exists on the Models page and on the account's credits.
767
+ */
768
+ export async function conversationAccess() {
769
+ const cfg = await config();
770
+ if (!cfg.keys.openai) {
771
+ throw new ModelUnavailableError(note("noOpenaiApiKey"));
772
+ }
773
+ return { apiKey: cfg.keys.openai, model: CONVERSATION_MODEL };
774
+ }
775
+ /** Whether a live conversation can be started here. */
776
+ export async function conversationAvailable() {
777
+ return Boolean((await config()).keys.openai);
778
+ }
779
+ /** Why the own AI Gateway key cannot reach what a class runs on, or null. */
780
+ async function gatewayProblem(cls, ref) {
781
+ const { provider, id } = parse(ref);
782
+ const key = (await config()).keys.gateway;
783
+ if (!key || (provider !== null && provider !== "gateway")) {
784
+ return null;
785
+ }
786
+ return (await gatewayRefuses(key, id, cls)) ? freeTierText(id) : null;
787
+ }
788
+ /**
789
+ * Whether a class can run here, found out without calling it: null, or what the person reads.
790
+ * A class the gateway serves counts as there unless its catalog leaves it unbound.
791
+ */
792
+ export async function classProblem(cls) {
793
+ try {
794
+ const resolved = cls === "image"
795
+ ? await imageModel()
796
+ : cls === "video"
797
+ ? await videoModel()
798
+ : cls === "speech"
799
+ ? await speechModel()
800
+ : cls === "audio"
801
+ ? (await listenerModel()).resolved
802
+ : await textModel(cls);
803
+ if (resolved.metered) {
804
+ const catalog = await classCatalog();
805
+ const named = creditsModelOf((await config()).settings.bindings[cls]);
806
+ if (named) {
807
+ return catalog?.models && !catalog.models.some((m) => m.id === named)
808
+ ? note("modelNotAvailableAccount")
809
+ : null;
810
+ }
811
+ return catalog?.classes && !catalog.classes[cls] ? note("accountNoModelSet") : null;
812
+ }
813
+ const client = harness(resolved.vendor);
814
+ if (client && (await detectHarness(client.id))?.auth === "none") {
815
+ return signedOut(client);
816
+ }
817
+ return resolved.gateway ? await gatewayProblem(cls, resolved.ref) : null;
818
+ }
819
+ catch (err) {
820
+ if (err instanceof ModelUnavailableError) {
821
+ return saidOf(err);
822
+ }
823
+ throw err;
824
+ }
825
+ }
826
+ /** Clients that can make a film: they work in a folder of their own with a shell and skills. */
827
+ const FILM_CLIENTS = new Set(["claude"]);
828
+ /**
829
+ * The installed client a film step of this class runs on, and the model it names; or why a
830
+ * film cannot be made here. Films never run on credits or keys: one takes a client many minutes.
831
+ */
832
+ export async function filmClient(cls) {
833
+ const cfg = await config();
834
+ const client = managed ? null : harness(cfg.settings.source);
835
+ if (!client || !FILM_CLIENTS.has(client.id)) {
836
+ return {
837
+ problem: note("claudeCodeMakesFilms"),
838
+ };
839
+ }
840
+ const status = await detectHarness(client.id);
841
+ if (!status?.version) {
842
+ return { problem: notInstalled(client) };
843
+ }
844
+ if (status.auth === "none") {
845
+ return { problem: signedOut(client) };
846
+ }
847
+ // A class that thinks on Apple Intelligence makes its films on the client's model of the class.
848
+ const ref = ownRef(cfg, cls);
849
+ return {
850
+ client,
851
+ alias: ref.startsWith(`${client.id}/`) ? ref.slice(client.id.length + 1) : client.classes[cls],
852
+ };
853
+ }
854
+ /** Whether any text model can answer at all: the studio says so before the first chat turn. */
855
+ export async function hasTextModel() {
856
+ try {
857
+ await textModel("standard");
858
+ return true;
859
+ }
860
+ catch {
861
+ return false;
862
+ }
863
+ }
864
+ export async function classWays() {
865
+ const cfg = await config();
866
+ const ways = {};
867
+ for (const cls of MODEL_CLASSES) {
868
+ const ref = ownRef(cfg, cls);
869
+ if (ref) {
870
+ const { provider, vendor } = parse(ref);
871
+ const client = provider === null && isHarnessVendor(vendor);
872
+ ways[cls] = {
873
+ kind: client ? "client" : isLocalProvider(provider) ? "local" : "key",
874
+ by: client
875
+ ? vendor
876
+ : isLocalProvider(provider)
877
+ ? provider
878
+ : providerOfRef(ref) === "gateway" && !cfg.keys.gateway
879
+ ? vendor
880
+ : providerOfRef(ref),
881
+ ref,
882
+ problem: client
883
+ ? null
884
+ : provider === "apple"
885
+ ? await appleProblem(cls)
886
+ : await gatewayProblem(cls, ref),
887
+ };
888
+ continue;
889
+ }
890
+ let problem = null;
891
+ const access = await creditAccess(cfg, creditsChosen(cfg, cls)).catch((err) => {
892
+ problem = saidOf(err);
893
+ return null;
894
+ });
895
+ ways[cls] = access
896
+ ? {
897
+ kind: "credits",
898
+ by: null,
899
+ ref: creditsModelOf(cfg.settings.bindings[cls]) ??
900
+ (await classCatalog())?.classes[cls]?.model ??
901
+ "",
902
+ problem: null,
903
+ }
904
+ : { kind: "none", by: null, ref: "", problem: problem ?? missingText(cfg, cls) };
905
+ }
906
+ return ways;
907
+ }
908
+ /**
909
+ * Why this Mac's models do not take a class right now; null while they do. Text needs Apple
910
+ * Intelligence switched on. A voice note needs only the Mac (macOS 26 on Apple silicon, the
911
+ * helper; the system fetches a language on its first note), and ffmpeg for a recording it
912
+ * cannot read itself: a browser's WebM or Ogg.
913
+ */
914
+ async function appleProblem(cls) {
915
+ const status = await appleStatus();
916
+ if (cls !== "audio") {
917
+ return status.available ? null : appleUnavailableNote(status);
918
+ }
919
+ if (!status.supported || status.reason === "missing") {
920
+ return appleUnavailableNote(status);
921
+ }
922
+ return (await hasFfmpeg()) ? null : note("macNeedsFfmpegTranscribe");
923
+ }
924
+ /** At start the ways are asked once, so a model the key cannot reach shows before a run meets it. */
925
+ export async function checkModelsAtStart() {
926
+ for (const [cls, way] of Object.entries(await classWays())) {
927
+ if (way.kind !== "none" && way.problem) {
928
+ console.warn(`models: ${cls}: ${saidText(way.problem, "en")}`);
929
+ }
930
+ }
931
+ }
932
+ /** The ways with their problems in a language, for whoever reads them. */
933
+ export function waysIn(ways, lang) {
934
+ return Object.fromEntries(Object.entries(ways).map(([cls, way]) => [
935
+ cls,
936
+ { ...way, problem: way.problem && saidText(way.problem, lang) },
937
+ ]));
938
+ }
939
+ /** What the settings page shows: the choices, which keys are there (never the keys), and the ways. */
940
+ export async function modelSettings(lang = "en") {
941
+ const cfg = await config();
942
+ return {
943
+ source: cfg.settings.source,
944
+ bindings: cfg.settings.bindings,
945
+ ollamaUrl: cfg.settings.ollamaUrl ?? env.ollamaUrl,
946
+ creditFallback: cfg.settings.creditFallback !== false,
947
+ keys: Object.fromEntries(LOCAL_KEYS.map((k) => [k, Boolean(cfg.keys[k])])),
948
+ ways: waysIn(await classWays(), lang),
949
+ /** Apple Intelligence on this Mac: shown where the machine can run it, picked once it answers. */
950
+ apple: managed ? null : await appleStatus(),
951
+ /** What a class costs on the credits, where they are at hand. */
952
+ prices: (await classCatalog())?.classes ?? null,
953
+ /** The media models the credits pay for, which a binding may name. */
954
+ creditModels: (await classCatalog())?.models ?? [],
955
+ };
956
+ }
957
+ // --- Checking a key ------------------------------------------------------------------
958
+ /** A cheap authenticated call: whether the provider takes the key. Nothing is generated. */
959
+ export async function checkKey(name, key, lang = "en") {
960
+ const requests = {
961
+ openai: ["https://api.openai.com/v1/models", { authorization: `Bearer ${key}` }],
962
+ anthropic: [
963
+ "https://api.anthropic.com/v1/models?limit=1",
964
+ { "x-api-key": key, "anthropic-version": "2023-06-01" },
965
+ ],
966
+ google: [
967
+ "https://generativelanguage.googleapis.com/v1beta/models?pageSize=1",
968
+ { "x-goog-api-key": key },
969
+ ],
970
+ fal: [
971
+ "https://api.fal.ai/v1/models?endpoint_id=fal-ai/flux/schnell",
972
+ { authorization: `Key ${key}` },
973
+ ],
974
+ elevenlabs: ["https://api.elevenlabs.io/v1/models", { "xi-api-key": key }],
975
+ replicate: ["https://api.replicate.com/v1/account", { authorization: `Bearer ${key}` }],
976
+ gateway: ["https://ai-gateway.vercel.sh/v1/credits", { authorization: `Bearer ${key}` }],
977
+ };
978
+ const [url, headers] = requests[name];
979
+ try {
980
+ const res = await fetch(url, { headers, signal: AbortSignal.timeout(10_000) });
981
+ if (res.ok) {
982
+ return { ok: true, message: null };
983
+ }
984
+ const body = await res.text().catch(() => "");
985
+ // A key restricted to some permissions is still a key the provider knows.
986
+ if (res.status === 403 || /missing_permissions/.test(body)) {
987
+ return { ok: true, message: null };
988
+ }
989
+ return {
990
+ ok: false,
991
+ message: res.status === 401
992
+ ? serverText(lang, "keyUnknown")
993
+ : serverText(lang, "providerError", { status: String(res.status) }),
994
+ };
995
+ }
996
+ catch {
997
+ return {
998
+ ok: false,
999
+ message: serverText(lang, "providerUnreachable"),
1000
+ };
1001
+ }
1002
+ }
1003
+ /**
1004
+ * Of these bindings, the ones for classes that run on nothing of this machine's own yet: a key
1005
+ * the setup tested is a key in use. Classes on a key, a client or a local model keep theirs —
1006
+ * choosing between them is for the settings.
1007
+ */
1008
+ function freeClasses(bindings) {
1009
+ return Object.fromEntries(Object.entries(bindings).filter(([cls]) => !ownRef(local, cls)));
1010
+ }
1011
+ const setupOutcome = () => ({
1012
+ ok: true,
1013
+ message: null,
1014
+ text: ownRef(local, "standard") || null,
1015
+ });
1016
+ /** The setup's test of one key: checked with the provider, stored, and put to use. */
1017
+ export async function setUpKey(name, key, lang = "en") {
1018
+ const value = key?.trim() || local.keys[name];
1019
+ if (!value) {
1020
+ return { ok: false, message: serverText(lang, "keyUnknown"), text: null };
1021
+ }
1022
+ const checked = await checkKey(name, value, lang);
1023
+ if (!checked.ok) {
1024
+ return { ...checked, text: null };
1025
+ }
1026
+ const provider = providerInfo(name);
1027
+ const offered = { ...provider?.text };
1028
+ for (const [cap, models] of Object.entries(provider?.models ?? {})) {
1029
+ const first = models?.[0]?.ref;
1030
+ if (cap !== "text" && first) {
1031
+ for (const cls of CAPABILITY_CLASSES[cap]) {
1032
+ offered[cls] = first;
1033
+ }
1034
+ }
1035
+ }
1036
+ // Free before the key is stored: a key can make a default reachable that it would not name.
1037
+ await saveLocalModels({ keys: { [name]: value }, bindings: freeClasses(offered) });
1038
+ return setupOutcome();
1039
+ }
1040
+ /** The setup's test of Ollama: it answers at the address and has a model to think with. */
1041
+ export async function setUpOllama(url, lang = "en") {
1042
+ const base = url.trim().replace(/\/+$/, "");
1043
+ let models;
1044
+ try {
1045
+ const res = await fetch(`${base}/models`, { signal: AbortSignal.timeout(5_000) });
1046
+ const body = (await res.json());
1047
+ models = (body.data ?? []).map((m) => m.id);
1048
+ }
1049
+ catch {
1050
+ return { ok: false, message: serverText(lang, "ollamaUnreachable"), text: null };
1051
+ }
1052
+ if (!models.length) {
1053
+ return { ok: false, message: serverText(lang, "ollamaNoModel"), text: null };
1054
+ }
1055
+ const bound = local.settings.bindings.standard?.replace(/^ollama:/, "");
1056
+ const model = bound && models.includes(bound) ? bound : models[0];
1057
+ const text = Object.fromEntries(TEXT_CLASSES.map((c) => [c, `ollama:${model}`]));
1058
+ await saveLocalModels({ ollamaUrl: base, bindings: freeClasses(text) });
1059
+ return setupOutcome();
1060
+ }
1061
+ let catalog = new Map();
1062
+ let catalogLoadedAt = 0;
1063
+ /** Public prices of the AI Gateway: what a runtime that runs alone shows as the cost of a run. */
1064
+ export async function loadCatalog(force = false) {
1065
+ if (!force && Date.now() - catalogLoadedAt < 6 * 3600_000 && catalog.size) {
1066
+ return;
1067
+ }
1068
+ try {
1069
+ const res = await fetch("https://ai-gateway.vercel.sh/v1/models", {
1070
+ signal: AbortSignal.timeout(10_000),
1071
+ });
1072
+ const body = (await res.json());
1073
+ catalog = new Map(body.data.map((m) => [m.id, m]));
1074
+ catalogLoadedAt = Date.now();
1075
+ }
1076
+ catch {
1077
+ // Prices fall back to conservative defaults below.
1078
+ }
1079
+ }
1080
+ /** The AI Gateway's prices, by its id of the model: what an own key of the same model costs too. */
1081
+ function priceOf(ref) {
1082
+ const { provider, id } = parse(ref);
1083
+ return (catalog.get(provider === null || provider === "gateway" ? id : `${provider}/${id}`)?.pricing ??
1084
+ {});
1085
+ }
1086
+ /** A model's context window in tokens, as the AI Gateway's catalog gives it; null where unknown. */
1087
+ export function contextWindowOf(ref) {
1088
+ const { provider, id } = parse(ref);
1089
+ const entry = catalog.get(provider === null || provider === "gateway" ? id : `${provider}/${id}`);
1090
+ return typeof entry?.context_window === "number" ? entry.context_window : null;
1091
+ }
1092
+ const n = (v, fallback) => {
1093
+ const x = Number(v);
1094
+ return Number.isFinite(x) ? x : fallback;
1095
+ };
1096
+ export function usageOf(u) {
1097
+ return {
1098
+ inputTokens: u?.inputTokens ?? 0,
1099
+ outputTokens: u?.outputTokens ?? 0,
1100
+ cachedInputTokens: u?.cachedInputTokens ?? u?.inputTokenDetails?.cacheReadTokens ?? 0,
1101
+ };
1102
+ }
1103
+ /** A class's model as Mastra's `Agent` takes it: the AI SDK's model, typed by Mastra's own copy of it. */
1104
+ export function agentModel(resolved) {
1105
+ return resolved.model;
1106
+ }
1107
+ /**
1108
+ * What an agent's call used over all its steps: `totalUsage` where Mastra's result has it, else
1109
+ * `usage`. Takes a stream's promises and a finished call's values alike.
1110
+ */
1111
+ export async function totalUsage(out) {
1112
+ const result = out;
1113
+ return (await result.totalUsage) ?? (await result.usage);
1114
+ }
1115
+ /** Provider cost of a text call a runtime that runs alone made; the gateway books the others itself. */
1116
+ export function costOf(resolved, usage) {
1117
+ // An installed client answers on its subscription: nothing to book.
1118
+ return resolved.metered || isHarnessVendor(resolved.vendor)
1119
+ ? 0
1120
+ : tokenCostUsd(resolved.ref, usageOf(usage));
1121
+ }
1122
+ export function tokenCostUsd(ref, usage) {
1123
+ if (!usage) {
1124
+ return 0;
1125
+ }
1126
+ const p = priceOf(ref);
1127
+ const input = n(p.input, 0.000003);
1128
+ const output = n(p.output, 0.000015);
1129
+ const cached = n(p.input_cache_read, input / 10);
1130
+ const cachedTokens = usage.cachedInputTokens ?? 0;
1131
+ const freshInput = Math.max(0, (usage.inputTokens ?? 0) - cachedTokens);
1132
+ return freshInput * input + cachedTokens * cached + (usage.outputTokens ?? 0) * output;
1133
+ }
1134
+ export function imageCostUsd(ref) {
1135
+ const p = priceOf(ref);
1136
+ const tiers = p.image_dimension_quality_pricing;
1137
+ const tier = tiers?.find((t) => t.size === "default") ?? tiers?.[0];
1138
+ return n(tier?.cost ?? p.image, 0.08);
1139
+ }
1140
+ export function videoCostUsd(ref, seconds, resolution = "720p") {
1141
+ const p = priceOf(ref);
1142
+ const tiers = p.video_duration_pricing;
1143
+ // We render at 720p or 480p with sound: price that tier where the catalog has one; else the
1144
+ // dearest below 4K, so the estimate is never short.
1145
+ const exact = tiers?.filter((t) => t.resolution === resolution) ?? [];
1146
+ const usable = exact.length ? exact : (tiers?.filter((t) => t.resolution !== "4k") ?? []);
1147
+ const withAudio = usable.filter((t) => t.audio !== false);
1148
+ const pick = withAudio.length ? withAudio : usable;
1149
+ const perSecond = pick.length ? Math.max(...pick.map((t) => n(t.cost_per_second, 0))) : 0.4;
1150
+ return perSecond * seconds;
1151
+ }
1152
+ /** Speech is priced per character read, or per audio token (about 25 a second, 15 characters). */
1153
+ export function speechCostUsd(ref, characters) {
1154
+ const p = priceOf(ref);
1155
+ if (p.speech_input_character_cost !== undefined) {
1156
+ return n(p.speech_input_character_cost, 0.00003) * characters;
1157
+ }
1158
+ return n(p.audio_output_token_cost, 0.00001) * Math.ceil((characters / 15) * 25);
1159
+ }
1160
+ export const WEB_SEARCH_COST_USD = 0.01;
1161
+ /** A transcript, priced by the minute; the gateway's catalog has no transcription prices. */
1162
+ export function transcriptionCostUsd(seconds) {
1163
+ return (seconds / 60) * 0.006;
1164
+ }