@zackbart/connecta 0.34.1 → 0.36.0

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 (441) hide show
  1. package/CHANGELOG.md +418 -0
  2. package/README.md +65 -105
  3. package/SECURITY.md +3 -2
  4. package/bin/connecta.mjs +41 -16
  5. package/bin/version.mjs +1 -1
  6. package/dist/access-tokens.d.ts +2 -1
  7. package/dist/access-tokens.js +66 -30
  8. package/dist/activity.d.ts +1 -1
  9. package/dist/activity.js +2 -0
  10. package/dist/agent-config.d.ts +23 -0
  11. package/dist/agent-config.js +59 -0
  12. package/dist/agent-description.js +16 -0
  13. package/dist/agent-identity.js +4 -4
  14. package/dist/agent-observation.js +39 -0
  15. package/dist/auth/clerk-sdk/client.d.ts +2 -0
  16. package/dist/auth/clerk-sdk/generated.js +554 -563
  17. package/dist/auth/clerk-transport.js +70 -3
  18. package/dist/auth/clerk.d.ts +13 -0
  19. package/dist/auth/clerk.js +653 -246
  20. package/dist/auth/cloudflare-access.js +46 -42
  21. package/dist/auth/downstream-client-metadata.js +2 -2
  22. package/dist/auth/downstream-oauth.js +2044 -1161
  23. package/dist/auth/downstream-revocation.js +88 -88
  24. package/dist/auth/native-call.js +20 -0
  25. package/dist/auth/native.js +184 -0
  26. package/dist/auth/oauth-start-reset.js +11 -5
  27. package/dist/auth/oauth-token-response.js +121 -102
  28. package/dist/auth/oauth-v2-migration.js +97 -78
  29. package/dist/auth/renewable-grant.js +10 -0
  30. package/dist/auth/static-oauth.js +751 -281
  31. package/dist/auth/vercel-agent.js +266 -0
  32. package/dist/auth-admission.js +204 -0
  33. package/dist/auth-elicitation.js +350 -241
  34. package/dist/authorization-handoff.js +164 -90
  35. package/dist/call-admission-context.js +3 -10
  36. package/dist/call-admission.js +2 -0
  37. package/dist/call-classification.js +9 -0
  38. package/dist/call-recovery.js +32 -13
  39. package/dist/canonical-json.js +8 -0
  40. package/dist/catalog-cache.js +369 -213
  41. package/dist/catalog-service.js +659 -530
  42. package/dist/catalog.js +5 -1
  43. package/dist/concurrency.js +0 -25
  44. package/dist/config-defaults.d.ts +1 -0
  45. package/dist/config-defaults.js +1 -0
  46. package/dist/config-schema.d.ts +3 -1
  47. package/dist/config-schema.js +13 -3
  48. package/dist/config.d.ts +40 -6
  49. package/dist/config.js +40 -26
  50. package/dist/connector-caller.d.ts +4 -0
  51. package/dist/connector-caller.js +17 -4
  52. package/dist/connectors/api-connector.d.ts +29 -7
  53. package/dist/connectors/api-connector.js +145 -57
  54. package/dist/connectors/guarded-fetch.d.ts +51 -17
  55. package/dist/connectors/guarded-fetch.js +215 -121
  56. package/dist/connectors/negotiation-cache.js +20 -10
  57. package/dist/connectors/remote-mcp.d.ts +10 -30
  58. package/dist/connectors/remote-mcp.js +955 -563
  59. package/dist/d1.js +10 -243
  60. package/dist/derived-context.js +5 -0
  61. package/dist/describe-config.d.ts +3 -1
  62. package/dist/describe-config.js +2 -0
  63. package/dist/downstream-input.js +347 -260
  64. package/dist/errors.d.ts +87 -6
  65. package/dist/errors.js +114 -4
  66. package/dist/execute.js +87 -50
  67. package/dist/executor-admission.js +3 -0
  68. package/dist/executors/quickjs.js +163 -112
  69. package/dist/guarded-transport.js +50 -0
  70. package/dist/guest-runtime.js +1 -1
  71. package/dist/http/client.js +439 -0
  72. package/dist/http/errors.js +558 -0
  73. package/dist/http/fields.js +49 -0
  74. package/dist/http/hatch-paging.js +318 -0
  75. package/dist/http/hatch.js +1279 -0
  76. package/dist/http/json-schema.js +267 -0
  77. package/dist/http/node-transport.js +241 -0
  78. package/dist/http/oauth.js +162 -0
  79. package/dist/http/paging.js +227 -0
  80. package/dist/http/provider.js +221 -0
  81. package/dist/http/safety.js +82 -0
  82. package/dist/http/services.js +79 -0
  83. package/dist/http/tools.js +379 -0
  84. package/dist/http/vendor-index.js +138 -0
  85. package/dist/http/vendor.js +448 -0
  86. package/dist/http/write-request.js +66 -0
  87. package/dist/http-policy.d.ts +11 -0
  88. package/dist/http-policy.js +45 -0
  89. package/dist/inbound-credential.js +1 -1
  90. package/dist/index.d.ts +2 -2
  91. package/dist/index.js +4 -0
  92. package/dist/invocation-auth.js +30 -19
  93. package/dist/invocation.js +218 -85
  94. package/dist/mcp-result.js +0 -4
  95. package/dist/membership.d.ts +4 -0
  96. package/dist/meta-output.js +1 -1
  97. package/dist/meta-tools.js +531 -406
  98. package/dist/native-recovery.js +52 -0
  99. package/dist/native-tools.js +382 -0
  100. package/dist/node.d.ts +4 -3
  101. package/dist/node.js +45 -8
  102. package/dist/oauth-handoff.js +177 -104
  103. package/dist/oauth-partition.js +6 -20
  104. package/dist/oauth-sealing.js +6 -7
  105. package/dist/operation-vocabulary.js +101 -0
  106. package/dist/operator-record.d.ts +2 -2
  107. package/dist/operator-record.js +17 -4
  108. package/dist/operator-ui/contract.d.ts +8 -3
  109. package/dist/operator-ui/generated.js +4 -4
  110. package/dist/operator-ui/model.d.ts +2 -3
  111. package/dist/program-writes.js +47 -26
  112. package/dist/provider-call-facts.d.ts +115 -0
  113. package/dist/provider-call-facts.js +182 -0
  114. package/dist/provider-call.d.ts +112 -0
  115. package/dist/provider-call.js +271 -0
  116. package/dist/provider.d.ts +65 -31
  117. package/dist/provider.js +116 -39
  118. package/dist/providers/_shared/google/hatch.js +91 -0
  119. package/dist/providers/_shared/google/kit.js +427 -0
  120. package/dist/providers/_shared/google/result-size.js +15 -2
  121. package/dist/providers/_shared/google/service-account.d.ts +2 -1
  122. package/dist/providers/_shared/google/service-account.js +8 -4
  123. package/dist/providers/_shared/google/workspace.d.ts +6 -210
  124. package/dist/providers/_shared/google/workspace.js +6 -425
  125. package/dist/providers/_shared/rest/compact-index.js +86 -0
  126. package/dist/providers/_shared/rest/kit.js +146 -0
  127. package/dist/providers/_shared/rest/operation-index.js +230 -14
  128. package/dist/providers/_shared/rest/response-media.js +11 -0
  129. package/dist/providers/_shared/rest/value-safety.js +353 -195
  130. package/dist/providers/_shared/sensitive-fields.js +27 -0
  131. package/dist/providers/breeze/api.js +584 -0
  132. package/dist/providers/breeze/index.d.ts +1 -3
  133. package/dist/providers/breeze/index.js +67 -1420
  134. package/dist/providers/breeze/openapi.generated.js +60 -0
  135. package/dist/providers/breeze/skill.generated.js +2 -6
  136. package/dist/providers/breeze/tools.js +696 -0
  137. package/dist/providers/ccb/api.js +496 -0
  138. package/dist/providers/ccb/index.d.ts +15 -37
  139. package/dist/providers/ccb/index.js +77 -1702
  140. package/dist/providers/ccb/lib/constants.d.ts +34 -0
  141. package/dist/providers/ccb/lib/constants.js +74 -0
  142. package/dist/providers/ccb/lib/guide.js +9 -0
  143. package/dist/providers/ccb/lib/verdicts.js +387 -0
  144. package/dist/providers/ccb/openapi.generated.js +397 -0
  145. package/dist/providers/ccb/safety.js +295 -0
  146. package/dist/providers/ccb/skill.generated.js +2 -6
  147. package/dist/providers/ccb/tools.js +853 -0
  148. package/dist/providers/cloudflare/api.js +1066 -0
  149. package/dist/providers/cloudflare/index.d.ts +5 -22
  150. package/dist/providers/cloudflare/index.js +79 -197
  151. package/dist/providers/cloudflare/lib/config.d.ts +16 -0
  152. package/dist/providers/cloudflare/lib/config.js +2 -0
  153. package/dist/providers/cloudflare/lib/discovery.js +29 -0
  154. package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
  155. package/dist/providers/cloudflare/lib/hatch.js +103 -0
  156. package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
  157. package/dist/providers/cloudflare/lib/response-media.js +75 -0
  158. package/dist/providers/cloudflare/openapi.generated.js +4 -3
  159. package/dist/providers/cloudflare/{value-safety.js → safety.js} +487 -0
  160. package/dist/providers/cloudflare/skill.generated.js +2 -10
  161. package/dist/providers/cloudflare/tools.js +360 -0
  162. package/dist/providers/docs/api.js +51 -0
  163. package/dist/providers/docs/index.d.ts +1 -12
  164. package/dist/providers/docs/index.js +53 -1165
  165. package/dist/providers/docs/lib/constants.d.ts +18 -0
  166. package/dist/providers/docs/lib/constants.js +18 -0
  167. package/dist/providers/docs/lib/discovery.js +26 -0
  168. package/dist/providers/docs/lib/requests.js +48 -0
  169. package/dist/providers/docs/lib/schemas.js +168 -0
  170. package/dist/providers/docs/openapi.generated.js +16 -0
  171. package/dist/providers/docs/safety.js +54 -0
  172. package/dist/providers/docs/skill.generated.js +2 -6
  173. package/dist/providers/docs/tools.js +801 -0
  174. package/dist/providers/drive/api.js +177 -0
  175. package/dist/providers/drive/index.d.ts +1 -12
  176. package/dist/providers/drive/index.js +38 -1448
  177. package/dist/providers/drive/lib/constants.d.ts +6 -0
  178. package/dist/providers/drive/lib/constants.js +6 -0
  179. package/dist/providers/drive/lib/contracts.js +532 -0
  180. package/dist/providers/drive/lib/hatch.js +197 -0
  181. package/dist/providers/drive/openapi.generated.js +77 -0
  182. package/dist/providers/drive/safety.js +90 -0
  183. package/dist/providers/drive/skill.generated.js +2 -6
  184. package/dist/providers/drive/tools.js +1290 -0
  185. package/dist/providers/forms/api.js +71 -0
  186. package/dist/providers/forms/index.d.ts +1 -12
  187. package/dist/providers/forms/index.js +49 -1145
  188. package/dist/providers/forms/lib/constants.d.ts +14 -0
  189. package/dist/providers/forms/lib/constants.js +19 -0
  190. package/dist/providers/forms/lib/hatch.js +72 -0
  191. package/dist/providers/forms/openapi.generated.js +23 -0
  192. package/dist/providers/forms/safety.js +34 -0
  193. package/dist/providers/forms/skill.generated.js +2 -8
  194. package/dist/providers/forms/tools.js +1023 -0
  195. package/dist/providers/github/api.js +150 -0
  196. package/dist/providers/github/index.d.ts +4 -5
  197. package/dist/providers/github/index.js +30 -87
  198. package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
  199. package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
  200. package/dist/providers/github/lib/discovery.js +132 -0
  201. package/dist/providers/github/lib/operations.js +58 -0
  202. package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
  203. package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
  204. package/dist/providers/github/openapi.generated.js +1216 -0
  205. package/dist/providers/github/safety.js +216 -0
  206. package/dist/providers/github/skill.generated.js +2 -5
  207. package/dist/providers/github/tools.js +506 -0
  208. package/dist/providers/gmail/api.js +114 -0
  209. package/dist/providers/gmail/index.d.ts +1 -12
  210. package/dist/providers/gmail/index.js +73 -1621
  211. package/dist/providers/gmail/lib/constants.d.ts +13 -0
  212. package/dist/providers/gmail/lib/constants.js +16 -0
  213. package/dist/providers/gmail/lib/index.js +173 -0
  214. package/dist/providers/gmail/lib/schemas.js +176 -0
  215. package/dist/providers/gmail/lib/threading.js +120 -0
  216. package/dist/providers/gmail/openapi.generated.js +92 -0
  217. package/dist/providers/gmail/safety.js +21 -0
  218. package/dist/providers/gmail/skill.generated.js +2 -6
  219. package/dist/providers/gmail/tools.js +1435 -0
  220. package/dist/providers/overflow/api.js +1008 -0
  221. package/dist/providers/overflow/index.d.ts +1 -17
  222. package/dist/providers/overflow/index.js +60 -1488
  223. package/dist/providers/overflow/skill.generated.js +2 -5
  224. package/dist/providers/overflow/tools.js +873 -0
  225. package/dist/providers/planning-center/api.js +467 -0
  226. package/dist/providers/planning-center/index.d.ts +4 -35
  227. package/dist/providers/planning-center/index.js +69 -2445
  228. package/dist/providers/planning-center/lib/constants.d.ts +58 -0
  229. package/dist/providers/planning-center/lib/constants.js +83 -0
  230. package/dist/providers/planning-center/lib/discovery.js +40 -0
  231. package/dist/providers/planning-center/lib/guide.js +39 -0
  232. package/dist/providers/planning-center/lib/json-api.js +83 -0
  233. package/dist/providers/planning-center/lib/verdicts.js +1758 -0
  234. package/dist/providers/planning-center/openapi.generated.js +1764 -0
  235. package/dist/providers/planning-center/safety.js +188 -0
  236. package/dist/providers/planning-center/skill.generated.js +2 -27
  237. package/dist/providers/planning-center/tools.js +1010 -0
  238. package/dist/providers/quickbooks/api.js +299 -0
  239. package/dist/providers/quickbooks/index.d.ts +13 -0
  240. package/dist/providers/quickbooks/index.js +93 -0
  241. package/dist/providers/quickbooks/lib/query.js +118 -0
  242. package/dist/providers/quickbooks/lib/writes.js +168 -0
  243. package/dist/providers/quickbooks/safety.js +222 -0
  244. package/dist/providers/quickbooks/skill.generated.js +5 -0
  245. package/dist/providers/quickbooks/tools.js +117 -0
  246. package/dist/providers/sheets/api.js +133 -0
  247. package/dist/providers/sheets/index.d.ts +4 -16
  248. package/dist/providers/sheets/index.js +61 -1172
  249. package/dist/providers/sheets/lib/constants.d.ts +14 -0
  250. package/dist/providers/sheets/lib/constants.js +14 -0
  251. package/dist/providers/sheets/lib/hatch.js +163 -0
  252. package/dist/providers/sheets/lib/requests.js +105 -0
  253. package/dist/providers/sheets/lib/schemas.js +138 -0
  254. package/dist/providers/sheets/openapi.generated.js +30 -0
  255. package/dist/providers/sheets/safety.js +27 -0
  256. package/dist/providers/sheets/skill.generated.js +2 -7
  257. package/dist/providers/sheets/tools.js +733 -0
  258. package/dist/providers/slides/api.js +147 -0
  259. package/dist/providers/slides/index.d.ts +2 -10
  260. package/dist/providers/slides/index.js +48 -2555
  261. package/dist/providers/slides/lib/constants.d.ts +11 -0
  262. package/dist/providers/slides/lib/constants.js +11 -0
  263. package/dist/providers/slides/lib/requests.js +60 -0
  264. package/dist/providers/slides/lib/schemas.js +565 -0
  265. package/dist/providers/slides/openapi.generated.js +18 -0
  266. package/dist/providers/slides/skill.generated.js +2 -6
  267. package/dist/providers/slides/tools.js +1968 -0
  268. package/dist/providers/stripe/api.js +401 -0
  269. package/dist/providers/stripe/index.d.ts +5 -27
  270. package/dist/providers/stripe/index.js +74 -210
  271. package/dist/providers/stripe/lib/config.d.ts +4 -0
  272. package/dist/providers/stripe/lib/config.js +2 -0
  273. package/dist/providers/stripe/openapi.generated.js +4 -3
  274. package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
  275. package/dist/providers/stripe/skill.generated.js +2 -11
  276. package/dist/providers/stripe/tools.js +77 -0
  277. package/dist/providers/tithely/api.js +531 -0
  278. package/dist/providers/tithely/index.d.ts +17 -0
  279. package/dist/providers/tithely/index.js +86 -965
  280. package/dist/providers/tithely/skill.generated.js +2 -5
  281. package/dist/providers/tithely/tools.js +426 -0
  282. package/dist/providers/vercel/api.js +551 -0
  283. package/dist/providers/vercel/index.d.ts +9 -25
  284. package/dist/providers/vercel/index.js +65 -260
  285. package/dist/providers/vercel/lib/config.d.ts +2 -0
  286. package/dist/providers/vercel/lib/config.js +2 -0
  287. package/dist/providers/vercel/lib/index.js +117 -0
  288. package/dist/providers/vercel/lib/logs.js +100 -0
  289. package/dist/providers/vercel/lib/pin.js +270 -0
  290. package/dist/providers/vercel/openapi.generated.js +10 -5
  291. package/dist/providers/vercel/{value-safety.js → safety.js} +247 -38
  292. package/dist/providers/vercel/skill.generated.js +2 -7
  293. package/dist/providers/vercel/tools.js +642 -0
  294. package/dist/redaction-identity.js +47 -0
  295. package/dist/redaction-marker.js +125 -0
  296. package/dist/registry.d.ts +21 -15
  297. package/dist/registry.js +409 -376
  298. package/dist/routes/access-tokens.js +1 -1
  299. package/dist/routes/actor-labels.js +1 -1
  300. package/dist/routes/connect.js +1 -1
  301. package/dist/routes/dispatch.js +103 -0
  302. package/dist/routes/mcp.js +170 -79
  303. package/dist/routes/oauth.js +6 -1
  304. package/dist/routes/operator.js +2 -2
  305. package/dist/routes/shared.d.ts +5 -4
  306. package/dist/routes/shared.js +244 -177
  307. package/dist/routes/skills.js +10 -8
  308. package/dist/routes/ui-config.js +27 -3
  309. package/dist/routes/ui-data.js +21 -20
  310. package/dist/routes/ui.js +1 -1
  311. package/dist/runtime/call-admission.js +0 -26
  312. package/dist/runtime/call-entry.js +30 -0
  313. package/dist/runtime/call.js +552 -0
  314. package/dist/runtime/connector-scope.js +7 -0
  315. package/dist/runtime/credential-probe.js +34 -0
  316. package/dist/runtime/executor.js +40 -0
  317. package/dist/runtime/native-storage.js +63 -0
  318. package/dist/runtime/registry.js +27 -0
  319. package/dist/runtime/request-scope.js +72 -0
  320. package/dist/runtime/run.js +15 -7
  321. package/dist/runtime/shared-read.js +34 -5
  322. package/dist/runtime/storage.js +34 -21
  323. package/dist/sent-secrets.d.ts +194 -37
  324. package/dist/sent-secrets.js +1743 -340
  325. package/dist/server.js +5 -56
  326. package/dist/skills.js +322 -223
  327. package/dist/sqlite.js +15 -14
  328. package/dist/storage/d1-copy.js +255 -0
  329. package/dist/storage/d1-driver.js +15 -0
  330. package/dist/storage/keys.js +28 -0
  331. package/dist/storage/sql-driver.js +88 -0
  332. package/dist/storage/sql-native.js +544 -0
  333. package/dist/storage/sql-schema.d.ts +6 -0
  334. package/dist/storage/sql-schema.js +15 -0
  335. package/dist/storage/sql.d.ts +14 -20
  336. package/dist/storage/sql.js +29 -535
  337. package/dist/timeout.js +0 -26
  338. package/dist/tool-safety.js +20 -10
  339. package/dist/types.d.ts +9 -109
  340. package/dist/ui.d.ts +1 -1
  341. package/dist/ui.js +0 -1
  342. package/dist/upward.js +3 -0
  343. package/dist/usage-guide.js +3 -1
  344. package/dist/validate.d.ts +4 -2
  345. package/dist/validate.js +86 -9
  346. package/dist/version.d.ts +1 -1
  347. package/dist/version.js +1 -1
  348. package/dist/worker.js +312 -292
  349. package/dist/workspace-subject.js +5 -1
  350. package/dist/write-outcome.js +454 -34
  351. package/documentation/README.md +28 -7
  352. package/documentation/architecture.md +2409 -255
  353. package/documentation/auth.md +867 -114
  354. package/documentation/code-mode.md +49 -16
  355. package/documentation/deploying.md +164 -20
  356. package/documentation/hosted-mcp-migration.md +460 -0
  357. package/documentation/integrating.md +129 -144
  358. package/documentation/meta-tools.md +409 -32
  359. package/documentation/operating.md +27 -3
  360. package/documentation/operator-tests.md +3 -0
  361. package/documentation/operator-ui.md +84 -48
  362. package/documentation/provider-auth-migration.md +16 -63
  363. package/documentation/provider-migration-0.29.md +10 -5
  364. package/documentation/provider-migration-0.35.md +975 -0
  365. package/documentation/providers.md +1347 -0
  366. package/examples/worker/AGENTS.md +23 -9
  367. package/examples/worker/README.md +254 -135
  368. package/examples/worker/alchemy.run.ts +116 -0
  369. package/examples/worker/gitignore +10 -0
  370. package/examples/worker/src/connecta.config.ts +23 -21
  371. package/examples/worker/wrangler.jsonc +14 -5
  372. package/guides/connecting-agents.md +157 -0
  373. package/guides/how-it-works.md +156 -0
  374. package/guides/safety-and-trust.md +153 -0
  375. package/package.json +30 -50
  376. package/templates/node/.env.example +3 -0
  377. package/templates/node/README.md +22 -2
  378. package/templates/node/docker-compose.yml +1 -0
  379. package/templates/node/package.json +3 -2
  380. package/templates/node/src/connecta.config.ts +8 -0
  381. package/dist/catalog-drift.d.ts +0 -138
  382. package/dist/catalog-drift.js +0 -578
  383. package/dist/operator-ui/agents.d.ts +0 -74
  384. package/dist/operator-ui/agents.js +0 -104
  385. package/dist/operator-ui/config-summary.js +0 -565
  386. package/dist/operator-ui/connection-map.js +0 -376
  387. package/dist/operator-ui/endpoints.js +0 -221
  388. package/dist/operator-ui/fix-prompts.d.ts +0 -18
  389. package/dist/operator-ui/fix-prompts.js +0 -87
  390. package/dist/operator-ui/health.js +0 -52
  391. package/dist/operator-ui/member-page.js +0 -97
  392. package/dist/operator-ui/overview.js +0 -123
  393. package/dist/operator-ui/setup-commands.js +0 -51
  394. package/dist/operator-ui/view.d.ts +0 -401
  395. package/dist/operator-ui/view.js +0 -705
  396. package/dist/providers/_shared/request-token.js +0 -83
  397. package/dist/providers/_shared/rest/dispatch.js +0 -41
  398. package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
  399. package/dist/providers/_shared/rest/tools.d.ts +0 -141
  400. package/dist/providers/_shared/rest/tools.js +0 -569
  401. package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
  402. package/dist/providers/basecamp/index.d.ts +0 -45
  403. package/dist/providers/basecamp/index.js +0 -997
  404. package/dist/providers/basecamp/skill.generated.js +0 -9
  405. package/dist/providers/cloudflare/rest.d.ts +0 -37
  406. package/dist/providers/cloudflare/rest.js +0 -1097
  407. package/dist/providers/github/hosted.js +0 -218
  408. package/dist/providers/github/rest.js +0 -321
  409. package/dist/providers/infisical/index.d.ts +0 -7
  410. package/dist/providers/infisical/index.js +0 -713
  411. package/dist/providers/infisical/skill.generated.js +0 -8
  412. package/dist/providers/intercom/index.d.ts +0 -24
  413. package/dist/providers/intercom/index.js +0 -121
  414. package/dist/providers/intercom/skill.generated.js +0 -8
  415. package/dist/providers/linear/index.d.ts +0 -55
  416. package/dist/providers/linear/index.js +0 -537
  417. package/dist/providers/linear/skill.generated.js +0 -8
  418. package/dist/providers/mixpanel/index.d.ts +0 -59
  419. package/dist/providers/mixpanel/index.js +0 -472
  420. package/dist/providers/mixpanel/operations.js +0 -20
  421. package/dist/providers/mixpanel/rest.js +0 -603
  422. package/dist/providers/mixpanel/skill.generated.js +0 -10
  423. package/dist/providers/mixpanel/value-safety.js +0 -16
  424. package/dist/providers/notion/index.d.ts +0 -67
  425. package/dist/providers/notion/index.js +0 -368
  426. package/dist/providers/notion/openapi.generated.js +0 -75
  427. package/dist/providers/notion/rest.d.ts +0 -24
  428. package/dist/providers/notion/rest.js +0 -1226
  429. package/dist/providers/notion/skill.generated.js +0 -11
  430. package/dist/providers/notion/value-safety.js +0 -56
  431. package/dist/providers/revenuecat/index.d.ts +0 -51
  432. package/dist/providers/revenuecat/index.js +0 -697
  433. package/dist/providers/revenuecat/skill.generated.js +0 -11
  434. package/dist/providers/stripe/rest.d.ts +0 -14
  435. package/dist/providers/stripe/rest.js +0 -402
  436. package/dist/providers/vercel/rest.d.ts +0 -20
  437. package/dist/providers/vercel/rest.js +0 -1020
  438. package/dist/providers/wispr-flow/index.d.ts +0 -14
  439. package/dist/providers/wispr-flow/index.js +0 -117
  440. package/dist/providers/wispr-flow/skill.generated.js +0 -8
  441. /package/dist/providers/github/{key.js → lib/key.js} +0 -0
@@ -0,0 +1,506 @@
1
+ import { Effect, Option, Schema } from "effect";
2
+ import { api } from "../../connectors/api.js";
3
+ import { deriveContext } from "../../derived-context.js";
4
+ import { ConnectorCallError } from "../../errors.js";
5
+ import { Refusal } from "../../http/errors.js";
6
+ import { fieldsInput, fieldsProblem, selectFields } from "../../http/fields.js";
7
+ import { hatch } from "../../http/hatch.js";
8
+ import { githubPage } from "../../http/hatch-paging.js";
9
+ import { Integer, isMinChars } from "../../http/json-schema.js";
10
+ import { boundCursor, StandardPage } from "../../http/paging.js";
11
+ import { Dispatches } from "../../http/services.js";
12
+ import { read, write } from "../../http/tools.js";
13
+ import { Operation } from "../../runtime/call.js";
14
+ import { encodeForAgent, redactBeforeCut } from "../../sent-secrets.js";
15
+ import { tracksEveryRequest } from "../../provider-call.js";
16
+ import { checked } from "../_shared/rest/kit.js";
17
+ import { withholdEmbeddedUrlsIn } from "../_shared/rest/value-safety.js";
18
+ import { githubConnection, rejectedToken } from "./api.js";
19
+ import { contentsPath, githubIndex, GITHUB_HATCH } from "./lib/discovery.js";
20
+ import { READ_PERMISSIONS, RELEASE_PERMISSIONS, hatchRefusal } from "./lib/operations.js";
21
+ import { target, workflowPath, isPullHead } from "./lib/scope.js";
22
+ const text = (description) => Schema.String.check(isMinChars(1)).annotate({ description });
23
+ const row = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
24
+ const repoPath = (t) => `/repos/${encodeURIComponent(t.owner)}/${encodeURIComponent(t.repo)}`;
25
+ const ownerRepo = { owner: text("Configured repository owner."), repo: text("Repository name under that owner.") };
26
+ const optional = (fields) => Schema.Struct(Object.fromEntries(Object.entries(fields).map(([k, v]) => [k, Schema.optionalKey(v)])));
27
+ const ScopeRow = optional({
28
+ org: Schema.String,
29
+ repo: Schema.String,
30
+ owner: Schema.String,
31
+ id: Integer,
32
+ full_name: Schema.String,
33
+ access: Schema.Literals(["read", "read-write"]),
34
+ workflows: Schema.Literal("write"),
35
+ });
36
+ const SearchRow = optional({
37
+ scope: Schema.String,
38
+ repository: Schema.String,
39
+ item: Schema.Record(Schema.String, Schema.Unknown),
40
+ });
41
+ const common = {
42
+ limit: Schema.optionalKey(Integer.check(Schema.isBetween({ minimum: 1, maximum: 100 })).annotate({
43
+ description: "Maximum returned items, default 30; at most 100. Keep the same limit with cursor; omission uses 30.",
44
+ default: 30,
45
+ })),
46
+ cursor: Schema.optionalKey(text("Opaque page.nextCursor from this same search or scope listing.")),
47
+ fields: Schema.optionalKey(fieldsInput("Dot paths into each item; only narrows already-safe data.")),
48
+ };
49
+ const scopeNames = new Set(["org", "repo", "owner", "id", "full_name", "access", "workflows"]);
50
+ const searchNames = new Set(["scope", "repository", "item"]);
51
+ const contextOf = Effect.gen(function* () {
52
+ const op = yield* Effect.serviceOption(Operation);
53
+ if (Option.isNone(op) || !op.value)
54
+ return yield* Effect.die(new Error("Missing GitHub call context."));
55
+ return op.value;
56
+ });
57
+ function selectors(input, policy) {
58
+ if (!Array.isArray(input) || input.length === 0 || input.length > 100)
59
+ throw new ConnectorCallError("invalid_args", "Select between 1 and 100 configured scopes.");
60
+ return input.map((item) => {
61
+ const value = row(item);
62
+ if ((value.org === undefined) === (value.repo === undefined))
63
+ throw new ConnectorCallError("invalid_args", "Each selector names either org or repo.");
64
+ if (value.org !== undefined) {
65
+ const owner = policy.owner(value.org);
66
+ const grant = policy.scopes.find((scope) => scope.org === owner);
67
+ if (!grant)
68
+ throw new ConnectorCallError("invalid_args", "This org is not configured for org-wide access.");
69
+ return grant;
70
+ }
71
+ const parts = typeof value.repo === "string" ? value.repo.split("/") : [];
72
+ const t = target(parts[0], parts.length === 2 ? parts[1] : undefined);
73
+ const grant = policy.scope(t);
74
+ return { repo: `${t.owner}/${t.repo}`, access: grant.access };
75
+ });
76
+ }
77
+ function searchPartitions(scopes) {
78
+ const orgs = new Set(scopes.flatMap((scope) => (scope.org ? [scope.org] : [])));
79
+ const repos = new Map();
80
+ for (const scope of scopes) {
81
+ if (!scope.repo)
82
+ continue;
83
+ const [owner, repo] = scope.repo.split("/");
84
+ if (orgs.has(owner))
85
+ continue;
86
+ if (!repos.has(owner))
87
+ repos.set(owner, new Set());
88
+ repos.get(owner).add(repo);
89
+ }
90
+ const partitions = [...orgs].sort().map((owner) => ({ id: `org:${owner}`, owner, qualifier: `org:${owner}` }));
91
+ const result = [...partitions];
92
+ for (const [owner, names] of [...repos].sort(([a], [b]) => a.localeCompare(b))) {
93
+ const sorted = [...names].sort();
94
+ for (let offset = 0; offset < sorted.length; offset += 5) {
95
+ const chunk = sorted.slice(offset, offset + 5);
96
+ result.push({
97
+ id: chunk.map((repo) => `${owner}/${repo}`).join(","),
98
+ owner,
99
+ repos: chunk,
100
+ qualifier: chunk.map((repo) => `repo:${owner}/${repo}`).join(" "),
101
+ });
102
+ }
103
+ }
104
+ if (result.length > 20)
105
+ throw new ConnectorCallError("invalid_args", "Search fan-out exceeds 20 partitions. Select fewer scopes.");
106
+ return result;
107
+ }
108
+ function resultTarget(item, kind) {
109
+ if (kind === "repositories" || kind === "code") {
110
+ const name = kind === "code" ? row(item.repository).full_name : item.full_name;
111
+ const parts = typeof name === "string" ? name.split("/") : [];
112
+ return target(parts[0], parts.length === 2 ? parts[1] : undefined);
113
+ }
114
+ let url;
115
+ try {
116
+ url = new URL(item.repository_url);
117
+ }
118
+ catch {
119
+ throw new ConnectorCallError("connector_call_failed", "GitHub search omitted the repository binding.");
120
+ }
121
+ const match = /^\/repos\/([^/]+)\/([^/]+)$/.exec(url.pathname);
122
+ if (url.origin !== "https://api.github.com" || !match || url.search || url.hash)
123
+ throw new ConnectorCallError("connector_call_failed", "GitHub search returned an invalid repository binding.");
124
+ return target(match[1], match[2]);
125
+ }
126
+ function authCall(ctx, run) {
127
+ return Effect.tryPromise({
128
+ try: (interrupted) => run(deriveContext(ctx, { signal: ctx.signal ? AbortSignal.any([ctx.signal, interrupted]) : interrupted })),
129
+ catch: (error) => error,
130
+ }).pipe(Effect.catch((error) => error instanceof ConnectorCallError ? Effect.fail(new Refusal({ error })) : Effect.die(error)));
131
+ }
132
+ /** Redact before decoding/cutting; keep only bounded inline UTF-8 content and patches. */
133
+ function boundedData(data, ctx) {
134
+ const cutPaths = new Set();
135
+ const cut = (value, path) => {
136
+ let end = 32768;
137
+ if (value.length <= end)
138
+ return value;
139
+ if (/[\uD800-\uDBFF]/.test(value[end - 1]))
140
+ end--;
141
+ cutPaths.add(path);
142
+ return value.slice(0, end);
143
+ };
144
+ const visit = (value, path) => {
145
+ if (Array.isArray(value))
146
+ return value.map((v) => visit(v, `${path}[]`));
147
+ if (value === null || typeof value !== "object")
148
+ return value;
149
+ const source = value;
150
+ const out = Object.create(null);
151
+ for (const [key, entry] of Object.entries(source)) {
152
+ const at = `${path}.${key}`;
153
+ if (key === "patch" && typeof entry === "string")
154
+ out[key] = cut(withholdEmbeddedUrlsIn(redactBeforeCut(ctx, entry)), at);
155
+ else if (key === "content" && source.encoding === "base64" && typeof entry === "string") {
156
+ if (entry.includes("[redacted]")) {
157
+ out[key] = "[redacted]";
158
+ continue;
159
+ }
160
+ const normalized = entry.replace(/\s/g, "");
161
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(normalized))
162
+ throw new ConnectorCallError("connector_call_failed", "GitHub returned invalid base64 file content.");
163
+ let decoded;
164
+ try {
165
+ decoded = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(Uint8Array.from(atob(normalized), (c) => c.charCodeAt(0)));
166
+ }
167
+ catch {
168
+ throw new ConnectorCallError("invalid_args", "This GitHub connection returns inline UTF-8 file content only; binary and raw downloads are unavailable.");
169
+ }
170
+ const safe = withholdEmbeddedUrlsIn(redactBeforeCut(ctx, decoded));
171
+ out[key] = encodeForAgent(ctx, cut(safe, at));
172
+ }
173
+ else
174
+ out[key] = visit(entry, at);
175
+ }
176
+ return out;
177
+ };
178
+ const safe = visit(data, "data");
179
+ return { data: safe, ...(cutPaths.size ? { truncated: [...cutPaths] } : {}) };
180
+ }
181
+ export function restConnector(id, auth, policy) {
182
+ const connection = githubConnection(), adapter = githubPage("https://api.github.com");
183
+ const send = (request, bearer, options = {}) => Effect.gen(function* () {
184
+ const dispatches = yield* Dispatches, start = dispatches.length;
185
+ return yield* connection
186
+ .sendPath({
187
+ method: request.method,
188
+ path: request.path,
189
+ template: request.path,
190
+ ...(request.query
191
+ ? {
192
+ query: Object.entries(request.query).flatMap(([k, v]) => v === undefined ? [] : (Array.isArray(v) ? v : [v]).map((entry) => [k, String(entry)])),
193
+ }
194
+ : {}),
195
+ ...(request.body !== undefined ? { json: JSON.stringify(request.body) } : {}),
196
+ headers: { Authorization: `Bearer ${bearer}` },
197
+ }, options)
198
+ .pipe(Effect.ensuring(Effect.sync(() => {
199
+ if (dispatches
200
+ .slice(start)
201
+ .some((d) => d.status !== undefined && rejectedToken(d.status, d.headers ?? new Headers())))
202
+ void auth.rejectToken(bearer);
203
+ })));
204
+ });
205
+ const cursorArgs = (args) => {
206
+ const { fields: _fields, cursor: _cursor, ...rest } = args;
207
+ return { ...rest, grants: policy.scopes };
208
+ };
209
+ const decode = (name, args) => args.cursor === undefined
210
+ ? Effect.succeed(undefined)
211
+ : boundCursor.decode(name, cursorArgs(args), String(args.cursor));
212
+ const finish = (name, args, items, next, extra = {}) => Effect.gen(function* () {
213
+ const page = next === undefined
214
+ ? { hasMore: false }
215
+ : {
216
+ hasMore: true,
217
+ nextCursor: yield* boundCursor.encode(name, cursorArgs(args), JSON.stringify({ ...next, limit: args.limit })),
218
+ };
219
+ return {
220
+ items: selectFields(items, args.fields),
221
+ page,
222
+ ...extra,
223
+ };
224
+ });
225
+ const parse = (cursor, limit) => {
226
+ if (cursor === undefined)
227
+ return {};
228
+ const position = JSON.parse(cursor);
229
+ if (!Number.isSafeInteger(position.limit) || position.limit < 1 || position.limit > 100)
230
+ throw new ConnectorCallError("invalid_args", "This listing cursor is out of date; start a new listing.");
231
+ if (position.limit !== limit)
232
+ throw new ConnectorCallError("invalid_args", `This cursor was issued with limit ${position.limit}; repeat limit ${position.limit} or start a new listing.`);
233
+ return position;
234
+ };
235
+ const namesCheck = (fields, names) => checked(() => {
236
+ const bad = fieldsProblem(fields, names, "an item");
237
+ if (bad)
238
+ throw bad;
239
+ });
240
+ const scopesTool = read(connection, {
241
+ name: "list_scopes",
242
+ description: "List configured GitHub grants locally, or verified reachable repositories for one configured owner. Without owner this reads configuration without network access.",
243
+ input: Schema.Struct({ owner: Schema.optionalKey(text("Configured owner; omit for local grants.")), ...common }),
244
+ output: Schema.Struct({ items: Schema.Array(ScopeRow), page: StandardPage }),
245
+ run: (args) => Effect.gen(function* () {
246
+ yield* namesCheck(args.fields, scopeNames);
247
+ const limit = args.limit ?? 30, normalized = { ...args, limit };
248
+ const raw = yield* decode("list_scopes", normalized);
249
+ const position = yield* checked(() => parse(raw, limit));
250
+ const page = position.page ?? 1;
251
+ if (!Number.isSafeInteger(page) || page < 1 || page > 1000)
252
+ return yield* checked(() => {
253
+ throw new ConnectorCallError("invalid_args", "This repository cursor is outside its page bound.");
254
+ });
255
+ if (args.owner === undefined) {
256
+ const items = policy.scopes.slice((page - 1) * limit, page * limit);
257
+ return yield* finish("list_scopes", normalized, items, page * limit < policy.scopes.length ? { page: page + 1 } : undefined);
258
+ }
259
+ const owner = yield* checked(() => policy.owner(args.owner)), ctx = yield* contextOf;
260
+ const token = yield* authCall(ctx, (view) => auth.token(owner, undefined, { metadata: "read" }, view));
261
+ const answer = yield* send({ method: "GET", path: "/installation/repositories", query: { page, per_page: limit } }, token, { responseHeaders: ["link"] });
262
+ const body = row(answer.data);
263
+ if (!Array.isArray(body.repositories))
264
+ return yield* checked(() => {
265
+ throw new ConnectorCallError("connector_call_failed", "GitHub returned an invalid repository list.");
266
+ });
267
+ const items = [];
268
+ for (const entry of body.repositories) {
269
+ const repository = row(entry), t = yield* checked(() => resultTarget(repository, "repositories"));
270
+ if (t.owner !== owner)
271
+ return yield* checked(() => {
272
+ throw new ConnectorCallError("connector_call_failed", "GitHub returned a repository from another installation owner.");
273
+ });
274
+ yield* authCall(ctx, (view) => auth.checkResult(t, repository.id, token, view));
275
+ const grant = yield* checked(() => policy.scope(t));
276
+ items.push({
277
+ id: repository.id,
278
+ full_name: `${t.owner}/${t.repo}`,
279
+ owner: t.owner,
280
+ repo: t.repo,
281
+ access: grant.access,
282
+ ...(grant.workflows ? { workflows: grant.workflows } : {}),
283
+ });
284
+ }
285
+ const next = yield* checked(() => adapter.next(answer.data, {
286
+ path: "/installation/repositories",
287
+ query: { page, per_page: limit },
288
+ headers: answer.headers ?? {},
289
+ }));
290
+ if (next !== null && page === 1000)
291
+ return yield* checked(() => {
292
+ throw new ConnectorCallError("connector_call_failed", "The GitHub repository list exceeded its page bound; narrow the configured scope.");
293
+ });
294
+ return yield* finish("list_scopes", normalized, items, next === null ? undefined : { page: adapter.decode(next) });
295
+ }),
296
+ });
297
+ const searchTool = read(connection, {
298
+ name: "search_scoped",
299
+ description: "Search GitHub repositories, issues, pull requests or code within configured scopes. Results carry verified repository and partition bindings; continuation visits one bounded partition page at a time.",
300
+ input: Schema.Struct({
301
+ terms: text("Literal words only; qualifiers and Boolean operators are refused."),
302
+ kind: Schema.Literals(["repositories", "issues", "pull-requests", "code"]).annotate({
303
+ description: "Resource to search.",
304
+ }),
305
+ scopes: Schema.optionalKey(Schema.Array(Schema.Struct({
306
+ org: Schema.optionalKey(text("Configured organization grant.")),
307
+ repo: Schema.optionalKey(text("Exact configured owner/repository.")),
308
+ }))
309
+ .check(Schema.isMinLength(1), Schema.isMaxLength(100))
310
+ .annotate({ description: "Optional subset of the configured scopes." })),
311
+ ...common,
312
+ }),
313
+ output: Schema.Struct({ items: Schema.Array(SearchRow), page: StandardPage, incomplete_results: Schema.Boolean }),
314
+ run: (args) => Effect.gen(function* () {
315
+ yield* namesCheck(args.fields, searchNames);
316
+ const selected = yield* checked(() => {
317
+ if (!/^[a-z0-9_. -]{1,128}$/i.test(args.terms) || /\b(?:OR|NOT|AND)\b/i.test(args.terms))
318
+ throw new ConnectorCallError("invalid_args", "Search terms must be literal words, without qualifiers or Boolean operators.");
319
+ return args.scopes === undefined ? policy.scopes : selectors(args.scopes, policy);
320
+ });
321
+ const partitions = yield* checked(() => searchPartitions(selected)), limit = args.limit ?? 30, normalized = { ...args, limit };
322
+ const raw = yield* decode("search_scoped", normalized), at = yield* checked(() => parse(raw, limit));
323
+ const partitionIndex = at.partition ?? 0, page = at.page ?? 1;
324
+ if (!Number.isSafeInteger(partitionIndex) ||
325
+ partitionIndex < 0 ||
326
+ partitionIndex >= partitions.length ||
327
+ !Number.isSafeInteger(page) ||
328
+ page < 1 ||
329
+ page > 10)
330
+ return yield* checked(() => {
331
+ throw new ConnectorCallError("invalid_args", "This search cursor is outside the selected partitions.");
332
+ });
333
+ const partition = partitions[partitionIndex], kind = args.kind === "pull-requests" ? "issues" : args.kind;
334
+ const type = args.kind === "issues" ? " is:issue" : args.kind === "pull-requests" ? " is:pr" : "";
335
+ const query = `${partition.qualifier} ${args.terms.trim()}${type}`;
336
+ if (query.length > 256)
337
+ return yield* checked(() => {
338
+ throw new ConnectorCallError("invalid_args", "The scoped search query exceeds GitHub's 256-character bound. Select fewer repositories.");
339
+ });
340
+ const permissions = args.kind === "repositories"
341
+ ? { metadata: "read" }
342
+ : args.kind === "code"
343
+ ? { contents: "read" }
344
+ : args.kind === "issues"
345
+ ? { issues: "read" }
346
+ : { pull_requests: "read" };
347
+ const ctx = yield* contextOf, token = yield* authCall(ctx, (view) => auth.token(partition.owner, partition.repos, permissions, view));
348
+ const answer = yield* send({ method: "GET", path: `/search/${kind}`, query: { q: query, page, per_page: limit } }, token, { responseHeaders: ["link"] });
349
+ const body = row(answer.data);
350
+ if (!Array.isArray(body.items))
351
+ return yield* checked(() => {
352
+ throw new ConnectorCallError("connector_call_failed", "GitHub returned an invalid search result.");
353
+ });
354
+ const items = [], seen = new Set();
355
+ for (const value of body.items) {
356
+ const item = row(value), t = yield* checked(() => resultTarget(item, args.kind));
357
+ yield* checked(() => policy.owner(t.owner));
358
+ if (t.owner !== partition.owner)
359
+ return yield* checked(() => {
360
+ throw new ConnectorCallError("connector_call_failed", "GitHub search escaped its selected partition; results were withheld.");
361
+ });
362
+ yield* authCall(ctx, (view) => auth.checkResult(t, args.kind === "repositories" ? item.id : args.kind === "code" ? row(item.repository).id : undefined, token, view));
363
+ const key = `${args.kind}:${t.owner}/${t.repo}:${args.kind === "code" ? item.path : item.id}`;
364
+ if (!seen.has(key)) {
365
+ seen.add(key);
366
+ items.push({ scope: partition.id, repository: `${t.owner}/${t.repo}`, item: value });
367
+ }
368
+ }
369
+ const next = yield* checked(() => adapter.next(body, {
370
+ path: `/search/${kind}`,
371
+ query: { q: query, page, per_page: limit },
372
+ headers: answer.headers ?? {},
373
+ }));
374
+ const capped = next !== null && page === 10;
375
+ const position = next !== null && !capped
376
+ ? { partition: partitionIndex, page: adapter.decode(next) }
377
+ : partitionIndex + 1 < partitions.length
378
+ ? { partition: partitionIndex + 1, page: 1 }
379
+ : undefined;
380
+ const incomplete = at.incomplete === true || body.incomplete_results === true || capped;
381
+ return {
382
+ ...(yield* finish("search_scoped", normalized, items, position === undefined ? undefined : { ...position, incomplete })),
383
+ incomplete_results: incomplete,
384
+ };
385
+ }),
386
+ });
387
+ const mergeTool = write(connection, {
388
+ name: "merge_pull_request",
389
+ description: "Merge a same-repository GitHub pull request once, guarded by expected head SHA and explicit workflow-write scope. A failed preflight sends no merge.",
390
+ destructive: true,
391
+ writeSteps: { pull: "once", write: "once" },
392
+ input: Schema.Struct({
393
+ ...ownerRepo,
394
+ pull_number: Integer.check(Schema.isGreaterThanOrEqualTo(1)).annotate({ description: "Pull request number." }),
395
+ sha: text("Expected head commit SHA; reread after a conflict."),
396
+ merge_method: Schema.optionalKey(Schema.Literals(["merge", "squash", "rebase"]).annotate({ description: "Merge strategy, default merge." })),
397
+ }),
398
+ output: Schema.Record(Schema.String, Schema.Unknown),
399
+ run: (args) => Effect.gen(function* () {
400
+ const t = yield* checked(() => target(args.owner, args.repo));
401
+ yield* checked(() => policy.scope(t, true, true));
402
+ const ctx = yield* contextOf, reader = yield* authCall(ctx, (view) => auth.repo(t, { pull_requests: "read" }, view));
403
+ const pr = row((yield* send({ method: "GET", path: `${repoPath(t)}/pulls/${args.pull_number}` }, reader, {
404
+ step: "pull",
405
+ effect: "read",
406
+ })).data);
407
+ const head = row(pr.head), base = row(pr.base);
408
+ if (head.sha !== args.sha ||
409
+ row(head.repo).full_name?.toLowerCase() !== `${t.owner}/${t.repo}` ||
410
+ row(base.repo).full_name?.toLowerCase() !== `${t.owner}/${t.repo}`)
411
+ return yield* checked(() => {
412
+ throw new ConnectorCallError("conflict", "Merge requires the expected head SHA and a same-repository head and base. Reread the pull request.");
413
+ });
414
+ const token = yield* authCall(ctx, (view) => auth.repo(t, { contents: "write", pull_requests: "write", workflows: "write" }, view));
415
+ return row((yield* send({
416
+ method: "PUT",
417
+ path: `${repoPath(t)}/pulls/${args.pull_number}/merge`,
418
+ body: { sha: args.sha, merge_method: args.merge_method ?? "merge" },
419
+ }, token)).data);
420
+ }),
421
+ });
422
+ const binding = {
423
+ review: "Repository-ID-bound App tokens and per-operation documented permissions; all unreviewed operations, raw search and merge refuse before authentication.",
424
+ truncated: true,
425
+ path: contentsPath,
426
+ refusal: (op) => hatchRefusal(`${op.method} ${op.path}`),
427
+ refuse: (call) => {
428
+ const key = `${call.method} ${call.op.path}`, refusal = hatchRefusal(key);
429
+ if (refusal)
430
+ return refusal;
431
+ const { params } = githubIndex.resolve(call.method, call.path), t = target(params.owner, params.repo);
432
+ policy.scope(t, Boolean(RELEASE_PERMISSIONS[key]));
433
+ if (params.path !== undefined && params.path !== "")
434
+ workflowPath(decodeURIComponent(params.path));
435
+ const pullList = key === "GET /repos/{owner}/{repo}/pulls";
436
+ if (pullList && call.query.head !== undefined && !isPullHead(call.query.head))
437
+ return "GitHub needs head in owner:branch form; a bare branch is silently ignored. Pass a valid login and branch.";
438
+ // A PR head label only filters this repository's PRs. Owner-prefixed
439
+ // object refs can select fork-network objects and remain unavailable.
440
+ const refs = [
441
+ params.ref,
442
+ params.basehead,
443
+ params.tag,
444
+ ...["ref", "sha", "base", "branch", "head_sha"].map((name) => call.query[name]),
445
+ ...(pullList ? [] : [call.query.head]),
446
+ ...["target_commitish", "tag_name"].map((name) => row(call.body)[name]),
447
+ ];
448
+ for (const ref of refs) {
449
+ if (typeof ref !== "string")
450
+ continue;
451
+ let decoded;
452
+ try {
453
+ decoded = decodeURIComponent(ref);
454
+ }
455
+ catch {
456
+ return "Use a literal repository-local ref, without malformed percent-encoding.";
457
+ }
458
+ if (/[:%]/.test(decoded))
459
+ return params.basehead !== undefined
460
+ ? "Compare refs within this repository; cross-repository compare is unavailable."
461
+ : "Use refs within this repository; owner-prefixed and encoded repository aliases are unavailable.";
462
+ }
463
+ const perPage = call.query.per_page;
464
+ if (perPage !== undefined &&
465
+ (!/^\d+$/.test(String(perPage)) ||
466
+ !Number.isSafeInteger(Number(perPage)) ||
467
+ Number(perPage) < 1 ||
468
+ Number(perPage) > 100))
469
+ return "GitHub per_page must be a whole number from 1 to 100.";
470
+ return undefined;
471
+ },
472
+ send: (call, options) => Effect.gen(function* () {
473
+ const key = `${call.method} ${call.op.path}`, { params } = githubIndex.resolve(call.method, call.path), t = target(params.owner, params.repo), ctx = yield* contextOf;
474
+ const permissions = READ_PERMISSIONS[key] ?? RELEASE_PERMISSIONS[key];
475
+ if (!permissions)
476
+ return yield* checked(() => {
477
+ throw new ConnectorCallError("invalid_args", "This GitHub operation has no reviewed permission declaration.");
478
+ });
479
+ const token = yield* authCall(ctx, (view) => auth.repo(t, permissions, view));
480
+ const answer = yield* send({
481
+ method: call.method,
482
+ path: call.path,
483
+ query: call.query,
484
+ ...(call.body === undefined ? {} : { body: call.body }),
485
+ }, token, options);
486
+ const bounded = yield* checked(() => boundedData(answer.data, ctx));
487
+ return { ...answer, ...bounded };
488
+ }),
489
+ };
490
+ return tracksEveryRequest(api(id, {
491
+ tools: [
492
+ ...hatch({
493
+ ...GITHUB_HATCH,
494
+ connection,
495
+ binding,
496
+ notes: {
497
+ read: "Only the reviewed repository reads are available. Nested contents paths such as /repos/alice/widget/contents/src/index.ts and the root /repos/alice/widget/contents are accepted. In commits/{ref} and compare/{basehead}, encode branch-name slashes as %2F. Owner-prefixed object refs are refused; PR-list head filters require login:branch. Pass page.nextCursor as cursor; raw page numbers, diffs and log downloads are refused.",
498
+ write: "Only release create/update/delete are available here. Use merge_pull_request for merges.",
499
+ },
500
+ }),
501
+ scopesTool,
502
+ searchTool,
503
+ mergeTool,
504
+ ],
505
+ }));
506
+ }
@@ -0,0 +1,114 @@
1
+ // Gmail's declared operations are confined to the delegated user's own mailbox.
2
+ import { Effect, Schema } from "effect";
3
+ import { encodedFieldProvenance, get, post, put, vendor } from "../../http/vendor.js";
4
+ import { googleErrors } from "../_shared/google/kit.js";
5
+ import { gmailSafety } from "./safety.js";
6
+ import { ConnectorCallError } from "../../errors.js";
7
+ import { GMAIL_SCOPES } from "./lib/constants.js";
8
+ export const GMAIL = {
9
+ provider: "Gmail",
10
+ api: "Gmail API",
11
+ scopes: GMAIL_SCOPES,
12
+ // users/me confines every request to the token's own mailbox.
13
+ notFound: "absent",
14
+ };
15
+ const Id = { id: Schema.String };
16
+ const Format = {
17
+ format: Schema.optionalKey(Schema.String),
18
+ metadataHeaders: Schema.optionalKey(Schema.Array(Schema.String)),
19
+ };
20
+ const Page = {
21
+ q: Schema.optionalKey(Schema.String),
22
+ maxResults: Schema.optionalKey(Schema.String),
23
+ pageToken: Schema.optionalKey(Schema.String),
24
+ };
25
+ export const listLabels = get("listLabels", "/labels", { success: Schema.Unknown });
26
+ const OPERATIONS = [
27
+ get("listThreads", "/threads", {
28
+ query: { ...Page, includeSpamTrash: Schema.optionalKey(Schema.String) },
29
+ success: Schema.Unknown,
30
+ }),
31
+ get("getThread", "/threads/:id", { params: Id, query: Format, success: Schema.Unknown }),
32
+ get("getMessage", "/messages/:id", { params: Id, query: Format, success: Schema.Unknown }),
33
+ get("getAttachment", "/messages/:messageId/attachments/:id", {
34
+ params: { messageId: Schema.String, ...Id },
35
+ success: Schema.Unknown,
36
+ }),
37
+ listLabels,
38
+ get("listMessages", "/messages", { query: Page, success: Schema.Unknown }),
39
+ get("getLabel", "/labels/:id", { params: Id, success: Schema.Unknown }),
40
+ get("getProfile", "/profile", { success: Schema.Unknown }),
41
+ get("listHistory", "/history", { query: Page, success: Schema.Unknown }),
42
+ get("listDrafts", "/drafts", { query: Page, success: Schema.Unknown }),
43
+ get("getDraft", "/drafts/:id", { params: Id, query: Format, success: Schema.Unknown }),
44
+ post("createDraft", "/drafts", { payload: Schema.Unknown, success: Schema.Unknown }),
45
+ put("updateDraft", "/drafts/:id", { params: Id, payload: Schema.Unknown, success: Schema.Unknown }),
46
+ ];
47
+ // The manifest uses Discovery's path; transport stays fixed beneath users/me.
48
+ const bodyPaths = [
49
+ "payload",
50
+ "message.payload",
51
+ "messages[*].payload",
52
+ "drafts[*].message.payload",
53
+ "history[*].messages[*].payload",
54
+ "history[*].messagesAdded[*].message.payload",
55
+ "history[*].messagesDeleted[*].message.payload",
56
+ "history[*].labelsAdded[*].message.payload",
57
+ "history[*].labelsRemoved[*].message.payload",
58
+ ].flatMap((prefix) =>
59
+ // The normal MIME walk and a parsed forwarded-message walk each admit 20 levels.
60
+ Array.from({ length: 41 }, (_, depth) => `${prefix}${".parts[*]".repeat(depth)}.body.data`));
61
+ export const GmailApi = {
62
+ ...vendor({ name: "Gmail", manifestBase: "/gmail/v1/users/{userId}" }, ...OPERATIONS),
63
+ // Legacy body reads accept optional sizes. Attachment projection instead checks
64
+ // originalLength against the fresh part's required size and the download's optional size.
65
+ encodedFields: [
66
+ ...bodyPaths,
67
+ "data",
68
+ "raw",
69
+ "message.raw",
70
+ "messages[*].raw",
71
+ "drafts[*].message.raw",
72
+ "history[*].messages[*].raw",
73
+ "history[*].messagesAdded[*].message.raw",
74
+ "history[*].messagesDeleted[*].message.raw",
75
+ "history[*].labelsAdded[*].message.raw",
76
+ "history[*].labelsRemoved[*].message.raw",
77
+ ].map((path) => ({
78
+ path,
79
+ encoding: "base64url",
80
+ review: "Gmail's bounded MIME projection decodes this body or attachment data before rendering or byte encoding.",
81
+ })),
82
+ };
83
+ const bodyProvenance = new WeakMap();
84
+ /** Product-private metadata copied only by the result hook, before the projection validates attachment sizes. */
85
+ export const gmailBodyProvenance = (body) => bodyProvenance.get(body);
86
+ export const result = (body) => Effect.sync(() => {
87
+ const visit = (node, path) => {
88
+ if (node === null || typeof node !== "object")
89
+ return;
90
+ const provenance = encodedFieldProvenance(body, [...path, "data"]);
91
+ if (provenance)
92
+ bodyProvenance.set(node, provenance);
93
+ // A generic MIME read cannot expose an encoded lane beyond the reviewed
94
+ // decoder paths. Named walks are bounded too, but hatch JSON keeps the whole graph.
95
+ if (!provenance && typeof node["data"] === "string" && path.at(-1) === "body")
96
+ throw new ConnectorCallError("connector_call_failed", "Gmail returned MIME data beyond the reviewed nesting boundary.", { retryable: false });
97
+ for (const [key, child] of Object.entries(node))
98
+ if (child !== null && typeof child === "object")
99
+ visit(child, [...path, Array.isArray(node) ? Number(key) : key]);
100
+ };
101
+ visit(body, []);
102
+ return body;
103
+ });
104
+ const standardErrors = googleErrors(GMAIL, { standard: true });
105
+ export const errors = {
106
+ ...standardErrors,
107
+ meanings: { ...standardErrors.meanings, conflicts: ["A thread changed before its content cursor"] },
108
+ };
109
+ export const safety = gmailSafety(OPERATIONS);
110
+ export const probe = {
111
+ operation: listLabels,
112
+ request: {},
113
+ describe: () => "Gmail delegation has no credential to test.",
114
+ };
@@ -1,17 +1,6 @@
1
1
  import { type GoogleWorkspaceOptions } from "../_shared/google/workspace.js";
2
+ export { GMAIL_API_BASE_URL, GMAIL_SCOPES } from "./lib/constants.js";
2
3
  export type { GoogleServiceAccount, GoogleSubjectContext, GoogleWorkspaceOptions, GoogleWorkspaceSubject, } from "../_shared/google/workspace.js";
3
- /**
4
- * Gmail's API root, confined to the delegated user's own mailbox: `me` is the
5
- * token's subject, and no path can climb out of it.
6
- */
7
- export declare const GMAIL_API_BASE_URL = "https://gmail.googleapis.com/gmail/v1/users/me";
8
- /**
9
- * Exactly the scopes this connection requests, and exactly what the Admin
10
- * console's domain-wide delegation entry must list (comma-separated there).
11
- * `gmail.compose` technically permits sending; the tool surface does not.
12
- */
13
- export declare const GMAIL_SCOPES: readonly ["https://www.googleapis.com/auth/gmail.readonly", "https://www.googleapis.com/auth/gmail.compose"];
14
- /** Options for {@link gmail}: the shared Workspace delegation options. */
15
4
  export type GmailOptions = GoogleWorkspaceOptions;
16
5
  /**
17
6
  * A maintained, draft-only Gmail connection acting as each signed-in