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,567 @@
1
+ import { defineConnector, } from "../../shims/connections-sdk.js";
2
+ import { z } from "zod";
3
+ import { connectorAction, GOOGLE_OAUTH2, googleJson } from "../shared.js";
4
+ const GMAIL_API = "https://gmail.googleapis.com/gmail/v1/users/me";
5
+ const SCOPE_READONLY = "https://www.googleapis.com/auth/gmail.readonly";
6
+ const SCOPE_COMPOSE = "https://www.googleapis.com/auth/gmail.compose";
7
+ const SCOPE_MODIFY = "https://www.googleapis.com/auth/gmail.modify";
8
+ const SCOPE_SEND = "https://www.googleapis.com/auth/gmail.send";
9
+ function getHeader(headers, name) {
10
+ return headers?.find((h) => h.name.toLowerCase() === name.toLowerCase())
11
+ ?.value;
12
+ }
13
+ function parseEmailAddress(raw) {
14
+ // "John Doe <john@example.com>" → { name: "John Doe", email: "john@example.com" }
15
+ const match = raw.match(/^(.+?)\s*<([^>]+)>$/);
16
+ if (match) {
17
+ return { email: match[2], name: match[1].trim().replace(/^"|"$/g, "") };
18
+ }
19
+ return { email: raw.trim() };
20
+ }
21
+ function parseAddressList(raw) {
22
+ if (!raw) {
23
+ return [];
24
+ }
25
+ return raw
26
+ .split(",")
27
+ .map((a) => parseEmailAddress(a.trim()).email)
28
+ .filter((e) => e.length > 0);
29
+ }
30
+ /** To is often empty for mailing lists / Google Groups; Delivered-To has the mailbox. */
31
+ function parseRecipientEmails(headers) {
32
+ const to = parseAddressList(getHeader(headers, "To"));
33
+ if (to.length > 0) {
34
+ return to;
35
+ }
36
+ const delivered = parseAddressList(getHeader(headers, "Delivered-To"));
37
+ if (delivered.length > 0) {
38
+ return delivered;
39
+ }
40
+ return parseAddressList(getHeader(headers, "X-Original-To"));
41
+ }
42
+ /** Gmail uses URL-safe base64 for message bodies. */
43
+ function decodeBase64Url(data) {
44
+ const base64 = data.replace(/-/g, "+").replace(/_/g, "/");
45
+ return Buffer.from(base64, "base64").toString("utf-8");
46
+ }
47
+ /** Gmail attachment API returns URL-safe base64 without padding. */
48
+ export function gmailAttachmentDataToBase64(data) {
49
+ const base64 = data.replace(/-/g, "+").replace(/_/g, "/");
50
+ const pad = base64.length % 4;
51
+ return pad > 0 ? base64 + "=".repeat(4 - pad) : base64;
52
+ }
53
+ function extractBody(payload, mimeType) {
54
+ if (!payload) {
55
+ return null;
56
+ }
57
+ if (payload.mimeType === mimeType && payload.body?.data) {
58
+ return decodeBase64Url(payload.body.data);
59
+ }
60
+ if (payload.parts) {
61
+ for (const part of payload.parts) {
62
+ const result = extractBody(part, mimeType);
63
+ if (result) {
64
+ return result;
65
+ }
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+ function parseGmailMessage(raw) {
71
+ const headers = raw.payload?.headers;
72
+ const { email: fromEmail, name: fromName } = parseEmailAddress(getHeader(headers, "From") ?? "");
73
+ return {
74
+ body_text: extractBody(raw.payload, "text/plain") ?? raw.snippet ?? null,
75
+ cc: parseAddressList(getHeader(headers, "Cc")),
76
+ date: raw.internalDate
77
+ ? new Date(Number(raw.internalDate)).toISOString()
78
+ : (getHeader(headers, "Date") ?? null),
79
+ from_email: fromEmail || null,
80
+ from_name: fromName ?? null,
81
+ message_id: raw.id,
82
+ subject: getHeader(headers, "Subject") ?? null,
83
+ to: parseRecipientEmails(headers),
84
+ };
85
+ }
86
+ // ─── Inbound message stream (module consumption API only) ───
87
+ function collectAttachments(payload, out) {
88
+ if (!payload) {
89
+ return;
90
+ }
91
+ if (payload.filename && payload.body?.attachmentId) {
92
+ out.push({
93
+ attachment_id: payload.body.attachmentId,
94
+ content_id: getHeader(payload.headers, "Content-ID") ?? null,
95
+ filename: payload.filename,
96
+ mime_type: payload.mimeType ?? null,
97
+ size: payload.body.size ?? null,
98
+ });
99
+ }
100
+ for (const part of payload.parts ?? []) {
101
+ collectAttachments(part, out);
102
+ }
103
+ }
104
+ function toInboundMessage(raw) {
105
+ const parsed = parseGmailMessage(raw);
106
+ const attachments = [];
107
+ collectAttachments(raw.payload, attachments);
108
+ return {
109
+ attachments,
110
+ body_html: extractBody(raw.payload, "text/html"),
111
+ body_text: parsed.body_text,
112
+ cc: parsed.cc,
113
+ from_email: parsed.from_email,
114
+ from_name: parsed.from_name,
115
+ provider_message_id: parsed.message_id,
116
+ provider_thread_id: raw.threadId ?? null,
117
+ received_at: parsed.date,
118
+ subject: parsed.subject,
119
+ to: parsed.to,
120
+ };
121
+ }
122
+ /** Drafts and chats are never "inbound"; everything else flows through. */
123
+ const EXCLUDED_LABELS = new Set(["CHAT", "DRAFT"]);
124
+ const DEFAULT_PULL_LIMIT = 50;
125
+ const MAX_PULL_LIMIT = 100;
126
+ function parseBackfillCursor(cursor) {
127
+ if (!cursor.startsWith("{")) {
128
+ return null;
129
+ }
130
+ try {
131
+ const parsed = JSON.parse(cursor);
132
+ return parsed.backfill ?? null;
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ }
138
+ function backfillQuery(since) {
139
+ const sinceSeconds = since ? Math.floor(Date.parse(since) / 1000) : null;
140
+ const window = sinceSeconds !== null && Number.isFinite(sinceSeconds)
141
+ ? `after:${sinceSeconds}`
142
+ : "newer_than:30d";
143
+ return `${window} -in:chats -in:draft`;
144
+ }
145
+ function pullLimit(ctx) {
146
+ return Math.min(Math.max(ctx.limit ?? DEFAULT_PULL_LIMIT, 1), MAX_PULL_LIMIT);
147
+ }
148
+ // Gmail caps concurrent requests per user ("Too many concurrent requests",
149
+ // 429 rateLimitExceeded) — fetch details in small chunks, never all at once.
150
+ const DETAIL_FETCH_CONCURRENCY = 5;
151
+ const RATE_LIMIT_RETRIES = 3;
152
+ const RATE_LIMIT_BACKOFF_MS = 1000;
153
+ function sleep(ms) {
154
+ return new Promise((resolve) => setTimeout(resolve, ms));
155
+ }
156
+ async function fetchMessageDetail(ctx, id) {
157
+ for (let attempt = 0;; attempt++) {
158
+ try {
159
+ return await googleJson(ctx, `${GMAIL_API}/messages/${encodeURIComponent(id)}?format=full`);
160
+ }
161
+ catch (error) {
162
+ // Deleted between listing and fetch — skip instead of failing the pull.
163
+ if (error instanceof Error && error.message.includes("(404)")) {
164
+ ctx.log("gmail stream: message vanished before fetch", { id });
165
+ return null;
166
+ }
167
+ // Transient rate limit — back off and retry before failing the pull.
168
+ if (error instanceof Error &&
169
+ error.message.includes("(429)") &&
170
+ attempt < RATE_LIMIT_RETRIES) {
171
+ const delay = RATE_LIMIT_BACKOFF_MS * 2 ** attempt;
172
+ ctx.log("gmail stream: rate limited, backing off", { delay, id });
173
+ await sleep(delay);
174
+ continue;
175
+ }
176
+ throw error;
177
+ }
178
+ }
179
+ }
180
+ async function fetchInboundMessages(ctx, ids) {
181
+ const messages = [];
182
+ for (let i = 0; i < ids.length; i += DETAIL_FETCH_CONCURRENCY) {
183
+ const chunk = ids.slice(i, i + DETAIL_FETCH_CONCURRENCY);
184
+ messages.push(...(await Promise.all(chunk.map((id) => fetchMessageDetail(ctx, id)))));
185
+ }
186
+ return messages
187
+ .filter((m) => m !== null)
188
+ .map(toInboundMessage);
189
+ }
190
+ /** First pull (or backfill continuation): messages.list over a date window. */
191
+ async function pullBackfillPage(ctx, historyId, pageToken) {
192
+ const url = new URL(`${GMAIL_API}/messages`);
193
+ url.searchParams.set("q", backfillQuery(ctx.since));
194
+ url.searchParams.set("maxResults", String(pullLimit(ctx)));
195
+ if (pageToken) {
196
+ url.searchParams.set("pageToken", pageToken);
197
+ }
198
+ const list = await googleJson(ctx, url.toString());
199
+ const items = await fetchInboundMessages(ctx, (list.messages ?? []).map((m) => m.id));
200
+ return {
201
+ hasMore: Boolean(list.nextPageToken),
202
+ items,
203
+ nextCursor: list.nextPageToken
204
+ ? JSON.stringify({
205
+ backfill: { historyId, pageToken: list.nextPageToken },
206
+ })
207
+ : historyId,
208
+ };
209
+ }
210
+ /** Incremental pull: history.list from the cursor historyId. */
211
+ async function pullIncremental(ctx, cursor) {
212
+ const limit = pullLimit(ctx);
213
+ const ids = new Set();
214
+ let pageToken;
215
+ let lastRecordId = null;
216
+ let latestHistoryId = null;
217
+ let stoppedEarly = false;
218
+ do {
219
+ const url = new URL(`${GMAIL_API}/history`);
220
+ url.searchParams.set("startHistoryId", cursor);
221
+ url.searchParams.set("historyTypes", "messageAdded");
222
+ url.searchParams.set("maxResults", "100");
223
+ if (pageToken) {
224
+ url.searchParams.set("pageToken", pageToken);
225
+ }
226
+ let data;
227
+ try {
228
+ data = await googleJson(ctx, url.toString());
229
+ }
230
+ catch (error) {
231
+ // Gmail expires history ids (~a week); the consumer must restart with
232
+ // a null cursor (its own dedupe absorbs the backfill overlap).
233
+ if (error instanceof Error && error.message.includes("(404)")) {
234
+ throw new Error("gmail_stream_cursor_expired: the history cursor is no longer valid; pull again with a null cursor to re-backfill");
235
+ }
236
+ throw error;
237
+ }
238
+ latestHistoryId = data.historyId ?? latestHistoryId;
239
+ for (const record of data.history ?? []) {
240
+ const added = (record.messagesAdded ?? [])
241
+ .map((m) => m.message)
242
+ .filter((m) => Boolean(m?.id) &&
243
+ !(m?.labelIds ?? []).some((l) => EXCLUDED_LABELS.has(l)));
244
+ if (ids.size + added.length > limit && ids.size > 0) {
245
+ // Stop BEFORE this record: history ids are only safe cursor points
246
+ // at record granularity (startHistoryId is exclusive).
247
+ stoppedEarly = true;
248
+ break;
249
+ }
250
+ for (const message of added) {
251
+ ids.add(message.id);
252
+ }
253
+ lastRecordId = record.id;
254
+ }
255
+ if (stoppedEarly) {
256
+ break;
257
+ }
258
+ pageToken = data.nextPageToken;
259
+ } while (pageToken);
260
+ const items = await fetchInboundMessages(ctx, [...ids]);
261
+ return {
262
+ hasMore: stoppedEarly,
263
+ items,
264
+ nextCursor: stoppedEarly
265
+ ? (lastRecordId ?? cursor)
266
+ : (latestHistoryId ?? cursor),
267
+ };
268
+ }
269
+ async function pullGmailStream(ctx, cursor) {
270
+ if (cursor === null) {
271
+ // Capture the mailbox history id BEFORE listing so the switch to
272
+ // incremental pulls never leaves a gap (overlap is deduped downstream
273
+ // by provider_message_id).
274
+ const profile = await googleJson(ctx, `${GMAIL_API}/profile`);
275
+ return pullBackfillPage(ctx, profile.historyId, null);
276
+ }
277
+ const backfill = parseBackfillCursor(cursor);
278
+ if (backfill) {
279
+ return pullBackfillPage(ctx, backfill.historyId, backfill.pageToken);
280
+ }
281
+ return pullIncremental(ctx, cursor);
282
+ }
283
+ // ─── RFC 822 assembly for drafts/sending ───
284
+ function encodeHeaderValue(value) {
285
+ // RFC 2047 B-encoding for non-ASCII header values.
286
+ return /^[\x20-\x7e]*$/.test(value)
287
+ ? value
288
+ : `=?UTF-8?B?${Buffer.from(value, "utf-8").toString("base64")}?=`;
289
+ }
290
+ function buildRawMessage(input) {
291
+ const lines = [
292
+ `To: ${input.to.join(", ")}`,
293
+ ...(input.cc?.length ? [`Cc: ${input.cc.join(", ")}`] : []),
294
+ `Subject: ${encodeHeaderValue(input.subject)}`,
295
+ "MIME-Version: 1.0",
296
+ 'Content-Type: text/plain; charset="UTF-8"',
297
+ "Content-Transfer-Encoding: 8bit",
298
+ "",
299
+ input.body_text,
300
+ ];
301
+ return Buffer.from(lines.join("\r\n"), "utf-8").toString("base64url");
302
+ }
303
+ // ─── Shared schema fragments ───
304
+ const emailAddress = z.string().min(3);
305
+ const composeFields = {
306
+ body_text: z.string().describe("Plain-text body of the email."),
307
+ cc: z
308
+ .array(emailAddress.describe("Cc recipient email address."))
309
+ .optional()
310
+ .describe("Optional list of Cc recipient email addresses."),
311
+ subject: z.string().describe("Email subject line."),
312
+ to: z
313
+ .array(emailAddress.describe("Recipient email address."))
314
+ .min(1)
315
+ .describe("List of recipient email addresses (To)."),
316
+ };
317
+ // ─── Connector ───
318
+ export const gmailConnector = defineConnector({
319
+ actions: [
320
+ connectorAction({
321
+ description: "Fetch a Gmail attachment body by message id and attachment id (for inbox previews).",
322
+ group: "read",
323
+ handler: async (input, ctx) => {
324
+ const data = await googleJson(ctx, `${GMAIL_API}/messages/${encodeURIComponent(input.message_id)}/attachments/${encodeURIComponent(input.attachment_id)}`);
325
+ if (!data.data) {
326
+ throw new Error("gmail attachment has no data");
327
+ }
328
+ return {
329
+ data_base64: gmailAttachmentDataToBase64(data.data),
330
+ size: data.size ?? null,
331
+ };
332
+ },
333
+ id: "get_attachment",
334
+ inputSchema: z.object({
335
+ attachment_id: z
336
+ .string()
337
+ .describe("Gmail attachment id from message metadata."),
338
+ message_id: z.string().describe("Gmail message id."),
339
+ }),
340
+ providerScopes: [SCOPE_READONLY],
341
+ summary: "Fetch a Gmail attachment",
342
+ }),
343
+ connectorAction({
344
+ description: "Search Gmail threads with a Gmail query string and return brief per-thread info (subject, sender, date, snippet, message count).",
345
+ group: "read",
346
+ handler: async (input, ctx) => {
347
+ const url = new URL(`${GMAIL_API}/threads`);
348
+ url.searchParams.set("q", input.q);
349
+ url.searchParams.set("maxResults", String(input.max_results ?? 10));
350
+ const list = await googleJson(ctx, url.toString());
351
+ const threads = list.threads ?? [];
352
+ const detailed = await Promise.all(threads.map(async (t) => {
353
+ const detail = await googleJson(ctx, `${GMAIL_API}/threads/${encodeURIComponent(t.id)}?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Date`);
354
+ const messages = detail.messages ?? [];
355
+ const last = messages.at(-1);
356
+ const headers = last?.payload?.headers;
357
+ return {
358
+ from: getHeader(headers, "From") ?? null,
359
+ last_message_at: last?.internalDate
360
+ ? new Date(Number(last.internalDate)).toISOString()
361
+ : (getHeader(headers, "Date") ?? null),
362
+ message_count: messages.length,
363
+ snippet: t.snippet ?? null,
364
+ subject: getHeader(headers, "Subject") ?? null,
365
+ thread_id: t.id,
366
+ };
367
+ }));
368
+ return {
369
+ result_size_estimate: list.resultSizeEstimate ?? detailed.length,
370
+ threads: detailed,
371
+ };
372
+ },
373
+ id: "search_threads",
374
+ inputSchema: z.object({
375
+ max_results: z
376
+ .number()
377
+ .int()
378
+ .min(1)
379
+ .max(25)
380
+ .optional()
381
+ .describe("Maximum number of threads to return (1-25, default 10)."),
382
+ q: z
383
+ .string()
384
+ .describe('Gmail search query, same syntax as the Gmail search box (e.g. "from:alice@example.com is:unread newer_than:7d").'),
385
+ }),
386
+ providerScopes: [SCOPE_READONLY],
387
+ summary: "Search Gmail threads",
388
+ }),
389
+ connectorAction({
390
+ description: "Fetch a Gmail thread by id with all messages parsed: from/to/cc, subject, date and plain-text body.",
391
+ group: "read",
392
+ handler: async (input, ctx) => {
393
+ const thread = await googleJson(ctx, `${GMAIL_API}/threads/${encodeURIComponent(input.thread_id)}?format=full`);
394
+ return {
395
+ message_count: thread.messages?.length ?? 0,
396
+ messages: (thread.messages ?? []).map(parseGmailMessage),
397
+ thread_id: thread.id,
398
+ };
399
+ },
400
+ id: "get_thread",
401
+ inputSchema: z.object({
402
+ thread_id: z
403
+ .string()
404
+ .describe("Gmail thread id (from search_threads results)."),
405
+ }),
406
+ providerScopes: [SCOPE_READONLY],
407
+ summary: "Read a full Gmail thread",
408
+ }),
409
+ connectorAction({
410
+ description: "List all Gmail labels (system and user-created) with their ids, for use with modify_labels.",
411
+ group: "read",
412
+ handler: async (_input, ctx) => {
413
+ const data = await googleJson(ctx, `${GMAIL_API}/labels`);
414
+ return {
415
+ labels: (data.labels ?? []).map((l) => ({
416
+ id: l.id,
417
+ name: l.name,
418
+ type: l.type ?? "user",
419
+ })),
420
+ };
421
+ },
422
+ id: "list_labels",
423
+ inputSchema: z.object({}),
424
+ providerScopes: [SCOPE_READONLY],
425
+ summary: "List Gmail labels",
426
+ }),
427
+ connectorAction({
428
+ description: "List Gmail drafts with their ids, recipients and subjects.",
429
+ group: "read",
430
+ handler: async (input, ctx) => {
431
+ const url = new URL(`${GMAIL_API}/drafts`);
432
+ url.searchParams.set("maxResults", String(input.max_results ?? 10));
433
+ const list = await googleJson(ctx, url.toString());
434
+ const drafts = await Promise.all((list.drafts ?? []).map(async (d) => {
435
+ const detail = await googleJson(ctx, `${GMAIL_API}/drafts/${encodeURIComponent(d.id)}?format=metadata`);
436
+ const headers = detail.message?.payload?.headers;
437
+ return {
438
+ draft_id: d.id,
439
+ message_id: detail.message?.id ?? d.message?.id ?? null,
440
+ subject: getHeader(headers, "Subject") ?? null,
441
+ thread_id: detail.message?.threadId ?? null,
442
+ to: parseAddressList(getHeader(headers, "To")),
443
+ };
444
+ }));
445
+ return { drafts };
446
+ },
447
+ id: "list_drafts",
448
+ inputSchema: z.object({
449
+ max_results: z
450
+ .number()
451
+ .int()
452
+ .min(1)
453
+ .max(25)
454
+ .optional()
455
+ .describe("Maximum number of drafts to return (1-25, default 10)."),
456
+ }),
457
+ providerScopes: [SCOPE_READONLY],
458
+ summary: "List Gmail drafts",
459
+ }),
460
+ connectorAction({
461
+ description: "Create a Gmail draft (plain text). The draft is saved but NOT sent; use send_message to actually send email.",
462
+ group: "write",
463
+ handler: async (input, ctx) => {
464
+ const result = await googleJson(ctx, `${GMAIL_API}/drafts`, {
465
+ body: JSON.stringify({ message: { raw: buildRawMessage(input) } }),
466
+ headers: { "Content-Type": "application/json" },
467
+ method: "POST",
468
+ });
469
+ return {
470
+ draft_id: result.id,
471
+ message_id: result.message?.id ?? null,
472
+ thread_id: result.message?.threadId ?? null,
473
+ };
474
+ },
475
+ id: "create_draft",
476
+ inputSchema: z.object(composeFields),
477
+ providerScopes: [SCOPE_COMPOSE, SCOPE_MODIFY],
478
+ summary: "Create a Gmail draft",
479
+ }),
480
+ connectorAction({
481
+ description: "Add and/or remove labels on a Gmail message (label ids from list_labels, e.g. STARRED, or a user label id).",
482
+ group: "write",
483
+ handler: async (input, ctx) => {
484
+ const result = await googleJson(ctx, `${GMAIL_API}/messages/${encodeURIComponent(input.message_id)}/modify`, {
485
+ body: JSON.stringify({
486
+ addLabelIds: input.add ?? [],
487
+ removeLabelIds: input.remove ?? [],
488
+ }),
489
+ headers: { "Content-Type": "application/json" },
490
+ method: "POST",
491
+ });
492
+ return {
493
+ label_ids: result.labelIds ?? [],
494
+ message_id: result.id,
495
+ thread_id: result.threadId ?? null,
496
+ };
497
+ },
498
+ id: "modify_labels",
499
+ inputSchema: z
500
+ .object({
501
+ add: z
502
+ .array(z.string().describe("Label id to add."))
503
+ .optional()
504
+ .describe("Label ids to add to the message."),
505
+ message_id: z.string().describe("Gmail message id to modify."),
506
+ remove: z
507
+ .array(z.string().describe("Label id to remove."))
508
+ .optional()
509
+ .describe("Label ids to remove from the message."),
510
+ })
511
+ .refine((v) => (v.add?.length ?? 0) + (v.remove?.length ?? 0) > 0, {
512
+ message: "Provide at least one label id in add or remove.",
513
+ }),
514
+ providerScopes: [SCOPE_COMPOSE, SCOPE_MODIFY],
515
+ summary: "Modify Gmail message labels",
516
+ }),
517
+ connectorAction({
518
+ description: "Send an email via Gmail immediately (plain text). This delivers mail to the recipients — irreversible.",
519
+ group: "destructive",
520
+ handler: async (input, ctx) => {
521
+ const result = await googleJson(ctx, `${GMAIL_API}/messages/send`, {
522
+ body: JSON.stringify({ raw: buildRawMessage(input) }),
523
+ headers: { "Content-Type": "application/json" },
524
+ method: "POST",
525
+ });
526
+ return {
527
+ message_id: result.id,
528
+ sent: true,
529
+ thread_id: result.threadId ?? null,
530
+ };
531
+ },
532
+ id: "send_message",
533
+ inputSchema: z.object(composeFields),
534
+ providerScopes: [SCOPE_SEND],
535
+ summary: "Send an email via Gmail",
536
+ }),
537
+ connectorAction({
538
+ description: "Move a Gmail message to the trash (recoverable in Gmail for ~30 days).",
539
+ group: "destructive",
540
+ handler: async (input, ctx) => {
541
+ const result = await googleJson(ctx, `${GMAIL_API}/messages/${encodeURIComponent(input.message_id)}/trash`, { method: "POST" });
542
+ return {
543
+ label_ids: result.labelIds ?? [],
544
+ message_id: result.id,
545
+ trashed: true,
546
+ };
547
+ },
548
+ id: "trash_message",
549
+ inputSchema: z.object({
550
+ message_id: z.string().describe("Gmail message id to move to trash."),
551
+ }),
552
+ providerScopes: [SCOPE_MODIFY],
553
+ summary: "Trash a Gmail message",
554
+ }),
555
+ ],
556
+ auth: { kind: "oauth2", oauth2: GOOGLE_OAUTH2 },
557
+ description: "Read, draft, label and send email in a connected Gmail account.",
558
+ icon: "logo:gmail",
559
+ id: "google-gmail",
560
+ moduleId: "connections-google",
561
+ name: "Gmail",
562
+ // Inbound message stream for module consumers (inbox, KB, …): incremental
563
+ // via history.list (cursor = historyId), initial backfill via messages.list
564
+ // over a date window. Never projected as an agent tool.
565
+ stream: { kind: "messages", pull: pullGmailStream },
566
+ toolPrefix: "gmail",
567
+ });
@@ -0,0 +1,11 @@
1
+ import { calendarConnector } from "./connectors/calendar.js";
2
+ import { contactsConnector } from "./connectors/contacts.js";
3
+ import { driveConnector } from "./connectors/drive.js";
4
+ import { gmailConnector } from "./connectors/gmail.js";
5
+ /** All connectors this module registers, in registration order. */
6
+ export const googleConnectors = [
7
+ gmailConnector,
8
+ driveConnector,
9
+ calendarConnector,
10
+ contactsConnector,
11
+ ];
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Shared Google OAuth2 config for all three connectors. One OAuth client
3
+ * (GOOGLE_OAUTH_CLIENT_ID/SECRET) covers Gmail, Drive and Calendar; scopes are
4
+ * requested incrementally per enabled action group
5
+ * (`include_granted_scopes=true`).
6
+ */
7
+ export const GOOGLE_OAUTH2 = {
8
+ authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
9
+ baseScopes: ["openid", "email"],
10
+ clientIdEnv: "GOOGLE_OAUTH_CLIENT_ID",
11
+ clientSecretEnv: "GOOGLE_OAUTH_CLIENT_SECRET",
12
+ extraAuthParams: {
13
+ access_type: "offline",
14
+ include_granted_scopes: "true",
15
+ prompt: "consent",
16
+ },
17
+ resolveAccount: async (accessToken, fetchImpl) => {
18
+ const res = await fetchImpl("https://www.googleapis.com/oauth2/v2/userinfo", {
19
+ headers: { Authorization: `Bearer ${accessToken}` },
20
+ });
21
+ if (!res.ok) {
22
+ throw new Error(`google_api_error (${res.status}): ${await errorExcerpt(res)}`);
23
+ }
24
+ const data = (await res.json());
25
+ return {
26
+ ...(data.id ? { externalId: data.id } : {}),
27
+ label: data.email ?? "Google account",
28
+ };
29
+ },
30
+ tokenUrl: "https://oauth2.googleapis.com/token",
31
+ };
32
+ async function errorExcerpt(res) {
33
+ const body = await res.text().catch(() => "");
34
+ return body.replace(/\s+/g, " ").trim().slice(0, 200) || res.statusText;
35
+ }
36
+ /**
37
+ * Bearer-authenticated fetch against a Google API. Throws
38
+ * `google_api_error (<status>): <short body excerpt>` on non-2xx responses.
39
+ */
40
+ export async function googleFetch(ctx, url, init) {
41
+ const res = await ctx.fetchImpl(url, {
42
+ ...init,
43
+ headers: {
44
+ Authorization: `Bearer ${ctx.accessToken}`,
45
+ ...(init?.headers ?? {}),
46
+ },
47
+ });
48
+ if (!res.ok) {
49
+ throw new Error(`google_api_error (${res.status}): ${await errorExcerpt(res)}`);
50
+ }
51
+ return res;
52
+ }
53
+ /** googleFetch + JSON body. */
54
+ export async function googleJson(ctx, url, init) {
55
+ const res = await googleFetch(ctx, url, init);
56
+ return (await res.json());
57
+ }
58
+ /**
59
+ * Build a `ConnectorAction` whose handler receives zod-parsed, typed input.
60
+ * The runtime hands the raw operation input straight to the handler, so each
61
+ * action validates against its own inputSchema before touching the provider.
62
+ */
63
+ export function connectorAction(def) {
64
+ return {
65
+ description: def.description,
66
+ group: def.group,
67
+ handler: (input, ctx) => def.handler(def.inputSchema.parse(input), ctx),
68
+ id: def.id,
69
+ inputSchema: def.inputSchema,
70
+ providerScopes: def.providerScopes,
71
+ summary: def.summary,
72
+ };
73
+ }