@zackbart/connecta 0.34.0 → 0.35.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 (428) hide show
  1. package/CHANGELOG.md +393 -0
  2. package/README.md +62 -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 +2051 -1160
  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 +663 -271
  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 +15 -7
  53. package/dist/connectors/api-connector.js +147 -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/option-shapes.js +1 -1
  58. package/dist/connectors/remote-mcp.d.ts +12 -30
  59. package/dist/connectors/remote-mcp.js +960 -564
  60. package/dist/d1.js +10 -243
  61. package/dist/derived-context.js +5 -0
  62. package/dist/describe-config.d.ts +5 -1
  63. package/dist/describe-config.js +3 -0
  64. package/dist/downstream-input.js +347 -260
  65. package/dist/errors.d.ts +87 -6
  66. package/dist/errors.js +114 -4
  67. package/dist/execute.js +87 -50
  68. package/dist/executor-admission.js +3 -0
  69. package/dist/executors/quickjs.js +163 -112
  70. package/dist/guarded-transport.js +50 -0
  71. package/dist/guest-runtime.js +1 -1
  72. package/dist/http/client.js +439 -0
  73. package/dist/http/errors.js +558 -0
  74. package/dist/http/fields.js +49 -0
  75. package/dist/http/hatch-paging.js +318 -0
  76. package/dist/http/hatch.js +1279 -0
  77. package/dist/http/json-schema.js +267 -0
  78. package/dist/http/node-transport.js +241 -0
  79. package/dist/http/oauth.js +162 -0
  80. package/dist/http/paging.js +228 -0
  81. package/dist/http/provider.js +221 -0
  82. package/dist/http/safety.js +82 -0
  83. package/dist/http/services.js +79 -0
  84. package/dist/http/tools.js +383 -0
  85. package/dist/http/vendor-index.js +138 -0
  86. package/dist/http/vendor.js +448 -0
  87. package/dist/http/write-request.js +66 -0
  88. package/dist/http-policy.d.ts +11 -0
  89. package/dist/http-policy.js +45 -0
  90. package/dist/inbound-credential.js +1 -1
  91. package/dist/index.d.ts +2 -2
  92. package/dist/index.js +4 -0
  93. package/dist/invocation-auth.js +30 -19
  94. package/dist/invocation.js +221 -87
  95. package/dist/mcp-result.js +0 -4
  96. package/dist/membership.d.ts +4 -0
  97. package/dist/meta-output.js +1 -1
  98. package/dist/meta-tools.js +534 -408
  99. package/dist/native-recovery.js +52 -0
  100. package/dist/native-tools.js +382 -0
  101. package/dist/node.d.ts +4 -3
  102. package/dist/node.js +45 -8
  103. package/dist/oauth-handoff.js +177 -104
  104. package/dist/oauth-partition.js +6 -20
  105. package/dist/oauth-sealing.js +6 -7
  106. package/dist/operation-vocabulary.js +101 -0
  107. package/dist/operator-record.d.ts +2 -2
  108. package/dist/operator-record.js +17 -4
  109. package/dist/operator-ui/agents.d.ts +5 -4
  110. package/dist/operator-ui/agents.js +17 -7
  111. package/dist/operator-ui/config-summary.js +8 -4
  112. package/dist/operator-ui/connection-map.js +9 -6
  113. package/dist/operator-ui/contract.d.ts +7 -3
  114. package/dist/operator-ui/endpoints.js +5 -14
  115. package/dist/operator-ui/fix-prompts.d.ts +1 -1
  116. package/dist/operator-ui/fix-prompts.js +0 -8
  117. package/dist/operator-ui/generated.js +3 -3
  118. package/dist/operator-ui/health.js +52 -0
  119. package/dist/operator-ui/model.d.ts +2 -3
  120. package/dist/operator-ui/overview.js +11 -19
  121. package/dist/operator-ui/setup-commands.js +0 -4
  122. package/dist/operator-ui/view.d.ts +5 -29
  123. package/dist/operator-ui/view.js +3 -59
  124. package/dist/program-writes.js +47 -26
  125. package/dist/provider-call-facts.d.ts +115 -0
  126. package/dist/provider-call-facts.js +182 -0
  127. package/dist/provider-call.d.ts +112 -0
  128. package/dist/provider-call.js +271 -0
  129. package/dist/provider.d.ts +71 -32
  130. package/dist/provider.js +134 -40
  131. package/dist/providers/_shared/google/hatch.js +91 -0
  132. package/dist/providers/_shared/google/kit.js +537 -0
  133. package/dist/providers/_shared/google/result-size.js +15 -2
  134. package/dist/providers/_shared/google/service-account.d.ts +2 -1
  135. package/dist/providers/_shared/google/service-account.js +8 -4
  136. package/dist/providers/_shared/google/workspace.d.ts +8 -209
  137. package/dist/providers/_shared/google/workspace.js +7 -426
  138. package/dist/providers/_shared/rest/compact-index.js +86 -0
  139. package/dist/providers/_shared/rest/dispatch.js +3 -23
  140. package/dist/providers/_shared/rest/kit.js +546 -0
  141. package/dist/providers/_shared/rest/operation-index.js +230 -14
  142. package/dist/providers/_shared/rest/response-media.js +11 -0
  143. package/dist/providers/_shared/rest/tools.js +101 -78
  144. package/dist/providers/_shared/rest/value-safety.js +353 -195
  145. package/dist/providers/_shared/sensitive-fields.js +27 -0
  146. package/dist/providers/breeze/api.js +584 -0
  147. package/dist/providers/breeze/index.d.ts +3 -3
  148. package/dist/providers/breeze/index.js +67 -1420
  149. package/dist/providers/breeze/openapi.generated.js +60 -0
  150. package/dist/providers/breeze/skill.generated.js +2 -6
  151. package/dist/providers/breeze/tools.js +696 -0
  152. package/dist/providers/ccb/api.js +496 -0
  153. package/dist/providers/ccb/index.d.ts +17 -37
  154. package/dist/providers/ccb/index.js +77 -1702
  155. package/dist/providers/ccb/lib/constants.d.ts +34 -0
  156. package/dist/providers/ccb/lib/constants.js +74 -0
  157. package/dist/providers/ccb/lib/guide.js +9 -0
  158. package/dist/providers/ccb/lib/verdicts.js +387 -0
  159. package/dist/providers/ccb/openapi.generated.js +397 -0
  160. package/dist/providers/ccb/safety.js +295 -0
  161. package/dist/providers/ccb/skill.generated.js +2 -6
  162. package/dist/providers/ccb/tools.js +853 -0
  163. package/dist/providers/cloudflare/api.js +1066 -0
  164. package/dist/providers/cloudflare/index.d.ts +7 -22
  165. package/dist/providers/cloudflare/index.js +80 -194
  166. package/dist/providers/cloudflare/lib/config.d.ts +16 -0
  167. package/dist/providers/cloudflare/lib/config.js +2 -0
  168. package/dist/providers/cloudflare/lib/discovery.js +29 -0
  169. package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
  170. package/dist/providers/cloudflare/lib/hatch.js +103 -0
  171. package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
  172. package/dist/providers/cloudflare/lib/response-media.js +75 -0
  173. package/dist/providers/cloudflare/openapi.generated.js +3299 -3208
  174. package/dist/providers/cloudflare/{value-safety.js → safety.js} +538 -18
  175. package/dist/providers/cloudflare/skill.generated.js +2 -10
  176. package/dist/providers/cloudflare/tools.js +360 -0
  177. package/dist/providers/docs/api.js +51 -0
  178. package/dist/providers/docs/index.d.ts +1 -12
  179. package/dist/providers/docs/index.js +53 -1165
  180. package/dist/providers/docs/lib/constants.d.ts +18 -0
  181. package/dist/providers/docs/lib/constants.js +18 -0
  182. package/dist/providers/docs/lib/discovery.js +26 -0
  183. package/dist/providers/docs/lib/requests.js +48 -0
  184. package/dist/providers/docs/lib/schemas.js +168 -0
  185. package/dist/providers/docs/openapi.generated.js +16 -0
  186. package/dist/providers/docs/safety.js +54 -0
  187. package/dist/providers/docs/skill.generated.js +2 -6
  188. package/dist/providers/docs/tools.js +802 -0
  189. package/dist/providers/drive/api.js +177 -0
  190. package/dist/providers/drive/index.d.ts +1 -12
  191. package/dist/providers/drive/index.js +38 -1448
  192. package/dist/providers/drive/lib/constants.d.ts +6 -0
  193. package/dist/providers/drive/lib/constants.js +6 -0
  194. package/dist/providers/drive/lib/contracts.js +532 -0
  195. package/dist/providers/drive/lib/hatch.js +197 -0
  196. package/dist/providers/drive/openapi.generated.js +77 -0
  197. package/dist/providers/drive/safety.js +90 -0
  198. package/dist/providers/drive/skill.generated.js +2 -6
  199. package/dist/providers/drive/tools.js +1290 -0
  200. package/dist/providers/forms/api.js +71 -0
  201. package/dist/providers/forms/index.d.ts +1 -12
  202. package/dist/providers/forms/index.js +49 -1145
  203. package/dist/providers/forms/lib/constants.d.ts +14 -0
  204. package/dist/providers/forms/lib/constants.js +19 -0
  205. package/dist/providers/forms/lib/hatch.js +72 -0
  206. package/dist/providers/forms/openapi.generated.js +23 -0
  207. package/dist/providers/forms/safety.js +34 -0
  208. package/dist/providers/forms/skill.generated.js +2 -8
  209. package/dist/providers/forms/tools.js +1024 -0
  210. package/dist/providers/github/api.js +150 -0
  211. package/dist/providers/github/index.d.ts +4 -5
  212. package/dist/providers/github/index.js +30 -87
  213. package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
  214. package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
  215. package/dist/providers/github/lib/discovery.js +132 -0
  216. package/dist/providers/github/lib/operations.js +58 -0
  217. package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
  218. package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
  219. package/dist/providers/github/openapi.generated.js +1216 -0
  220. package/dist/providers/github/safety.js +216 -0
  221. package/dist/providers/github/skill.generated.js +2 -5
  222. package/dist/providers/github/tools.js +506 -0
  223. package/dist/providers/gmail/api.js +114 -0
  224. package/dist/providers/gmail/index.d.ts +1 -12
  225. package/dist/providers/gmail/index.js +73 -1621
  226. package/dist/providers/gmail/lib/constants.d.ts +13 -0
  227. package/dist/providers/gmail/lib/constants.js +16 -0
  228. package/dist/providers/gmail/lib/index.js +173 -0
  229. package/dist/providers/gmail/lib/schemas.js +176 -0
  230. package/dist/providers/gmail/lib/threading.js +120 -0
  231. package/dist/providers/gmail/openapi.generated.js +92 -0
  232. package/dist/providers/gmail/safety.js +21 -0
  233. package/dist/providers/gmail/skill.generated.js +2 -6
  234. package/dist/providers/gmail/tools.js +1435 -0
  235. package/dist/providers/overflow/api.js +1008 -0
  236. package/dist/providers/overflow/index.d.ts +3 -17
  237. package/dist/providers/overflow/index.js +60 -1488
  238. package/dist/providers/overflow/skill.generated.js +2 -5
  239. package/dist/providers/overflow/tools.js +873 -0
  240. package/dist/providers/planning-center/api.js +467 -0
  241. package/dist/providers/planning-center/index.d.ts +6 -35
  242. package/dist/providers/planning-center/index.js +69 -2445
  243. package/dist/providers/planning-center/lib/constants.d.ts +58 -0
  244. package/dist/providers/planning-center/lib/constants.js +83 -0
  245. package/dist/providers/planning-center/lib/discovery.js +40 -0
  246. package/dist/providers/planning-center/lib/guide.js +39 -0
  247. package/dist/providers/planning-center/lib/json-api.js +83 -0
  248. package/dist/providers/planning-center/lib/verdicts.js +1758 -0
  249. package/dist/providers/planning-center/openapi.generated.js +1764 -0
  250. package/dist/providers/planning-center/safety.js +188 -0
  251. package/dist/providers/planning-center/skill.generated.js +2 -27
  252. package/dist/providers/planning-center/tools.js +1010 -0
  253. package/dist/providers/sheets/api.js +133 -0
  254. package/dist/providers/sheets/index.d.ts +4 -16
  255. package/dist/providers/sheets/index.js +61 -1172
  256. package/dist/providers/sheets/lib/constants.d.ts +14 -0
  257. package/dist/providers/sheets/lib/constants.js +14 -0
  258. package/dist/providers/sheets/lib/hatch.js +163 -0
  259. package/dist/providers/sheets/lib/requests.js +105 -0
  260. package/dist/providers/sheets/lib/schemas.js +138 -0
  261. package/dist/providers/sheets/openapi.generated.js +30 -0
  262. package/dist/providers/sheets/safety.js +27 -0
  263. package/dist/providers/sheets/skill.generated.js +2 -7
  264. package/dist/providers/sheets/tools.js +734 -0
  265. package/dist/providers/slides/api.js +147 -0
  266. package/dist/providers/slides/index.d.ts +2 -10
  267. package/dist/providers/slides/index.js +48 -2555
  268. package/dist/providers/slides/lib/constants.d.ts +11 -0
  269. package/dist/providers/slides/lib/constants.js +11 -0
  270. package/dist/providers/slides/lib/requests.js +60 -0
  271. package/dist/providers/slides/lib/schemas.js +565 -0
  272. package/dist/providers/slides/openapi.generated.js +18 -0
  273. package/dist/providers/slides/skill.generated.js +2 -6
  274. package/dist/providers/slides/tools.js +1968 -0
  275. package/dist/providers/stripe/api.js +401 -0
  276. package/dist/providers/stripe/index.d.ts +7 -27
  277. package/dist/providers/stripe/index.js +75 -191
  278. package/dist/providers/stripe/lib/config.d.ts +4 -0
  279. package/dist/providers/stripe/lib/config.js +2 -0
  280. package/dist/providers/stripe/openapi.generated.js +4 -3
  281. package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
  282. package/dist/providers/stripe/skill.generated.js +2 -10
  283. package/dist/providers/stripe/tools.js +77 -0
  284. package/dist/providers/tithely/api.js +531 -0
  285. package/dist/providers/tithely/index.d.ts +19 -0
  286. package/dist/providers/tithely/index.js +86 -965
  287. package/dist/providers/tithely/skill.generated.js +2 -5
  288. package/dist/providers/tithely/tools.js +426 -0
  289. package/dist/providers/vercel/api.js +551 -0
  290. package/dist/providers/vercel/index.d.ts +11 -25
  291. package/dist/providers/vercel/index.js +67 -261
  292. package/dist/providers/vercel/lib/config.d.ts +2 -0
  293. package/dist/providers/vercel/lib/config.js +2 -0
  294. package/dist/providers/vercel/lib/index.js +117 -0
  295. package/dist/providers/vercel/lib/logs.js +100 -0
  296. package/dist/providers/vercel/lib/pin.js +270 -0
  297. package/dist/providers/vercel/openapi.generated.js +398 -376
  298. package/dist/providers/vercel/{value-safety.js → safety.js} +261 -42
  299. package/dist/providers/vercel/skill.generated.js +2 -7
  300. package/dist/providers/vercel/tools.js +642 -0
  301. package/dist/redaction-identity.js +47 -0
  302. package/dist/redaction-marker.js +125 -0
  303. package/dist/registry.d.ts +30 -28
  304. package/dist/registry.js +446 -413
  305. package/dist/routes/access-tokens.js +1 -1
  306. package/dist/routes/actor-labels.js +1 -1
  307. package/dist/routes/connect.js +1 -1
  308. package/dist/routes/dispatch.js +103 -0
  309. package/dist/routes/mcp.js +170 -79
  310. package/dist/routes/oauth.js +3 -1
  311. package/dist/routes/operator.js +2 -2
  312. package/dist/routes/shared.d.ts +5 -4
  313. package/dist/routes/shared.js +244 -177
  314. package/dist/routes/skills.js +10 -8
  315. package/dist/routes/ui-config.js +27 -3
  316. package/dist/routes/ui-data.js +29 -22
  317. package/dist/routes/ui.js +1 -1
  318. package/dist/runtime/call-admission.js +0 -26
  319. package/dist/runtime/call-entry.js +30 -0
  320. package/dist/runtime/call.js +556 -0
  321. package/dist/runtime/connector-scope.js +7 -0
  322. package/dist/runtime/credential-probe.js +34 -0
  323. package/dist/runtime/executor.js +40 -0
  324. package/dist/runtime/native-storage.js +63 -0
  325. package/dist/runtime/registry.js +27 -0
  326. package/dist/runtime/request-scope.js +72 -0
  327. package/dist/runtime/run.js +15 -7
  328. package/dist/runtime/shared-read.js +34 -5
  329. package/dist/runtime/storage.js +34 -21
  330. package/dist/sent-secrets.d.ts +194 -37
  331. package/dist/sent-secrets.js +1743 -340
  332. package/dist/server.js +5 -56
  333. package/dist/skills.js +330 -224
  334. package/dist/sqlite.js +15 -14
  335. package/dist/storage/d1-copy.js +255 -0
  336. package/dist/storage/d1-driver.js +15 -0
  337. package/dist/storage/keys.js +28 -0
  338. package/dist/storage/sql-driver.js +88 -0
  339. package/dist/storage/sql-native.js +544 -0
  340. package/dist/storage/sql-schema.d.ts +6 -0
  341. package/dist/storage/sql-schema.js +15 -0
  342. package/dist/storage/sql.d.ts +14 -20
  343. package/dist/storage/sql.js +29 -535
  344. package/dist/timeout.js +0 -26
  345. package/dist/tool-safety.js +20 -10
  346. package/dist/types.d.ts +11 -99
  347. package/dist/upward.js +3 -0
  348. package/dist/usage-guide.js +3 -1
  349. package/dist/validate.d.ts +4 -2
  350. package/dist/validate.js +86 -9
  351. package/dist/version.d.ts +1 -1
  352. package/dist/version.js +1 -1
  353. package/dist/worker.js +312 -292
  354. package/dist/workspace-subject.js +5 -1
  355. package/dist/write-outcome.js +454 -34
  356. package/documentation/README.md +27 -7
  357. package/documentation/architecture.md +2550 -186
  358. package/documentation/auth.md +821 -98
  359. package/documentation/code-mode.md +56 -21
  360. package/documentation/deploying.md +164 -20
  361. package/documentation/hosted-mcp-migration.md +460 -0
  362. package/documentation/integrating.md +144 -105
  363. package/documentation/meta-tools.md +411 -34
  364. package/documentation/operating.md +28 -4
  365. package/documentation/operator-tests.md +11 -2
  366. package/documentation/operator-ui.md +103 -33
  367. package/documentation/provider-auth-migration.md +38 -63
  368. package/documentation/provider-migration-0.29.md +10 -5
  369. package/documentation/provider-migration-0.35.md +975 -0
  370. package/documentation/providers.md +965 -0
  371. package/examples/worker/AGENTS.md +23 -9
  372. package/examples/worker/README.md +254 -135
  373. package/examples/worker/alchemy.run.ts +116 -0
  374. package/examples/worker/gitignore +10 -0
  375. package/examples/worker/src/connecta.config.ts +23 -21
  376. package/examples/worker/wrangler.jsonc +14 -5
  377. package/guides/connecting-agents.md +157 -0
  378. package/guides/how-it-works.md +156 -0
  379. package/guides/safety-and-trust.md +153 -0
  380. package/package.json +27 -42
  381. package/templates/node/.env.example +3 -0
  382. package/templates/node/README.md +22 -2
  383. package/templates/node/docker-compose.yml +1 -0
  384. package/templates/node/package.json +3 -2
  385. package/templates/node/src/connecta.config.ts +8 -0
  386. package/dist/catalog-drift.d.ts +0 -134
  387. package/dist/catalog-drift.js +0 -493
  388. package/dist/providers/_shared/request-token.js +0 -83
  389. package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
  390. package/dist/providers/_shared/rest/tools.d.ts +0 -141
  391. package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
  392. package/dist/providers/basecamp/index.d.ts +0 -43
  393. package/dist/providers/basecamp/index.js +0 -997
  394. package/dist/providers/basecamp/skill.generated.js +0 -9
  395. package/dist/providers/cloudflare/rest.d.ts +0 -37
  396. package/dist/providers/cloudflare/rest.js +0 -1078
  397. package/dist/providers/github/hosted.js +0 -218
  398. package/dist/providers/github/rest.js +0 -321
  399. package/dist/providers/infisical/index.d.ts +0 -7
  400. package/dist/providers/infisical/index.js +0 -713
  401. package/dist/providers/infisical/skill.generated.js +0 -8
  402. package/dist/providers/linear/index.d.ts +0 -55
  403. package/dist/providers/linear/index.js +0 -352
  404. package/dist/providers/linear/skill.generated.js +0 -8
  405. package/dist/providers/mixpanel/index.d.ts +0 -57
  406. package/dist/providers/mixpanel/index.js +0 -468
  407. package/dist/providers/mixpanel/operations.js +0 -20
  408. package/dist/providers/mixpanel/rest.js +0 -603
  409. package/dist/providers/mixpanel/skill.generated.js +0 -10
  410. package/dist/providers/mixpanel/value-safety.js +0 -16
  411. package/dist/providers/notion/index.d.ts +0 -65
  412. package/dist/providers/notion/index.js +0 -368
  413. package/dist/providers/notion/openapi.generated.js +0 -75
  414. package/dist/providers/notion/rest.d.ts +0 -24
  415. package/dist/providers/notion/rest.js +0 -1226
  416. package/dist/providers/notion/skill.generated.js +0 -11
  417. package/dist/providers/notion/value-safety.js +0 -56
  418. package/dist/providers/revenuecat/index.d.ts +0 -49
  419. package/dist/providers/revenuecat/index.js +0 -557
  420. package/dist/providers/revenuecat/skill.generated.js +0 -11
  421. package/dist/providers/stripe/rest.d.ts +0 -14
  422. package/dist/providers/stripe/rest.js +0 -402
  423. package/dist/providers/vercel/rest.d.ts +0 -20
  424. package/dist/providers/vercel/rest.js +0 -1020
  425. package/dist/providers/wispr-flow/index.d.ts +0 -14
  426. package/dist/providers/wispr-flow/index.js +0 -117
  427. package/dist/providers/wispr-flow/skill.generated.js +0 -8
  428. /package/dist/providers/github/{key.js → lib/key.js} +0 -0
@@ -0,0 +1,267 @@
1
+ // Provider kit (decision 0010, B.4). Unreachable from src/index.ts.
2
+ //
3
+ // One Schema is a tool's type, its argument decoder, and its catalog JSON
4
+ // Schema. Effect's generator is correct but spells some things differently
5
+ // from the catalogs agents already read, and catalog bytes may not change
6
+ // without a recorded decision. This renders the "connecta dialect": the same
7
+ // constraints, minus what JSON Schema already defaults or Effect adds for its
8
+ // own decoder.
9
+ //
10
+ // - `additionalProperties: true` is the default and is dropped. Inputs are
11
+ // closed (`onExcessProperty: "error"`), and their root always names
12
+ // `required`, as every hand-written input does.
13
+ // - Nothing the decoder enforces is dropped. `Schema.Int` refuses integers
14
+ // past 2^53 and says so (`minimum`/`maximum`); catalogs have always meant
15
+ // any whole number, so kit Schemas use `Integer` below, which decodes and
16
+ // renders exactly that.
17
+ // - String lengths count code points, as JSON Schema and api()'s validator
18
+ // (@cfworker/json-schema's `ucs2length`) do: an astral character is one,
19
+ // a combining mark is one, and a lone surrogate is one. Effect's
20
+ // `isMinLength`/`isMaxLength` count UTF-16 units and render an
21
+ // approximation, so kit Schemas use `isMinChars`/`isMaxChars` below, and an
22
+ // input whose check renders approximately, or not at all, is refused at
23
+ // construction.
24
+ // - An array's own keywords (`maxItems`, `description`) come before `items`.
25
+ // - `anyOf` over primitive branches with disjoint types, each constrained only
26
+ // by keywords that apply to its own type, becomes one `type` array. Bare
27
+ // object and array branches also collapse. JSON
28
+ // Schema applies `minLength` only to strings and `minimum` only to numbers,
29
+ // so the two spellings accept exactly the same values.
30
+ //
31
+ // Only the stable `effect` Schema module is used here.
32
+ import { Schema } from "effect";
33
+ const NUMERIC = new Set(["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"]);
34
+ const OWN_KEYWORDS = {
35
+ string: new Set(["minLength", "maxLength", "pattern", "format"]),
36
+ integer: NUMERIC,
37
+ number: NUMERIC,
38
+ boolean: new Set(),
39
+ null: new Set(),
40
+ // Bare object/array unions are exact too; constrained branches keep anyOf.
41
+ object: new Set(),
42
+ array: new Set(["items", "minItems", "maxItems", "uniqueItems"]),
43
+ };
44
+ /**
45
+ * A whole number, as JSON Schema's `type: integer` and api()'s validator mean
46
+ * it: finite, with no safe-integer bound. Use it, not `Schema.Int`, wherever a
47
+ * catalog or a request encodes an integer.
48
+ */
49
+ export const Integer = Schema.Finite.check(Schema.makeFilter((value) => Number.isInteger(value), {
50
+ expected: "an integer",
51
+ toJsonSchema: () => ({ type: "integer" }),
52
+ }));
53
+ /**
54
+ * A string's length as JSON Schema counts it: code points, with a lone
55
+ * surrogate counted as one. The same loop as api()'s validator.
56
+ */
57
+ export function codePoints(value) {
58
+ let count = 0;
59
+ for (let index = 0; index < value.length; count++) {
60
+ const unit = value.charCodeAt(index++);
61
+ if (unit >= 0xd800 && unit <= 0xdbff && index < value.length && (value.charCodeAt(index) & 0xfc00) === 0xdc00) {
62
+ index++;
63
+ }
64
+ }
65
+ return count;
66
+ }
67
+ /** At least `minimum` code points, rendered `minLength` exactly. */
68
+ export function isMinChars(minimum) {
69
+ return Schema.makeFilter((value) => codePoints(value) >= minimum, {
70
+ expected: `a string of at least ${minimum} characters`,
71
+ toJsonSchema: () => ({ minLength: minimum }),
72
+ });
73
+ }
74
+ /** At most `maximum` code points, rendered `maxLength` exactly. */
75
+ export function isMaxChars(maximum) {
76
+ return Schema.makeFilter((value) => codePoints(value) <= maximum, {
77
+ expected: `a string of at most ${maximum} characters`,
78
+ toJsonSchema: () => ({ maxLength: maximum }),
79
+ });
80
+ }
81
+ const JSON_TYPES = {
82
+ String: "string",
83
+ Number: "number",
84
+ Boolean: "boolean",
85
+ Arrays: "array",
86
+ Objects: "object",
87
+ };
88
+ /**
89
+ * Refuse an input whose checks the catalog cannot state exactly: a check
90
+ * with no JSON Schema rendering, or one Effect marks approximate (a UTF-16
91
+ * string length, a RegExp whose flags differ). The validator api() runs on
92
+ * the catalog and the decoder would then disagree about some value.
93
+ */
94
+ function exactChecks(ast, seen = new Set()) {
95
+ if (seen.has(ast))
96
+ return;
97
+ seen.add(ast);
98
+ // Transformations can change what the decoder accepts; the inline renderer
99
+ // has no reviewed exactness contract for them.
100
+ if (ast.encoding)
101
+ throw new Error("A kit input encoding has no exact catalog spelling.");
102
+ const type = JSON_TYPES[ast._tag];
103
+ const visit = (checks) => {
104
+ for (const check of checks) {
105
+ if (check._tag === "FilterGroup") {
106
+ visit(check.checks);
107
+ continue;
108
+ }
109
+ const render = check.annotations?.toJsonSchema;
110
+ const rendered = render?.({ type, schemas: [] });
111
+ if (rendered === undefined || Array.isArray(rendered)) {
112
+ throw new Error(`A kit input check (${String(check.annotations?.expected ?? "a filter")}) has no exact catalog spelling; ` +
113
+ "use the kit's isMinChars/isMaxChars or Integer, or a check that renders exactly.");
114
+ }
115
+ }
116
+ };
117
+ if (ast.checks)
118
+ visit(ast.checks);
119
+ switch (ast._tag) {
120
+ case "Objects":
121
+ for (const property of ast.propertySignatures)
122
+ exactChecks(property.type, seen);
123
+ for (const index of ast.indexSignatures) {
124
+ exactChecks(index.parameter, seen);
125
+ exactChecks(index.type, seen);
126
+ }
127
+ return;
128
+ case "Arrays":
129
+ for (const element of [...ast.elements, ...ast.rest])
130
+ exactChecks(element, seen);
131
+ return;
132
+ case "Union":
133
+ for (const member of ast.types)
134
+ exactChecks(member, seen);
135
+ return;
136
+ case "Suspend":
137
+ exactChecks(ast.thunk(), seen);
138
+ return;
139
+ case "String":
140
+ case "Number":
141
+ case "Boolean":
142
+ case "Null":
143
+ case "Unknown":
144
+ case "Never":
145
+ case "Literal":
146
+ return;
147
+ default:
148
+ throw new Error(`A kit input AST form (${ast._tag}) has no exact catalog spelling.`);
149
+ }
150
+ }
151
+ /** Preserve an explicitly empty required list in a legacy output catalog. */
152
+ export function emptyRequired(schema) {
153
+ return schema.annotate({ "connecta/emptyRequired": true });
154
+ }
155
+ function restoreRequired(node, ast) {
156
+ if (ast.annotations?.["connecta/emptyRequired"] === true) {
157
+ if (node["type"] !== "object" || (Array.isArray(node["required"]) && node["required"].length > 0))
158
+ throw new Error("emptyRequired requires an object with no required properties.");
159
+ node["required"] = [];
160
+ }
161
+ if (ast._tag === "Objects") {
162
+ const properties = node["properties"];
163
+ for (const property of ast.propertySignatures) {
164
+ const child = properties?.[String(property.name)];
165
+ if (child && typeof child === "object")
166
+ restoreRequired(child, property.type);
167
+ }
168
+ }
169
+ else if (ast._tag === "Arrays") {
170
+ const item = node["items"];
171
+ if (item && typeof item === "object" && ast.rest[0])
172
+ restoreRequired(item, ast.rest[0]);
173
+ }
174
+ }
175
+ /** Render a Schema as catalog JSON Schema. Throws on a schema that needs `$defs`, or an inexact input check. */
176
+ export function catalogSchema(schema, mode) {
177
+ if (mode === "input")
178
+ exactChecks(schema.ast);
179
+ const document = Schema.toJsonSchemaDocument(schema, mode === "input" ? { onExcessProperty: "error" } : {});
180
+ if (Object.keys(document.definitions).length > 0) {
181
+ throw new Error("A catalog schema is rendered inline; remove the identifier that asked for a definition.");
182
+ }
183
+ restoreRequired(document.schema, schema.ast);
184
+ let root = normalize(document.schema);
185
+ if (mode === "input" &&
186
+ root["type"] === "object" &&
187
+ root["additionalProperties"] === false &&
188
+ !("properties" in root)) {
189
+ const { type, ...rest } = root;
190
+ root = { type, properties: {}, ...rest };
191
+ }
192
+ if (mode === "input" && root["type"] === "object" && !("required" in root)) {
193
+ return Object.fromEntries(Object.entries(root).flatMap(([key, value]) => key === "properties"
194
+ ? [
195
+ [key, value],
196
+ ["required", []],
197
+ ]
198
+ : [[key, value]]));
199
+ }
200
+ return root;
201
+ }
202
+ function normalize(value) {
203
+ if (Array.isArray(value))
204
+ return value.map(normalize);
205
+ if (!value || typeof value !== "object")
206
+ return value;
207
+ const node = value;
208
+ const out = {};
209
+ for (const [key, child] of Object.entries(node)) {
210
+ if (key === "additionalProperties" && child === true)
211
+ continue;
212
+ if (key === "properties") {
213
+ out[key] = Object.fromEntries(Object.entries(child).map(([name, schema]) => [name, normalize(schema)]));
214
+ continue;
215
+ }
216
+ if (key === "anyOf") {
217
+ const branches = child.map(normalize);
218
+ const merged = collapse(branches);
219
+ if (merged)
220
+ Object.assign(out, merged);
221
+ else
222
+ out[key] = branches;
223
+ continue;
224
+ }
225
+ out[key] = key === "items" || key === "additionalProperties" ? normalize(child) : child;
226
+ }
227
+ // A discrete integer constraint may render in one allOf member beside its integer type.
228
+ // Flatten disjoint keywords so enum/default/max remain one usable parameter contract.
229
+ const members = out["allOf"];
230
+ if (out["type"] === "integer" && Array.isArray(members) && members.length === 1) {
231
+ const part = members[0];
232
+ if (Array.isArray(part["enum"]) &&
233
+ Object.keys(part).every((key) => ["enum", "minimum", "maximum", "description", "default"].includes(key) &&
234
+ (!(key in out) || out[key] === part[key]))) {
235
+ delete out["allOf"];
236
+ Object.assign(out, part);
237
+ }
238
+ }
239
+ // Hand-written catalogs spell an array's own keywords before its `items`.
240
+ if ("items" in out) {
241
+ const { items, ...rest } = out;
242
+ return { ...rest, items };
243
+ }
244
+ return out;
245
+ }
246
+ /** One `type` array for disjoint branches whose constraints cannot collide, else undefined. */
247
+ function collapse(branches) {
248
+ const types = [];
249
+ const merged = {};
250
+ for (const branch of branches) {
251
+ const type = branch["type"];
252
+ if (typeof type !== "string" || types.includes(type) || !OWN_KEYWORDS[type])
253
+ return undefined;
254
+ for (const [key, keyword] of Object.entries(branch)) {
255
+ if (key === "type")
256
+ continue;
257
+ if (!OWN_KEYWORDS[type].has(key) || key in merged)
258
+ return undefined;
259
+ merged[key] = keyword;
260
+ }
261
+ types.push(type);
262
+ }
263
+ // integer and number share keywords, so they never merge.
264
+ if (types.includes("integer") && types.includes("number"))
265
+ return undefined;
266
+ return { type: types, ...merged };
267
+ }
@@ -0,0 +1,241 @@
1
+ // Node-only: owned undici dispatcher and Node stream adapters, reached via /node.
2
+ import { Readable } from "node:stream";
3
+ import { isIP } from "node:net";
4
+ import { createBrotliDecompress, createGunzip, createInflate } from "node:zlib";
5
+ import { Client, ProxyAgent } from "undici";
6
+ import { ConnectorCallError } from "../errors.js";
7
+ /** Pull-based bridge: Node 22's Readable.toWeb can enqueue after Web cancellation. */
8
+ function responseBody(body, signal) {
9
+ const chunks = body[Symbol.asyncIterator]();
10
+ let closed = false;
11
+ return new ReadableStream({
12
+ async pull(controller) {
13
+ try {
14
+ const chunk = await chunks.next();
15
+ if (closed)
16
+ return;
17
+ if (chunk.done) {
18
+ closed = true;
19
+ controller.close();
20
+ }
21
+ else
22
+ controller.enqueue(chunk.value);
23
+ }
24
+ catch (error) {
25
+ if (closed)
26
+ return;
27
+ closed = true;
28
+ controller.error(signal.aborted ? signal.reason : error);
29
+ }
30
+ },
31
+ cancel() {
32
+ closed = true;
33
+ body.destroy();
34
+ // A pending next() can still settle: it checks closed before touching
35
+ // the canceled controller, and every iterator rejection is observed.
36
+ void chunks.return?.().catch(() => { });
37
+ },
38
+ });
39
+ }
40
+ /**
41
+ * One Client per send, no shared/global dispatcher or interceptor. request()
42
+ * avoids fetch's 421 retry; HTTP/1.1 and non-idempotent dispatch disable the
43
+ * HTTP/2 REFUSED_STREAM and pipeline recovery paths, for every method.
44
+ * The response/abort owns the client until the body ends or is cancelled.
45
+ */
46
+ export function createNodeGuardedTransport(proxy) {
47
+ let proxyUri;
48
+ let proxyToken;
49
+ if (proxy !== undefined) {
50
+ try {
51
+ const url = new URL(proxy);
52
+ if (!["http:", "https:"].includes(url.protocol) || url.pathname !== "/" || url.search || url.hash)
53
+ throw new Error();
54
+ if (url.username || url.password) {
55
+ if (!url.username)
56
+ throw new Error();
57
+ proxyToken = `Basic ${Buffer.from(`${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}`).toString("base64")}`;
58
+ }
59
+ url.username = "";
60
+ url.password = "";
61
+ proxyUri = url.href;
62
+ }
63
+ catch {
64
+ // Configuration can contain passwords: never include the input or cause.
65
+ throw new Error("proxy must be an absolute HTTP(S) proxy URL with no path, query or fragment.");
66
+ }
67
+ }
68
+ return {
69
+ delaysDispatch: true,
70
+ async send(url, init, beforeDispatch = () => { }) {
71
+ const framed = new Request(url, init);
72
+ if (!framed.headers.has("user-agent"))
73
+ framed.headers.set("user-agent", "connecta");
74
+ let dispatched = false;
75
+ const dispatch = () => {
76
+ framed.signal.throwIfAborted();
77
+ if (dispatched)
78
+ throw new Error("A transport send cannot open a second connection.");
79
+ beforeDispatch();
80
+ dispatched = true;
81
+ };
82
+ const clientOptions = { allowH2: false, pipelining: 0, interceptors: { Client: [] } };
83
+ let tunnelClient;
84
+ const client = proxyUri
85
+ ? new ProxyAgent({
86
+ uri: proxyUri,
87
+ ...(proxyToken ? { token: proxyToken } : {}),
88
+ proxyTunnel: true,
89
+ ...clientOptions,
90
+ interceptors: { Agent: [], Client: [] },
91
+ // Neither side of CONNECT uses a Pool, interceptor or global dispatcher.
92
+ clientFactory: (origin, options) => {
93
+ const connect = options.connect;
94
+ return (tunnelClient = new Client(origin, {
95
+ ...options,
96
+ ...clientOptions,
97
+ connect: (parameters, callback) => {
98
+ const name = parameters.servername;
99
+ // ProxyAgent 6 explicitly sets SNI to the proxy hostname.
100
+ // Node 26 rejects IP SNI. Omit it for literal IPs; tls.connect
101
+ // still checks the certificate against its connection host.
102
+ connect({ ...parameters, ...(name && isIP(name.replace(/^\[|\]$/g, "")) ? { servername: "" } : {}) }, callback);
103
+ },
104
+ }));
105
+ },
106
+ factory: (origin, options) => {
107
+ const connect = options.connect;
108
+ return new Client(origin, {
109
+ ...options,
110
+ ...clientOptions,
111
+ connect: (options, callback) => {
112
+ let settled = false;
113
+ connect(options, (error, socket) => {
114
+ // undici 6's refused HTTP CONNECT can invoke its callback twice.
115
+ if (settled)
116
+ return;
117
+ settled = true;
118
+ if (error || !socket) {
119
+ // A CONNECT reset is pre-send, never a recoverable
120
+ // UND_ERR_SOCKET that Client may silently reconnect.
121
+ callback(new Error("Outbound proxy tunnel failed."), null);
122
+ return;
123
+ }
124
+ try {
125
+ dispatch();
126
+ }
127
+ catch (error) {
128
+ socket.destroy();
129
+ callback(error, null);
130
+ return;
131
+ }
132
+ callback(null, socket);
133
+ });
134
+ },
135
+ });
136
+ },
137
+ })
138
+ : new Client(url.origin, clientOptions);
139
+ const destroy = () => Promise.allSettled([tunnelClient?.destroy(), client.destroy()]);
140
+ const abort = () => {
141
+ void destroy();
142
+ };
143
+ framed.signal.addEventListener("abort", abort, { once: true });
144
+ try {
145
+ framed.signal.throwIfAborted();
146
+ if (!proxyUri)
147
+ dispatch();
148
+ const answer = await client.request({
149
+ origin: url.origin,
150
+ path: `${url.pathname}${url.search}`,
151
+ method: framed.method,
152
+ headers: Object.fromEntries(framed.headers),
153
+ // Keep serialized SDK/provider bodies sized: undici computes their exact
154
+ // UTF-8 Content-Length instead of using chunked transfer encoding.
155
+ body: typeof init.body === "string"
156
+ ? init.body
157
+ : init.body instanceof URLSearchParams
158
+ ? init.body.toString()
159
+ : framed.body
160
+ ? Readable.fromWeb(framed.body)
161
+ : null,
162
+ signal: framed.signal,
163
+ idempotent: false,
164
+ maxRedirections: 0,
165
+ });
166
+ const headers = new Headers();
167
+ for (const [name, values] of Object.entries(answer.headers)) {
168
+ for (const value of Array.isArray(values) ? values : values === undefined ? [] : [values]) {
169
+ headers.append(name, value);
170
+ }
171
+ }
172
+ const raw = answer.body;
173
+ // Undici emits AbortError even for a deliberate destroy() with no
174
+ // consumer (HEAD or an unsupported encoding). Active Web/decode stream
175
+ // listeners still receive read failures; cleanup always has an observer.
176
+ raw.on("error", () => { });
177
+ const close = () => {
178
+ framed.signal.removeEventListener("abort", abort);
179
+ void destroy();
180
+ };
181
+ raw.once("close", close);
182
+ if (framed.method === "HEAD" || [204, 205, 304].includes(answer.statusCode)) {
183
+ raw.destroy();
184
+ return new Response(null, { status: answer.statusCode, headers });
185
+ }
186
+ let body = raw;
187
+ // Native fetch decodes content encodings before exposing response bytes.
188
+ // Bound the number of decoders; an unsupported chain fails closed.
189
+ const encodings = (headers.get("content-encoding") ?? "")
190
+ .split(",")
191
+ .map((value) => value.trim())
192
+ .filter(Boolean);
193
+ const unreadable = () => {
194
+ raw.destroy();
195
+ // Preserve the physical status (notably a write's 401) even when
196
+ // the content encoding cannot be read. A body failure is not a lost reply.
197
+ return new Response(new ReadableStream({
198
+ start(controller) {
199
+ controller.error(new Error("Unsupported response content encoding."));
200
+ },
201
+ }), { status: answer.statusCode, headers });
202
+ };
203
+ if (encodings.length > 5)
204
+ return unreadable();
205
+ for (const encoding of encodings.reverse()) {
206
+ if (encoding === "identity")
207
+ continue;
208
+ const decode = encoding === "gzip" || encoding === "x-gzip"
209
+ ? createGunzip()
210
+ : encoding === "deflate"
211
+ ? createInflate()
212
+ : encoding === "br"
213
+ ? createBrotliDecompress()
214
+ : undefined;
215
+ if (!decode)
216
+ return unreadable();
217
+ const previous = body;
218
+ previous.once("error", (error) => decode.destroy(error));
219
+ decode.once("close", () => previous.destroy());
220
+ body = previous.pipe(decode);
221
+ }
222
+ return new Response(responseBody(body, framed.signal), { status: answer.statusCode, headers });
223
+ }
224
+ catch (error) {
225
+ // ProxyAgent destroys its destination Agent before its CONNECT Client.
226
+ // An unfinished CONNECT holds the former's connector open: cancel both
227
+ // together so a reset/abort cannot deadlock cleanup.
228
+ framed.signal.removeEventListener("abort", abort);
229
+ await destroy();
230
+ if (framed.signal.aborted)
231
+ throw framed.signal.reason;
232
+ if (proxyUri)
233
+ throw new ConnectorCallError("unavailable", dispatched
234
+ ? "The proxied request did not return a confirmed response."
235
+ : "The outbound proxy tunnel could not be established; the destination request was not sent.", { retryable: false });
236
+ throw error;
237
+ }
238
+ },
239
+ };
240
+ }
241
+ export const nodeGuardedTransport = createNodeGuardedTransport();
@@ -0,0 +1,162 @@
1
+ // Refresh-once middleware (decision 0010, B.2). Unreachable from src/index.ts.
2
+ //
3
+ // Refresh-once for a connection that authenticates with a token it can
4
+ // renew: a downstream OAuth grant, a service-account assertion, a machine
5
+ // login. A 401 earns one renewal. A read is then replayed once; a second 401
6
+ // is the grant's own refusal. A write is never sent again (INV-9): the
7
+ // renewal still runs, so the next deliberate call has a fresh token, and this
8
+ // call answers with the write's own 401.
9
+ //
10
+ // This file decides nothing about that write. The transport recorded its
11
+ // answer in the call's dispatch ledger when the resource server gave it, and
12
+ // api()'s boundary delegates the verdict to the core's settleWrite. The
13
+ // middleware records facts only, including a successful renewal. By default a failed
14
+ // renewal is discarded; the static bridge can opt into the core's grant diagnostic.
15
+ // Neither path can turn the write into a known refusal or advertise a retry; the
16
+ // ledger notes only a renewal that succeeded, so the agent is not sent to
17
+ // re-authorize a credential that already works. Even a wrapper here that
18
+ // did resend would be stopped by the transport's one-dispatch guard.
19
+ //
20
+ // The core's positive-read classifier is authoritative: only a positively
21
+ // classified GET/HEAD is replayable. Overrides force a write, and a reviewed
22
+ // POST or an auxiliary label cannot bypass the physical-write reservation.
23
+ //
24
+ // The grant itself (storage, coordination across isolates, the token
25
+ // request, the request-scope latch) stays in src/auth/.
26
+ import { Cause, Context, Effect, Option } from "effect";
27
+ import * as HttpClient from "effect/http/HttpClient";
28
+ import * as HttpClientResponse from "effect/http/HttpClientResponse";
29
+ import { readRequest, renewedAfterUnauthorized } from "../provider-call.js";
30
+ import { renewableGrantFor } from "../auth/renewable-grant.js";
31
+ import { ConnectorCallError } from "../errors.js";
32
+ import { responseRequest } from "./client.js";
33
+ import { Refusal, RequestRefused } from "./errors.js";
34
+ import { Dispatches, Signal, duringCall } from "./services.js";
35
+ /** The calling owner's renewable token, as the operations a transport needs from it. */
36
+ export class Grant extends Context.Service()("connecta/http/Grant") {
37
+ }
38
+ /** Bridge a production static OAuth facade without adding anything to its public interface. */
39
+ export function grantFromOAuth(oauth) {
40
+ const grant = renewableGrantFor(oauth);
41
+ const use = (work) => Effect.tryPromise({
42
+ try: work,
43
+ catch: (error) => new Refusal({
44
+ error: error instanceof ConnectorCallError
45
+ ? error
46
+ : new ConnectorCallError("connector_call_failed", "The OAuth grant could not be used.", {
47
+ retryable: false,
48
+ }),
49
+ }),
50
+ });
51
+ const missing = () => Promise.reject(new ConnectorCallError("connector_call_failed", "This OAuth facade has no renewable grant."));
52
+ return {
53
+ token: use(grant ? () => grant.token() : missing),
54
+ refresh: (stale) => use(grant ? () => grant.refresh(stale) : missing),
55
+ rejected: use(grant ? () => grant.rejected() : missing),
56
+ };
57
+ }
58
+ /** Headers for `GuardedOptions.authenticate`: the grant's bearer token. */
59
+ export const bearer = Grant.use((grant) => Effect.map(grant.token, (token) => ({ Authorization: `Bearer ${token}` })));
60
+ /** The bearer token a prepared request carried, as the grant issued it. */
61
+ function sentToken(request) {
62
+ return (request.headers["authorization"] ?? "").replace(/^Bearer /, "");
63
+ }
64
+ /** Wrap a guarded client whose `authenticate` is {@link bearer}. */
65
+ export function refreshOnce(guarded, options) {
66
+ if (options?.writeRenewal === "discard" && options.writeUnauthorized)
67
+ throw new Error("A write that is not renewed has no renewal facts to map.");
68
+ const inner = guarded.client;
69
+ const challengeFailure = (entry) => Effect.gen(function* () {
70
+ const error = new ConnectorCallError("connector_call_failed", "The OAuth challenge exceeded its response ceiling.", { retryable: false });
71
+ const refusal = new Refusal({
72
+ error: !readRequest(entry ?? {}) && options?.writeUnauthorized
73
+ ? options.writeUnauthorized({ _tag: "Failed", failure: error })
74
+ : error,
75
+ });
76
+ const dispatch = entry ? (yield* Dispatches).find((item) => item.request === entry) : undefined;
77
+ if (dispatch)
78
+ dispatch.failure = refusal;
79
+ return yield* Effect.fail(refusal);
80
+ });
81
+ const execute = (request) => Effect.gen(function* () {
82
+ const response = yield* inner.execute(request).pipe(Effect.catchTag("ResponseTooLarge", (failure) => {
83
+ const entry = responseRequest(failure);
84
+ return options?.challengeMaxBytes !== undefined && entry?.status === 401
85
+ ? challengeFailure(entry)
86
+ : Effect.fail(failure);
87
+ }));
88
+ if (response.status !== 401)
89
+ return response;
90
+ // Read under the ceiling now, so nothing is left open while renewing.
91
+ const reply = yield* guarded
92
+ .read(response, options?.challengeMaxBytes)
93
+ .pipe(Effect.catchTag("ResponseTooLarge", (failure) => options?.challengeMaxBytes !== undefined
94
+ ? challengeFailure(responseRequest(response))
95
+ : Effect.fail(failure)));
96
+ const grant = yield* Grant;
97
+ const stale = sentToken(response.request);
98
+ const entry = responseRequest(response);
99
+ if (!readRequest(entry ?? {}) && options?.writeRenewal === "discard") {
100
+ // No renewal: the next deliberate call obtains a fresh token. The
101
+ // write's own 401 is answered; nothing is marked renewed (Q8).
102
+ if (grant.discard)
103
+ yield* grant.discard(stale);
104
+ return HttpClientResponse.fromWeb(request, new Response(reply.text ?? "", { status: reply.status, headers: reply.headers }));
105
+ }
106
+ if (!readRequest(entry ?? {})) {
107
+ // Whatever renewal meets is the authorization server's, not the
108
+ // write's. The ledger keeps only whether it succeeded, which decides
109
+ // whether the agent still needs the handoff, never the outcome.
110
+ const renewal = yield* Effect.exit(grant.refresh(stale));
111
+ // Recorded on this request's own ledger entry, and only for a token
112
+ // other than the one it sent; the core decides from it (Q8).
113
+ if (renewal._tag === "Success" && !(yield* Signal)?.aborted)
114
+ renewedAfterUnauthorized(entry, stale, renewal.value);
115
+ if (options?.writeUnauthorized) {
116
+ const cause = renewal._tag === "Failure" ? Cause.findErrorOption(renewal.cause) : Option.none();
117
+ const failure = Option.isSome(cause) ? cause.value : undefined;
118
+ const facts = renewal._tag === "Success"
119
+ ? { _tag: renewal.value === stale ? "Unchanged" : "Renewed" }
120
+ : {
121
+ _tag: "Failed",
122
+ failure: failure?._tag === "Refusal"
123
+ ? failure.error
124
+ : (failure ?? new Error("OAuth renewal did not complete.")),
125
+ };
126
+ // This is still the resource request's own 401. The common failure
127
+ // mapper attaches its provenance; settleWrite decides the outcome.
128
+ return yield* Effect.fail(new Refusal({ error: options.writeUnauthorized(facts) }));
129
+ }
130
+ return HttpClientResponse.fromWeb(request, new Response(reply.text ?? "", { status: reply.status, headers: reply.headers }));
131
+ }
132
+ const fresh = yield* grant.refresh(stale);
133
+ if ((yield* Signal)?.aborted)
134
+ return yield* Effect.fail(new RequestRefused({ message: "This call has ended; no renewal work may continue." }));
135
+ renewedAfterUnauthorized(entry, stale, fresh);
136
+ // `authenticate` runs again on the replay and reads the renewed token.
137
+ const replay = yield* inner
138
+ .execute(request)
139
+ .pipe(Effect.catchTag("ResponseTooLarge", (failure) => options?.challengeMaxBytes !== undefined && responseRequest(failure)?.status === 401
140
+ ? grant.rejected
141
+ : Effect.fail(failure)));
142
+ if (replay.status !== 401)
143
+ return replay;
144
+ if (options?.replayRejection === "answer") {
145
+ // The connection reads and redacts this answer like any other.
146
+ if (grant.discard)
147
+ yield* grant.discard(sentToken(replay.request));
148
+ return replay;
149
+ }
150
+ yield* Effect.ignore(guarded.read(replay));
151
+ return yield* grant.rejected;
152
+ }).pipe(duringCall, Effect.catchTag("RequestRefused", (failure) => Signal.use((signal) => Effect.fail(signal?.aborted
153
+ ? new Refusal({
154
+ error: new ConnectorCallError("connector_call_failed", "This call has ended; no renewal work may continue."),
155
+ })
156
+ : failure))));
157
+ return {
158
+ provider: guarded.provider,
159
+ client: HttpClient.makeWith((request) => Effect.flatMap(request, execute), (request) => Effect.succeed(request)),
160
+ read: guarded.read,
161
+ };
162
+ }