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,126 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { resolveEnv, run } from "./env.js";
5
+ import { failureOf, HarnessModel, runClient, } from "./model.js";
6
+ /**
7
+ * Gemini CLI: headless it answers on the Google sign-in it cached, or on a key from the shell.
8
+ * The system prompt replaces its own (GEMINI_SYSTEM_MD); tools go over MCP through a project
9
+ * settings file in the call's folder, with its built-in tools switched off; the answer is asked
10
+ * for as JSON where a schema is wanted.
11
+ */
12
+ export const GEMINI_BIN = process.env.GEMINI_BIN?.trim() || "gemini";
13
+ const CLASSES = {
14
+ classifier: "gemini-2.5-flash-lite",
15
+ standard: "gemini-2.5-flash",
16
+ high: "gemini-2.5-pro",
17
+ highest: "gemini-2.5-pro",
18
+ };
19
+ /** Where the client keeps its state: its own home, else the person's. */
20
+ const geminiHome = (env) => env.GEMINI_CLI_HOME?.trim() || join(env.HOME || homedir(), ".gemini");
21
+ const spec = {
22
+ id: "gemini",
23
+ passThrough: /^(GEMINI_API_KEY|GOOGLE_API_KEY|GOOGLE_CLOUD_PROJECT|GOOGLE_CLOUD_LOCATION|GOOGLE_APPLICATION_CREDENTIALS|GOOGLE_GENAI_USE_VERTEXAI|GOOGLE_GENAI_USE_GCA|GEMINI_CLI_HOME)$/,
24
+ keyVars: ["GEMINI_API_KEY", "GOOGLE_API_KEY", "GOOGLE_GENAI_USE_VERTEXAI"],
25
+ // The client has no status command: a key in the environment, else the sign-in it cached.
26
+ async auth(env) {
27
+ if (env.GEMINI_API_KEY || env.GOOGLE_API_KEY || env.GOOGLE_GENAI_USE_VERTEXAI) {
28
+ return "api_key";
29
+ }
30
+ return existsSync(join(geminiHome(env), "oauth_creds.json")) ? "subscription" : "none";
31
+ },
32
+ };
33
+ /** The client's output as the model call wants it; the lines it logs before the JSON are skipped. */
34
+ export function geminiAnswer(stdout) {
35
+ const at = stdout.search(/^\{/m);
36
+ if (at < 0) {
37
+ return null;
38
+ }
39
+ try {
40
+ return JSON.parse(stdout.slice(at));
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ class GeminiModel extends HarnessModel {
47
+ provider = "gemini-cli";
48
+ async invoke(call) {
49
+ const { env, auth } = await resolveEnv(spec);
50
+ const systemFile = join(call.dir, "system.md");
51
+ writeFileSync(systemFile, call.system || "You are a helpful assistant.");
52
+ const core = [];
53
+ if (call.webSearch) {
54
+ core.push("google_web_search");
55
+ }
56
+ if (call.files.length) {
57
+ core.push("read_file");
58
+ }
59
+ mkdirSync(join(call.dir, ".gemini"), { recursive: true });
60
+ writeFileSync(join(call.dir, ".gemini", "settings.json"), JSON.stringify({
61
+ mcpServers: call.bridgeUrl
62
+ ? { step: { httpUrl: call.bridgeUrl, trust: true, timeout: 600_000 } }
63
+ : {},
64
+ tools: { core },
65
+ }));
66
+ const args = [
67
+ "-o",
68
+ "json",
69
+ "-m",
70
+ this.modelId,
71
+ "--approval-mode",
72
+ "yolo",
73
+ "--allowed-mcp-server-names",
74
+ call.bridgeUrl ? "step" : "none",
75
+ ];
76
+ const { stdout, stderr, code } = await runClient(GEMINI_BIN, args, {
77
+ cwd: call.dir,
78
+ env: {
79
+ ...env,
80
+ GEMINI_SYSTEM_MD: systemFile,
81
+ NO_COLOR: "1",
82
+ ...(auth === "subscription" ? { GOOGLE_GENAI_USE_GCA: "true" } : {}),
83
+ },
84
+ signal: call.signal,
85
+ stdin: call.prompt,
86
+ });
87
+ const result = geminiAnswer(stdout);
88
+ if (!result) {
89
+ throw failureOf(this.harness, stderr.trim() || `exit ${code}`);
90
+ }
91
+ if (result.error) {
92
+ throw failureOf(this.harness, result.error.code === 41 ? "not logged in" : (result.error.message ?? "error"));
93
+ }
94
+ let input = 0;
95
+ let cacheRead = 0;
96
+ let output = 0;
97
+ for (const model of Object.values(result.stats?.models ?? {})) {
98
+ input += model.tokens?.prompt ?? 0;
99
+ cacheRead += model.tokens?.cached ?? 0;
100
+ output += model.tokens?.candidates ?? 0;
101
+ }
102
+ return {
103
+ text: result.response ?? "",
104
+ usage: { input, cacheRead, output },
105
+ sessionId: result.session_id,
106
+ };
107
+ }
108
+ }
109
+ export const gemini = {
110
+ ...spec,
111
+ name: "Gemini CLI",
112
+ bin: GEMINI_BIN,
113
+ classes: CLASSES,
114
+ async version(env) {
115
+ try {
116
+ const { stdout } = await run(GEMINI_BIN, ["--version"], { timeout: 10_000, env });
117
+ return stdout.trim().split("\n").pop()?.trim() || null;
118
+ }
119
+ catch {
120
+ return null;
121
+ }
122
+ },
123
+ // No login command: the client's own app asks for the sign-in when it has none.
124
+ login: { args: [], interactive: true },
125
+ model: (alias, effort) => new GeminiModel(gemini, alias || CLASSES.standard, effort),
126
+ };
@@ -0,0 +1,99 @@
1
+ import { mkdirSync } from "node:fs";
2
+ import { installCommand } from "../cli/clients.js";
3
+ import { inherited, runtimePath } from "../cli/environment.js";
4
+ import { layout } from "../cli/home.js";
5
+ import { CLIENTS, vendorApp } from "../cli/machine.js";
6
+ import { claude } from "./claude.js";
7
+ import { codex } from "./codex.js";
8
+ import { cursor } from "./cursor.js";
9
+ import { forgetEnv, loginShellEnv, resolveEnv } from "./env.js";
10
+ import { gemini } from "./gemini.js";
11
+ export { HarnessModel, notInstalled, signedOut } from "./model.js";
12
+ export { HARNESS_IDS } from "./types.js";
13
+ /**
14
+ * The AI clients this runtime can run as a model, in the order the setup offers them. The first
15
+ * one installed is the one the setup recommends.
16
+ */
17
+ export const HARNESSES = [codex, claude, gemini, cursor];
18
+ export function harness(id) {
19
+ return HARNESSES.find((h) => h.id === id) ?? null;
20
+ }
21
+ /** Whether a binding's vendor names a client: `claude/sonnet`, `codex/default`. */
22
+ export function isHarnessVendor(vendor) {
23
+ return HARNESSES.some((h) => h.id === vendor);
24
+ }
25
+ const detected = new Map();
26
+ /** One client, as it is now; asked again after a minute — it may get installed or signed in meanwhile. */
27
+ export async function detectHarness(id, force = false) {
28
+ const h = harness(id);
29
+ if (!h) {
30
+ return null;
31
+ }
32
+ const hit = detected.get(id);
33
+ if (!force && hit && Date.now() - hit.at < 60_000) {
34
+ return hit.value;
35
+ }
36
+ if (force) {
37
+ // The person may just have signed in or set a key in their shell.
38
+ forgetEnv(id);
39
+ }
40
+ const client = CLIENTS.find((c) => c.id === id);
41
+ let value = {
42
+ id,
43
+ name: h.name,
44
+ install: client ? installCommand(client, layout()) : "",
45
+ app: client ? vendorApp(client) : null,
46
+ version: null,
47
+ auth: "none",
48
+ interactiveLogin: h.login.interactive,
49
+ images: Boolean(h.image),
50
+ };
51
+ try {
52
+ const { env, auth } = await resolveEnv(h);
53
+ const version = await h.version(env);
54
+ if (version) {
55
+ value = { ...value, version, auth };
56
+ }
57
+ }
58
+ catch {
59
+ // not installed
60
+ }
61
+ detected.set(id, { at: Date.now(), value });
62
+ return value;
63
+ }
64
+ /** Every client this runtime knows, installed or not. */
65
+ export function detectHarnesses(force = false) {
66
+ return Promise.all(HARNESSES.map((h) => detectHarness(h.id, force)));
67
+ }
68
+ /** The clients installed on this machine. */
69
+ export async function installedHarnesses() {
70
+ return (await detectHarnesses()).filter((h) => h.version !== null);
71
+ }
72
+ /**
73
+ * What the inline terminal runs to install a client: the command the terminal setup runs, shown
74
+ * first. A client from npm goes into the install's own prefix with the npm of its own Node.
75
+ */
76
+ export async function installSpec(id) {
77
+ const client = CLIENTS.find((c) => c.id === id);
78
+ if (!client) {
79
+ return null;
80
+ }
81
+ const paths = layout();
82
+ mkdirSync(paths.clients, { recursive: true });
83
+ const shell = await loginShellEnv();
84
+ return {
85
+ bin: "bash",
86
+ args: [
87
+ "-c",
88
+ 'printf "\\033[2m$ %s\\033[0m\\n\\n" "$1"; set -o pipefail; eval "$1"',
89
+ "install",
90
+ installCommand(client, paths),
91
+ ],
92
+ env: {
93
+ ...inherited(process.env),
94
+ PATH: runtimePath(paths, { ...process.env, PATH: shell.PATH || process.env.PATH }),
95
+ TERM: "xterm-256color",
96
+ COLORTERM: "truecolor",
97
+ },
98
+ };
99
+ }
@@ -0,0 +1,267 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { nanoid } from "nanoid";
5
+ import { env } from "../env.js";
6
+ import { openBridge } from "../mcp/bridge.js";
7
+ import { ModelUnavailableError } from "../model-errors.js";
8
+ import { extensionOf, jsonInstruction, parseJsonAnswer, renderPrompt } from "./prompt.js";
9
+ const workDir = join(env.dataDir, "harness");
10
+ export const signedOut = (h) => `${h.name} ist auf diesem Gerät nicht angemeldet. Melde dich in den Einstellungen unter „Modelle & Konto“ an.`;
11
+ export const notInstalled = (h) => `${h.name} ist auf diesem Gerät nicht installiert. Installiere es in den Einstellungen unter „Modelle & Konto“.`;
12
+ export const exhausted = (h) => h.exhausted ??
13
+ `Das Konto, mit dem ${h.name} angemeldet ist, hat kein Guthaben oder Kontingent mehr.`;
14
+ /** The failure a client reports, as the person should read it. */
15
+ export function failureOf(h, detail) {
16
+ if (/credit balance|billing|too low|usage limit|quota|insufficient|rate.?limit|\b429\b/i.test(detail)) {
17
+ return new ModelUnavailableError(exhausted(h));
18
+ }
19
+ if (/authenticat|log ?in|logged in|oauth|credential|unauthori[sz]ed|\b401\b|api key/i.test(detail)) {
20
+ return new ModelUnavailableError(signedOut(h));
21
+ }
22
+ return new Error(`${h.name}: ${detail}`.slice(0, 500));
23
+ }
24
+ /** Runs a client once with the prompt on stdin; a client that is not there raises ENOENT. */
25
+ export function runClient(bin, args, opts) {
26
+ return new Promise((resolve, reject) => {
27
+ const child = spawn(bin, args, {
28
+ cwd: opts.cwd,
29
+ stdio: ["pipe", "pipe", "pipe"],
30
+ signal: opts.signal,
31
+ env: opts.env,
32
+ });
33
+ let stdout = "";
34
+ let stderr = "";
35
+ child.stdout.on("data", (chunk) => {
36
+ stdout += String(chunk);
37
+ });
38
+ child.stderr.on("data", (chunk) => {
39
+ stderr = (stderr + String(chunk)).slice(-4000);
40
+ });
41
+ child.on("error", reject);
42
+ child.on("close", (code) => resolve({ stdout, stderr, code }));
43
+ child.stdin.on("error", () => {
44
+ // The client went away before reading the prompt; `close` reports it.
45
+ });
46
+ child.stdin.end(opts.stdin ?? "");
47
+ });
48
+ }
49
+ /** The last line of an output that is a JSON object. */
50
+ export function lastJson(stdout) {
51
+ for (const line of stdout.trim().split("\n").reverse()) {
52
+ try {
53
+ const value = JSON.parse(line);
54
+ if (value && typeof value === "object") {
55
+ return value;
56
+ }
57
+ }
58
+ catch {
59
+ // not it
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+ export class HarnessModel {
65
+ specificationVersion = "v3";
66
+ modelId;
67
+ supportedUrls = {};
68
+ /** Whether the client validates structured output against the schema itself. */
69
+ nativeSchema = false;
70
+ effort;
71
+ harness;
72
+ executors = new Map();
73
+ constructor(harness, alias, effort) {
74
+ this.harness = harness;
75
+ this.modelId = alias;
76
+ this.effort = effort;
77
+ }
78
+ /** The tools a call may name, by the names the agent gives them: the client runs them over the bridge. */
79
+ attach(tools) {
80
+ for (const [name, tool] of Object.entries(tools)) {
81
+ const t = tool;
82
+ if (typeof t?.execute === "function") {
83
+ this.executors.set(name, t);
84
+ if (t.id) {
85
+ this.executors.set(t.id, t);
86
+ }
87
+ }
88
+ }
89
+ }
90
+ async doGenerate(options) {
91
+ const dir = join(workDir, nanoid(10));
92
+ mkdirSync(dir, { recursive: true });
93
+ const { system, prompt, files, warnings } = renderPrompt(options.prompt, dir);
94
+ const bridged = [];
95
+ let webSearch = false;
96
+ for (const tool of options.tools ?? []) {
97
+ if (tool.type !== "function") {
98
+ warnings.push({ type: "unsupported", feature: `tool ${tool.name}` });
99
+ continue;
100
+ }
101
+ // The client searches with its own tool; naming `web_search` is what switches it on.
102
+ if (tool.name === "web_search") {
103
+ webSearch = true;
104
+ continue;
105
+ }
106
+ const executor = this.executors.get(tool.name);
107
+ if (!executor?.execute) {
108
+ warnings.push({
109
+ type: "unsupported",
110
+ feature: `tool ${tool.name}`,
111
+ details: "no executor",
112
+ });
113
+ continue;
114
+ }
115
+ bridged.push({
116
+ name: tool.name,
117
+ description: tool.description,
118
+ inputSchema: tool.inputSchema,
119
+ execute: (input) => executor.execute(input),
120
+ toModelOutput: executor.toModelOutput,
121
+ });
122
+ }
123
+ for (const key of ["temperature", "topP", "topK", "seed", "stopSequences"]) {
124
+ if (options[key] !== undefined) {
125
+ warnings.push({ type: "unsupported", feature: key });
126
+ }
127
+ }
128
+ const structured = options.responseFormat?.type === "json";
129
+ const schema = structured && options.responseFormat?.type === "json"
130
+ ? (options.responseFormat.schema ?? null)
131
+ : null;
132
+ const bridge = bridged.length ? openBridge(bridged) : null;
133
+ try {
134
+ const answer = await this.invoke({
135
+ dir,
136
+ system: structured && !this.nativeSchema
137
+ ? [system, jsonInstruction(schema)].filter(Boolean).join("\n\n")
138
+ : system,
139
+ prompt,
140
+ files,
141
+ bridgeUrl: bridge?.url ?? null,
142
+ schema: this.nativeSchema ? schema : null,
143
+ webSearch,
144
+ effort: this.effort,
145
+ signal: options.abortSignal,
146
+ });
147
+ const u = answer.usage ?? {};
148
+ const cacheRead = u.cacheRead ?? 0;
149
+ const cacheWrite = u.cacheWrite ?? 0;
150
+ const input = u.input ?? 0;
151
+ return {
152
+ content: [
153
+ {
154
+ type: "text",
155
+ text: structured ? JSON.stringify(parseJsonAnswer(answer.text)) : answer.text,
156
+ },
157
+ ],
158
+ finishReason: { unified: "stop", raw: answer.stopReason },
159
+ usage: {
160
+ inputTokens: {
161
+ total: input,
162
+ noCache: Math.max(0, input - cacheRead - cacheWrite),
163
+ cacheRead,
164
+ cacheWrite,
165
+ },
166
+ outputTokens: { total: u.output ?? 0, text: u.output ?? 0, reasoning: undefined },
167
+ raw: u,
168
+ },
169
+ providerMetadata: { [this.provider]: answer.meta ?? {} },
170
+ response: { id: answer.sessionId, modelId: this.modelId },
171
+ warnings,
172
+ };
173
+ }
174
+ catch (err) {
175
+ if (err.code === "ENOENT") {
176
+ throw new ModelUnavailableError(notInstalled(this.harness));
177
+ }
178
+ throw err;
179
+ }
180
+ finally {
181
+ bridge?.close();
182
+ rmSync(dir, { recursive: true, force: true });
183
+ }
184
+ }
185
+ /** The answer as one piece: the client prints it when it is done. */
186
+ async doStream(options) {
187
+ const result = await this.doGenerate(options);
188
+ const text = result.content.find((c) => c.type === "text");
189
+ const parts = [
190
+ { type: "stream-start", warnings: result.warnings },
191
+ { type: "response-metadata", ...result.response },
192
+ { type: "text-start", id: "0" },
193
+ { type: "text-delta", id: "0", delta: text?.type === "text" ? text.text : "" },
194
+ { type: "text-end", id: "0" },
195
+ {
196
+ type: "finish",
197
+ usage: result.usage,
198
+ finishReason: result.finishReason,
199
+ providerMetadata: result.providerMetadata,
200
+ },
201
+ ];
202
+ return {
203
+ stream: new ReadableStream({
204
+ start(controller) {
205
+ for (const part of parts) {
206
+ controller.enqueue(part);
207
+ }
208
+ controller.close();
209
+ },
210
+ }),
211
+ };
212
+ }
213
+ }
214
+ /** A client that makes images on the person's sign-in: one client run per image. */
215
+ export class HarnessImageModel {
216
+ specificationVersion = "v3";
217
+ modelId;
218
+ maxImagesPerCall = 1;
219
+ harness;
220
+ constructor(harness, alias) {
221
+ this.harness = harness;
222
+ this.modelId = alias;
223
+ }
224
+ async doGenerate(options) {
225
+ const dir = join(workDir, nanoid(10));
226
+ mkdirSync(dir, { recursive: true });
227
+ const warnings = [];
228
+ const files = [];
229
+ for (const file of options.files ?? []) {
230
+ if (file.type !== "file") {
231
+ warnings.push({ type: "unsupported", feature: "image url" });
232
+ continue;
233
+ }
234
+ const path = join(dir, `ref-${files.length + 1}.${extensionOf(file.mediaType)}`);
235
+ writeFileSync(path, typeof file.data === "string" ? Buffer.from(file.data, "base64") : file.data);
236
+ files.push(path);
237
+ }
238
+ for (const key of ["size", "seed", "mask"]) {
239
+ if (options[key] !== undefined) {
240
+ warnings.push({ type: "unsupported", feature: key });
241
+ }
242
+ }
243
+ try {
244
+ const image = await this.invoke({
245
+ dir,
246
+ prompt: options.prompt ?? "",
247
+ files,
248
+ aspectRatio: options.aspectRatio,
249
+ signal: options.abortSignal,
250
+ });
251
+ return {
252
+ images: [image],
253
+ warnings,
254
+ response: { timestamp: new Date(), modelId: this.modelId, headers: undefined },
255
+ };
256
+ }
257
+ catch (err) {
258
+ if (err.code === "ENOENT") {
259
+ throw new ModelUnavailableError(notInstalled(this.harness));
260
+ }
261
+ throw err;
262
+ }
263
+ finally {
264
+ rmSync(dir, { recursive: true, force: true });
265
+ }
266
+ }
267
+ }
@@ -0,0 +1,121 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ /** The extension a saved attachment gets, so the client reads it as what it is. */
4
+ export function extensionOf(mediaType, filename) {
5
+ const own = filename?.match(/\.([a-z0-9]{1,5})$/i)?.[1];
6
+ if (own) {
7
+ return own.toLowerCase();
8
+ }
9
+ const sub = mediaType.split("/")[1] ?? "bin";
10
+ return { jpeg: "jpg", "svg+xml": "svg", plain: "txt", markdown: "md" }[sub] ?? sub;
11
+ }
12
+ /**
13
+ * The conversation as a client takes it: one system prompt and one prompt on stdin. A single
14
+ * user message goes as it is; a longer thread is written out with its speakers, and the client
15
+ * is asked to answer its last message. Attachments are saved to `dir` and named in the prompt.
16
+ */
17
+ export function renderPrompt(messages, dir) {
18
+ const warnings = [];
19
+ const files = [];
20
+ const system = [];
21
+ const turns = [];
22
+ const saveFile = (input) => {
23
+ // A file's content comes tagged: { type: "data", data } | { type: "url", url } | { type: "text", text }.
24
+ const tagged = input.data;
25
+ const data = tagged && typeof tagged === "object" && typeof tagged.type === "string"
26
+ ? tagged.type === "url"
27
+ ? tagged.url
28
+ : tagged.type === "text"
29
+ ? Buffer.from(tagged.text ?? "", "utf8")
30
+ : tagged.data
31
+ : input.data;
32
+ const part = { ...input, data };
33
+ if (part.data instanceof URL) {
34
+ return `[Attachment at ${part.data.href} (${part.mediaType})]`;
35
+ }
36
+ if (typeof part.data !== "string" && !(part.data instanceof Uint8Array)) {
37
+ warnings.push({ type: "unsupported", feature: "file parts" });
38
+ return `[Attachment: ${part.filename ?? part.mediaType}]`;
39
+ }
40
+ if (!dir) {
41
+ warnings.push({ type: "unsupported", feature: "file parts" });
42
+ return `[Attachment: ${part.filename ?? part.mediaType}]`;
43
+ }
44
+ const path = join(dir, `${files.length + 1}.${extensionOf(part.mediaType, part.filename)}`);
45
+ const bytes = typeof part.data === "string" ? Buffer.from(part.data, "base64") : part.data;
46
+ writeFileSync(path, bytes);
47
+ files.push(path);
48
+ return `[Attached file — open it to read it: ${path}]`;
49
+ };
50
+ for (const m of messages) {
51
+ if (m.role === "system") {
52
+ system.push(m.content);
53
+ continue;
54
+ }
55
+ const text = m.content
56
+ .map((part) => {
57
+ switch (part.type) {
58
+ case "text":
59
+ return part.text;
60
+ case "file":
61
+ return saveFile(part);
62
+ case "reasoning":
63
+ return "";
64
+ case "tool-call":
65
+ return `[Called ${part.toolName} with ${JSON.stringify(part.input)}]`;
66
+ case "tool-result": {
67
+ const out = part.output;
68
+ const value = out.type === "text" || out.type === "error-text"
69
+ ? String(out.value)
70
+ : out.type === "json" || out.type === "error-json"
71
+ ? JSON.stringify(out.value)
72
+ : out.type === "execution-denied"
73
+ ? `denied${out.reason ? `: ${out.reason}` : ""}`
74
+ : JSON.stringify(out.value ?? out);
75
+ return `[Result of ${part.toolName}: ${value}]`;
76
+ }
77
+ default:
78
+ return "";
79
+ }
80
+ })
81
+ .filter(Boolean)
82
+ .join("\n");
83
+ turns.push({ role: m.role, text });
84
+ }
85
+ const prompt = turns.length === 1 && turns[0].role === "user"
86
+ ? turns[0].text
87
+ : turns
88
+ .map((t) => `${t.role === "user" ? "User" : t.role === "tool" ? "Tool" : "Assistant"}:\n${t.text}`)
89
+ .join("\n\n");
90
+ if (turns.length > 1) {
91
+ system.push("The prompt holds the conversation so far, each message headed by its speaker. Answer the last message as the assistant — the answer only, no speaker label.");
92
+ }
93
+ return { system: system.join("\n\n"), prompt, files, warnings };
94
+ }
95
+ /** What a client without a schema option is told so its answer parses. */
96
+ export function jsonInstruction(schema) {
97
+ return [
98
+ "Answer with one JSON value only — no prose, no code fence — that matches this JSON Schema:",
99
+ JSON.stringify(schema ?? { type: "object" }),
100
+ ].join("\n");
101
+ }
102
+ /** A client's answer as JSON: the fences and prose some wrap around it are taken off. */
103
+ export function parseJsonAnswer(text) {
104
+ const trimmed = text.trim();
105
+ const unfenced = trimmed
106
+ .replace(/^\s*```(?:json)?\s*/i, "")
107
+ .replace(/\s*```\s*$/, "")
108
+ .trim();
109
+ try {
110
+ return JSON.parse(unfenced);
111
+ }
112
+ catch {
113
+ // Prose around the value: take the outermost object or array.
114
+ const start = unfenced.search(/[{[]/);
115
+ const end = Math.max(unfenced.lastIndexOf("}"), unfenced.lastIndexOf("]"));
116
+ if (start >= 0 && end > start) {
117
+ return JSON.parse(unfenced.slice(start, end + 1));
118
+ }
119
+ throw new Error("The answer is not JSON.");
120
+ }
121
+ }