@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,1066 @@
1
+ // Cloudflare's pinned REST contracts, confinement, framing and failure semantics.
2
+ import { responseMedia } from "../_shared/rest/response-media.js";
3
+ import { EXCLUSIVE_PATH_IDS } from "./lib/path-bounds.js";
4
+ import { RESPONSE_MEDIA } from "./lib/response-media.js";
5
+ import { Effect, Option, Schema } from "effect";
6
+ import { ConnectorCallError } from "../../errors.js";
7
+ import { Refusal, StatusFailure, toConnectorCallError, } from "../../http/errors.js";
8
+ import { Budget, Credentials, Classification, Dispatches, Signal } from "../../http/services.js";
9
+ import { restSafety } from "../../http/safety.js";
10
+ import { get, post, vendor as declareVendor, } from "../../http/vendor.js";
11
+ import { callMemo, Operation as ContextView } from "../../runtime/call.js";
12
+ import { checked, indexedVendor } from "../_shared/rest/kit.js";
13
+ import { Integer } from "../../http/json-schema.js";
14
+ import { cloudflarePage } from "../../http/hatch-paging.js";
15
+ import { OperationIndex } from "../_shared/rest/operation-index.js";
16
+ import { valueSafety, withholdEmbeddedUrlsIn, refuseWithheldLinks, refuseWithheldValues, refuseWithheldUpload, refuseWithheldBase64, } from "../_shared/rest/value-safety.js";
17
+ import { CLOUDFLARE_VALUE_SAFETY } from "./safety.js";
18
+ import { openapi } from "./openapi.generated.js";
19
+ import { cloudflareDiscovery } from "./lib/discovery.js";
20
+ import { CLOUDFLARE_API_BASE } from "./lib/config.js";
21
+ export const MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
22
+ /** Zone-ownership reads a pin check may spend: per tool call, and per five minutes per connector. */
23
+ const MAX_LOOKUPS_PER_CALL = 3;
24
+ const MAX_LOOKUPS_PER_WINDOW = 60;
25
+ const LOOKUP_WINDOW_MS = 300_000;
26
+ export const READ_CAP_MS = 60_000;
27
+ export const WRITE_STEPS = Object.freeze({ lookup1: "once", lookup2: "once", lookup3: "once", write: "once" });
28
+ /**
29
+ * POSTs that only read, admitted by `cloudflare_api_read`, each reviewed
30
+ * against the pinned spec for four disqualifiers: it persists state, it is
31
+ * billed per call or runs a model, it sends traffic outside Cloudflare, or it
32
+ * activates something. Requests that look like reads but are not stay writes:
33
+ * D1 `…/query` and `…/raw` and Durable Object `…/query/v2` run SQL that can
34
+ * write; Workers AI `…/ai/run`, AI Search and AutoRAG search (query rewriting
35
+ * and reranking run billed models), browser rendering, web search and brand
36
+ * logo search run models or fetch the web; monetization "checks" activate an
37
+ * entitlement; flag evaluation is metered; robots.txt bulk reads fetch
38
+ * origins; previews, tests, scans, exports and ownership challenges create
39
+ * state or send traffic; and token or credential POSTs are refused outright
40
+ * (`CLOUDFLARE_VALUE_SAFETY`).
41
+ */
42
+ export const READ_POSTS = [
43
+ [
44
+ "POST",
45
+ "/accounts/{account_id}/resource-library/applications/lookup",
46
+ "Looks up application names in a sentence; changes no application or rule.",
47
+ ],
48
+ [
49
+ "POST",
50
+ "/accounts/{account_id}/data-security/posture/finding_types/validate",
51
+ "Checks a candidate finding definition without creating or reserving it.",
52
+ ],
53
+ ["POST", "/accounts/{account_id}/analytics/query/{dataset}/summary", "Analytics query; aggregates only."],
54
+ ["POST", "/accounts/{account_id}/analytics/query/{dataset}/timeseries", "Analytics query; aggregates only."],
55
+ ["POST", "/accounts/{account_id}/analytics/query/{dataset}/top-n", "Analytics query; aggregates only."],
56
+ [
57
+ "POST",
58
+ "/accounts/{account_id}/analytics/query/data-security/content-findings/top-n",
59
+ "Analytics query; aggregates only.",
60
+ ],
61
+ [
62
+ "POST",
63
+ "/accounts/{account_id}/analytics/query/data-security/findings/summary",
64
+ "Analytics query; aggregates only.",
65
+ ],
66
+ [
67
+ "POST",
68
+ "/accounts/{account_id}/analytics/query/data-security/findings/timeseries",
69
+ "Analytics query; aggregates only.",
70
+ ],
71
+ [
72
+ "POST",
73
+ "/accounts/{account_id}/analytics_engine/sql",
74
+ "Analytics Engine SQL is SELECT-only; the body is the query.",
75
+ ],
76
+ ["POST", "/accounts/{account_tag}/analytics/sql", "SQL API over analytics datasets; read-only by contract."],
77
+ ["POST", "/analytics/sql", "SQL API over analytics datasets; read-only by contract."],
78
+ ["POST", "/accounts/{account_id}/logs/explorer/query/sql", "Log Explorer SQL query; reads stored logs."],
79
+ ["POST", "/zones/{zone_id}/logs/explorer/query/sql", "Log Explorer SQL query; reads stored logs."],
80
+ ["POST", "/accounts/{account_id}/storage/kv/namespaces/{namespace_id}/bulk/get", "Reads several KV values at once."],
81
+ ["POST", "/accounts/{account_id}/vectorize/v2/indexes/{index_name}/query", "Nearest-vector query; no model runs."],
82
+ ["POST", "/accounts/{account_id}/vectorize/v2/indexes/{index_name}/get_by_ids", "Reads vectors by id."],
83
+ ["POST", "/accounts/{account_id}/workers/observability/telemetry/query", "Runs a telemetry query; saves nothing."],
84
+ ["POST", "/accounts/{account_id}/workers/observability/telemetry/keys", "Lists telemetry keys."],
85
+ ["POST", "/accounts/{account_id}/workers/observability/telemetry/values", "Lists telemetry values."],
86
+ [
87
+ "POST",
88
+ "/accounts/{account_id}/browser-extension/config/logs/extension-events/search",
89
+ "Searches extension event logs.",
90
+ ],
91
+ [
92
+ "POST",
93
+ "/accounts/{account_id}/browser-extension/config/logs/extension-events/timeseries",
94
+ "Extension event aggregates.",
95
+ ],
96
+ ["POST", "/accounts/{account_id}/billable/usage", "Filtered usage read; Cloudflare documents it read-only."],
97
+ ["POST", "/accounts/{account_id}/cloudforce-one/events", "Filters and lists threat events."],
98
+ ["POST", "/accounts/{account_id}/cloudforce-one/requests", "Lists intelligence requests."],
99
+ ["POST", "/accounts/{account_id}/cloudforce-one/requests/priority", "Lists priority intelligence requirements."],
100
+ ["POST", "/accounts/{account_id}/cloudforce-one/requests/{request_id}/asset", "Lists request assets."],
101
+ ["POST", "/accounts/{account_id}/cloudforce-one/requests/{request_id}/message", "Lists request messages."],
102
+ [
103
+ "POST",
104
+ "/accounts/{account_id}/cloudforce-one/v2/brand-protection/takedown-notices/lookup",
105
+ "Looks up takedown notices.",
106
+ ],
107
+ ["POST", "/accounts/{account_id}/cloudforce-one/v2/collections/{collection_id}/search", "Searches collection items."],
108
+ ["POST", "/accounts/{account_id}/cloudforce-one/v2/priority-intelligence/quota", "Reads the PIR quota."],
109
+ ["POST", "/accounts/{account_id}/cloudforce-one/rules/validate", "Validates a rule; stores nothing."],
110
+ ["POST", "/accounts/{account_id}/cloudforce-one/rules/structured/validate", "Validates a rule; stores nothing."],
111
+ ["POST", "/accounts/{account_id}/access/custom_pages/validate", "Validates a page template; stores nothing."],
112
+ ["POST", "/accounts/{account_id}/dlp/patterns/validate", "Validates a regex; stores nothing."],
113
+ ["POST", "/accounts/{account_id}/pipelines/v1/validate_sql", "Validates SQL; stores nothing."],
114
+ ["POST", "/accounts/{account_id}/logpush/validate/origin", "Validates logpull options; stores nothing."],
115
+ ["POST", "/zones/{zone_id}/logpush/validate/origin", "Validates logpull options; stores nothing."],
116
+ ["POST", "/accounts/{account_id}/logpush/transformers/preview", "Stateless transformer preview."],
117
+ ["POST", "/accounts/{account_id}/registrar/domain-check", "Domain availability check."],
118
+ ["POST", "/accounts/{account_id}/registrar/domain-transfer-check", "Transfer eligibility check."],
119
+ ["POST", "/accounts/{account_id}/registrar-sandbox/domain-check", "Domain availability check."],
120
+ ["POST", "/accounts/{account_id}/pay-per-crawl/zones_can_be_enabled/query", "Reads a zone setting."],
121
+ ["POST", "/accounts/{account_id}/ai-gateway/billing/topup/eligibility", "Eligibility check; starts nothing."],
122
+ ["POST", "/accounts/{account_id}/ai-gateway/billing/topup/status", "Reads a payment status."],
123
+ ["POST", "/accounts/{account_id}/request-tracer/trace", "Simulated trace; sends no traffic."],
124
+ ["POST", "/accounts/{account_id}/magic/cloud/resources/policy-preview", "Evaluates a policy against the catalog."],
125
+ ["POST", "/accounts/{account_id}/magic/cloud/onramps/{onramp_id}/export", "Renders Terraform; changes nothing."],
126
+ ["POST", "/zones/{zone_id}/email/sending/subdomains/preview", "Previews DNS records; stores nothing."],
127
+ ["POST", "/zones/{zone_id}/token_validation/rules/preview", "Previews a rule; stores nothing."],
128
+ ];
129
+ /**
130
+ * Operations `cloudflare_api_upload` sends, by method and template: Workers
131
+ * scripts and versions (including Workers for Platforms), KV values, R2
132
+ * objects, Pages deployments, Images, Stream, DNS zone-file import, and
133
+ * Snippets. Other multipart operations are refused rather than guessed at.
134
+ */
135
+ export const UPLOADS = [
136
+ ["PUT", "/accounts/{account_id}/workers/scripts/{script_name}"],
137
+ ["PUT", "/accounts/{account_id}/workers/scripts/{script_name}/content"],
138
+ ["PATCH", "/accounts/{account_id}/workers/scripts/{script_name}/settings"],
139
+ ["POST", "/accounts/{account_id}/workers/scripts/{script_name}/versions"],
140
+ ["PUT", "/accounts/{account_id}/workers/dispatch/namespaces/{dispatch_namespace}/scripts/{script_name}"],
141
+ ["PUT", "/accounts/{account_id}/workers/dispatch/namespaces/{dispatch_namespace}/scripts/{script_name}/content"],
142
+ ["PATCH", "/accounts/{account_id}/workers/dispatch/namespaces/{dispatch_namespace}/scripts/{script_name}/settings"],
143
+ ["PUT", "/accounts/{account_id}/workers/services/{service_name}/environments/{environment_name}/content"],
144
+ ["PUT", "/accounts/{account_id}/storage/kv/namespaces/{namespace_id}/values/{key_name}"],
145
+ ["PUT", "/accounts/{account_id}/r2/buckets/{bucket_name}/objects/{object_key}"],
146
+ ["POST", "/accounts/{account_id}/pages/projects/{project_name}/deployments"],
147
+ ["POST", "/accounts/{account_id}/images/v1"],
148
+ ["POST", "/accounts/{account_id}/stream"],
149
+ ["PUT", "/accounts/{account_id}/stream/{identifier}/captions/{language}"],
150
+ ["POST", "/zones/{zone_id}/dns_records/import"],
151
+ ["PUT", "/zones/{zone_id}/snippets/{snippet_name}"],
152
+ ];
153
+ /**
154
+ * Operations with no account or zone in their path that a pinned connector
155
+ * still admits, each holding to the pin some other way: the identity reads
156
+ * name only the credential's own user, the three lists are filtered to the
157
+ * pin, zone creation must name a pinned account, and the rest is public data.
158
+ * Every other unscoped operation is refused under a pin (default deny).
159
+ */
160
+ export const PIN_SAFE_UNSCOPED = new Set([
161
+ "GET /user",
162
+ "GET /user/tokens/verify",
163
+ "GET /accounts",
164
+ "GET /zones",
165
+ "POST /zones",
166
+ "GET /memberships",
167
+ "GET /ips",
168
+ // CWS category definitions and filter-builder schema are global catalogs,
169
+ // not account policy, credential, or deployment records.
170
+ "GET /cws/categories/apis",
171
+ "GET /cws/categories/applications",
172
+ "GET /cws/categories/applications/{application_key}/versions",
173
+ "GET /cws/categories/applications/{application_key}/versions/latest",
174
+ "GET /cws/categories/applications/{application_key}/versions/{version_tag}",
175
+ "GET /cws/categories/operations",
176
+ "GET /cws/filter-schema",
177
+ "POST /graphql",
178
+ ]);
179
+ /** Why each family of unscoped operations is refused under a pin; `test` checks every one is covered. */
180
+ export const PIN_REFUSED_UNSCOPED = {
181
+ accounts: "creates or moves accounts outside the pin",
182
+ analytics: "queries datasets across every account; use POST /accounts/{account_tag}/analytics/sql",
183
+ api: "is an internal health route",
184
+ billing: "is user-level billing",
185
+ certificates: "manages Origin CA certificates across every zone the user holds",
186
+ internal: "is an internal route",
187
+ live: "is an internal health route",
188
+ memberships: "changes or reads one of the user's memberships, which may be another account's",
189
+ oauth: "lists user-level OAuth scopes",
190
+ organizations: "spans several accounts",
191
+ pages: "uses a Pages upload token, not this connector's credential",
192
+ produce: "writes to a pipeline the path does not scope",
193
+ radar: "is not a GET",
194
+ ready: "is an internal health route",
195
+ "signed-url": "is an internal test route",
196
+ subscriptions: "reads or acknowledges a stream the path does not scope",
197
+ tenants: "spans several accounts",
198
+ user: "acts on user-level settings shared by every account",
199
+ workers: "triggers a deploy hook the path does not scope",
200
+ zones: "is not a list or a create",
201
+ };
202
+ /** Request headers the generic tools accept; both are R2's. */
203
+ export const HEADERS = {
204
+ "cf-r2-jurisdiction": "R2 only: default, eu, us, fedramp, or fedramp-high, for a bucket outside the default jurisdiction.",
205
+ "cf-r2-storage-class": "R2 only: Standard or InfrequentAccess.",
206
+ };
207
+ const HEADER_VALUES = {
208
+ "cf-r2-jurisdiction": ["default", "eu", "us", "fedramp", "fedramp-high"],
209
+ "cf-r2-storage-class": ["Standard", "InfrequentAccess"],
210
+ };
211
+ /** Path parameters whose values may carry an encoded "/": R2 object keys and KV key names. */
212
+ const SLASH_PARAMS = ["object_key", "key_name"];
213
+ export function record(value) {
214
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
215
+ }
216
+ export function text(value) {
217
+ return typeof value === "string" && value.trim() !== "" ? value : undefined;
218
+ }
219
+ export function invalid(message) {
220
+ throw new ConnectorCallError("invalid_args", message);
221
+ }
222
+ function errorsOf(body) {
223
+ const errors = record(body)["errors"];
224
+ return Array.isArray(errors)
225
+ ? errors.filter((entry) => typeof entry === "object" && entry !== null)
226
+ : [];
227
+ }
228
+ /** Flatten Cloudflare's error array (and any nested chain) into one line. */
229
+ export function describeErrors(errors) {
230
+ const parts = [];
231
+ const walk = (list) => {
232
+ for (const entry of list) {
233
+ if (typeof entry !== "object" || entry === null) {
234
+ parts.push("Unknown error");
235
+ continue;
236
+ }
237
+ const code = typeof entry.code === "number" ? entry.code : undefined;
238
+ const message = typeof entry.message === "string" ? entry.message.slice(0, 500) : "Unknown error";
239
+ parts.push(code === undefined ? message : `${code}: ${message}`);
240
+ if (Array.isArray(entry.error_chain))
241
+ walk(entry.error_chain);
242
+ }
243
+ };
244
+ walk(errors.slice(0, 10));
245
+ return parts.length > 0 ? parts.join("; ") : "Cloudflare reported no detail.";
246
+ }
247
+ function errorCodes(errors) {
248
+ const codes = new Set();
249
+ const walk = (list) => {
250
+ for (const entry of list) {
251
+ if (typeof entry !== "object" || entry === null)
252
+ continue;
253
+ if (typeof entry.code === "number")
254
+ codes.add(entry.code);
255
+ if (Array.isArray(entry.error_chain))
256
+ walk(entry.error_chain);
257
+ }
258
+ };
259
+ walk(errors);
260
+ return codes;
261
+ }
262
+ /**
263
+ * Credential-shaped Cloudflare error codes that are *not* already implied by a
264
+ * 401 or 403: a missing or malformed `Authorization` header, and the legacy
265
+ * key/email headers. These arrive on HTTP 400, so status alone would misfile
266
+ * them as an argument problem the agent could repair.
267
+ *
268
+ * Deliberately excludes 10000. Cloudflare returns 10000 for "Authentication
269
+ * error" but has also been observed reusing it as a generic validation code,
270
+ * so routing on it would risk telling an agent its token was broken when its
271
+ * arguments were. Genuine 10000 auth failures arrive with 401 or 403.
272
+ *
273
+ * Cloudflare publishes no table mapping codes to causes; these six come from
274
+ * community reports and probing. Prefer `verify_credential` when a diagnosis
275
+ * matters. Cloudflare also rate-limits authentication failures separately
276
+ * (429 with code 10502), one more reason to verify once rather than retry.
277
+ */
278
+ const AUTH_ERROR_CODES = new Set([1001, 6003, 6111, 9103, 9106, 9107]);
279
+ /**
280
+ * The reviewed value-safety table over the pinned index: refusals, redaction
281
+ * of every successful body, and which operations withhold error text (any
282
+ * reviewed operation, or one whose request accepts a credential, header, or
283
+ * environment value, since a vendor error may echo what was submitted).
284
+ */
285
+ export const SAFETY = valueSafety(CLOUDFLARE_VALUE_SAFETY, () => cloudflareIndex);
286
+ const rejected = () => ({
287
+ code: "invalid_args",
288
+ words: (detail) => detail,
289
+ });
290
+ const authFailure = () => ({
291
+ code: "auth_required",
292
+ words: (detail) => `${detail} The configured credential was rejected. An operator must check or replace it in this connection; call verify_credential to confirm.`,
293
+ });
294
+ export const cloudflareErrors = {
295
+ meanings: { unknownId: "not_found", provesAbsence: true, conflicts: [] },
296
+ numericFacts: {
297
+ retryAfterMs: {
298
+ header: "retry-after",
299
+ format: "retry-after",
300
+ review: "HTTP Retry-After delay for failure recovery.",
301
+ },
302
+ },
303
+ provider: "Cloudflare",
304
+ facts: (body) => ({ codes: [...errorCodes(errorsOf(body))] }),
305
+ factVocabulary: { codes: [...AUTH_ERROR_CODES] },
306
+ headers: ["cf-ray"],
307
+ detail: ({ status, facts, headers, withheld, body }) => {
308
+ const codes = facts.codes;
309
+ const described = withheld
310
+ ? `${codes.length ? `Cloudflare error code ${codes.join(", ")}.` : "Cloudflare reported an error."} Its text is withheld because this operation handles credentials.`
311
+ : describeErrors(errorsOf(withholdEmbeddedUrlsIn(body)));
312
+ const detail = errorsOf(body).length || withheld
313
+ ? `Cloudflare returned HTTP ${status}: ${described.trim()}`
314
+ : `Cloudflare returned HTTP ${status}.`;
315
+ return `${detail}${/[.!?]$/.test(detail) ? "" : "."}${headers["cf-ray"] ? ` Ray ${headers["cf-ray"]}.` : ""}`;
316
+ },
317
+ classify: [
318
+ ({ status, facts }) => status !== 429 && facts.codes.some((code) => AUTH_ERROR_CODES.has(code)) ? authFailure() : undefined,
319
+ ],
320
+ statuses: {
321
+ 429: ({ retryAfterMs }) => ({
322
+ code: "rate_limited",
323
+ retryAfterMs: retryAfterMs ?? 300_000,
324
+ words: (detail) => `${detail} The documented limit is 1,200 requests per five minutes per user, counted across the dashboard and every token.`,
325
+ }),
326
+ 401: authFailure,
327
+ 403: () => ({
328
+ code: "provider_permission_denied",
329
+ words: (detail) => `${detail} Verify the credential first; if it is valid, the token or user lacks this permission or resource.`,
330
+ }),
331
+ 405: rejected,
332
+ 415: rejected,
333
+ 404: () => ({
334
+ code: "not_found",
335
+ words: (detail) => `${detail} Confirm the id with list_zones, list_accounts, or a list read.`,
336
+ }),
337
+ },
338
+ unreadable: "Cloudflare returned malformed JSON.",
339
+ };
340
+ function isEnvelope(body) {
341
+ const value = record(body);
342
+ return typeof value["success"] === "boolean" && ("result" in value || "errors" in value);
343
+ }
344
+ // --- Pins --------------------------------------------------------------------
345
+ const ACCOUNT_KEYS = new Set(["account_id", "accountId", "account.id", "account_tag", "accountTag"]);
346
+ const ZONE_KEYS = new Set(["zone_id", "zoneId", "zone.id", "zone_tag", "zoneTag"]);
347
+ /** Every account and zone id a call names in its path, query, or top-level body. */
348
+ function scopeIds(call) {
349
+ const template = call.op.path.slice(1).split("/");
350
+ const accounts = [];
351
+ const zones = [];
352
+ template.forEach((part, index) => {
353
+ if (!part.startsWith("{"))
354
+ return;
355
+ const value = call.params[part.slice(1, -1)];
356
+ if (value === undefined)
357
+ return;
358
+ if (template[index - 1] === "accounts")
359
+ accounts.push(decodeURIComponent(value));
360
+ if (template[index - 1] === "zones")
361
+ zones.push(decodeURIComponent(value));
362
+ });
363
+ const scan = (source) => {
364
+ for (const [key, value] of Object.entries(source)) {
365
+ const values = Array.isArray(value) ? value : [value];
366
+ for (const item of values) {
367
+ if (typeof item !== "string")
368
+ continue;
369
+ if (ACCOUNT_KEYS.has(key))
370
+ accounts.push(item);
371
+ if (ZONE_KEYS.has(key))
372
+ zones.push(item);
373
+ }
374
+ }
375
+ };
376
+ scan(record(call.query));
377
+ const body = record(call.body);
378
+ scan(body);
379
+ const account = text(record(body["account"])["id"]);
380
+ if (account)
381
+ accounts.push(account);
382
+ const zone = text(record(body["zone"])["id"]);
383
+ if (zone)
384
+ zones.push(zone);
385
+ return { accounts, zones, template };
386
+ }
387
+ // --- Framing -----------------------------------------------------------------
388
+ /** A pre-framed upload body, carried through `RestCall.body` to `encode`. */
389
+ export class RawUpload {
390
+ body;
391
+ contentType;
392
+ constructor(body, contentType) {
393
+ this.body = body;
394
+ this.contentType = contentType;
395
+ }
396
+ }
397
+ export function bytesFromBase64(value, field) {
398
+ try {
399
+ const binary = atob(value);
400
+ return Uint8Array.from(binary, (character) => character.charCodeAt(0));
401
+ }
402
+ catch {
403
+ return invalid(`${field} must be valid base64.`);
404
+ }
405
+ }
406
+ function queryOf(call) {
407
+ const query = {};
408
+ const scalar = (item) => typeof item === "string" || typeof item === "number" || typeof item === "boolean";
409
+ for (const [name, value] of Object.entries(call.query)) {
410
+ if (value === undefined)
411
+ continue;
412
+ if (scalar(value) || (Array.isArray(value) && value.every(scalar)))
413
+ query[name] = value;
414
+ else
415
+ invalid(`Cloudflare query parameter ${name} takes a string, number, boolean, or a list of them.`);
416
+ }
417
+ return query;
418
+ }
419
+ // --- The vendor ----------------------------------------------------------------
420
+ class CloudflareIndex extends OperationIndex {
421
+ contract(...args) {
422
+ const contract = super.contract(...args);
423
+ const exclusive = EXCLUSIVE_PATH_IDS[`${contract.method} ${contract.path}`] ?? [];
424
+ return {
425
+ ...contract,
426
+ parameters: contract.parameters.map((param) => param.in === "path" && exclusive.includes(param.name)
427
+ ? { ...param, schema: { ...param.schema, minimum: 1 } }
428
+ : param),
429
+ };
430
+ }
431
+ resolve(method, path) {
432
+ const match = super.resolve(method, path);
433
+ for (const name of EXCLUSIVE_PATH_IDS[`${match.op.method} ${match.op.path}`] ?? []) {
434
+ if (Number(decodeURIComponent(match.params[name])) < 1) {
435
+ throw new ConnectorCallError("invalid_args", `Not sent: Cloudflare path parameter ${name} must be at least 1. Read cloudflare_api_details for the pinned contract.`, {
436
+ validation: { issues: [{ path: `/path/${name}`, code: "minimum", expected: "at least 1" }] },
437
+ });
438
+ }
439
+ }
440
+ return match;
441
+ }
442
+ }
443
+ export const cloudflareIndex = new CloudflareIndex(cloudflareDiscovery(openapi), {
444
+ vendor: "cloudflare",
445
+ title: "Cloudflare",
446
+ slashParams: SLASH_PARAMS,
447
+ });
448
+ export const getUser = get("getUser", "/user", { success: Schema.Unknown });
449
+ export const verifyToken = get("verifyToken", "/user/tokens/verify", { success: Schema.Unknown });
450
+ export const verifyAccountToken = get("verifyAccountToken", "/accounts/:account_id/tokens/verify", {
451
+ params: { account_id: Schema.String },
452
+ success: Schema.Unknown,
453
+ });
454
+ const PageQuery = { page: Schema.optionalKey(Integer), per_page: Schema.optionalKey(Integer) };
455
+ export const listAccounts = get("listAccounts", "/accounts", {
456
+ query: { ...PageQuery, name: Schema.optionalKey(Schema.String) },
457
+ success: Schema.Unknown,
458
+ });
459
+ export const listZones = get("listZones", "/zones", {
460
+ query: {
461
+ ...PageQuery,
462
+ name: Schema.optionalKey(Schema.String),
463
+ status: Schema.optionalKey(Schema.String),
464
+ "account.id": Schema.optionalKey(Schema.String),
465
+ },
466
+ success: Schema.Unknown,
467
+ });
468
+ export const graphqlQuery = post("graphqlQuery", "/graphql", {
469
+ payload: Schema.Struct({
470
+ query: Schema.String,
471
+ variables: Schema.optionalKey(Schema.Record(Schema.String, Schema.Unknown)),
472
+ operationName: Schema.optionalKey(Schema.String),
473
+ }),
474
+ success: Schema.Struct({
475
+ data: Schema.optionalKey(Schema.Unknown),
476
+ errors: Schema.optionalKey(Schema.Union([Schema.Array(Schema.Unknown), Schema.Null])),
477
+ }),
478
+ });
479
+ const typed = [getUser, verifyToken, verifyAccountToken, listAccounts, listZones];
480
+ const indexed = indexedVendor("cloudflare", cloudflareIndex, typed);
481
+ const OBJECT_DOWNLOADS = new Set([
482
+ "/accounts/{account_id}/r2/buckets/{bucket_name}/objects/{object_key}",
483
+ "/accounts/{account_id}/storage/kv/namespaces/{namespace_id}/values/{key_name}",
484
+ ]);
485
+ /** Reviewed repairs for the pin's script label and caller-controlled object media. */
486
+ function cloudflareResponseMedia(status, operation, contentType = "") {
487
+ if (status < 200 || status > 299 || operation.flags?.["ownership"] === true)
488
+ return undefined;
489
+ const path = operation.template.replace(/:([^/]+)/g, "{$1}");
490
+ const type = contentType.split(";", 1)[0].trim().toLowerCase();
491
+ const json = /^(?:application\/json|[^/]+\/[^/]+\+json)$/.test(type);
492
+ if (operation.method === "GET" && OBJECT_DOWNLOADS.has(path))
493
+ return json ? undefined : "binary";
494
+ if (RESPONSE_MEDIA[`${operation.method} ${path}`]?.includes("string"))
495
+ return json ? undefined : type.startsWith("text/") ? "text" : "binary";
496
+ return responseMedia(RESPONSE_MEDIA, status, operation, contentType);
497
+ }
498
+ export const CloudflareApi = {
499
+ ...indexed,
500
+ api: declareVendor("cloudflare", ...typed, graphqlQuery).api,
501
+ indexed: {
502
+ operations: [...indexed.indexed.operations, "POST /graphql"].sort(),
503
+ api: () => {
504
+ const rest = Object.values(indexed.indexed.api().groups["operations"].endpoints);
505
+ return declareVendor("cloudflare", ...rest, graphqlQuery).api;
506
+ },
507
+ },
508
+ textBodies: {
509
+ review: "Pinned text/XML/CSV/SSE/NDJSON operations and the three reviewed raw Worker-script aliases accept text. Ownership lookups remain JSON-only (#1000).",
510
+ forResponse: (status, operation, type = "") => cloudflareResponseMedia(status, operation, type) === "text" ? "text" : undefined,
511
+ },
512
+ binaryBodies: {
513
+ review: "Pinned binary operations, three raw Worker-script aliases, and GET KV/R2 objects (any stored media) retain bounded bytes with sent-secret checks. Ownership lookups remain JSON-only (#1000).",
514
+ forResponse: (status, operation, type) => cloudflareResponseMedia(status, operation, type) === "binary",
515
+ },
516
+ };
517
+ const canonical = (operation) => operation.template.replace(/\/:([^/]+)/g, "/{$1}");
518
+ export const safety = () => {
519
+ const reviewed = restSafety(SAFETY, cloudflareIndex);
520
+ return {
521
+ reviewed: {
522
+ ...reviewed,
523
+ resolve: (method, path) => method === "POST" && path === "/graphql" ? { method, template: path } : reviewed.resolve(method, path),
524
+ redact: (body, operation) => isEnvelope(body)
525
+ ? {
526
+ ...record(SAFETY.redact(body, operation.method, canonical(operation))),
527
+ result: SAFETY.redact(body["result"], operation.method, canonical(operation)),
528
+ }
529
+ : SAFETY.redact(body, operation.method, canonical(operation)),
530
+ withholdsErrors: (operation) => operation.method === "POST" && operation.template === "/graphql" ? false : reviewed.withholdsErrors(operation),
531
+ },
532
+ sensitiveFields: { names: [], choice: "drop" },
533
+ };
534
+ };
535
+ export const result = (raw, answer) => Effect.suspend(() => {
536
+ let body = raw;
537
+ if (answer.bodyFormat === "text" && typeof body === "string") {
538
+ const type = (answer.headers.get("content-type") ?? "").toLowerCase();
539
+ body = /\b(?:x-ndjson|jsonl|stream\+json)\b/.test(type)
540
+ ? body
541
+ .split("\n")
542
+ .filter((line) => line.trim() !== "")
543
+ .map((line) => {
544
+ try {
545
+ return JSON.parse(line);
546
+ }
547
+ catch {
548
+ return { line };
549
+ }
550
+ })
551
+ : body === ""
552
+ ? null
553
+ : body;
554
+ }
555
+ if (answer.operation.flags?.["ownership"] !== true && isEnvelope(body) && body["success"] === false)
556
+ return Effect.fail(new StatusFailure({
557
+ status: answer.status === 200 ? 400 : answer.status,
558
+ headers: answer.headers,
559
+ body,
560
+ operation: answer.operation,
561
+ }));
562
+ return Effect.succeed(body ?? null);
563
+ });
564
+ export const authenticate = (options) => Effect.gen(function* () {
565
+ const values = yield* (yield* Credentials).values.pipe(Effect.mapError((error) => new Refusal({ error })));
566
+ if (options.auth.type === "globalApiKey") {
567
+ const email = values?.["email"]?.trim(), apiKey = values?.["apiKey"]?.trim();
568
+ if (!email || !apiKey)
569
+ return yield* Effect.fail(new Refusal({
570
+ error: new ConnectorCallError("auth_required", "No Cloudflare Global API Key and account email are configured for this connector. An operator must add both in this connection in the operator UI."),
571
+ }));
572
+ return { "X-Auth-Email": email, "X-Auth-Key": apiKey };
573
+ }
574
+ const token = values?.["value"]?.trim();
575
+ if (!token)
576
+ return yield* Effect.fail(new Refusal({
577
+ error: new ConnectorCallError("auth_required", "No Cloudflare API token is configured for this connector. An operator must add one in this connection in the operator UI."),
578
+ }));
579
+ return { Authorization: `Bearer ${token}` };
580
+ });
581
+ const attempt = (run) => Effect.tryPromise({ try: run, catch: (error) => error }).pipe(Effect.catch((error) => error instanceof ConnectorCallError ? Effect.fail(new Refusal({ error })) : Effect.die(error)));
582
+ const failure = (error) => error instanceof ConnectorCallError ? Effect.fail(new Refusal({ error })) : Effect.die(error);
583
+ const runtimes = new WeakMap();
584
+ export function cloudflareRuntime(connection, options) {
585
+ let runtime = runtimes.get(connection);
586
+ if (!runtime) {
587
+ runtime = makeRuntime(connection, options);
588
+ runtimes.set(connection, runtime);
589
+ }
590
+ return runtime;
591
+ }
592
+ function makeRuntime(connection, options) {
593
+ const operations = cloudflareIndex;
594
+ const { auth, pin } = options;
595
+ const pinnedAccounts = pin?.accountIds ? new Set(pin.accountIds) : undefined;
596
+ const pinnedZones = pin?.zoneIds ? new Set(pin.zoneIds) : undefined;
597
+ // --- Pin enforcement (default deny) ---------------------------------------
598
+ /** Zone id → owning account id, learned from a GET /zones/{id} read or a zone list. */
599
+ const owners = new Map();
600
+ /** Times of internal ownership reads, bounded per rolling window. */
601
+ const lookups = [];
602
+ const lookupKey = {};
603
+ const pinRefusal = (what) => {
604
+ throw new ConnectorCallError("provider_permission_denied", `${what} is outside this connector's pin. The deployment pins it to ${[
605
+ pinnedAccounts ? `accounts ${[...pinnedAccounts].join(", ")}` : "",
606
+ pinnedZones ? `zones ${[...pinnedZones].join(", ")}` : "",
607
+ ]
608
+ .filter(Boolean)
609
+ .join(" and ")}; a different account or zone needs its own connector.`, { retryable: false });
610
+ };
611
+ const assertAccount = (account) => {
612
+ if (!pinnedAccounts?.has(account))
613
+ pinRefusal(`Account ${account}`);
614
+ };
615
+ /**
616
+ * Charge one internal ownership read: at most `MAX_LOOKUPS_PER_CALL` per
617
+ * tool call and `MAX_LOOKUPS_PER_WINDOW` per five minutes per connector, so
618
+ * one call cannot spend the user's shared request limit on pin checks.
619
+ */
620
+ const chargeLookup = (ctx) => {
621
+ const state = callMemo(ctx, lookupKey, () => ({ used: 0 }));
622
+ const used = state.used;
623
+ if (used >= MAX_LOOKUPS_PER_CALL) {
624
+ throw new ConnectorCallError("invalid_args", `This call names more than ${MAX_LOOKUPS_PER_CALL} zones whose account the pin must check. Name fewer zones per call, or list them in pin.zoneIds.`);
625
+ }
626
+ const now = Date.now();
627
+ while (lookups.length > 0 && lookups[0] <= now - LOOKUP_WINDOW_MS)
628
+ lookups.shift();
629
+ if (lookups.length >= MAX_LOOKUPS_PER_WINDOW) {
630
+ throw new ConnectorCallError("rate_limited", "This connector's zone-ownership checks reached their per-window budget.", { retryAfterMs: Math.max(1_000, lookups[0] + LOOKUP_WINDOW_MS - now) });
631
+ }
632
+ lookups.push(now);
633
+ state.used = used + 1;
634
+ return state.used;
635
+ };
636
+ const assertZone = (zone) => Effect.gen(function* () {
637
+ if (pinnedZones?.has(zone))
638
+ return;
639
+ yield* checked(() => {
640
+ if (!pinnedAccounts)
641
+ pinRefusal(`Zone ${zone}`);
642
+ });
643
+ let owner = owners.get(zone);
644
+ if (owner === undefined) {
645
+ const view = yield* Effect.serviceOption(ContextView);
646
+ const ctx = Option.isSome(view) ? view.value : undefined;
647
+ const lookup = yield* checked(() => {
648
+ if (!ctx)
649
+ throw new Error("Ownership lookup has no call context.");
650
+ return chargeLookup(ctx);
651
+ });
652
+ yield* (yield* Budget).charge.pipe(Effect.catch(failure));
653
+ const reply = yield* connection
654
+ .sendPath({ method: "GET", path: `/zones/${encodeURIComponent(zone)}`, template: "/zones/{zone_id}" }, { effect: "read", step: `lookup${lookup}`, flags: { ownership: true } })
655
+ .pipe(Effect.catch((error) => {
656
+ if (error._tag === "UnreadableBody")
657
+ return Effect.succeed({ status: 200, data: undefined });
658
+ if (error._tag !== "ResponseTooLarge")
659
+ return Effect.fail(error);
660
+ return Dispatches.use((dispatches) => {
661
+ const own = dispatches.filter((dispatch) => dispatch.operation?.flags?.["ownership"] === true).at(-1);
662
+ const length = Number(own?.headers?.get("content-length"));
663
+ // Legacy pin reads swallowed a streamed-body decode failure, but not a declared ceiling.
664
+ return own?.status === 200 && !(length > MAX_RESPONSE_BYTES)
665
+ ? Effect.succeed({ status: 200, data: undefined })
666
+ : Effect.fail(error);
667
+ });
668
+ }));
669
+ owner = text(record(record(record(reply.data)["result"])["account"])["id"]) ?? "";
670
+ if (owner)
671
+ owners.set(zone, owner);
672
+ }
673
+ yield* checked(() => {
674
+ if (!owner || !pinnedAccounts?.has(owner))
675
+ pinRefusal(`Zone ${zone}`);
676
+ });
677
+ });
678
+ const admit = (call) => Effect.gen(function* () {
679
+ if (call.method !== "GET" &&
680
+ call.method !== "HEAD" &&
681
+ !READ_POSTS.some(([method, path]) => method === call.method && path === call.op.path)) {
682
+ yield* checked(() => refuseWithheldLinks([call.query, call.path], "Cloudflare"));
683
+ if (call.body instanceof RawUpload)
684
+ yield* attempt(() => refuseWithheldUpload(call.body.body, "Cloudflare"));
685
+ else
686
+ yield* checked(() => refuseWithheldLinks(call.body, "Cloudflare"));
687
+ if (call.method === "PUT" &&
688
+ call.op.path === "/accounts/{account_id}/storage/kv/namespaces/{namespace_id}/bulk" &&
689
+ Array.isArray(call.body)) {
690
+ for (const entry of call.body)
691
+ if (record(entry)["base64"] === true)
692
+ yield* attempt(() => refuseWithheldBase64(record(entry)["value"], "Cloudflare", 25 * 1024 * 1024));
693
+ }
694
+ }
695
+ if (!pin)
696
+ return;
697
+ const { accounts, zones, template } = scopeIds(call);
698
+ yield* checked(() => {
699
+ const scoped = template.some((part, index) => part.startsWith("{") && (template[index - 1] === "accounts" || template[index - 1] === "zones"));
700
+ if (!scoped) {
701
+ const key = `${call.op.method} ${call.op.path}`;
702
+ if (!PIN_SAFE_UNSCOPED.has(key) && !(call.op.method === "GET" && template[0] === "radar")) {
703
+ const reason = PIN_REFUSED_UNSCOPED[template[0] ?? ""] ?? "names no account or zone this pin can verify";
704
+ throw new ConnectorCallError("provider_permission_denied", `${key} ${reason}, so a pinned connector refuses it.`, { retryable: false });
705
+ }
706
+ if (key === "POST /zones") {
707
+ const account = text(record(record(call.body)["account"])["id"]);
708
+ if (!account)
709
+ invalid("A pinned connector creates a zone only in a pinned account: set body.account.id.");
710
+ assertAccount(account);
711
+ }
712
+ }
713
+ for (const account of accounts)
714
+ assertAccount(account);
715
+ });
716
+ for (const zone of zones)
717
+ yield* assertZone(zone);
718
+ });
719
+ /** A pinned connector's account and zone lists show only what the pin admits. */
720
+ const filterListed = (op, result) => {
721
+ if (!pin || op.method !== "GET" || !Array.isArray(result))
722
+ return result;
723
+ if (op.path === "/accounts")
724
+ return result.filter((item) => pinnedAccounts?.has(String(record(item)["id"])));
725
+ if (op.path === "/memberships") {
726
+ return result.filter((item) => pinnedAccounts?.has(String(record(record(item)["account"])["id"])));
727
+ }
728
+ if (op.path === "/zones") {
729
+ return result.filter((item) => {
730
+ const zone = record(item);
731
+ const id = String(zone["id"]);
732
+ const account = String(record(zone["account"])["id"]);
733
+ if (account && id)
734
+ owners.set(id, account);
735
+ return pinnedZones?.has(id) || pinnedAccounts?.has(account);
736
+ });
737
+ }
738
+ return result;
739
+ };
740
+ // --- Framing ---------------------------------------------------------------
741
+ const encode = (call) => {
742
+ const query = queryOf(call);
743
+ const headers = { ...call.headers };
744
+ if (call.body instanceof RawUpload) {
745
+ if (call.body.contentType)
746
+ headers["Content-Type"] = call.body.contentType;
747
+ return { query, headers, rawBody: call.body.body };
748
+ }
749
+ if (call.body === undefined)
750
+ return { query, headers };
751
+ const type = (operations.bodyType(call.op) ?? "application/json").toLowerCase();
752
+ if (/^text\/|javascript/.test(type)) {
753
+ if (typeof call.body !== "string")
754
+ invalid(`${call.op.method} ${call.op.path} takes a ${type} body: send it as a string.`);
755
+ return { query, headers: { ...headers, "Content-Type": type }, rawBody: call.body };
756
+ }
757
+ if (/ndjson|jsonl/.test(type)) {
758
+ const lines = typeof call.body === "string"
759
+ ? call.body
760
+ : Array.isArray(call.body)
761
+ ? call.body.map((item) => JSON.stringify(item)).join("\n")
762
+ : invalid(`${call.op.method} ${call.op.path} takes newline-delimited JSON: send a list of objects.`);
763
+ return { query, headers: { ...headers, "Content-Type": type }, rawBody: lines };
764
+ }
765
+ if (type === "application/x-www-form-urlencoded") {
766
+ const form = new URLSearchParams();
767
+ for (const [name, value] of Object.entries(record(call.body))) {
768
+ if (value !== undefined && value !== null)
769
+ form.append(name, typeof value === "string" ? value : JSON.stringify(value));
770
+ }
771
+ return { query, headers: { ...headers, "Content-Type": type }, rawBody: form.toString() };
772
+ }
773
+ if (typeof call.body === "string")
774
+ invalid(`${call.op.method} ${call.op.path} takes a JSON body, not a string.`);
775
+ if (type.endsWith("+json"))
776
+ return { query, headers: { ...headers, "Content-Type": type }, rawBody: JSON.stringify(call.body) };
777
+ return { query, headers, body: call.body };
778
+ };
779
+ /** Fill configured default ids into `{account_id}` and `{zone_id}` placeholders. */
780
+ const fillDefaults = (path) => {
781
+ if (!options.accountId && !options.zoneId)
782
+ return path;
783
+ const parts = path.split("/");
784
+ return parts
785
+ .map((part, position) => {
786
+ const previous = parts[position - 1];
787
+ if (options.accountId && previous === "accounts" && /^\{(account_id|account_tag|accountId)\}$/.test(part)) {
788
+ return encodeURIComponent(options.accountId);
789
+ }
790
+ if (options.zoneId && previous === "zones" && /^\{(zone_id|zone_identifier|zoneId)\}$/.test(part)) {
791
+ return encodeURIComponent(options.zoneId);
792
+ }
793
+ return part;
794
+ })
795
+ .join("/");
796
+ };
797
+ const scope = (call) => {
798
+ // NDJSON operations take a list of records; frame it before validation,
799
+ // so the index checks the string the vendor receives.
800
+ const type = (operations.bodyType(call.op) ?? "").toLowerCase();
801
+ if (/ndjson|jsonl/.test(type)) {
802
+ let rows;
803
+ if (Array.isArray(call.body))
804
+ rows = call.body;
805
+ else if (typeof call.body === "string") {
806
+ try {
807
+ rows = call.body
808
+ .split(/\r?\n/)
809
+ .filter((line) => line.trim())
810
+ .map((line) => JSON.parse(line));
811
+ }
812
+ catch {
813
+ invalid(`${call.op.method} ${call.op.path} takes valid newline-delimited JSON objects.`);
814
+ }
815
+ }
816
+ if (rows) {
817
+ if (!rows.every((item) => typeof item === "object" && item !== null && !Array.isArray(item)))
818
+ invalid(`${call.op.method} ${call.op.path} takes newline-delimited JSON: send a list of objects.`);
819
+ for (const row of rows)
820
+ refuseWithheldValues(row, "Cloudflare");
821
+ if (Array.isArray(call.body))
822
+ call = { ...call, body: rows.map((item) => JSON.stringify(item)).join("\n") };
823
+ }
824
+ }
825
+ for (const [name, value] of Object.entries(call.headers ?? {})) {
826
+ if (!/^\/accounts\/\{[^/]+\}\/r2\//.test(call.op.path)) {
827
+ invalid(`${name} applies only to R2 operations under /accounts/{account_id}/r2/.`);
828
+ }
829
+ if (!HEADER_VALUES[name]?.includes(value)) {
830
+ invalid(`${name} must be one of ${HEADER_VALUES[name]?.join(", ") ?? "its documented values"}.`);
831
+ }
832
+ }
833
+ return call;
834
+ };
835
+ /** Argument- and auth-dependent refusals; the table's own run in the generic tools. */
836
+ const refuse = (call) => {
837
+ const { method, op } = call;
838
+ if (method === "GET")
839
+ return undefined;
840
+ if (auth === "globalApiKey" && /^\/(?:user|memberships)(?:\/|$)/.test(op.path)) {
841
+ return "A Global API Key connector never writes user-level settings or memberships: the key is the user's own identity. Make that change in the Cloudflare dashboard.";
842
+ }
843
+ return undefined;
844
+ };
845
+ /** Next page: page numbers from result_info, or a cursor. */
846
+ const page = (body, call) => {
847
+ if (!isEnvelope(body))
848
+ return undefined;
849
+ const info = record(body["result_info"]);
850
+ if (Object.keys(info).length === 0)
851
+ return undefined;
852
+ const rawCursor = text(info["cursor"]) ?? text(record(info["cursors"])["after"]);
853
+ // Never echo or offer a modified cursor: it would not address the next page.
854
+ const cursor = rawCursor === undefined || withholdEmbeddedUrlsIn(rawCursor) === rawCursor ? rawCursor : undefined;
855
+ if (cursor !== undefined || "cursor" in info || "cursors" in info) {
856
+ const accepted = operations.contract(call.op).parameters.map((parameter) => parameter.name);
857
+ const param = accepted.includes("cursor") ? "cursor" : accepted.includes("after") ? "after" : "cursor";
858
+ const truncated = info["is_truncated"];
859
+ const hasMore = cursor !== undefined && truncated !== false && truncated !== "false";
860
+ return { hasMore, ...(hasMore ? { next: cursor, param } : {}) };
861
+ }
862
+ const current = typeof info["page"] === "number" ? info["page"] : undefined;
863
+ if (current === undefined)
864
+ return undefined;
865
+ const perPage = typeof info["per_page"] === "number" ? info["per_page"] : 0;
866
+ const totalPages = typeof info["total_pages"] === "number" ? info["total_pages"] : undefined;
867
+ const totalCount = typeof info["total_count"] === "number" ? info["total_count"] : undefined;
868
+ const count = typeof info["count"] === "number" ? info["count"] : 0;
869
+ const hasMore = totalPages !== undefined
870
+ ? current < totalPages
871
+ : totalCount !== undefined
872
+ ? current * perPage < totalCount
873
+ : perPage > 0 && count >= perPage;
874
+ return { hasMore, ...(hasMore ? { next: String(current + 1), param: "page" } : {}) };
875
+ };
876
+ const checkServer = (server) => server === undefined ? undefined : `This operation is served from ${server}, which this connector does not reach.`;
877
+ const call = (method, path, input = {}) => {
878
+ path = fillDefaults(path);
879
+ const { op, params } = operations.resolve(method, path);
880
+ return scope({
881
+ method,
882
+ path,
883
+ op,
884
+ params,
885
+ query: input.query ?? {},
886
+ body: input.body,
887
+ ...(input.headers ? { headers: input.headers } : {}),
888
+ });
889
+ };
890
+ const send = (call, envelope = false) => checked(() => {
891
+ const refusal = checkServer(call.op.server);
892
+ if (refusal)
893
+ invalid(refusal);
894
+ }).pipe(Effect.andThen(admit(call)), Effect.andThen(checked(() => {
895
+ const framing = call.op.path === "/graphql" ? { body: call.body } : encode(call);
896
+ const query = Object.entries(framing.query ?? {}).flatMap(([name, value]) => (Array.isArray(value) ? value : [value]).map((value) => [name, String(value)]));
897
+ return {
898
+ method: call.method,
899
+ path: call.path,
900
+ template: call.op.path,
901
+ query,
902
+ ...(framing.headers
903
+ ? {
904
+ headers: Object.fromEntries(Object.entries(framing.headers).filter((entry) => entry[1] !== undefined)),
905
+ }
906
+ : {}),
907
+ ...(framing.rawBody !== undefined
908
+ ? { rawBody: framing.rawBody }
909
+ : framing.body !== undefined
910
+ ? { json: JSON.stringify(framing.body) }
911
+ : {}),
912
+ };
913
+ })), Effect.flatMap((request) => connection.sendPath(request)), Effect.flatMap((reply) => checked(() => {
914
+ const body = reply.data;
915
+ const result = isEnvelope(body) ? filterListed(call.op, body["result"]) : body;
916
+ let info = isEnvelope(body) ? withholdEmbeddedUrlsIn(body["result_info"]) : undefined;
917
+ if (envelope &&
918
+ isEnvelope(body) &&
919
+ info !== undefined &&
920
+ result !== body["result"] &&
921
+ Array.isArray(result)) {
922
+ // Validate before dropping estate-wide counts; retain only page-level
923
+ // continuation metadata, never an unfiltered total for the pinned scope.
924
+ cloudflarePage.next(body, { path: call.path, query: call.query, headers: {} });
925
+ const original = record(info);
926
+ const totalPages = original["total_pages"] ??
927
+ (typeof original["total_count"] === "number" &&
928
+ typeof original["per_page"] === "number" &&
929
+ original["per_page"] > 0
930
+ ? Math.ceil(original["total_count"] / original["per_page"])
931
+ : undefined);
932
+ info = {
933
+ page: original["page"],
934
+ per_page: original["per_page"],
935
+ count: result.length,
936
+ total_pages: totalPages,
937
+ };
938
+ }
939
+ const data = isEnvelope(body) && envelope
940
+ ? { ...body, ...(info !== undefined ? { result_info: info } : {}), result }
941
+ : result;
942
+ const next = call.op.path === "/graphql" ? undefined : page(body, call);
943
+ return { status: reply.status, data, ...(next ? { page: next } : {}) };
944
+ })));
945
+ const read = (method, path, input = {}) => checked(() => call(method, path, input)).pipe(Effect.flatMap(send));
946
+ const verify = () => {
947
+ if (auth === "globalApiKey")
948
+ return read("GET", "/user").pipe(Effect.map(({ data }) => {
949
+ const user = record(data);
950
+ return { auth, status: "active", id: user["id"] ?? null, email: user["email"] ?? null, scope: "user" };
951
+ }));
952
+ return read("GET", "/user/tokens/verify").pipe(Effect.map(({ data }) => {
953
+ const token = record(data);
954
+ return {
955
+ auth,
956
+ status: token["status"] ?? null,
957
+ id: token["id"] ?? null,
958
+ expiresOn: token["expires_on"] ?? null,
959
+ scope: "user",
960
+ };
961
+ }), Effect.catch((error) => Effect.gen(function* () {
962
+ const account = options.accountId ?? (pin?.accountIds?.length === 1 ? pin.accountIds[0] : undefined);
963
+ const mapped = toConnectorCallError(error, cloudflareErrors, {
964
+ classification: yield* Classification,
965
+ dispatches: yield* Dispatches,
966
+ withholds: connection.safety.withholdsErrors,
967
+ signal: yield* Signal,
968
+ });
969
+ if (!account || mapped.code !== "auth_required")
970
+ return yield* Effect.fail(error);
971
+ const { data } = yield* read("GET", `/accounts/${encodeURIComponent(account)}/tokens/verify`);
972
+ const token = record(data);
973
+ return {
974
+ auth,
975
+ status: token["status"] ?? null,
976
+ id: token["id"] ?? null,
977
+ expiresOn: token["expires_on"] ?? null,
978
+ scope: "account",
979
+ accountId: account,
980
+ };
981
+ })));
982
+ };
983
+ return {
984
+ send,
985
+ sendEnvelope: (call) => send(call, true),
986
+ read,
987
+ call,
988
+ verify,
989
+ assertAccount,
990
+ assertZone,
991
+ refuse,
992
+ fillDefaults,
993
+ scope,
994
+ checkServer,
995
+ };
996
+ }
997
+ export function describeProbe(data, options) {
998
+ const verified = record(data);
999
+ if (options.auth.type === "globalApiKey")
1000
+ return { ok: true, message: `Global API Key verified for ${String(verified["email"])}.` };
1001
+ return verified["status"] === "active"
1002
+ ? {
1003
+ ok: true,
1004
+ message: `Token verified: active${verified["scope"] === "account" ? ` (account token for ${String(verified["accountId"])})` : ""}.`,
1005
+ }
1006
+ : { ok: false, message: `Token status is "${String(verified["status"])}".` };
1007
+ }
1008
+ function idsOf(value, path) {
1009
+ if (value === undefined)
1010
+ return undefined;
1011
+ if (!Array.isArray(value) ||
1012
+ value.length === 0 ||
1013
+ !value.every((item) => typeof item === "string" && item.trim() !== "")) {
1014
+ throw new Error(`${path} must be a non-empty list of ids.`);
1015
+ }
1016
+ return value.map((item) => item.trim());
1017
+ }
1018
+ function optionalId(value, path) {
1019
+ if (value === undefined)
1020
+ return undefined;
1021
+ if (typeof value !== "string" || value.trim() === "" || /[/?#\s]/.test(value.trim())) {
1022
+ throw new Error(`${path} must be a Cloudflare id.`);
1023
+ }
1024
+ return value.trim();
1025
+ }
1026
+ /** Normalized construction-only routing and confinement settings. */
1027
+ export function settings(options) {
1028
+ const auth = options.auth.type;
1029
+ if (auth !== "apiToken" && auth !== "globalApiKey")
1030
+ throw new Error("remoteMcp() with ConnectaConfig.classification for hosted MCP connections; use a distinct id beside the API connector.");
1031
+ const maxConcurrency = options.maxConcurrency ?? 6;
1032
+ if (!Number.isInteger(maxConcurrency) || maxConcurrency < 1) {
1033
+ throw new Error(`maxConcurrency must be a positive integer.`);
1034
+ }
1035
+ let pin;
1036
+ if (options.pin !== undefined) {
1037
+ const accountIds = idsOf(options.pin.accountIds, "pin.accountIds");
1038
+ const zoneIds = idsOf(options.pin.zoneIds, "pin.zoneIds");
1039
+ if (!accountIds && !zoneIds)
1040
+ throw new Error(`pin names accountIds, zoneIds, or both.`);
1041
+ pin = {
1042
+ ...(accountIds ? { accountIds } : {}),
1043
+ ...(zoneIds ? { zoneIds } : {}),
1044
+ };
1045
+ }
1046
+ if (auth === "globalApiKey") {
1047
+ const unpinned = options.unpinned;
1048
+ if (unpinned !== undefined && unpinned !== true) {
1049
+ throw new Error(`unpinned must be true when set.`);
1050
+ }
1051
+ if (pin && unpinned)
1052
+ throw new Error(`takes pin or unpinned: true, not both.`);
1053
+ if (!pin && !unpinned) {
1054
+ throw new Error(`requires pin: { accountIds, zoneIds } with globalApiKey auth, or unpinned: true: a Global API Key reaches every account and zone its user can.`);
1055
+ }
1056
+ }
1057
+ const accountId = optionalId(options.accountId, "accountId");
1058
+ const zoneId = optionalId(options.zoneId, "zoneId");
1059
+ if (pin && accountId && pin.accountIds && !pin.accountIds.includes(accountId)) {
1060
+ throw new Error(`accountId ${accountId} is outside its pin.`);
1061
+ }
1062
+ if (pin && zoneId && pin.zoneIds && !pin.accountIds && !pin.zoneIds.includes(zoneId)) {
1063
+ throw new Error(`zoneId ${zoneId} is outside its pin.`);
1064
+ }
1065
+ return { auth, baseUrl: options.baseUrl?.trim() || CLOUDFLARE_API_BASE, accountId, zoneId, pin };
1066
+ }