@zackbart/connecta 0.34.1 → 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 (429) hide show
  1. package/CHANGELOG.md +363 -0
  2. package/README.md +62 -106
  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 +12 -7
  53. package/dist/connectors/api-connector.js +142 -56
  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 +228 -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 +383 -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/agents.d.ts +5 -4
  109. package/dist/operator-ui/agents.js +17 -7
  110. package/dist/operator-ui/config-summary.js +5 -4
  111. package/dist/operator-ui/connection-map.js +7 -5
  112. package/dist/operator-ui/contract.d.ts +7 -3
  113. package/dist/operator-ui/endpoints.js +4 -13
  114. package/dist/operator-ui/fix-prompts.d.ts +1 -1
  115. package/dist/operator-ui/fix-prompts.js +0 -8
  116. package/dist/operator-ui/generated.js +3 -3
  117. package/dist/operator-ui/model.d.ts +2 -3
  118. package/dist/operator-ui/overview.js +5 -2
  119. package/dist/operator-ui/setup-commands.js +0 -4
  120. package/dist/operator-ui/view.d.ts +2 -25
  121. package/dist/operator-ui/view.js +2 -76
  122. package/dist/program-writes.js +47 -26
  123. package/dist/provider-call-facts.d.ts +115 -0
  124. package/dist/provider-call-facts.js +182 -0
  125. package/dist/provider-call.d.ts +112 -0
  126. package/dist/provider-call.js +271 -0
  127. package/dist/provider.d.ts +65 -31
  128. package/dist/provider.js +116 -39
  129. package/dist/providers/_shared/google/hatch.js +91 -0
  130. package/dist/providers/_shared/google/kit.js +537 -0
  131. package/dist/providers/_shared/google/result-size.js +15 -2
  132. package/dist/providers/_shared/google/service-account.d.ts +2 -1
  133. package/dist/providers/_shared/google/service-account.js +8 -4
  134. package/dist/providers/_shared/google/workspace.d.ts +6 -210
  135. package/dist/providers/_shared/google/workspace.js +6 -425
  136. package/dist/providers/_shared/rest/compact-index.js +86 -0
  137. package/dist/providers/_shared/rest/dispatch.js +3 -23
  138. package/dist/providers/_shared/rest/kit.js +546 -0
  139. package/dist/providers/_shared/rest/operation-index.js +230 -14
  140. package/dist/providers/_shared/rest/response-media.js +11 -0
  141. package/dist/providers/_shared/rest/tools.js +101 -78
  142. package/dist/providers/_shared/rest/value-safety.js +353 -195
  143. package/dist/providers/_shared/sensitive-fields.js +27 -0
  144. package/dist/providers/breeze/api.js +584 -0
  145. package/dist/providers/breeze/index.d.ts +1 -3
  146. package/dist/providers/breeze/index.js +67 -1420
  147. package/dist/providers/breeze/openapi.generated.js +60 -0
  148. package/dist/providers/breeze/skill.generated.js +2 -6
  149. package/dist/providers/breeze/tools.js +696 -0
  150. package/dist/providers/ccb/api.js +496 -0
  151. package/dist/providers/ccb/index.d.ts +15 -37
  152. package/dist/providers/ccb/index.js +77 -1702
  153. package/dist/providers/ccb/lib/constants.d.ts +34 -0
  154. package/dist/providers/ccb/lib/constants.js +74 -0
  155. package/dist/providers/ccb/lib/guide.js +9 -0
  156. package/dist/providers/ccb/lib/verdicts.js +387 -0
  157. package/dist/providers/ccb/openapi.generated.js +397 -0
  158. package/dist/providers/ccb/safety.js +295 -0
  159. package/dist/providers/ccb/skill.generated.js +2 -6
  160. package/dist/providers/ccb/tools.js +853 -0
  161. package/dist/providers/cloudflare/api.js +1066 -0
  162. package/dist/providers/cloudflare/index.d.ts +5 -22
  163. package/dist/providers/cloudflare/index.js +79 -197
  164. package/dist/providers/cloudflare/lib/config.d.ts +16 -0
  165. package/dist/providers/cloudflare/lib/config.js +2 -0
  166. package/dist/providers/cloudflare/lib/discovery.js +29 -0
  167. package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
  168. package/dist/providers/cloudflare/lib/hatch.js +103 -0
  169. package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
  170. package/dist/providers/cloudflare/lib/response-media.js +75 -0
  171. package/dist/providers/cloudflare/openapi.generated.js +4 -3
  172. package/dist/providers/cloudflare/{value-safety.js → safety.js} +487 -0
  173. package/dist/providers/cloudflare/skill.generated.js +2 -10
  174. package/dist/providers/cloudflare/tools.js +360 -0
  175. package/dist/providers/docs/api.js +51 -0
  176. package/dist/providers/docs/index.d.ts +1 -12
  177. package/dist/providers/docs/index.js +53 -1165
  178. package/dist/providers/docs/lib/constants.d.ts +18 -0
  179. package/dist/providers/docs/lib/constants.js +18 -0
  180. package/dist/providers/docs/lib/discovery.js +26 -0
  181. package/dist/providers/docs/lib/requests.js +48 -0
  182. package/dist/providers/docs/lib/schemas.js +168 -0
  183. package/dist/providers/docs/openapi.generated.js +16 -0
  184. package/dist/providers/docs/safety.js +54 -0
  185. package/dist/providers/docs/skill.generated.js +2 -6
  186. package/dist/providers/docs/tools.js +802 -0
  187. package/dist/providers/drive/api.js +177 -0
  188. package/dist/providers/drive/index.d.ts +1 -12
  189. package/dist/providers/drive/index.js +38 -1448
  190. package/dist/providers/drive/lib/constants.d.ts +6 -0
  191. package/dist/providers/drive/lib/constants.js +6 -0
  192. package/dist/providers/drive/lib/contracts.js +532 -0
  193. package/dist/providers/drive/lib/hatch.js +197 -0
  194. package/dist/providers/drive/openapi.generated.js +77 -0
  195. package/dist/providers/drive/safety.js +90 -0
  196. package/dist/providers/drive/skill.generated.js +2 -6
  197. package/dist/providers/drive/tools.js +1290 -0
  198. package/dist/providers/forms/api.js +71 -0
  199. package/dist/providers/forms/index.d.ts +1 -12
  200. package/dist/providers/forms/index.js +49 -1145
  201. package/dist/providers/forms/lib/constants.d.ts +14 -0
  202. package/dist/providers/forms/lib/constants.js +19 -0
  203. package/dist/providers/forms/lib/hatch.js +72 -0
  204. package/dist/providers/forms/openapi.generated.js +23 -0
  205. package/dist/providers/forms/safety.js +34 -0
  206. package/dist/providers/forms/skill.generated.js +2 -8
  207. package/dist/providers/forms/tools.js +1024 -0
  208. package/dist/providers/github/api.js +150 -0
  209. package/dist/providers/github/index.d.ts +4 -5
  210. package/dist/providers/github/index.js +30 -87
  211. package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
  212. package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
  213. package/dist/providers/github/lib/discovery.js +132 -0
  214. package/dist/providers/github/lib/operations.js +58 -0
  215. package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
  216. package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
  217. package/dist/providers/github/openapi.generated.js +1216 -0
  218. package/dist/providers/github/safety.js +216 -0
  219. package/dist/providers/github/skill.generated.js +2 -5
  220. package/dist/providers/github/tools.js +506 -0
  221. package/dist/providers/gmail/api.js +114 -0
  222. package/dist/providers/gmail/index.d.ts +1 -12
  223. package/dist/providers/gmail/index.js +73 -1621
  224. package/dist/providers/gmail/lib/constants.d.ts +13 -0
  225. package/dist/providers/gmail/lib/constants.js +16 -0
  226. package/dist/providers/gmail/lib/index.js +173 -0
  227. package/dist/providers/gmail/lib/schemas.js +176 -0
  228. package/dist/providers/gmail/lib/threading.js +120 -0
  229. package/dist/providers/gmail/openapi.generated.js +92 -0
  230. package/dist/providers/gmail/safety.js +21 -0
  231. package/dist/providers/gmail/skill.generated.js +2 -6
  232. package/dist/providers/gmail/tools.js +1435 -0
  233. package/dist/providers/overflow/api.js +1008 -0
  234. package/dist/providers/overflow/index.d.ts +1 -17
  235. package/dist/providers/overflow/index.js +60 -1488
  236. package/dist/providers/overflow/skill.generated.js +2 -5
  237. package/dist/providers/overflow/tools.js +873 -0
  238. package/dist/providers/planning-center/api.js +467 -0
  239. package/dist/providers/planning-center/index.d.ts +4 -35
  240. package/dist/providers/planning-center/index.js +69 -2445
  241. package/dist/providers/planning-center/lib/constants.d.ts +58 -0
  242. package/dist/providers/planning-center/lib/constants.js +83 -0
  243. package/dist/providers/planning-center/lib/discovery.js +40 -0
  244. package/dist/providers/planning-center/lib/guide.js +39 -0
  245. package/dist/providers/planning-center/lib/json-api.js +83 -0
  246. package/dist/providers/planning-center/lib/verdicts.js +1758 -0
  247. package/dist/providers/planning-center/openapi.generated.js +1764 -0
  248. package/dist/providers/planning-center/safety.js +188 -0
  249. package/dist/providers/planning-center/skill.generated.js +2 -27
  250. package/dist/providers/planning-center/tools.js +1010 -0
  251. package/dist/providers/sheets/api.js +133 -0
  252. package/dist/providers/sheets/index.d.ts +4 -16
  253. package/dist/providers/sheets/index.js +61 -1172
  254. package/dist/providers/sheets/lib/constants.d.ts +14 -0
  255. package/dist/providers/sheets/lib/constants.js +14 -0
  256. package/dist/providers/sheets/lib/hatch.js +163 -0
  257. package/dist/providers/sheets/lib/requests.js +105 -0
  258. package/dist/providers/sheets/lib/schemas.js +138 -0
  259. package/dist/providers/sheets/openapi.generated.js +30 -0
  260. package/dist/providers/sheets/safety.js +27 -0
  261. package/dist/providers/sheets/skill.generated.js +2 -7
  262. package/dist/providers/sheets/tools.js +734 -0
  263. package/dist/providers/slides/api.js +147 -0
  264. package/dist/providers/slides/index.d.ts +2 -10
  265. package/dist/providers/slides/index.js +48 -2555
  266. package/dist/providers/slides/lib/constants.d.ts +11 -0
  267. package/dist/providers/slides/lib/constants.js +11 -0
  268. package/dist/providers/slides/lib/requests.js +60 -0
  269. package/dist/providers/slides/lib/schemas.js +565 -0
  270. package/dist/providers/slides/openapi.generated.js +18 -0
  271. package/dist/providers/slides/skill.generated.js +2 -6
  272. package/dist/providers/slides/tools.js +1968 -0
  273. package/dist/providers/stripe/api.js +401 -0
  274. package/dist/providers/stripe/index.d.ts +5 -27
  275. package/dist/providers/stripe/index.js +74 -210
  276. package/dist/providers/stripe/lib/config.d.ts +4 -0
  277. package/dist/providers/stripe/lib/config.js +2 -0
  278. package/dist/providers/stripe/openapi.generated.js +4 -3
  279. package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
  280. package/dist/providers/stripe/skill.generated.js +2 -11
  281. package/dist/providers/stripe/tools.js +77 -0
  282. package/dist/providers/tithely/api.js +531 -0
  283. package/dist/providers/tithely/index.d.ts +17 -0
  284. package/dist/providers/tithely/index.js +86 -965
  285. package/dist/providers/tithely/skill.generated.js +2 -5
  286. package/dist/providers/tithely/tools.js +426 -0
  287. package/dist/providers/vercel/api.js +551 -0
  288. package/dist/providers/vercel/index.d.ts +9 -25
  289. package/dist/providers/vercel/index.js +66 -260
  290. package/dist/providers/vercel/lib/config.d.ts +2 -0
  291. package/dist/providers/vercel/lib/config.js +2 -0
  292. package/dist/providers/vercel/lib/index.js +117 -0
  293. package/dist/providers/vercel/lib/logs.js +100 -0
  294. package/dist/providers/vercel/lib/pin.js +270 -0
  295. package/dist/providers/vercel/openapi.generated.js +10 -5
  296. package/dist/providers/vercel/{value-safety.js → safety.js} +247 -38
  297. package/dist/providers/vercel/skill.generated.js +2 -7
  298. package/dist/providers/vercel/tools.js +642 -0
  299. package/dist/redaction-identity.js +47 -0
  300. package/dist/redaction-marker.js +125 -0
  301. package/dist/registry.d.ts +21 -15
  302. package/dist/registry.js +409 -376
  303. package/dist/routes/access-tokens.js +1 -1
  304. package/dist/routes/actor-labels.js +1 -1
  305. package/dist/routes/connect.js +1 -1
  306. package/dist/routes/dispatch.js +103 -0
  307. package/dist/routes/mcp.js +170 -79
  308. package/dist/routes/oauth.js +3 -1
  309. package/dist/routes/operator.js +2 -2
  310. package/dist/routes/shared.d.ts +5 -4
  311. package/dist/routes/shared.js +244 -177
  312. package/dist/routes/skills.js +10 -8
  313. package/dist/routes/ui-config.js +27 -3
  314. package/dist/routes/ui-data.js +21 -20
  315. package/dist/routes/ui.js +1 -1
  316. package/dist/runtime/call-admission.js +0 -26
  317. package/dist/runtime/call-entry.js +30 -0
  318. package/dist/runtime/call.js +556 -0
  319. package/dist/runtime/connector-scope.js +7 -0
  320. package/dist/runtime/credential-probe.js +34 -0
  321. package/dist/runtime/executor.js +40 -0
  322. package/dist/runtime/native-storage.js +63 -0
  323. package/dist/runtime/registry.js +27 -0
  324. package/dist/runtime/request-scope.js +72 -0
  325. package/dist/runtime/run.js +15 -7
  326. package/dist/runtime/shared-read.js +34 -5
  327. package/dist/runtime/storage.js +34 -21
  328. package/dist/sent-secrets.d.ts +194 -37
  329. package/dist/sent-secrets.js +1743 -340
  330. package/dist/server.js +5 -56
  331. package/dist/skills.js +322 -222
  332. package/dist/sqlite.js +15 -14
  333. package/dist/storage/d1-copy.js +255 -0
  334. package/dist/storage/d1-driver.js +15 -0
  335. package/dist/storage/keys.js +28 -0
  336. package/dist/storage/sql-driver.js +88 -0
  337. package/dist/storage/sql-native.js +544 -0
  338. package/dist/storage/sql-schema.d.ts +6 -0
  339. package/dist/storage/sql-schema.js +15 -0
  340. package/dist/storage/sql.d.ts +14 -20
  341. package/dist/storage/sql.js +29 -535
  342. package/dist/timeout.js +0 -26
  343. package/dist/tool-safety.js +20 -10
  344. package/dist/types.d.ts +7 -109
  345. package/dist/upward.js +3 -0
  346. package/dist/usage-guide.js +3 -1
  347. package/dist/validate.d.ts +4 -2
  348. package/dist/validate.js +86 -9
  349. package/dist/version.d.ts +1 -1
  350. package/dist/version.js +1 -1
  351. package/dist/worker.js +312 -292
  352. package/dist/workspace-subject.js +5 -1
  353. package/dist/write-outcome.js +454 -34
  354. package/documentation/README.md +27 -7
  355. package/documentation/architecture.md +2547 -226
  356. package/documentation/auth.md +821 -98
  357. package/documentation/code-mode.md +49 -16
  358. package/documentation/deploying.md +164 -20
  359. package/documentation/hosted-mcp-migration.md +460 -0
  360. package/documentation/integrating.md +129 -144
  361. package/documentation/meta-tools.md +409 -32
  362. package/documentation/operating.md +27 -3
  363. package/documentation/operator-tests.md +3 -0
  364. package/documentation/operator-ui.md +68 -45
  365. package/documentation/provider-auth-migration.md +16 -63
  366. package/documentation/provider-migration-0.29.md +10 -5
  367. package/documentation/provider-migration-0.35.md +975 -0
  368. package/documentation/providers.md +965 -0
  369. package/examples/worker/AGENTS.md +23 -9
  370. package/examples/worker/README.md +254 -135
  371. package/examples/worker/alchemy.run.ts +116 -0
  372. package/examples/worker/gitignore +10 -0
  373. package/examples/worker/src/connecta.config.ts +23 -21
  374. package/examples/worker/wrangler.jsonc +14 -5
  375. package/guides/connecting-agents.md +157 -0
  376. package/guides/how-it-works.md +156 -0
  377. package/guides/safety-and-trust.md +153 -0
  378. package/package.json +27 -46
  379. package/templates/node/.env.example +3 -0
  380. package/templates/node/README.md +22 -2
  381. package/templates/node/docker-compose.yml +1 -0
  382. package/templates/node/package.json +3 -2
  383. package/templates/node/src/connecta.config.ts +8 -0
  384. package/dist/catalog-drift.d.ts +0 -138
  385. package/dist/catalog-drift.js +0 -578
  386. package/dist/providers/_shared/request-token.js +0 -83
  387. package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
  388. package/dist/providers/_shared/rest/tools.d.ts +0 -141
  389. package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
  390. package/dist/providers/basecamp/index.d.ts +0 -45
  391. package/dist/providers/basecamp/index.js +0 -997
  392. package/dist/providers/basecamp/skill.generated.js +0 -9
  393. package/dist/providers/cloudflare/rest.d.ts +0 -37
  394. package/dist/providers/cloudflare/rest.js +0 -1097
  395. package/dist/providers/github/hosted.js +0 -218
  396. package/dist/providers/github/rest.js +0 -321
  397. package/dist/providers/infisical/index.d.ts +0 -7
  398. package/dist/providers/infisical/index.js +0 -713
  399. package/dist/providers/infisical/skill.generated.js +0 -8
  400. package/dist/providers/intercom/index.d.ts +0 -24
  401. package/dist/providers/intercom/index.js +0 -121
  402. package/dist/providers/intercom/skill.generated.js +0 -8
  403. package/dist/providers/linear/index.d.ts +0 -55
  404. package/dist/providers/linear/index.js +0 -537
  405. package/dist/providers/linear/skill.generated.js +0 -8
  406. package/dist/providers/mixpanel/index.d.ts +0 -59
  407. package/dist/providers/mixpanel/index.js +0 -472
  408. package/dist/providers/mixpanel/operations.js +0 -20
  409. package/dist/providers/mixpanel/rest.js +0 -603
  410. package/dist/providers/mixpanel/skill.generated.js +0 -10
  411. package/dist/providers/mixpanel/value-safety.js +0 -16
  412. package/dist/providers/notion/index.d.ts +0 -67
  413. package/dist/providers/notion/index.js +0 -368
  414. package/dist/providers/notion/openapi.generated.js +0 -75
  415. package/dist/providers/notion/rest.d.ts +0 -24
  416. package/dist/providers/notion/rest.js +0 -1226
  417. package/dist/providers/notion/skill.generated.js +0 -11
  418. package/dist/providers/notion/value-safety.js +0 -56
  419. package/dist/providers/revenuecat/index.d.ts +0 -51
  420. package/dist/providers/revenuecat/index.js +0 -697
  421. package/dist/providers/revenuecat/skill.generated.js +0 -11
  422. package/dist/providers/stripe/rest.d.ts +0 -14
  423. package/dist/providers/stripe/rest.js +0 -402
  424. package/dist/providers/vercel/rest.d.ts +0 -20
  425. package/dist/providers/vercel/rest.js +0 -1020
  426. package/dist/providers/wispr-flow/index.d.ts +0 -14
  427. package/dist/providers/wispr-flow/index.js +0 -117
  428. package/dist/providers/wispr-flow/skill.generated.js +0 -8
  429. /package/dist/providers/github/{key.js → lib/key.js} +0 -0
@@ -0,0 +1,1010 @@
1
+ // Curated Planning Center workflows over the standard provider kit.
2
+ import { Effect, Schema } from "effect";
3
+ import { ConnectorCallError } from "../../errors.js";
4
+ import { Refusal } from "../../http/errors.js";
5
+ import { Integer, isMinChars, emptyRequired } from "../../http/json-schema.js";
6
+ import { read, write, list as standardList } from "../../http/tools.js";
7
+ import { PLANNING_CENTER_APPS, MAX_PAGE_SIZE, planningCenterCall } from "./api.js";
8
+ import { excludedApps } from "./lib/guide.js";
9
+ import { asRecord, compact, resources, indexIncluded, relId, related, relatedMany, pick, attribute, writeDocument, pageOf, } from "./lib/json-api.js";
10
+ const fail = (code, message, options) => Effect.fail(new Refusal({ error: new ConnectorCallError(code, message, options) }));
11
+ const pattern = (source) => Schema.makeFilter((value) => typeof value !== "string" || new RegExp(source).test(value), {
12
+ toJsonSchema: () => ({ pattern: source }),
13
+ });
14
+ const STRING = Schema.String;
15
+ const NULLABLE_STRING = Schema.Union([Schema.String, Schema.Null]);
16
+ const INTEGER = Integer;
17
+ const NULLABLE_INTEGER = Schema.Union([Integer, Schema.Null]);
18
+ const BOOLEAN = Schema.Boolean;
19
+ const ARRAY = Schema.Array(Schema.Unknown).annotate({ toJsonSchema: () => ({ type: "array" }) });
20
+ const OBJECT = Schema.Record(Schema.String, Schema.Unknown);
21
+ function struct(properties, required) {
22
+ return Schema.Struct(Object.fromEntries(Object.entries(properties).map(([key, value]) => [
23
+ key,
24
+ required.includes(key) ? value : Schema.optionalKey(value),
25
+ ])));
26
+ }
27
+ function record(properties, required = ["id"]) {
28
+ const out = struct(properties, required);
29
+ // The legacy catalog explicitly spells empty required arrays on output records.
30
+ return required.length ? out : emptyRequired(out);
31
+ }
32
+ const input = (properties, required = []) => struct(properties, required);
33
+ // Preserve the explicit legacy additive annotation while the kit owns the verdict.
34
+ const additive = (connection, spec) => {
35
+ const tool = write(connection, spec);
36
+ tool.annotations = { readOnlyHint: false, destructiveHint: false };
37
+ return tool;
38
+ };
39
+ function idProperty(description) {
40
+ return Schema.String.check(pattern("^[0-9]+$")).annotate({ description });
41
+ }
42
+ const DATE_PROPERTY_PATTERN = "^\\d{4}-\\d{2}-\\d{2}(T[0-9:.]+(Z|[+-]\\d{2}:?\\d{2})?)?$";
43
+ function dateProperty(description) {
44
+ return Schema.String.check(pattern(DATE_PROPERTY_PATTERN)).annotate({
45
+ description: `${description} ISO 8601 date or date-time; a bare date is the organization's local day.`,
46
+ });
47
+ }
48
+ function orderProperty(values) {
49
+ return Schema.Literals(values.flatMap((value) => [value, `-${value}`])).annotate({
50
+ description: "Sort attribute; a leading '-' reverses it.",
51
+ });
52
+ }
53
+ // --- Output records ---------------------------------------------------------
54
+ const PERSON_SUMMARY = {
55
+ id: STRING,
56
+ name: STRING,
57
+ first_name: STRING,
58
+ last_name: STRING,
59
+ nickname: NULLABLE_STRING,
60
+ status: STRING,
61
+ membership: NULLABLE_STRING,
62
+ child: BOOLEAN,
63
+ primary_email: NULLABLE_STRING,
64
+ primary_phone: NULLABLE_STRING,
65
+ updated_at: STRING,
66
+ };
67
+ const PERSON_SUMMARY_SCHEMA = record(PERSON_SUMMARY);
68
+ const PERSON_SCHEMA = record({
69
+ ...PERSON_SUMMARY,
70
+ given_name: NULLABLE_STRING,
71
+ middle_name: NULLABLE_STRING,
72
+ birthdate: NULLABLE_STRING,
73
+ anniversary: NULLABLE_STRING,
74
+ gender: NULLABLE_STRING,
75
+ grade: NULLABLE_INTEGER,
76
+ inactivated_at: NULLABLE_STRING,
77
+ created_at: STRING,
78
+ primary_campus: OBJECT,
79
+ emails: ARRAY,
80
+ phone_numbers: ARRAY,
81
+ addresses: ARRAY,
82
+ households: ARRAY,
83
+ });
84
+ const CARD_SCHEMA = record({
85
+ id: STRING,
86
+ stage: STRING,
87
+ person_id: NULLABLE_STRING,
88
+ person_name: NULLABLE_STRING,
89
+ assignee_id: NULLABLE_STRING,
90
+ current_step_id: NULLABLE_STRING,
91
+ current_step_name: NULLABLE_STRING,
92
+ moved_to_step_at: NULLABLE_STRING,
93
+ snooze_until: NULLABLE_STRING,
94
+ overdue: BOOLEAN,
95
+ completed_at: NULLABLE_STRING,
96
+ removed_at: NULLABLE_STRING,
97
+ });
98
+ const NOTE_SCHEMA = record({
99
+ id: STRING,
100
+ note: STRING,
101
+ note_category_id: STRING,
102
+ category: NULLABLE_STRING,
103
+ person_id: STRING,
104
+ display_date: NULLABLE_STRING,
105
+ created_at: STRING,
106
+ created_by_id: NULLABLE_STRING,
107
+ });
108
+ const PLAN_PERSON_SCHEMA = record({
109
+ id: STRING,
110
+ name: STRING,
111
+ status: STRING,
112
+ team_position_name: NULLABLE_STRING,
113
+ team_id: NULLABLE_STRING,
114
+ team_name: NULLABLE_STRING,
115
+ person_id: NULLABLE_STRING,
116
+ decline_reason: NULLABLE_STRING,
117
+ });
118
+ const ITEM_SCHEMA = record({
119
+ id: STRING,
120
+ sequence: INTEGER,
121
+ item_type: STRING,
122
+ title: STRING,
123
+ length: NULLABLE_INTEGER,
124
+ key_name: NULLABLE_STRING,
125
+ service_position: NULLABLE_STRING,
126
+ song_id: NULLABLE_STRING,
127
+ arrangement_id: NULLABLE_STRING,
128
+ });
129
+ const MEMBERSHIP_SCHEMA = record({
130
+ id: STRING,
131
+ role: STRING,
132
+ joined_at: NULLABLE_STRING,
133
+ person_id: STRING,
134
+ first_name: STRING,
135
+ last_name: STRING,
136
+ });
137
+ const DESIGNATIONS = Schema.Array(record({ fund_id: NULLABLE_STRING, fund: NULLABLE_STRING, amount_cents: INTEGER }, ["amount_cents"]));
138
+ const DONATION_SCHEMA = record({
139
+ id: STRING,
140
+ amount_cents: INTEGER,
141
+ amount_currency: STRING,
142
+ fee_cents: NULLABLE_INTEGER,
143
+ received_at: NULLABLE_STRING,
144
+ payment_method: NULLABLE_STRING,
145
+ payment_status: NULLABLE_STRING,
146
+ refunded: BOOLEAN,
147
+ person_id: NULLABLE_STRING,
148
+ batch_id: NULLABLE_STRING,
149
+ campus_id: NULLABLE_STRING,
150
+ designations: DESIGNATIONS,
151
+ });
152
+ // --- Projections ------------------------------------------------------------
153
+ function primaryOf(rows, key) {
154
+ const primary = rows.find((row) => attribute(row, "primary") === true) ?? rows[0];
155
+ const value = attribute(primary, key);
156
+ return typeof value === "string" ? value : null;
157
+ }
158
+ function personSummary(person, index) {
159
+ return {
160
+ ...pick(person, ["name", "first_name", "last_name", "nickname", "status", "membership", "child"]),
161
+ primary_email: primaryOf(relatedMany(person, "emails", index), "address"),
162
+ primary_phone: primaryOf(relatedMany(person, "phone_numbers", index), "number"),
163
+ ...pick(person, ["updated_at"]),
164
+ id: person.id,
165
+ };
166
+ }
167
+ function personRecord(person, index) {
168
+ const campus = related(person, "primary_campus", index);
169
+ return compact({
170
+ ...personSummary(person, index),
171
+ ...pick(person, [
172
+ "given_name",
173
+ "middle_name",
174
+ "birthdate",
175
+ "anniversary",
176
+ "gender",
177
+ "grade",
178
+ "inactivated_at",
179
+ "created_at",
180
+ ]),
181
+ id: person.id,
182
+ primary_campus: campus
183
+ ? { id: campus.id, name: attribute(campus, "name") }
184
+ : relId(person, "primary_campus")
185
+ ? { id: relId(person, "primary_campus") }
186
+ : undefined,
187
+ emails: relatedMany(person, "emails", index).map((email) => pick(email, ["address", "location", "primary"])),
188
+ phone_numbers: relatedMany(person, "phone_numbers", index).map((phone) => pick(phone, ["number", "location", "primary"])),
189
+ addresses: relatedMany(person, "addresses", index).map((address) => pick(address, ["street_line_1", "street_line_2", "city", "state", "zip", "country_code", "location", "primary"])),
190
+ households: relatedMany(person, "households", index).map((household) => pick(household, ["name", "primary_contact_id", "member_count"])),
191
+ });
192
+ }
193
+ function cardRecord(card, index) {
194
+ const person = related(card, "person", index);
195
+ const step = related(card, "current_step", index);
196
+ return {
197
+ ...pick(card, ["stage"]),
198
+ person_id: relId(card, "person") ?? null,
199
+ person_name: attribute(person, "name") ?? null,
200
+ assignee_id: relId(card, "assignee") ?? null,
201
+ current_step_id: relId(card, "current_step") ?? null,
202
+ current_step_name: attribute(step, "name") ?? null,
203
+ ...pick(card, ["moved_to_step_at", "snooze_until", "overdue", "completed_at", "removed_at"]),
204
+ id: card.id,
205
+ };
206
+ }
207
+ function noteRecord(note, index) {
208
+ // `include=category` sideloads what the resource itself calls
209
+ // `note_category`; the flat `note_category_id` settles it either way.
210
+ const category = related(note, "category", index) ??
211
+ related(note, "note_category", index) ??
212
+ index.get(`NoteCategory:${String(attribute(note, "note_category_id"))}`);
213
+ return {
214
+ ...pick(note, ["note", "note_category_id", "person_id", "display_date", "created_at", "created_by_id"]),
215
+ category: attribute(category, "name") ?? null,
216
+ };
217
+ }
218
+ function planPersonRecord(member, index) {
219
+ const team = related(member, "team", index);
220
+ return {
221
+ ...pick(member, ["name", "status", "team_position_name", "decline_reason"]),
222
+ team_id: relId(member, "team") ?? null,
223
+ team_name: attribute(team, "name") ?? null,
224
+ person_id: relId(member, "person") ?? null,
225
+ };
226
+ }
227
+ function itemRecord(item) {
228
+ return {
229
+ ...pick(item, ["sequence", "item_type", "title", "length", "key_name", "service_position"]),
230
+ song_id: relId(item, "song") ?? null,
231
+ arrangement_id: relId(item, "arrangement") ?? null,
232
+ };
233
+ }
234
+ function membershipRecord(membership, index) {
235
+ const person = related(membership, "person", index);
236
+ return {
237
+ ...pick(membership, ["role", "joined_at"]),
238
+ person_id: relId(membership, "person") ?? null,
239
+ first_name: attribute(person, "first_name") ?? null,
240
+ last_name: attribute(person, "last_name") ?? null,
241
+ };
242
+ }
243
+ function designations(owner, index) {
244
+ return relatedMany(owner, "designations", index).map((designation) => {
245
+ const fund = related(designation, "fund", index);
246
+ return {
247
+ fund_id: relId(designation, "fund") ?? null,
248
+ fund: attribute(fund, "name") ?? null,
249
+ amount_cents: attribute(designation, "amount_cents"),
250
+ };
251
+ });
252
+ }
253
+ function donationRecord(donation, index) {
254
+ return {
255
+ ...pick(donation, [
256
+ "amount_cents",
257
+ "amount_currency",
258
+ "fee_cents",
259
+ "received_at",
260
+ "payment_method",
261
+ "payment_status",
262
+ "refunded",
263
+ ]),
264
+ person_id: relId(donation, "person") ?? null,
265
+ batch_id: relId(donation, "batch") ?? null,
266
+ campus_id: relId(donation, "campus") ?? null,
267
+ designations: designations(donation, index),
268
+ };
269
+ }
270
+ /** One native JSON:API page, with includes projected before fields selection. */
271
+ function listTool(connection, call, defaultPageSize, spec) {
272
+ return standardList(connection, {
273
+ name: spec.name,
274
+ description: spec.description,
275
+ filters: struct(spec.properties ?? {}, spec.required ?? []).fields,
276
+ row: spec.item,
277
+ limit: { default: defaultPageSize, max: MAX_PAGE_SIZE, description: "Rows per page, at most 100." },
278
+ cursor: "bound",
279
+ total: true,
280
+ page: (args, cursor) => Effect.gen(function* () {
281
+ const offset = cursor === undefined ? 0 : Number(cursor);
282
+ if (!Number.isSafeInteger(offset) ||
283
+ offset < 0 ||
284
+ (cursor !== undefined && !/^(?:0|[1-9][0-9]*)$/.test(cursor)))
285
+ return yield* fail("invalid_args", "This Planning Center cursor contains no valid offset.");
286
+ const document = asRecord(yield* call({
287
+ method: "GET",
288
+ path: spec.path(args),
289
+ query: {
290
+ ...spec.query?.(args),
291
+ include: spec.include,
292
+ per_page: args["limit"],
293
+ offset,
294
+ },
295
+ }));
296
+ if (!Array.isArray(document["data"]))
297
+ return yield* fail("connector_call_failed", "Planning Center returned a list without a data array.");
298
+ const page = yield* Effect.try({
299
+ try: () => pageOf(document, offset),
300
+ catch: (error) => new Refusal({ error: error }),
301
+ });
302
+ const index = indexIncluded(document);
303
+ return {
304
+ rows: resources(document["data"]).map((row) => spec.project(row, index)),
305
+ page: {
306
+ hasMore: page.hasMore,
307
+ nextCursor: page.nextOffset === null ? null : String(page.nextOffset),
308
+ ...(page.totalCount === null ? {} : { total: page.totalCount }),
309
+ },
310
+ };
311
+ }),
312
+ });
313
+ }
314
+ function segment(value) {
315
+ return encodeURIComponent(String(value));
316
+ }
317
+ /** The single resource a response carries, or a refusal to invent one. */
318
+ function created(document, what) {
319
+ return Effect.gen(function* () {
320
+ const resource = resources(asRecord(document)["data"])[0];
321
+ if (!resource) {
322
+ return yield* fail("connector_call_failed", `Planning Center answered without the ${what} it should return.`, {
323
+ retryable: false,
324
+ });
325
+ }
326
+ return resource;
327
+ });
328
+ }
329
+ /** `where[a][b][gte]` and `[lt]` for a half-open range on a (nested) attribute. */
330
+ function range(attribute, after, before) {
331
+ const key = `where[${attribute.join("][")}]`;
332
+ return {
333
+ [`${key}[gte]`]: typeof after === "string" ? after : undefined,
334
+ [`${key}[lt]`]: typeof before === "string" ? before : undefined,
335
+ };
336
+ }
337
+ const PERSON_ID = idProperty("Person id from search_people. The same id resolves in every Planning Center product.");
338
+ const SERVICE_TYPE_ID = idProperty("Service type id from list_service_types.");
339
+ const PLAN_ID = idProperty("Plan id from list_plans.");
340
+ const PERSON_WRITE_PROPERTIES = {
341
+ firstName: Schema.String.check(isMinChars(1)).annotate({ description: "First name." }),
342
+ lastName: Schema.String.check(isMinChars(1)).annotate({ description: "Last name." }),
343
+ nickname: Schema.Union([Schema.String, Schema.Null]).annotate({ description: "Nickname; null clears it." }),
344
+ middleName: Schema.Union([Schema.String, Schema.Null]).annotate({ description: "Middle name; null clears it." }),
345
+ birthdate: Schema.Union([Schema.String, Schema.Null])
346
+ .check(pattern("^\\d{4}-\\d{2}-\\d{2}$"))
347
+ .annotate({ description: "Birthdate, YYYY-MM-DD; null clears it." }),
348
+ anniversary: Schema.Union([Schema.String, Schema.Null])
349
+ .check(pattern("^\\d{4}-\\d{2}-\\d{2}$"))
350
+ .annotate({ description: "Anniversary, YYYY-MM-DD; null clears it." }),
351
+ gender: Schema.Union([Schema.String, Schema.Null]).annotate({
352
+ description: "Gender as the organization records it, e.g. Male or Female.",
353
+ }),
354
+ membership: Schema.Union([Schema.String, Schema.Null]).annotate({
355
+ description: "Membership type, one of the organization's own values such as Member or Visitor.",
356
+ }),
357
+ child: Schema.Boolean.annotate({ description: "Whether the person is a child." }),
358
+ primaryCampusId: Schema.String.check(pattern("^[0-9]+$")).annotate({
359
+ description: "Campus id from /people/v2/campuses.",
360
+ }),
361
+ };
362
+ function personAttributes(args) {
363
+ return {
364
+ first_name: args["firstName"],
365
+ last_name: args["lastName"],
366
+ nickname: args["nickname"],
367
+ middle_name: args["middleName"],
368
+ birthdate: args["birthdate"],
369
+ anniversary: args["anniversary"],
370
+ gender: args["gender"],
371
+ membership: args["membership"],
372
+ child: args["child"],
373
+ primary_campus_id: args["primaryCampusId"],
374
+ status: args["status"],
375
+ };
376
+ }
377
+ const WORKFLOW_CARD_ACTIONS = ["promote", "go_back", "skip_step", "snooze", "unsnooze", "remove", "restore"];
378
+ function tools(connection, call, defaultPageSize) {
379
+ const list = (spec) => listTool(connection, call, defaultPageSize, spec);
380
+ // --- Hatches --------------------------------------------------------------
381
+ // --- People ---------------------------------------------------------------
382
+ const people = [
383
+ read(connection, {
384
+ fields: true,
385
+ name: "get_me",
386
+ description: "Get the Planning Center person this token acts as, with the organization's name and time zone. Supplies personId for my-schedule and assignee questions.",
387
+ input: input({}),
388
+ output: record({
389
+ id: STRING,
390
+ name: STRING,
391
+ first_name: STRING,
392
+ last_name: STRING,
393
+ status: STRING,
394
+ organization: record({ id: STRING, name: STRING, time_zone: STRING }),
395
+ }),
396
+ run: (_args) => Effect.gen(function* () {
397
+ const document = asRecord(yield* call({ method: "GET", path: "/people/v2/me", query: { include: "organization" } }));
398
+ const me = yield* created(document, "person record");
399
+ const organization = resources(document["included"]).find((row) => row.type === "Organization");
400
+ return {
401
+ ...pick(me, ["name", "first_name", "last_name", "status"]),
402
+ ...(organization ? { organization: pick(organization, ["name", "time_zone"]) } : {}),
403
+ };
404
+ }),
405
+ }),
406
+ list({
407
+ name: "search_people",
408
+ description: "Search People profiles by name, email, or phone, with each person's primary email and phone. Omit search to list everyone; never searches custom fields or notes.",
409
+ item: PERSON_SUMMARY_SCHEMA,
410
+ properties: {
411
+ search: Schema.String.check(isMinChars(1)).annotate({
412
+ description: "Matches name, email address, or phone number.",
413
+ }),
414
+ status: Schema.Literals(["active", "inactive"]).annotate({ description: "Profile status." }),
415
+ membership: Schema.String.check(isMinChars(1)).annotate({ description: "Exact membership type, e.g. Member." }),
416
+ updatedSince: dateProperty("Only people updated at or after this time."),
417
+ order: orderProperty(["last_name", "first_name", "created_at", "updated_at"]),
418
+ },
419
+ path: () => "/people/v2/people",
420
+ query: (args) => ({
421
+ "where[search_name_or_email_or_phone_number]": args["search"],
422
+ "where[status]": args["status"],
423
+ "where[membership]": args["membership"],
424
+ "where[updated_at][gte]": args["updatedSince"],
425
+ order: args["order"],
426
+ }),
427
+ include: "emails,phone_numbers",
428
+ project: personSummary,
429
+ }),
430
+ read(connection, {
431
+ fields: true,
432
+ name: "get_person",
433
+ description: "Get one person's profile with every email, phone number, address, household, and primary campus. Custom fields are list_person_field_data.",
434
+ input: input({ personId: PERSON_ID }, ["personId"]),
435
+ output: PERSON_SCHEMA,
436
+ run: (args) => Effect.gen(function* () {
437
+ const document = asRecord(yield* call({
438
+ method: "GET",
439
+ path: `/people/v2/people/${segment(args["personId"])}`,
440
+ query: { include: "emails,phone_numbers,addresses,households,primary_campus" },
441
+ }));
442
+ const person = yield* created(document, "person record");
443
+ return personRecord(person, indexIncluded(document));
444
+ }),
445
+ }),
446
+ additive(connection, {
447
+ name: "create_person",
448
+ description: "Create a People profile. Emails and phone numbers are separate records; add them after with pco_api_write: POST /people/v2/people/{person_id}/emails or POST /people/v2/people/{person_id}/phone_numbers.",
449
+ destructive: false,
450
+ input: input(PERSON_WRITE_PROPERTIES, ["firstName", "lastName"]),
451
+ output: PERSON_SUMMARY_SCHEMA,
452
+ run: (args) => Effect.gen(function* () {
453
+ const document = yield* call({
454
+ method: "POST",
455
+ path: "/people/v2/people",
456
+ body: writeDocument("Person", personAttributes(args)),
457
+ });
458
+ return personSummary(yield* created(document, "person"), new Map());
459
+ }),
460
+ }),
461
+ write(connection, {
462
+ name: "update_person",
463
+ description: "Update fields on one People profile; omitted fields stay as they are. status inactive archives the person reversibly; this never deletes a profile.",
464
+ destructive: true,
465
+ input: input({
466
+ personId: PERSON_ID,
467
+ ...PERSON_WRITE_PROPERTIES,
468
+ status: Schema.Literals(["active", "inactive"]).annotate({
469
+ description: "inactive archives the profile; active restores it.",
470
+ }),
471
+ }, ["personId"]),
472
+ output: PERSON_SUMMARY_SCHEMA,
473
+ run: (args) => Effect.gen(function* () {
474
+ const attributes = compact(personAttributes(args));
475
+ if (Object.keys(attributes).length === 0) {
476
+ return yield* fail("invalid_args", "Nothing to update: provide at least one profile field.");
477
+ }
478
+ const document = yield* call({
479
+ method: "PATCH",
480
+ path: `/people/v2/people/${segment(args["personId"])}`,
481
+ body: { data: { type: "Person", id: String(args["personId"]), attributes } },
482
+ });
483
+ return personSummary(yield* created(document, "person update"), new Map());
484
+ }),
485
+ }),
486
+ list({
487
+ name: "list_workflows",
488
+ description: "List People workflows with their steps and ready, overdue, and unassigned card counts. Archived workflows are excluded unless asked for.",
489
+ item: record({
490
+ id: STRING,
491
+ name: STRING,
492
+ total_cards_count: INTEGER,
493
+ total_ready_card_count: INTEGER,
494
+ total_overdue_card_count: INTEGER,
495
+ total_unassigned_card_count: INTEGER,
496
+ my_ready_card_count: INTEGER,
497
+ archived_at: NULLABLE_STRING,
498
+ steps: ARRAY,
499
+ }),
500
+ properties: {
501
+ name: Schema.String.check(isMinChars(1)).annotate({
502
+ description: "Exact workflow name; end it with % for a prefix match.",
503
+ }),
504
+ includeArchived: Schema.Boolean.annotate({ description: "Include archived workflows." }),
505
+ },
506
+ path: () => "/people/v2/workflows",
507
+ query: (args) => ({
508
+ "where[name]": args["name"],
509
+ filter: args["includeArchived"] === true ? undefined : "not_archived",
510
+ }),
511
+ include: "steps",
512
+ project: (row, index) => ({
513
+ ...pick(row, [
514
+ "name",
515
+ "total_cards_count",
516
+ "total_ready_card_count",
517
+ "total_overdue_card_count",
518
+ "total_unassigned_card_count",
519
+ "my_ready_card_count",
520
+ "archived_at",
521
+ ]),
522
+ steps: relatedMany(row, "steps", index)
523
+ .map((step) => pick(step, ["name", "sequence"]))
524
+ .sort((a, b) => Number(a["sequence"] ?? 0) - Number(b["sequence"] ?? 0)),
525
+ }),
526
+ }),
527
+ list({
528
+ name: "list_workflow_cards",
529
+ description: "List the cards in one workflow with each card's person, assignee, and current step. Card actions need both the card id and its person_id.",
530
+ item: CARD_SCHEMA,
531
+ properties: {
532
+ workflowId: idProperty("Workflow id from list_workflows."),
533
+ stage: Schema.String.check(isMinChars(1)).annotate({
534
+ description: "Card stage as Planning Center reports it, e.g. ready, snoozed, completed, removed.",
535
+ }),
536
+ stepId: idProperty("Only cards on this step (list_workflows steps[].id)."),
537
+ assigneeId: idProperty("Only cards assigned to this person id."),
538
+ overdue: Schema.Boolean.annotate({ description: "Only overdue (true) or on-time (false) cards." }),
539
+ order: orderProperty(["created_at", "moved_to_step_at", "last_name"]),
540
+ },
541
+ required: ["workflowId"],
542
+ path: (args) => `/people/v2/workflows/${segment(args["workflowId"])}/cards`,
543
+ query: (args) => ({
544
+ "where[stage]": args["stage"],
545
+ "where[step_id]": args["stepId"],
546
+ "where[assignee_id]": args["assigneeId"],
547
+ "where[overdue]": args["overdue"],
548
+ order: args["order"],
549
+ }),
550
+ include: "person,current_step",
551
+ project: cardRecord,
552
+ }),
553
+ additive(connection, {
554
+ name: "add_workflow_card",
555
+ description: "Add a person to a workflow as a new card on its first step. Optionally assign it; the workflow's own step defaults apply otherwise.",
556
+ destructive: false,
557
+ input: input({
558
+ workflowId: idProperty("Workflow id from list_workflows."),
559
+ personId: PERSON_ID,
560
+ assigneeId: idProperty("Person id to assign the card to."),
561
+ }, ["workflowId", "personId"]),
562
+ output: CARD_SCHEMA,
563
+ run: (args) => Effect.gen(function* () {
564
+ return cardRecord(yield* created(yield* call({
565
+ method: "POST",
566
+ path: `/people/v2/workflows/${segment(args["workflowId"])}/cards`,
567
+ body: writeDocument("WorkflowCard", { person_id: args["personId"], assignee_id: args["assigneeId"] }),
568
+ }), "workflow card"), new Map());
569
+ }),
570
+ }),
571
+ write(connection, {
572
+ name: "apply_workflow_card_action",
573
+ description: "Promote, send back, skip a step on, snooze, unsnooze, remove, or restore one workflow card. Never emails; sending a card email is pco_api_write.",
574
+ destructive: true,
575
+ input: input({
576
+ personId: PERSON_ID.annotate({ description: "The card's person_id from list_workflow_cards." }),
577
+ cardId: idProperty("Workflow card id from list_workflow_cards."),
578
+ action: Schema.Literals([...WORKFLOW_CARD_ACTIONS]).annotate({
579
+ description: "promote completes the step; skip_step moves on without completing it; go_back returns to the previous step.",
580
+ }),
581
+ snoozeDays: Integer.check(Schema.isGreaterThanOrEqualTo(1), Schema.isLessThanOrEqualTo(365)).annotate({
582
+ description: "Days to snooze; required with action snooze and refused otherwise.",
583
+ }),
584
+ }, ["personId", "cardId", "action"]),
585
+ output: record({ ok: BOOLEAN }, ["ok"]),
586
+ run: (args) => Effect.gen(function* () {
587
+ const snooze = args["action"] === "snooze";
588
+ if (snooze !== (args["snoozeDays"] !== undefined)) {
589
+ return yield* fail("invalid_args", snooze ? "action snooze needs snoozeDays." : "snoozeDays applies only to action snooze.");
590
+ }
591
+ yield* call({
592
+ method: "POST",
593
+ path: `/people/v2/people/${segment(args["personId"])}/workflow_cards/${segment(args["cardId"])}/${String(args["action"])}`,
594
+ ...(snooze ? { body: { data: { attributes: { duration: args["snoozeDays"] } } } } : {}),
595
+ });
596
+ return { ok: true };
597
+ }),
598
+ }),
599
+ list({
600
+ name: "list_person_notes",
601
+ description: "List the notes on one person's profile with their category names, newest first by default. Workflow card notes are not profile notes.",
602
+ item: NOTE_SCHEMA,
603
+ properties: {
604
+ personId: PERSON_ID,
605
+ noteCategoryId: idProperty("Only notes in this category (/people/v2/note_categories)."),
606
+ order: orderProperty(["created_at", "display_date"]),
607
+ },
608
+ required: ["personId"],
609
+ path: (args) => `/people/v2/people/${segment(args["personId"])}/notes`,
610
+ query: (args) => ({ "where[note_category_id]": args["noteCategoryId"], order: args["order"] ?? "-created_at" }),
611
+ include: "category",
612
+ project: noteRecord,
613
+ }),
614
+ additive(connection, {
615
+ name: "add_person_note",
616
+ description: "Add a note to a person's profile in a note category. Category subscribers may be notified; read categories at /people/v2/note_categories.",
617
+ destructive: false,
618
+ input: input({
619
+ personId: PERSON_ID,
620
+ note: Schema.String.check(isMinChars(1)).annotate({ description: "Note text." }),
621
+ noteCategoryId: idProperty("Note category id; Planning Center files every note under one."),
622
+ }, ["personId", "note", "noteCategoryId"]),
623
+ output: NOTE_SCHEMA,
624
+ run: (args) => Effect.gen(function* () {
625
+ return noteRecord(yield* created(yield* call({
626
+ method: "POST",
627
+ path: `/people/v2/people/${segment(args["personId"])}/notes`,
628
+ body: writeDocument("Note", { note: args["note"], note_category_id: args["noteCategoryId"] }),
629
+ }), "note"), new Map());
630
+ }),
631
+ }),
632
+ list({
633
+ name: "list_person_field_data",
634
+ description: "List one person's custom field values, each labeled with its field name and data type. Write with pco_api_write: POST /people/v2/people/{person_id}/field_data to create, or PATCH /people/v2/field_data/{field_datum_id} to update.",
635
+ item: record({
636
+ id: STRING,
637
+ field_definition_id: STRING,
638
+ field: NULLABLE_STRING,
639
+ data_type: NULLABLE_STRING,
640
+ value: NULLABLE_STRING,
641
+ file_name: NULLABLE_STRING,
642
+ }),
643
+ properties: { personId: PERSON_ID },
644
+ required: ["personId"],
645
+ path: (args) => `/people/v2/people/${segment(args["personId"])}/field_data`,
646
+ include: "field_definition",
647
+ project: (row, index) => {
648
+ const definition = related(row, "field_definition", index);
649
+ return {
650
+ id: row.id,
651
+ field_definition_id: relId(row, "field_definition") ?? null,
652
+ field: attribute(definition, "name") ?? null,
653
+ data_type: attribute(definition, "data_type") ?? null,
654
+ value: attribute(row, "value") ?? null,
655
+ file_name: attribute(row, "file_name") ?? null,
656
+ };
657
+ },
658
+ }),
659
+ ];
660
+ // --- Services -------------------------------------------------------------
661
+ const services = [
662
+ list({
663
+ name: "list_service_types",
664
+ description: "List Services service types — the recurring services such as Sunday Morning. Supplies serviceTypeId for plans and teams.",
665
+ item: record({
666
+ id: STRING,
667
+ name: STRING,
668
+ frequency: NULLABLE_STRING,
669
+ sequence: NULLABLE_INTEGER,
670
+ parent_id: NULLABLE_STRING,
671
+ }),
672
+ path: () => "/services/v2/service_types",
673
+ query: () => ({ order: "sequence" }),
674
+ project: (row) => ({ ...pick(row, ["name", "frequency", "sequence"]), parent_id: relId(row, "parent") ?? null }),
675
+ }),
676
+ list({
677
+ name: "list_plans",
678
+ description: "List one service type's plans (individual services) by date. Defaults to upcoming plans, soonest first; get_plan returns the order of service and team.",
679
+ item: record({
680
+ id: STRING,
681
+ title: NULLABLE_STRING,
682
+ series_title: NULLABLE_STRING,
683
+ dates: STRING,
684
+ sort_date: NULLABLE_STRING,
685
+ items_count: INTEGER,
686
+ plan_people_count: INTEGER,
687
+ needed_positions_count: INTEGER,
688
+ planning_center_url: STRING,
689
+ }),
690
+ properties: {
691
+ serviceTypeId: SERVICE_TYPE_ID,
692
+ when: Schema.Literals(["future", "past", "all"]).annotate({
693
+ description: "future (default) sorts soonest first; past sorts most recent first.",
694
+ }),
695
+ },
696
+ required: ["serviceTypeId"],
697
+ path: (args) => `/services/v2/service_types/${segment(args["serviceTypeId"])}/plans`,
698
+ query: (args) => {
699
+ const when = args["when"] ?? "future";
700
+ return {
701
+ filter: when === "all" ? undefined : when,
702
+ order: when === "past" ? "-sort_date" : "sort_date",
703
+ };
704
+ },
705
+ project: (row) => pick(row, [
706
+ "title",
707
+ "series_title",
708
+ "dates",
709
+ "sort_date",
710
+ "items_count",
711
+ "plan_people_count",
712
+ "needed_positions_count",
713
+ "planning_center_url",
714
+ ]),
715
+ }),
716
+ read(connection, {
717
+ fields: true,
718
+ name: "get_plan",
719
+ writeSteps: { plan: "once", items: "once", team: "once" },
720
+ description: "Get one plan with its times, order of service, and scheduled team in a single call. Reports truncated sections past 100 rows; the hatch pages the remaining items and team.",
721
+ input: input({ serviceTypeId: SERVICE_TYPE_ID, planId: PLAN_ID }, ["serviceTypeId", "planId"]),
722
+ output: record({
723
+ id: STRING,
724
+ service_type_id: STRING,
725
+ title: NULLABLE_STRING,
726
+ series_title: NULLABLE_STRING,
727
+ dates: STRING,
728
+ sort_date: NULLABLE_STRING,
729
+ total_length: NULLABLE_INTEGER,
730
+ needed_positions_count: NULLABLE_INTEGER,
731
+ planning_center_url: STRING,
732
+ times: ARRAY,
733
+ items: Schema.Array(ITEM_SCHEMA),
734
+ teamMembers: Schema.Array(PLAN_PERSON_SCHEMA),
735
+ truncated: Schema.Array(STRING),
736
+ }, ["id", "service_type_id", "items", "teamMembers"]),
737
+ run: (args) => Effect.gen(function* () {
738
+ const base = `/services/v2/service_types/${segment(args["serviceTypeId"])}/plans/${segment(args["planId"])}`;
739
+ const [planDocument, itemDocument, teamDocument] = (yield* Effect.all([
740
+ call({ method: "GET", path: base, query: { include: "plan_times" } }, "plan"),
741
+ call({ method: "GET", path: `${base}/items`, query: { per_page: MAX_PAGE_SIZE } }, "items"),
742
+ call({ method: "GET", path: `${base}/team_members`, query: { include: "team", per_page: MAX_PAGE_SIZE } }, "team"),
743
+ ], { concurrency: "unbounded" })).map(asRecord);
744
+ const plan = yield* created(planDocument, "plan record");
745
+ const items = resources(itemDocument["data"]);
746
+ const members = resources(teamDocument["data"]);
747
+ const truncated = yield* Effect.try({
748
+ try: () => [
749
+ ...(pageOf(itemDocument).hasMore ? ["items[]"] : []),
750
+ ...(pageOf(teamDocument).hasMore ? ["teamMembers[]"] : []),
751
+ ],
752
+ catch: (error) => new Refusal({ error: error }),
753
+ });
754
+ const planIndex = indexIncluded(planDocument);
755
+ const teamIndex = indexIncluded(teamDocument);
756
+ return {
757
+ ...pick(plan, [
758
+ "title",
759
+ "series_title",
760
+ "dates",
761
+ "sort_date",
762
+ "total_length",
763
+ "needed_positions_count",
764
+ "planning_center_url",
765
+ ]),
766
+ times: relatedMany(plan, "plan_times", planIndex).map((time) => pick(time, ["name", "time_type", "starts_at", "ends_at"])),
767
+ service_type_id: String(args["serviceTypeId"]),
768
+ items: items.map(itemRecord).sort((a, b) => Number(a["sequence"] ?? 0) - Number(b["sequence"] ?? 0)),
769
+ teamMembers: members.map((member) => planPersonRecord(member, teamIndex)),
770
+ ...(truncated.length ? { truncated } : {}),
771
+ };
772
+ }),
773
+ }),
774
+ list({
775
+ name: "list_teams",
776
+ description: "List one service type's teams with their positions. Supplies teamId and position names for schedule_plan_person.",
777
+ item: record({ id: STRING, name: STRING, archived_at: NULLABLE_STRING, positions: ARRAY }),
778
+ properties: { serviceTypeId: SERVICE_TYPE_ID },
779
+ required: ["serviceTypeId"],
780
+ path: (args) => `/services/v2/service_types/${segment(args["serviceTypeId"])}/teams`,
781
+ include: "team_positions",
782
+ project: (row, index) => ({
783
+ ...pick(row, ["name", "archived_at"]),
784
+ positions: relatedMany(row, "team_positions", index).map((position) => pick(position, ["name"])),
785
+ }),
786
+ }),
787
+ list({
788
+ name: "list_person_schedules",
789
+ description: "List where one person is scheduled to serve across every service type, with team, position, and response. Defaults to upcoming.",
790
+ item: record({
791
+ id: STRING,
792
+ dates: STRING,
793
+ sort_date: NULLABLE_STRING,
794
+ service_type_name: STRING,
795
+ team_name: NULLABLE_STRING,
796
+ team_position_name: NULLABLE_STRING,
797
+ status: STRING,
798
+ decline_reason: NULLABLE_STRING,
799
+ plan_id: NULLABLE_STRING,
800
+ }),
801
+ properties: {
802
+ personId: PERSON_ID,
803
+ when: Schema.Literals(["future", "past"]).annotate({ description: "future (default) or past schedules." }),
804
+ },
805
+ required: ["personId"],
806
+ path: (args) => `/services/v2/people/${segment(args["personId"])}/schedules`,
807
+ query: (args) => ({
808
+ filter: args["when"] ?? "future",
809
+ order: args["when"] === "past" ? "-starts_at" : "starts_at",
810
+ }),
811
+ project: (row) => ({
812
+ ...pick(row, [
813
+ "dates",
814
+ "sort_date",
815
+ "service_type_name",
816
+ "team_name",
817
+ "team_position_name",
818
+ "status",
819
+ "decline_reason",
820
+ ]),
821
+ plan_id: relId(row, "plan") ?? null,
822
+ }),
823
+ }),
824
+ additive(connection, {
825
+ name: "schedule_plan_person",
826
+ description: "Schedule a person on a plan's team, unconfirmed by default. Prepares a notification only when asked; Planning Center sends it, not this call.",
827
+ destructive: false,
828
+ input: input({
829
+ serviceTypeId: SERVICE_TYPE_ID,
830
+ planId: PLAN_ID,
831
+ personId: PERSON_ID,
832
+ teamId: idProperty("Team id from list_teams."),
833
+ teamPositionName: Schema.String.check(isMinChars(1)).annotate({
834
+ description: "Position name from list_teams positions[].name.",
835
+ }),
836
+ status: Schema.Literals(["U", "C"]).annotate({ description: "U unconfirmed (default) or C confirmed." }),
837
+ prepareNotification: Schema.Boolean.annotate({
838
+ description: "Queue Planning Center's scheduling notification for this person.",
839
+ }),
840
+ notes: Schema.String.annotate({ description: "Note shown to the person." }),
841
+ }, ["serviceTypeId", "planId", "personId", "teamId"]),
842
+ output: PLAN_PERSON_SCHEMA,
843
+ run: (args) => Effect.gen(function* () {
844
+ return planPersonRecord(yield* created(yield* call({
845
+ method: "POST",
846
+ path: `/services/v2/service_types/${segment(args["serviceTypeId"])}/plans/${segment(args["planId"])}/team_members`,
847
+ body: writeDocument("PlanPerson", {
848
+ person_id: args["personId"],
849
+ team_id: args["teamId"],
850
+ team_position_name: args["teamPositionName"],
851
+ status: args["status"] ?? "U",
852
+ prepare_notification: args["prepareNotification"],
853
+ notes: args["notes"],
854
+ }),
855
+ }), "scheduled person"), new Map());
856
+ }),
857
+ }),
858
+ ];
859
+ // --- Groups ---------------------------------------------------------------
860
+ const groups = [
861
+ list({
862
+ name: "list_groups",
863
+ description: "List Groups groups with type, schedule, and member count. Archived groups are excluded unless asked for; the API cannot create a group.",
864
+ item: record({
865
+ id: STRING,
866
+ name: STRING,
867
+ memberships_count: INTEGER,
868
+ schedule: NULLABLE_STRING,
869
+ group_type_id: NULLABLE_STRING,
870
+ contact_email: NULLABLE_STRING,
871
+ archived_at: NULLABLE_STRING,
872
+ public_church_center_web_url: NULLABLE_STRING,
873
+ }),
874
+ properties: {
875
+ name: Schema.String.check(isMinChars(1)).annotate({
876
+ description: "Exact group name; end it with % for a prefix match.",
877
+ }),
878
+ groupTypeId: idProperty("Only groups of this type (/groups/v2/group_types)."),
879
+ archived: Schema.Literals(["exclude", "include", "only"]).annotate({
880
+ description: "Archived groups: exclude (default), include, or only.",
881
+ }),
882
+ order: orderProperty(["name", "created_at", "memberships_count"]),
883
+ },
884
+ path: () => "/groups/v2/groups",
885
+ query: (args) => ({
886
+ "where[name]": args["name"],
887
+ "where[group_type][id]": args["groupTypeId"],
888
+ "where[archive_status]": args["archived"] === "include" || args["archived"] === "only" ? args["archived"] : undefined,
889
+ order: args["order"],
890
+ }),
891
+ project: (row) => ({
892
+ ...pick(row, [
893
+ "name",
894
+ "memberships_count",
895
+ "schedule",
896
+ "contact_email",
897
+ "archived_at",
898
+ "public_church_center_web_url",
899
+ ]),
900
+ group_type_id: relId(row, "group_type") ?? null,
901
+ }),
902
+ }),
903
+ list({
904
+ name: "list_group_memberships",
905
+ description: "List one group's members and leaders with their names and join dates. Contact details come from get_person with person_id.",
906
+ item: MEMBERSHIP_SCHEMA,
907
+ properties: {
908
+ groupId: idProperty("Group id from list_groups."),
909
+ role: Schema.Literals(["member", "leader"]).annotate({ description: "Only members or only leaders." }),
910
+ order: orderProperty(["last_name", "first_name", "joined_at", "role"]),
911
+ },
912
+ required: ["groupId"],
913
+ path: (args) => `/groups/v2/groups/${segment(args["groupId"])}/memberships`,
914
+ query: (args) => ({ "where[role]": args["role"], order: args["order"] }),
915
+ include: "person",
916
+ project: membershipRecord,
917
+ }),
918
+ additive(connection, {
919
+ name: "add_group_member",
920
+ description: "Add a person to a group as a member or leader. Fails when they already belong; change a role or remove someone with pco_api_write.",
921
+ destructive: false,
922
+ input: input({
923
+ groupId: idProperty("Group id from list_groups."),
924
+ personId: PERSON_ID,
925
+ role: Schema.Literals(["member", "leader"]).annotate({ description: "Defaults to member." }),
926
+ joinedAt: dateProperty("When they joined. Defaults to now."),
927
+ }, ["groupId", "personId"]),
928
+ output: MEMBERSHIP_SCHEMA,
929
+ run: (args) => Effect.gen(function* () {
930
+ const document = asRecord(yield* call({
931
+ method: "POST",
932
+ path: `/groups/v2/groups/${segment(args["groupId"])}/memberships`,
933
+ query: { include: "person" },
934
+ body: writeDocument("Membership", {
935
+ person_id: args["personId"],
936
+ role: args["role"] ?? "member",
937
+ joined_at: args["joinedAt"],
938
+ }),
939
+ }));
940
+ return membershipRecord(yield* created(document, "membership"), indexIncluded(document));
941
+ }),
942
+ }),
943
+ ];
944
+ // --- Check-Ins (read-only product) -----------------------------------------
945
+ const checkIns = [];
946
+ // --- Calendar (events read-only) ------------------------------------------
947
+ const calendar = [];
948
+ // --- Registrations (read-only product) ------------------------------------
949
+ const registrations = [];
950
+ // --- Giving (reads; money moves only through pco_api_write) ---------------
951
+ const giving = [
952
+ list({
953
+ name: "list_donations",
954
+ description: "List Giving donations with their fund designations, newest first, by date, fund, or donor. Amounts are integer cents. Read-only.",
955
+ item: DONATION_SCHEMA,
956
+ properties: {
957
+ personId: idProperty("Only this donor's donations."),
958
+ receivedAfter: dateProperty("Only donations received at or after this time."),
959
+ receivedBefore: dateProperty("Only donations received before this time."),
960
+ fundId: idProperty("Only donations designated to this fund (list_funds)."),
961
+ succeededOnly: Schema.Boolean.annotate({ description: "Exclude pending and failed card or ACH payments." }),
962
+ order: orderProperty(["received_at", "created_at", "updated_at"]),
963
+ },
964
+ path: (args) => args["personId"] ? `/giving/v2/people/${segment(args["personId"])}/donations` : "/giving/v2/donations",
965
+ query: (args) => ({
966
+ ...range(["received_at"], args["receivedAfter"], args["receivedBefore"]),
967
+ "where[fund_id]": args["fundId"],
968
+ filter: args["succeededOnly"] === true ? "succeeded" : undefined,
969
+ order: args["order"] ?? "-received_at",
970
+ }),
971
+ include: "designations,designations.fund",
972
+ project: donationRecord,
973
+ }),
974
+ list({
975
+ name: "list_funds",
976
+ description: "List Giving funds with ledger codes and visibility. Supplies fundId for donation filters.",
977
+ item: record({
978
+ id: STRING,
979
+ name: STRING,
980
+ ledger_code: NULLABLE_STRING,
981
+ visibility: STRING,
982
+ default: BOOLEAN,
983
+ description: NULLABLE_STRING,
984
+ }),
985
+ path: () => "/giving/v2/funds",
986
+ project: (row) => pick(row, ["name", "ledger_code", "visibility", "default", "description"]),
987
+ }),
988
+ ];
989
+ // --- Webhooks -------------------------------------------------------------
990
+ const webhooks = [];
991
+ return {
992
+ apps: {
993
+ people,
994
+ services,
995
+ groups,
996
+ "check-ins": checkIns,
997
+ calendar,
998
+ registrations,
999
+ giving,
1000
+ publishing: [],
1001
+ webhooks,
1002
+ api: [],
1003
+ },
1004
+ };
1005
+ }
1006
+ export function planningCenterTools({ connection, pageSize, options, }) {
1007
+ const excluded = excludedApps(options.excludeApps);
1008
+ const surface = tools(connection, planningCenterCall(connection, options), pageSize);
1009
+ return PLANNING_CENTER_APPS.flatMap((app) => (excluded.has(app) ? [] : surface.apps[app]));
1010
+ }