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,193 @@
1
+ import { decryptToken, defineConnector, } from "../shims/connections-sdk.js";
2
+ import { z } from "zod";
3
+ import { headersFromRequired } from "./import-service.js";
4
+ import { executeHttpAction } from "./invoke/http-invoker.js";
5
+ import { mcpCallTool } from "./invoke/mcp-client.js";
6
+ import { connectionsRedirectUri, registerDynamicClient } from "./oauth-dcr.js";
7
+ /**
8
+ * Materialize a stored import record into a live `ConnectorDefinition`. From
9
+ * here on the framework owns everything — per-action operations, policy
10
+ * defaults by group, approvals, accounts, audit — identical to a hand-written
11
+ * provider.
12
+ */
13
+ /**
14
+ * MCP request headers: the connector's required headers first, then the
15
+ * credential — auth must win a name collision, never the other way round.
16
+ */
17
+ export function mcpHeaders(record, accessToken) {
18
+ const headers = headersFromRequired(record.required_headers);
19
+ const auth = record.auth_config;
20
+ if (auth.kind === "oauth2") {
21
+ return { ...headers, authorization: `Bearer ${accessToken}` };
22
+ }
23
+ if (auth.kind === "api_key" && auth.placement.in === "header") {
24
+ let credentials = {};
25
+ try {
26
+ credentials = JSON.parse(accessToken);
27
+ }
28
+ catch {
29
+ return headers;
30
+ }
31
+ const value = auth.placement.value_template.replace(/\{\{\s*([a-z0-9_]+)\s*\}\}/giu, (_m, key) => credentials[key] ?? "");
32
+ return { ...headers, [auth.placement.name]: value };
33
+ }
34
+ return headers;
35
+ }
36
+ function inputSchemaToZod(action) {
37
+ try {
38
+ const schema = z.fromJSONSchema(action.input_json_schema);
39
+ return schema instanceof z.ZodObject ? schema : z.object({}).loose();
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ }
45
+ function buildAction(record, action, live) {
46
+ const inputSchema = inputSchemaToZod(action);
47
+ if (!inputSchema) {
48
+ return null;
49
+ }
50
+ const group = action.classification === "read"
51
+ ? "read"
52
+ : action.classification === "destructive"
53
+ ? "destructive"
54
+ : "write";
55
+ return {
56
+ description: action.description.slice(0, 1024),
57
+ group,
58
+ handler: (input, ctx) => {
59
+ // Read the record through the live resolver: refresh updates base_url/
60
+ // auth without re-binding the already-registered operation closures.
61
+ const current = live();
62
+ const args = (input ?? {});
63
+ if (action.invoke.kind === "mcp") {
64
+ return mcpCallTool({
65
+ args,
66
+ endpoint: current.source_url,
67
+ headers: mcpHeaders(current, ctx.accessToken),
68
+ toolName: action.invoke.tool_name,
69
+ transport: current.mcp_transport,
70
+ });
71
+ }
72
+ return executeHttpAction({
73
+ accessToken: ctx.accessToken,
74
+ fetchImpl: ctx.fetchImpl,
75
+ input: args,
76
+ invoke: action.invoke,
77
+ record: current,
78
+ });
79
+ },
80
+ id: action.id,
81
+ inputSchema,
82
+ summary: action.summary.slice(0, 256),
83
+ };
84
+ }
85
+ function buildAuth(record, live, persistOAuthClient) {
86
+ const stored = record.auth_config;
87
+ if (stored.kind === "oauth2") {
88
+ const dcr = stored.dcr === true && Boolean(stored.registration_endpoint?.trim());
89
+ return {
90
+ kind: "oauth2",
91
+ oauth2: {
92
+ authUrl: stored.auth_url,
93
+ baseScopes: stored.scopes,
94
+ dynamicClientRegistration: dcr,
95
+ registerClient: dcr && persistOAuthClient
96
+ ? async () => {
97
+ const current = live();
98
+ // Public DCR clients store only client_id_enc (empty secret).
99
+ if (current.client_id_enc) {
100
+ return;
101
+ }
102
+ const endpoint = current.auth_config.kind === "oauth2"
103
+ ? current.auth_config.registration_endpoint
104
+ : stored.registration_endpoint;
105
+ if (!endpoint) {
106
+ throw new Error(`imported connector ${current.id} has no OAuth registration endpoint`);
107
+ }
108
+ const registered = await registerDynamicClient({
109
+ clientName: current.name,
110
+ redirectUri: connectionsRedirectUri(),
111
+ registrationEndpoint: endpoint,
112
+ scopes: current.auth_config.kind === "oauth2"
113
+ ? current.auth_config.scopes
114
+ : stored.scopes,
115
+ });
116
+ await persistOAuthClient(registered);
117
+ }
118
+ : undefined,
119
+ resolveClientCredentials: () => {
120
+ const current = live();
121
+ if (!current.client_id_enc) {
122
+ return Promise.reject(new Error(`imported connector ${current.id} has no OAuth client credentials — set them in the import console`));
123
+ }
124
+ return Promise.resolve({
125
+ clientId: decryptToken(current.client_id_enc),
126
+ clientSecret: current.client_secret_enc
127
+ ? decryptToken(current.client_secret_enc)
128
+ : "",
129
+ });
130
+ },
131
+ scopeSeparator: stored.scope_separator ?? " ",
132
+ tokenUrl: stored.token_url,
133
+ },
134
+ };
135
+ }
136
+ if (stored.kind === "api_key") {
137
+ return {
138
+ apiKey: {
139
+ fields: stored.fields.map((field) => ({
140
+ key: field.key,
141
+ label: field.label,
142
+ required: field.required ?? true,
143
+ secret: field.secret ?? true,
144
+ })),
145
+ // No live probe on connect: imported specs give no reliable cheap
146
+ // "whoami" endpoint; a bad key surfaces on first action instead.
147
+ verify: (credentials) => {
148
+ for (const field of stored.fields) {
149
+ if ((field.required ?? true) && !credentials[field.key]) {
150
+ return Promise.reject(new Error(`missing credential field "${field.key}"`));
151
+ }
152
+ }
153
+ return Promise.resolve({ label: record.domain });
154
+ },
155
+ },
156
+ kind: "api_key",
157
+ };
158
+ }
159
+ // "none": model as api_key with zero fields so connect works without input.
160
+ return {
161
+ apiKey: {
162
+ fields: [],
163
+ verify: () => Promise.resolve({ label: record.domain }),
164
+ },
165
+ kind: "api_key",
166
+ };
167
+ }
168
+ export function buildImportedConnector(record, resolveLive, persistOAuthClient) {
169
+ const live = () => resolveLive?.() ?? record;
170
+ const actions = [];
171
+ const skippedActions = [];
172
+ for (const normalized of record.actions) {
173
+ const action = buildAction(record, normalized, live);
174
+ if (action) {
175
+ actions.push(action);
176
+ }
177
+ else {
178
+ skippedActions.push(normalized.id);
179
+ }
180
+ }
181
+ const connector = defineConnector({
182
+ actions,
183
+ auth: buildAuth(record, live, persistOAuthClient),
184
+ description: `${record.name} — imported from ${record.domain} (${record.source_kind})`,
185
+ icon: "plug",
186
+ id: record.id,
187
+ moduleId: "connections-external",
188
+ name: record.name,
189
+ tenantId: record.tenant_id,
190
+ toolPrefix: record.tool_prefix,
191
+ });
192
+ return { connector, skippedActions };
193
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Import-time rejection. Routes answer 422 for this and 502 for everything
3
+ * else, so anything an admin can fix by choosing a different source, filling a
4
+ * field, or picking another surface must be raised as this error.
5
+ */
6
+ export class ImportValidationError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "ImportValidationError";
10
+ }
11
+ }
@@ -0,0 +1,270 @@
1
+ import { createHash } from "node:crypto";
2
+ import { encryptToken, getConnectorDefinition } from "../shims/connections-sdk.js";
3
+ import { ImportValidationError } from "./errors.js";
4
+ import { mapAuth } from "./importer/map-auth.js";
5
+ import { normalizeMcpServer } from "./importer/normalize-mcp.js";
6
+ import { MAX_SPEC_BYTES, normalizeOpenApiSpec, } from "./importer/normalize-openapi.js";
7
+ import { isUnauthorizedMcpError } from "./invoke/mcp-client.js";
8
+ import { createGuardedFetch, fetchTextBounded } from "./net/guarded-fetch.js";
9
+ import { resolveMcpTransport, } from "./registry-client.js";
10
+ /**
11
+ * Import/refresh pipeline shared by the admin routes: fetch source → apply the
12
+ * registry's spec overrides → normalize → map auth → assemble the stored
13
+ * record. Pure of DB and registry-transport concerns — the routes/plugin own
14
+ * persistence, registry lookups and (re-)registration.
15
+ *
16
+ * Every registry fact this pipeline reads is either applied or refuses the
17
+ * import. A surface that needs a URL variable, an environment-sourced header,
18
+ * or auth we cannot express never becomes a half-working connector.
19
+ */
20
+ const SPEC_FETCH_TIMEOUT_MS = 30_000;
21
+ /** Operation/tool ids must satisfy core's strict snake_case tool-id contract. */
22
+ const TOOL_PREFIX_RE = /^[a-z][a-z0-9_]{1,30}$/u;
23
+ export function validateConnectorNaming(params) {
24
+ if (!/^[a-z][a-z0-9-]{1,59}$/u.test(params.id)) {
25
+ throw new ImportValidationError(`connector id "${params.id}" must be kebab-case (a-z, 0-9, -)`);
26
+ }
27
+ if (!TOOL_PREFIX_RE.test(params.toolPrefix)) {
28
+ throw new ImportValidationError(`tool prefix "${params.toolPrefix}" must be snake_case, start with a letter, and stay short`);
29
+ }
30
+ const existing = getConnectorDefinition(params.id, params.tenantId);
31
+ if (existing && existing.moduleId !== "connections-external") {
32
+ throw new ImportValidationError(`connector id "${params.id}" collides with built-in connector "${existing.name}"`);
33
+ }
34
+ }
35
+ // ---------------------------------------------------------------------------
36
+ // Registry surface → import decisions
37
+ // ---------------------------------------------------------------------------
38
+ /** Connector id suggested by a registry surface slug ("stripe-mcp-server"). */
39
+ export function connectorIdFromSlug(slug) {
40
+ return slug
41
+ .toLowerCase()
42
+ .replace(/[^a-z0-9]+/gu, "-")
43
+ .replace(/^[-0-9]+|-+$/gu, "")
44
+ .slice(0, 60);
45
+ }
46
+ /** Tool prefix suggested by a registry surface slug ("stripe_mcp_server"). */
47
+ export function toolPrefixFromSlug(slug) {
48
+ return slug
49
+ .toLowerCase()
50
+ .replace(/[^a-z0-9]+/gu, "_")
51
+ .replace(/^[_0-9]+|_+$/gu, "")
52
+ .slice(0, 30);
53
+ }
54
+ /**
55
+ * Static headers the API requires on every request. The registry also sources
56
+ * headers from the environment of the machine that talks to the API; those
57
+ * have no value we could store, so they block the import rather than being
58
+ * dropped on the floor.
59
+ */
60
+ export function resolveRequiredHeaders(surface) {
61
+ const headers = [];
62
+ for (const header of surface.required_headers) {
63
+ const kind = header.source?.kind ?? "unknown";
64
+ if (kind !== "static") {
65
+ throw new ImportValidationError(`the registry requires header "${header.name}" from a ${kind} source — only static header values can be stored on an imported connector`);
66
+ }
67
+ const value = header.source?.value;
68
+ if (!value) {
69
+ throw new ImportValidationError(`the registry requires header "${header.name}" but publishes no value for it`);
70
+ }
71
+ headers.push({
72
+ description: header.description ?? null,
73
+ name: header.name,
74
+ value,
75
+ });
76
+ }
77
+ return headers;
78
+ }
79
+ /** Registry variables have no value at import time — reject, never guess. */
80
+ export function assertNoRegistryVariables(surface) {
81
+ if (surface.variables.length === 0) {
82
+ return;
83
+ }
84
+ const names = surface.variables.map((variable) => variable.name).join(", ");
85
+ throw new ImportValidationError(`this surface is templated on ${names} (${surface.variables
86
+ .map((variable) => variable.resolveFrom ?? variable.name)
87
+ .join("; ")}) — per-tenant URL variables are not supported; paste a resolved spec or endpoint URL instead`);
88
+ }
89
+ // ---------------------------------------------------------------------------
90
+ // Fetch + normalize
91
+ // ---------------------------------------------------------------------------
92
+ export async function fetchSpecText(url, fetchImpl = createGuardedFetch()) {
93
+ return await fetchTextBounded({
94
+ fetchImpl,
95
+ headers: { accept: "application/json, application/yaml, text/yaml, */*" },
96
+ maxBytes: MAX_SPEC_BYTES,
97
+ signal: AbortSignal.timeout(SPEC_FETCH_TIMEOUT_MS),
98
+ url,
99
+ });
100
+ }
101
+ /** Registry surface whose MCP/API calls need a credential. */
102
+ export function surfaceRequiresAuth(surface) {
103
+ return surface?.auth.status === "required";
104
+ }
105
+ function emptyMcpNormalize() {
106
+ return {
107
+ actions: [],
108
+ applied_overrides: 0,
109
+ base_url: null,
110
+ description: null,
111
+ dropped_count: 0,
112
+ security_schemes: null,
113
+ skipped: [],
114
+ title: null,
115
+ };
116
+ }
117
+ export function deferredMcpPrepared(sourceUrl) {
118
+ return {
119
+ deferred_mcp_tools: true,
120
+ normalized: emptyMcpNormalize(),
121
+ spec_hash: createHash("sha256")
122
+ .update(`deferred-mcp:${sourceUrl}`)
123
+ .digest("hex"),
124
+ };
125
+ }
126
+ /** Fetch + normalize one source. openapi → spec text; mcp → live tools/list. */
127
+ export async function prepareSource(params) {
128
+ const fetchImpl = params.fetchImpl ?? createGuardedFetch();
129
+ const overrides = params.specOverrides ?? [];
130
+ if (params.sourceKind === "openapi") {
131
+ const specText = await fetchSpecText(params.sourceUrl, fetchImpl);
132
+ const normalized = await normalizeOpenApiSpec(specText, {
133
+ specOverrides: overrides,
134
+ });
135
+ return {
136
+ normalized,
137
+ // Overrides are part of what was imported: a changed patch must show up
138
+ // as a changed spec hash on refresh.
139
+ spec_hash: createHash("sha256")
140
+ .update(specText)
141
+ .update(JSON.stringify(overrides))
142
+ .digest("hex"),
143
+ };
144
+ }
145
+ if (params.deferMcpTools) {
146
+ return deferredMcpPrepared(params.sourceUrl);
147
+ }
148
+ try {
149
+ const normalized = await normalizeMcpServer({
150
+ endpoint: params.sourceUrl,
151
+ fetchImpl,
152
+ headers: {
153
+ ...headersFromRequired(params.requiredHeaders ?? []),
154
+ ...params.mcpHeaders,
155
+ },
156
+ transport: params.transport ?? null,
157
+ });
158
+ return {
159
+ normalized,
160
+ spec_hash: createHash("sha256")
161
+ .update(JSON.stringify(normalized.actions))
162
+ .digest("hex"),
163
+ };
164
+ }
165
+ catch (error) {
166
+ if ((params.deferOnUnauthorized ?? true) && isUnauthorizedMcpError(error)) {
167
+ return deferredMcpPrepared(params.sourceUrl);
168
+ }
169
+ throw error;
170
+ }
171
+ }
172
+ /** Stored required headers as a request header map. */
173
+ export function headersFromRequired(headers) {
174
+ const out = {};
175
+ for (const header of headers) {
176
+ out[header.name] = header.value;
177
+ }
178
+ return out;
179
+ }
180
+ export function assembleRecord(params) {
181
+ validateConnectorNaming({
182
+ id: params.id,
183
+ tenantId: params.tenantId,
184
+ toolPrefix: params.toolPrefix,
185
+ });
186
+ const { normalized } = params.prepared;
187
+ const warnings = [];
188
+ const surface = params.surface ?? null;
189
+ if (surface) {
190
+ assertNoRegistryVariables(surface);
191
+ }
192
+ const requiredHeaders = surface ? resolveRequiredHeaders(surface) : [];
193
+ const mapped = mapAuth({
194
+ discover: params.discover ?? null,
195
+ securitySchemes: normalized.security_schemes,
196
+ sourceKind: params.sourceKind,
197
+ surface,
198
+ });
199
+ if (!mapped.ok) {
200
+ throw new ImportValidationError(mapped.reason);
201
+ }
202
+ const auth = mapped.auth;
203
+ if (auth.kind === "oauth2" && !params.oauthClient) {
204
+ if (auth.dcr && auth.registration_endpoint) {
205
+ warnings.push("oauth2 client will be registered on first authenticate (dynamic client registration)");
206
+ }
207
+ else {
208
+ warnings.push("oauth2 connector imported without client credentials — connects will fail until they are set");
209
+ }
210
+ }
211
+ const filter = params.actionFilter?.length
212
+ ? new Set(params.actionFilter)
213
+ : null;
214
+ const actions = filter
215
+ ? normalized.actions.filter((action) => filter.has(action.id))
216
+ : normalized.actions;
217
+ const deferredMcp = params.prepared.deferred_mcp_tools === true && params.sourceKind === "mcp";
218
+ if (actions.length === 0 && !deferredMcp) {
219
+ throw new ImportValidationError("no importable actions (empty spec, all filtered out, or all skipped)");
220
+ }
221
+ if (deferredMcp) {
222
+ warnings.push("tools load once an account is connected");
223
+ }
224
+ if (normalized.dropped_count > 0) {
225
+ warnings.push(`${normalized.dropped_count} operation(s) dropped by the per-connector cap`);
226
+ }
227
+ if (normalized.skipped.length > 0) {
228
+ warnings.push(`${normalized.skipped.length} operation(s) skipped: ${normalized.skipped
229
+ .slice(0, 5)
230
+ .map((s) => `${s.id} (${s.reason})`)
231
+ .join(", ")}${normalized.skipped.length > 5 ? ", …" : ""}`);
232
+ }
233
+ if (normalized.applied_overrides > 0) {
234
+ warnings.push(`${normalized.applied_overrides} registry spec override(s) applied before normalization`);
235
+ }
236
+ const baseUrl = params.baseUrlOverride ?? normalized.base_url ?? null;
237
+ if (params.sourceKind === "openapi" && !baseUrl) {
238
+ throw new ImportValidationError("spec declares no server URL — provide base_url explicitly");
239
+ }
240
+ const record = {
241
+ actions,
242
+ auth_config: auth,
243
+ base_url: baseUrl,
244
+ client_id_enc: params.oauthClient
245
+ ? encryptToken(params.oauthClient.clientId)
246
+ : null,
247
+ client_secret_enc: params.oauthClient
248
+ ? encryptToken(params.oauthClient.clientSecret)
249
+ : null,
250
+ domain: params.domain,
251
+ id: params.id,
252
+ imported_at: new Date().toISOString(),
253
+ imported_by: params.importedBy,
254
+ mcp_transport: params.sourceKind === "mcp"
255
+ ? ((surface ? resolveMcpTransport(surface) : null) ?? "streamable-http")
256
+ : null,
257
+ name: params.name ?? normalized.title ?? surface?.name ?? params.domain,
258
+ refreshed_at: null,
259
+ registry_snapshot: params.rawDiscover ?? null,
260
+ registry_surface_slug: surface?.slug ?? null,
261
+ required_headers: requiredHeaders,
262
+ source_kind: params.sourceKind,
263
+ source_url: params.sourceUrl,
264
+ spec_hash: params.prepared.spec_hash,
265
+ status: "enabled",
266
+ tenant_id: params.tenantId,
267
+ tool_prefix: params.toolPrefix,
268
+ };
269
+ return { record, warnings };
270
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Map an HTTP method (plus path hints) onto the connections-framework action
3
+ * groups. Groups drive default policy (read→allow, write/destructive→ask) and
4
+ * the approval contract, so lean conservative: anything ambiguous is `write`,
5
+ * clearly irreversible verbs are `destructive`.
6
+ */
7
+ const DESTRUCTIVE_PATH_HINT = /\b(delete|remove|destroy|purge|revoke|cancel|terminate)\b/iu;
8
+ export function classifyHttpOperation(method, pathTemplate) {
9
+ const m = method.toLowerCase();
10
+ if (m === "get" || m === "head" || m === "options") {
11
+ return "read";
12
+ }
13
+ if (m === "delete") {
14
+ return "destructive";
15
+ }
16
+ if (DESTRUCTIVE_PATH_HINT.test(pathTemplate)) {
17
+ return "destructive";
18
+ }
19
+ return "write";
20
+ }
21
+ /**
22
+ * MCP tool annotations → classification. `readOnlyHint` is trusted for reads;
23
+ * `destructiveHint` for destructive; everything else defaults to `write`
24
+ * (server-provided hints are advisory, so no hint never yields `read`).
25
+ */
26
+ export function classifyMcpTool(annotations) {
27
+ if (annotations.readOnlyHint === true) {
28
+ return "read";
29
+ }
30
+ if (annotations.destructiveHint === true) {
31
+ return "destructive";
32
+ }
33
+ return "write";
34
+ }
@@ -0,0 +1,185 @@
1
+ import { discoverOAuthFacts, } from "../registry-client.js";
2
+ /** Map an OpenAPI 3.x securitySchemes object (spec wins over registry facts). */
3
+ export function mapAuthFromSecuritySchemes(schemes) {
4
+ const entries = Object.entries(schemes);
5
+ // Prefer a static API key / bearer when the spec also advertises OAuth —
6
+ // personal access tokens work without an admin-registered developer app.
7
+ // Figma REST is the canonical case (PersonalAccessToken + OAuth2).
8
+ const bearer = entries.find(([, s]) => s.type === "http" && s.scheme?.toLowerCase() === "bearer");
9
+ if (bearer) {
10
+ return {
11
+ fields: [
12
+ { key: "api_token", label: "API token", required: true, secret: true },
13
+ ],
14
+ kind: "api_key",
15
+ placement: {
16
+ in: "header",
17
+ name: "Authorization",
18
+ value_template: "Bearer {{api_token}}",
19
+ },
20
+ };
21
+ }
22
+ const apiKey = entries.find(([, s]) => s.type === "apiKey" && (s.in === "header" || s.in === "query"));
23
+ if (apiKey) {
24
+ const scheme = apiKey[1];
25
+ return {
26
+ fields: [
27
+ { key: "api_key", label: "API key", required: true, secret: true },
28
+ ],
29
+ kind: "api_key",
30
+ placement: {
31
+ in: scheme.in === "query" ? "query" : "header",
32
+ name: scheme.name ?? "X-API-Key",
33
+ value_template: "{{api_key}}",
34
+ },
35
+ };
36
+ }
37
+ const oauth = entries.find(([, s]) => s.type === "oauth2" && s.flows?.authorizationCode);
38
+ if (oauth) {
39
+ const flow = oauth[1].flows?.authorizationCode;
40
+ if (flow?.authorizationUrl && flow.tokenUrl) {
41
+ return {
42
+ auth_url: flow.authorizationUrl,
43
+ kind: "oauth2",
44
+ scopes: Object.keys(flow.scopes ?? {}),
45
+ token_url: flow.tokenUrl,
46
+ };
47
+ }
48
+ }
49
+ return null;
50
+ }
51
+ /** Fallback: map the domain's discovered OAuth facts. */
52
+ export function mapAuthFromRegistry(discover) {
53
+ const oauth = discoverOAuthFacts(discover);
54
+ if (oauth?.authorizationEndpoint &&
55
+ oauth.tokenEndpoint &&
56
+ (oauth.grantTypes.length === 0 ||
57
+ oauth.grantTypes.includes("authorization_code"))) {
58
+ const dcr = oauth.dcr === true && Boolean(oauth.registrationEndpoint?.trim());
59
+ return {
60
+ auth_url: oauth.authorizationEndpoint,
61
+ kind: "oauth2",
62
+ scopes: oauth.scopes,
63
+ token_url: oauth.tokenEndpoint,
64
+ ...(dcr
65
+ ? {
66
+ dcr: true,
67
+ registration_endpoint: oauth.registrationEndpoint,
68
+ }
69
+ : {}),
70
+ };
71
+ }
72
+ return null;
73
+ }
74
+ /** A credential id reduced to a stored field key. */
75
+ function fieldKey(credentialId) {
76
+ const key = credentialId
77
+ .toLowerCase()
78
+ .replace(/[^a-z0-9]+/gu, "_")
79
+ .replace(/^_+|_+$/gu, "")
80
+ .slice(0, 40);
81
+ return key || "api_key";
82
+ }
83
+ /** Whether a mechanic describes an OAuth flow rather than a static credential. */
84
+ function isOauthMechanics(mechanics) {
85
+ if (mechanics.source === "well-known") {
86
+ return true;
87
+ }
88
+ return (mechanics.source === "spec" &&
89
+ (mechanics.scheme ?? "").toLowerCase().includes("oauth"));
90
+ }
91
+ /**
92
+ * Map one registry auth alternative. Returns null when this alternative is not
93
+ * expressible — the caller tries the next one before giving up.
94
+ */
95
+ function mapSurfaceAuthEntry(params) {
96
+ const { entry } = params;
97
+ if (entry.use.length === 0) {
98
+ return null;
99
+ }
100
+ if (entry.use.length === 1 && isOauthMechanics(entry.use[0].mechanics)) {
101
+ return params.discover ? mapAuthFromRegistry(params.discover) : null;
102
+ }
103
+ // A single header/query credential is the only static placement the stored
104
+ // config can express; multi-credential alternatives (client id + secret
105
+ // headers) have no single placement and fall through to the next entry.
106
+ if (entry.use.length !== 1) {
107
+ return null;
108
+ }
109
+ const { id, mechanics } = entry.use[0];
110
+ if (mechanics.source !== "http") {
111
+ return null;
112
+ }
113
+ const placementIn = mechanics.in === "query" ? "query" : "header";
114
+ const name = mechanics.headerName ?? (placementIn === "query" ? "api_key" : "X-API-Key");
115
+ if (placementIn === "header" && !mechanics.headerName) {
116
+ return null;
117
+ }
118
+ const key = fieldKey(id);
119
+ const scheme = mechanics.scheme?.trim();
120
+ return {
121
+ fields: [
122
+ {
123
+ key,
124
+ label: params.credentialLabel(id),
125
+ required: true,
126
+ secret: true,
127
+ },
128
+ ],
129
+ kind: "api_key",
130
+ placement: {
131
+ in: placementIn,
132
+ name,
133
+ value_template: scheme ? `${scheme} {{${key}}}` : `{{${key}}}`,
134
+ },
135
+ };
136
+ }
137
+ /** Human-readable summary of what the registry said, for reject reasons. */
138
+ function describeSurfaceAuth(surface) {
139
+ if (surface.auth.entries.length === 0) {
140
+ return `auth status "${surface.auth.status}" with no credential facts`;
141
+ }
142
+ const shapes = surface.auth.entries.map((entry) => entry.use.map((use) => `${use.id} via ${use.mechanics.source}`).join(" + "));
143
+ return `alternatives: ${shapes.join("; ")}`;
144
+ }
145
+ export function mapAuth(params) {
146
+ const surface = params.surface ?? null;
147
+ if (surface?.auth.status === "none") {
148
+ return { auth: { kind: "none" }, ok: true };
149
+ }
150
+ if (params.securitySchemes) {
151
+ const fromSpec = mapAuthFromSecuritySchemes(params.securitySchemes);
152
+ if (fromSpec) {
153
+ return { auth: fromSpec, ok: true };
154
+ }
155
+ }
156
+ if (surface) {
157
+ const credentials = params.discover?.credentials ?? {};
158
+ const credentialLabel = (id) => credentials[id]?.label ?? "API credential";
159
+ for (const entry of surface.auth.entries) {
160
+ const mapped = mapSurfaceAuthEntry({
161
+ credentialLabel,
162
+ discover: params.discover ?? null,
163
+ entry,
164
+ });
165
+ if (mapped) {
166
+ return { auth: mapped, ok: true };
167
+ }
168
+ }
169
+ }
170
+ if (params.discover) {
171
+ const fromRegistry = mapAuthFromRegistry(params.discover);
172
+ if (fromRegistry) {
173
+ return { auth: fromRegistry, ok: true };
174
+ }
175
+ }
176
+ const found = surface
177
+ ? describeSurfaceAuth(surface)
178
+ : params.sourceKind === "openapi"
179
+ ? "no usable securitySchemes in the spec"
180
+ : "no registry auth facts for this endpoint";
181
+ return {
182
+ ok: false,
183
+ reason: `cannot map authentication (${found}) — supported: no auth, OAuth 2 authorization_code, or a single header/query API key`,
184
+ };
185
+ }