@jskit-ai/connectors-catalog 0.1.1

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 (399) hide show
  1. package/README.md +192 -0
  2. package/docs/ai-upstream-notices.md +51 -0
  3. package/docs/ai.md +191 -0
  4. package/docs/airtable.md +130 -0
  5. package/docs/algolia.md +190 -0
  6. package/docs/amazon-redshift.md +229 -0
  7. package/docs/amplitude.md +210 -0
  8. package/docs/apify.md +162 -0
  9. package/docs/apollo-io.md +200 -0
  10. package/docs/asana.md +126 -0
  11. package/docs/ashby.md +188 -0
  12. package/docs/atlassian.md +224 -0
  13. package/docs/attention.md +162 -0
  14. package/docs/aws-athena.md +150 -0
  15. package/docs/aws-credentials.md +75 -0
  16. package/docs/aws-s3.md +193 -0
  17. package/docs/bigquery.md +146 -0
  18. package/docs/brevo.md +122 -0
  19. package/docs/calendly.md +158 -0
  20. package/docs/canva.md +191 -0
  21. package/docs/chargebee.md +192 -0
  22. package/docs/clay.md +194 -0
  23. package/docs/clickhouse.md +242 -0
  24. package/docs/confidence-exp.md +74 -0
  25. package/docs/confidence-flags.md +75 -0
  26. package/docs/confidence-oauth.md +137 -0
  27. package/docs/contentful.md +123 -0
  28. package/docs/databricks.md +180 -0
  29. package/docs/dbt-semantic-layer.md +175 -0
  30. package/docs/elevenlabs.md +102 -0
  31. package/docs/figma.md +132 -0
  32. package/docs/firebase-cloud-messaging.md +262 -0
  33. package/docs/firecrawl.md +137 -0
  34. package/docs/fireflies.md +95 -0
  35. package/docs/fireworks-ai.md +53 -0
  36. package/docs/gatewayapi.md +113 -0
  37. package/docs/gemini-enterprise.md +224 -0
  38. package/docs/github-api.md +133 -0
  39. package/docs/gitlab-api.md +138 -0
  40. package/docs/gmail.md +133 -0
  41. package/docs/gong.md +205 -0
  42. package/docs/google-ads.md +156 -0
  43. package/docs/google-analytics.md +202 -0
  44. package/docs/google-docs.md +105 -0
  45. package/docs/google-drive.md +108 -0
  46. package/docs/google-maps-platform.md +208 -0
  47. package/docs/google-oauth.md +123 -0
  48. package/docs/google-search-console.md +124 -0
  49. package/docs/google-sheets.md +94 -0
  50. package/docs/google-slides.md +95 -0
  51. package/docs/granola.md +178 -0
  52. package/docs/hex.md +161 -0
  53. package/docs/heygen.md +124 -0
  54. package/docs/hubspot.md +142 -0
  55. package/docs/incident-io.md +118 -0
  56. package/docs/inngest.md +178 -0
  57. package/docs/klipy.md +149 -0
  58. package/docs/lexware.md +144 -0
  59. package/docs/lightspeed.md +193 -0
  60. package/docs/linear.md +137 -0
  61. package/docs/linkedin.md +196 -0
  62. package/docs/logo-dev.md +152 -0
  63. package/docs/mailgun.md +153 -0
  64. package/docs/mapbox.md +174 -0
  65. package/docs/microsoft-excel.md +97 -0
  66. package/docs/microsoft-fabric.md +249 -0
  67. package/docs/microsoft-oauth.md +143 -0
  68. package/docs/microsoft-onedrive.md +76 -0
  69. package/docs/microsoft-onenote.md +75 -0
  70. package/docs/microsoft-outlook.md +90 -0
  71. package/docs/microsoft-powerpoint.md +119 -0
  72. package/docs/microsoft-sharepoint.md +75 -0
  73. package/docs/microsoft-teams.md +72 -0
  74. package/docs/microsoft-word.md +119 -0
  75. package/docs/miro.md +117 -0
  76. package/docs/n8n.md +169 -0
  77. package/docs/notion.md +137 -0
  78. package/docs/oura.md +161 -0
  79. package/docs/paddle.md +168 -0
  80. package/docs/perplexity.md +85 -0
  81. package/docs/pipedrive.md +120 -0
  82. package/docs/polar.md +63 -0
  83. package/docs/posthog.md +227 -0
  84. package/docs/prestashop.md +151 -0
  85. package/docs/replicate.md +60 -0
  86. package/docs/resend.md +153 -0
  87. package/docs/salesforce.md +234 -0
  88. package/docs/sanity.md +122 -0
  89. package/docs/semrush.md +276 -0
  90. package/docs/sentry.md +112 -0
  91. package/docs/sevdesk.md +107 -0
  92. package/docs/shopify.md +206 -0
  93. package/docs/slack.md +257 -0
  94. package/docs/snowflake.md +271 -0
  95. package/docs/storyblok.md +130 -0
  96. package/docs/stripe.md +77 -0
  97. package/docs/supabase.md +51 -0
  98. package/docs/tally.md +105 -0
  99. package/docs/telegram.md +165 -0
  100. package/docs/tiktok.md +201 -0
  101. package/docs/twilio.md +284 -0
  102. package/docs/twitch.md +428 -0
  103. package/docs/wave.md +440 -0
  104. package/docs/wix.md +1249 -0
  105. package/docs/wiz.md +177 -0
  106. package/docs/woocommerce.md +400 -0
  107. package/docs/wordpress-com.md +358 -0
  108. package/docs/wordpress-self-hosted.md +252 -0
  109. package/docs/workday.md +292 -0
  110. package/docs/x-twitter.md +188 -0
  111. package/docs/xero.md +429 -0
  112. package/docs/zoho-books.md +214 -0
  113. package/docs/zoho-crm.md +192 -0
  114. package/package.json +153 -0
  115. package/patterns/ai-connections/PATTERN.md +84 -0
  116. package/patterns/ai-connections/example/ai-model.js +11 -0
  117. package/patterns/ai-connections/example/perplexity-answer.js +29 -0
  118. package/patterns/api-key-connection/PATTERN.md +368 -0
  119. package/patterns/api-key-connection/example/connections.js +18 -0
  120. package/patterns/api-key-connection/example/integrations.json +19 -0
  121. package/patterns/assistant-mcp/PATTERN.md +110 -0
  122. package/patterns/assistant-mcp/example/integrations.json +13 -0
  123. package/patterns/assistant-mcp-oauth/PATTERN.md +235 -0
  124. package/patterns/assistant-mcp-oauth/example/integrations.json +102 -0
  125. package/patterns/aws-storage-queries/PATTERN.md +169 -0
  126. package/patterns/aws-storage-queries/example/formats/data-formats.js +35 -0
  127. package/patterns/aws-storage-queries/example/formats/package-lock.json +49 -0
  128. package/patterns/aws-storage-queries/example/formats/package.json +12 -0
  129. package/patterns/aws-storage-queries/example/formats/verify-formats.mjs +37 -0
  130. package/patterns/aws-storage-queries/example/integrations.json +38 -0
  131. package/patterns/aws-storage-queries/example/s3-transfer.js +34 -0
  132. package/patterns/event-delivery/PATTERN.md +151 -0
  133. package/patterns/event-delivery/example/integrations.json +17 -0
  134. package/patterns/firebase-messaging/PATTERN.md +156 -0
  135. package/patterns/firebase-messaging/example/server/notifications.js +23 -0
  136. package/patterns/google-ads-search/PATTERN.md +92 -0
  137. package/patterns/google-ads-search/example/ads-setup.js +25 -0
  138. package/patterns/oauth-connection/PATTERN.md +724 -0
  139. package/patterns/oauth-connection/example/integrations.json +20 -0
  140. package/patterns/paddle-catalogue/PATTERN.md +78 -0
  141. package/patterns/paddle-catalogue/example/create-products.js +38 -0
  142. package/patterns/public-image/PATTERN.md +84 -0
  143. package/patterns/public-image/example/integrations.json +13 -0
  144. package/patterns/public-image/example/logo-url.js +16 -0
  145. package/patterns/redshift-queries/PATTERN.md +150 -0
  146. package/patterns/redshift-queries/example/integrations.json +33 -0
  147. package/patterns/source-scanning/PATTERN.md +83 -0
  148. package/patterns/source-scanning/example/source-scanner.js +13 -0
  149. package/src/client/firebase-cloud-messaging.js +10 -0
  150. package/src/client/google-analytics.js +72 -0
  151. package/src/client/google-maps-platform.js +39 -0
  152. package/src/client/logo-dev.js +36 -0
  153. package/src/server/ai.js +46 -0
  154. package/src/server/airtable.js +67 -0
  155. package/src/server/algolia.js +63 -0
  156. package/src/server/amazon-redshift.js +96 -0
  157. package/src/server/amplitude.js +31 -0
  158. package/src/server/apify.js +68 -0
  159. package/src/server/apollo-io.js +85 -0
  160. package/src/server/asana.js +64 -0
  161. package/src/server/ashby.js +61 -0
  162. package/src/server/atlassian.js +32 -0
  163. package/src/server/attention.js +93 -0
  164. package/src/server/aws-athena.js +82 -0
  165. package/src/server/aws-s3.js +49 -0
  166. package/src/server/awsTransport.js +75 -0
  167. package/src/server/bigquery.js +97 -0
  168. package/src/server/brevo.js +97 -0
  169. package/src/server/calendly.js +77 -0
  170. package/src/server/canva.js +13 -0
  171. package/src/server/chargebee.js +88 -0
  172. package/src/server/clay.js +78 -0
  173. package/src/server/clickhouse.js +66 -0
  174. package/src/server/confidence.js +29 -0
  175. package/src/server/contentful.js +40 -0
  176. package/src/server/databricks.js +55 -0
  177. package/src/server/dbt-semantic-layer.js +95 -0
  178. package/src/server/elevenlabs.js +81 -0
  179. package/src/server/figma.js +24 -0
  180. package/src/server/firebase-cloud-messaging.js +111 -0
  181. package/src/server/firecrawl.js +70 -0
  182. package/src/server/fireflies.js +31 -0
  183. package/src/server/fireworks-ai.js +16 -0
  184. package/src/server/gatewayapi.js +48 -0
  185. package/src/server/gemini-enterprise.js +47 -0
  186. package/src/server/github-api.js +90 -0
  187. package/src/server/gitlab-api.js +67 -0
  188. package/src/server/gmail.js +60 -0
  189. package/src/server/gong.js +52 -0
  190. package/src/server/google-ads-search.js +134 -0
  191. package/src/server/google-ads.js +80 -0
  192. package/src/server/google-docs.js +36 -0
  193. package/src/server/google-drive.js +71 -0
  194. package/src/server/google-maps-platform.js +90 -0
  195. package/src/server/google-search-console.js +31 -0
  196. package/src/server/google-sheets.js +60 -0
  197. package/src/server/google-slides.js +36 -0
  198. package/src/server/google.js +61 -0
  199. package/src/server/granola.js +115 -0
  200. package/src/server/graphqlOperation.js +15 -0
  201. package/src/server/hex.js +29 -0
  202. package/src/server/heygen.js +54 -0
  203. package/src/server/hubspot.js +85 -0
  204. package/src/server/incident-io.js +68 -0
  205. package/src/server/inngest.js +86 -0
  206. package/src/server/jsonOperation.js +34 -0
  207. package/src/server/klipy.js +34 -0
  208. package/src/server/lexware.js +59 -0
  209. package/src/server/lightspeed.js +82 -0
  210. package/src/server/linear.js +52 -0
  211. package/src/server/linkedin.js +74 -0
  212. package/src/server/mailgun.js +76 -0
  213. package/src/server/mapbox.js +46 -0
  214. package/src/server/mcpRegistration.js +41 -0
  215. package/src/server/mcpTransport.js +60 -0
  216. package/src/server/microsoft-excel.js +67 -0
  217. package/src/server/microsoft-fabric.js +81 -0
  218. package/src/server/microsoft-onedrive.js +58 -0
  219. package/src/server/microsoft-onenote.js +52 -0
  220. package/src/server/microsoft-outlook.js +58 -0
  221. package/src/server/microsoft-powerpoint.js +5 -0
  222. package/src/server/microsoft-sharepoint.js +46 -0
  223. package/src/server/microsoft-teams.js +29 -0
  224. package/src/server/microsoft-word.js +5 -0
  225. package/src/server/microsoft.js +88 -0
  226. package/src/server/miro.js +29 -0
  227. package/src/server/n8n.js +78 -0
  228. package/src/server/notion.js +76 -0
  229. package/src/server/oura.js +48 -0
  230. package/src/server/paddle.js +83 -0
  231. package/src/server/perplexity.js +15 -0
  232. package/src/server/pipedrive.js +95 -0
  233. package/src/server/polar.js +20 -0
  234. package/src/server/posthog.js +44 -0
  235. package/src/server/prestashop.js +42 -0
  236. package/src/server/replicate.js +42 -0
  237. package/src/server/resend.js +93 -0
  238. package/src/server/salesforce.js +84 -0
  239. package/src/server/sanity.js +31 -0
  240. package/src/server/semrush.js +359 -0
  241. package/src/server/sentry.js +30 -0
  242. package/src/server/sevdesk.js +20 -0
  243. package/src/server/shopify.js +99 -0
  244. package/src/server/slack.js +143 -0
  245. package/src/server/snowflake.js +139 -0
  246. package/src/server/storyblok.js +49 -0
  247. package/src/server/stripe.js +13 -0
  248. package/src/server/supabase.js +13 -0
  249. package/src/server/tally.js +78 -0
  250. package/src/server/telegram.js +55 -0
  251. package/src/server/tiktok.js +86 -0
  252. package/src/server/twilio.js +90 -0
  253. package/src/server/twitch.js +409 -0
  254. package/src/server/wave.js +520 -0
  255. package/src/server/wix.js +1048 -0
  256. package/src/server/wiz.js +117 -0
  257. package/src/server/woocommerce.js +412 -0
  258. package/src/server/wordpress-com.js +236 -0
  259. package/src/server/wordpress-self-hosted.js +134 -0
  260. package/src/server/workday.js +136 -0
  261. package/src/server/x-twitter.js +110 -0
  262. package/src/server/xero.js +397 -0
  263. package/src/server/zoho-books.js +90 -0
  264. package/src/server/zoho-crm.js +89 -0
  265. package/src/server/zohoOAuth.js +21 -0
  266. package/src/shared/ai-models.json +8049 -0
  267. package/src/shared/ai.js +63 -0
  268. package/src/shared/algolia.js +29 -0
  269. package/src/shared/amazon-redshift.js +51 -0
  270. package/src/shared/amplitude.js +28 -0
  271. package/src/shared/atlassian.js +43 -0
  272. package/src/shared/aws.js +72 -0
  273. package/src/shared/canva.js +66 -0
  274. package/src/shared/chargebee.js +26 -0
  275. package/src/shared/clickhouse.js +38 -0
  276. package/src/shared/confidence.js +28 -0
  277. package/src/shared/databricks.js +33 -0
  278. package/src/shared/dbt-semantic-layer.js +32 -0
  279. package/src/shared/definitions.js +140 -0
  280. package/src/shared/figma.js +21 -0
  281. package/src/shared/firebase-cloud-messaging.js +45 -0
  282. package/src/shared/gemini-enterprise.js +33 -0
  283. package/src/shared/gong.js +33 -0
  284. package/src/shared/google-ads-search.js +31 -0
  285. package/src/shared/google-ads.js +28 -0
  286. package/src/shared/google-maps-platform.js +28 -0
  287. package/src/shared/google.js +133 -0
  288. package/src/shared/granola.js +25 -0
  289. package/src/shared/hex.js +28 -0
  290. package/src/shared/inngest.js +35 -0
  291. package/src/shared/lightspeed.js +75 -0
  292. package/src/shared/linkedin.js +27 -0
  293. package/src/shared/logo-dev.js +21 -0
  294. package/src/shared/mapbox.js +38 -0
  295. package/src/shared/mcp.js +92 -0
  296. package/src/shared/microsoft-fabric.js +47 -0
  297. package/src/shared/microsoft.js +231 -0
  298. package/src/shared/miro.js +27 -0
  299. package/src/shared/oura.js +23 -0
  300. package/src/shared/posthog.js +33 -0
  301. package/src/shared/prestashop.js +23 -0
  302. package/src/shared/salesforce.js +36 -0
  303. package/src/shared/semrush.js +24 -0
  304. package/src/shared/shopify.js +51 -0
  305. package/src/shared/siteUrl.js +12 -0
  306. package/src/shared/slack.js +88 -0
  307. package/src/shared/snowflake.js +55 -0
  308. package/src/shared/tiktok.js +22 -0
  309. package/src/shared/tokens.js +747 -0
  310. package/src/shared/twilio.js +41 -0
  311. package/src/shared/twitch.js +49 -0
  312. package/src/shared/wave.js +52 -0
  313. package/src/shared/wix.js +40 -0
  314. package/src/shared/wiz.js +41 -0
  315. package/src/shared/wordpress-com.js +30 -0
  316. package/src/shared/wordpress.js +69 -0
  317. package/src/shared/workday.js +50 -0
  318. package/src/shared/x-twitter.js +19 -0
  319. package/src/shared/xero.js +46 -0
  320. package/src/shared/zoho-books.js +39 -0
  321. package/src/shared/zoho-crm.js +43 -0
  322. package/src/shared/zoho-regions.js +12 -0
  323. package/test/ai.test.js +136 -0
  324. package/test/airtable.test.js +125 -0
  325. package/test/algolia.test.js +207 -0
  326. package/test/amazon-redshift.test.js +272 -0
  327. package/test/amplitude.test.js +239 -0
  328. package/test/apify.test.js +93 -0
  329. package/test/apollo-io.test.js +125 -0
  330. package/test/asana.test.js +99 -0
  331. package/test/ashby.test.js +97 -0
  332. package/test/atlassian.test.js +249 -0
  333. package/test/attention.test.js +79 -0
  334. package/test/aws.test.js +405 -0
  335. package/test/brevo.test.js +71 -0
  336. package/test/calendly.test.js +136 -0
  337. package/test/canva.test.js +258 -0
  338. package/test/chargebee.test.js +195 -0
  339. package/test/clickhouse.test.js +278 -0
  340. package/test/confidence.test.js +205 -0
  341. package/test/databricks.test.js +253 -0
  342. package/test/dbt-semantic-layer.test.js +221 -0
  343. package/test/designMcp.test.js +336 -0
  344. package/test/elevenlabs.test.js +52 -0
  345. package/test/firebase-cloud-messaging.test.js +242 -0
  346. package/test/firecrawl.test.js +66 -0
  347. package/test/gemini-enterprise.test.js +218 -0
  348. package/test/github-api.test.js +167 -0
  349. package/test/gitlab-api.test.js +160 -0
  350. package/test/gong.test.js +192 -0
  351. package/test/google-ads.test.js +350 -0
  352. package/test/google-maps-platform.test.js +191 -0
  353. package/test/google.test.js +710 -0
  354. package/test/granola.test.js +240 -0
  355. package/test/hex.test.js +253 -0
  356. package/test/heygen.test.js +121 -0
  357. package/test/hubspot.test.js +103 -0
  358. package/test/inngest.test.js +219 -0
  359. package/test/lightspeed.test.js +250 -0
  360. package/test/linear.test.js +146 -0
  361. package/test/linkedin.test.js +243 -0
  362. package/test/logo-dev.test.js +79 -0
  363. package/test/mailgun.test.js +107 -0
  364. package/test/mapbox.test.js +181 -0
  365. package/test/mcp.test.js +227 -0
  366. package/test/microsoft-fabric.test.js +258 -0
  367. package/test/microsoft.test.js +554 -0
  368. package/test/n8nOAuth.test.js +188 -0
  369. package/test/notionOAuth.test.js +183 -0
  370. package/test/oura.test.js +211 -0
  371. package/test/paddleCatalogue.test.js +51 -0
  372. package/test/paddlePermissions.test.js +101 -0
  373. package/test/perplexityComposition.test.js +39 -0
  374. package/test/pipedriveOAuth.test.js +140 -0
  375. package/test/posthog.test.js +200 -0
  376. package/test/prestashop.test.js +207 -0
  377. package/test/resend.test.js +163 -0
  378. package/test/salesforce.test.js +276 -0
  379. package/test/semrush.test.js +479 -0
  380. package/test/sentry.test.js +35 -0
  381. package/test/shopify.test.js +247 -0
  382. package/test/slack.test.js +320 -0
  383. package/test/snowflake.test.js +391 -0
  384. package/test/tally.test.js +75 -0
  385. package/test/telegram.test.js +58 -0
  386. package/test/tiktok.test.js +243 -0
  387. package/test/tokens.test.js +1079 -0
  388. package/test/twilio.test.js +221 -0
  389. package/test/twitch.test.js +573 -0
  390. package/test/wave.test.js +678 -0
  391. package/test/wix.test.js +1099 -0
  392. package/test/wiz.test.js +185 -0
  393. package/test/wordpress-com.test.js +469 -0
  394. package/test/wordpress.test.js +660 -0
  395. package/test/workday.test.js +314 -0
  396. package/test/x-twitter.test.js +256 -0
  397. package/test/xero.test.js +700 -0
  398. package/test/zoho-books.test.js +253 -0
  399. package/test/zoho-crm.test.js +223 -0
@@ -0,0 +1,39 @@
1
+ /** Compose an app-owned Google map after the application has loaded Google's SDK. */
2
+ async function mountGoogleMap({ element, maps = globalThis.google?.maps, center, zoom = 12, mapId, markers = [], path = [], signal } = {}) {
3
+ const coordinate = point => {
4
+ if (!point || !Number.isFinite(point.lat) || Math.abs(point.lat) > 90 || !Number.isFinite(point.lng) || Math.abs(point.lng) > 180) throw new Error("Use finite lat/lng coordinates within Earth bounds.");
5
+ return { lat: point.lat, lng: point.lng };
6
+ };
7
+ if (!element?.replaceChildren || typeof maps?.importLibrary !== "function") throw new Error("Provide a map container and load the Google Maps JavaScript SDK first.");
8
+ if (!Number.isFinite(zoom) || zoom < 0 || zoom > 22) throw new Error("Use zoom between 0 and 22.");
9
+ if (!Array.isArray(markers) || markers.length > 100 || !Array.isArray(path) || path.length > 10000) throw new Error("Use at most 100 markers and 10000 route points.");
10
+ if (markers.length && (typeof mapId !== "string" || !mapId.trim())) throw new Error("Advanced markers require a Google JavaScript map ID.");
11
+ const position = coordinate(center);
12
+ const markerOptions = markers.map(marker => {
13
+ if (marker.title !== undefined && typeof marker.title !== "string") throw new Error("Marker titles must be text.");
14
+ return { position: coordinate(marker.position), title: marker.title || "" };
15
+ });
16
+ const route = path.map(coordinate);
17
+ signal?.throwIfAborted();
18
+ const library = await maps.importLibrary("maps");
19
+ const markerLibrary = markerOptions.length ? await maps.importLibrary("marker") : null;
20
+ signal?.throwIfAborted();
21
+ const map = new library.Map(element, { center: position, zoom, ...(mapId ? { mapId } : {}) });
22
+ const pins = []; let line; let disposed = false;
23
+ function dispose() {
24
+ if (disposed) return;
25
+ disposed = true;
26
+ signal?.removeEventListener("abort", dispose);
27
+ for (const pin of pins) { maps.event.clearInstanceListeners(pin); pin.map = null; }
28
+ if (line) { maps.event.clearInstanceListeners(line); line.setMap(null); }
29
+ maps.event.clearInstanceListeners(map);
30
+ element.replaceChildren();
31
+ }
32
+ try {
33
+ for (const options of markerOptions) pins.push(new markerLibrary.AdvancedMarkerElement({ ...options, map }));
34
+ if (route.length) line = new library.Polyline({ map, path: route });
35
+ signal?.addEventListener("abort", dispose, { once: true });
36
+ return { map, dispose };
37
+ } catch (error) { dispose(); throw error; }
38
+ }
39
+ export { mountGoogleMap };
@@ -0,0 +1,36 @@
1
+ import { createSchema } from "json-rest-schema";
2
+ import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
3
+
4
+ const logoDevImageSchema = createSchema({
5
+ publishableKey: { type: "string", required: true, minLength: 4, maxLength: 512,
6
+ validator: (value) => /^pk_[A-Za-z0-9_-]+$/.test(value) || "Use a Logo.dev publishable key starting with pk_." },
7
+ domain: { type: "string", minLength: 3, maxLength: 253,
8
+ validator: (value) => /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$/i.test(value) || "Enter an ASCII domain without a URL, port or path." },
9
+ ticker: { type: "string", minLength: 1, maxLength: 32,
10
+ validator: value => /^[a-z0-9][a-z0-9.-]*$/iu.test(value) || "Enter a stock ticker, optionally with its exchange suffix." },
11
+ email: { type: "string", minLength: 3, maxLength: 320,
12
+ validator: value => /^[^\s@]+@[^\s@]+$/u.test(value) || "Enter an email address for company-domain lookup." },
13
+ size: { type: "integer", min: 1, max: 800, defaultTo: 128 },
14
+ format: { type: "string", enum: ["png", "jpg", "webp", "svg"], defaultTo: "png" },
15
+ theme: { type: "string", enum: ["auto", "light", "dark"], defaultTo: "auto" },
16
+ greyscale: { type: "boolean", defaultTo: false },
17
+ retina: { type: "boolean", defaultTo: false },
18
+ fallback: { type: "string", enum: ["monogram", "404"], defaultTo: "monogram" }
19
+ });
20
+
21
+ function createLogoDevImageUrl(input) {
22
+ const { publishableKey, domain, ticker, email, ...parameters } = validateSchemaPayload({ schema: logoDevImageSchema, mode: "replace" }, input, { statusCode: 422 });
23
+ if ([domain, ticker, email].filter(value => value !== undefined).length !== 1) {
24
+ const error = new Error("Choose exactly one domain, stock ticker or email address.");
25
+ error.fieldErrors = { domain: "Choose exactly one lookup value." }; throw error;
26
+ }
27
+ // Resolve email locally: never transmit the address's local part to the CDN.
28
+ if (email !== undefined) return createLogoDevImageUrl({ publishableKey, domain: email.split("@")[1], ...parameters });
29
+ const identifier = ticker === undefined ? domain.toLowerCase() : `ticker/${ticker.toUpperCase()}`;
30
+ const url = new URL(`https://img.logo.dev/${identifier}`);
31
+ url.searchParams.set("token", publishableKey);
32
+ for (const [key, value] of Object.entries(parameters)) url.searchParams.set(key, String(value));
33
+ return url.href;
34
+ }
35
+
36
+ export { createLogoDevImageUrl, logoDevImageSchema };
@@ -0,0 +1,46 @@
1
+ import { ConnectorError, createEnvironmentReferenceResolver } from "@jskit-ai/connectors-core/server";
2
+ import { validateIntegrationConfiguration } from "@jskit-ai/connectors-core/shared/configuration";
3
+ import { aiCatalogue, aiDefinition, getAiModel } from "../shared/ai.js";
4
+
5
+ // A server-only credential boundary. The consuming application's SDK owns all
6
+ // inference, transport, streaming, tool execution and error/fallback policy.
7
+ function createAiConnectionResolver({ configuration, authorize, resolveReference = createEnvironmentReferenceResolver() }) {
8
+ if (typeof authorize !== "function") throw new TypeError("AI connections require application authorization.");
9
+ const config = validateIntegrationConfiguration(configuration, { providers: [aiDefinition], allowUnknownProviders: true });
10
+
11
+ async function resolve({ context, integrationId }) {
12
+ const integration = Object.hasOwn(config.integrations, integrationId) ? config.integrations[integrationId] : null;
13
+ if (integration?.provider !== "ai") throw new ConnectorError("connector_not_found", "This AI connection is not configured.", { statusCode: 404 });
14
+ const owner = await authorize(context, { integrationId, operation: "ai.resolve", accountMode: integration.accountMode });
15
+ if (!owner?.applicationId || !owner?.subjectId) {
16
+ throw new ConnectorError("connector_access_denied", "Access to this AI connection was denied.", { statusCode: 403 });
17
+ }
18
+ const model = getAiModel(integration.settings.model);
19
+ const provider = aiCatalogue.providers.find((item) => item.id === model.providerId);
20
+ let apiKey;
21
+ if (integration.authentication.method === "none") {
22
+ // OpenCode Zen's published no-account protocol uses this fixed marker.
23
+ // It is not a user's secret and requires no OpenCode process or account.
24
+ apiKey = "public";
25
+ } else {
26
+ if (integration.accountMode === "per-user" && integration.authentication.secretRef.startsWith("env:")) {
27
+ throw new ConnectorError("connector_binding_invalid", "Individual AI accounts require a user-owned reference, not a shared environment variable.", { statusCode: 422 });
28
+ }
29
+ try {
30
+ apiKey = await resolveReference(integration.authentication.secretRef, { ...owner }, {
31
+ integrationId, accountMode: integration.accountMode, providerId: provider.id
32
+ });
33
+ if (typeof apiKey !== "string" || !apiKey.trim() || /[\r\n\0]/u.test(apiKey) || apiKey.length > 16384) throw new Error();
34
+ } catch {
35
+ throw new ConnectorError("connector_binding_missing", "The authorized AI account's API key is unavailable.", { statusCode: 422 });
36
+ }
37
+ }
38
+ // Never expose this result through a browser API or include it in logs.
39
+ // Missing baseURL means use the named provider SDK's own default endpoint.
40
+ return Object.freeze({ providerId: provider.id, model: model.id, sdkPackage: model.sdkPackage,
41
+ ...(provider.baseURL ? { baseURL: provider.baseURL } : {}), apiKey, access: model.access });
42
+ }
43
+ return Object.freeze({ resolve });
44
+ }
45
+
46
+ export { createAiConnectionResolver };
@@ -0,0 +1,67 @@
1
+ import { createSchema } from "json-rest-schema";
2
+ import { airtableDefinition } from "../shared/tokens.js";
3
+ import { jsonOperation, validatedOperation } from "./jsonOperation.js";
4
+
5
+ const identifier = { type: "string", required: true, minLength: 1, maxLength: 200,
6
+ pattern: "^[A-Za-z0-9_-]+$" };
7
+ const table = { type: "string", required: true, minLength: 1, maxLength: 1000,
8
+ validator: value => ![".", ".."].includes(value) || "Use a table ID or name." };
9
+ const name = { type: "string", required: true, minLength: 1, maxLength: 1000 };
10
+ const description = { type: "string", maxLength: 20000 };
11
+ const fields = { type: "object", required: true, additionalProperties: true,
12
+ validator: value => (Object.keys(value).length > 0 && Object.keys(value).every(key => key.length > 0 && key.length <= 1000)) || "Provide named fields to change." };
13
+ const fieldSchema = createSchema({ name, type: { ...name, maxLength: 100 },
14
+ description, options: { type: "object", additionalProperties: true } });
15
+ const tableFields = { type: "array", required: true,
16
+ validator: value => (value.length > 0 && value.length <= 500 && new Set(value.map(field => field.name.toLowerCase())).size === value.length) || "Provide 1–500 fields with unique names.",
17
+ items: { type: "object", schema: fieldSchema } };
18
+ const tableSchema = createSchema({ name, description, fields: tableFields });
19
+ const basePath = baseId => `https://api.airtable.com/v0/meta/bases/${encodeURIComponent(baseId)}/tables`;
20
+ const recordPath = (baseId, tableId) => `https://api.airtable.com/v0/${encodeURIComponent(baseId)}/${encodeURIComponent(tableId)}`;
21
+ const recordResult = result => typeof result?.id === "string" && !!result.fields && typeof result.fields === "object";
22
+ const tableResult = result => typeof result?.id === "string" && typeof result.name === "string";
23
+
24
+ const airtableProvider = Object.freeze({
25
+ ...airtableDefinition, apiOrigins: ["https://api.airtable.com"],
26
+ apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}` }) },
27
+ checkOperation: "bases.list",
28
+ operations: {
29
+ "bases.create": validatedOperation({ name, workspaceId: identifier,
30
+ tables: { type: "array", required: true,
31
+ validator: value => value.length > 0 || "Provide at least one table.",
32
+ items: { type: "object", schema: tableSchema } }
33
+ }, body => ({ method: "POST", url: "https://api.airtable.com/v0/meta/bases", body }),
34
+ result => typeof result?.id === "string" && Array.isArray(result.tables)),
35
+ "tables.list": validatedOperation({ baseId: identifier }, ({ baseId }) => ({ method: "GET", url: basePath(baseId) }),
36
+ result => Array.isArray(result?.tables)),
37
+ "tables.create": validatedOperation({ baseId: identifier, name, description,
38
+ fields: tableFields
39
+ }, ({ baseId, ...body }) => ({ method: "POST", url: basePath(baseId), body }), tableResult),
40
+ "tables.update": validatedOperation({ baseId: identifier, tableId: table, name, description },
41
+ ({ baseId, tableId, ...body }) => ({ method: "PATCH", url: `${basePath(baseId)}/${encodeURIComponent(tableId)}`, body }), tableResult),
42
+ "fields.create": validatedOperation({ baseId: identifier, tableId: identifier,
43
+ field: { type: "object", required: true, schema: fieldSchema } },
44
+ ({ baseId, tableId, field }) => ({ method: "POST", url: `${basePath(baseId)}/${tableId}/fields`, body: field }), tableResult),
45
+ "fields.update": validatedOperation({ baseId: identifier, tableId: identifier, fieldId: identifier, name, description,
46
+ options: { type: "object", additionalProperties: true } },
47
+ ({ baseId, tableId, fieldId, ...body }) => ({ method: "PATCH", url: `${basePath(baseId)}/${tableId}/fields/${fieldId}`, body }), tableResult),
48
+ "records.list": validatedOperation({ baseId: identifier, tableId: table,
49
+ pageSize: { type: "integer", min: 1, max: 100, defaultTo: 100 },
50
+ offset: { type: "string", minLength: 1, maxLength: 4096 },
51
+ view: { type: "string", minLength: 1, maxLength: 1000 },
52
+ filterByFormula: { type: "string", minLength: 1, maxLength: 10000 }
53
+ }, ({ baseId, tableId, ...body }) => ({ method: "POST", url: `${recordPath(baseId, tableId)}/listRecords`, body }),
54
+ result => Array.isArray(result?.records) && (result.offset === undefined || typeof result.offset === "string")),
55
+ "records.create": validatedOperation({ baseId: identifier, tableId: table, fields },
56
+ ({ baseId, tableId, fields }) => ({ method: "POST", url: recordPath(baseId, tableId), body: { fields, typecast: false } }), recordResult),
57
+ "records.update": validatedOperation({ baseId: identifier, tableId: table, recordId: identifier, fields },
58
+ ({ baseId, tableId, recordId, fields }) => ({ method: "PATCH", url: `${recordPath(baseId, tableId)}/${recordId}`, body: { fields, typecast: false } }), recordResult),
59
+ "records.delete": validatedOperation({ baseId: identifier, tableId: table, recordId: identifier },
60
+ ({ baseId, tableId, recordId }) => ({ method: "DELETE", url: `${recordPath(baseId, tableId)}/${recordId}` }),
61
+ result => typeof result?.id === "string" && result.deleted === true),
62
+ "bases.list": jsonOperation("https://api.airtable.com/v0/meta/bases", {
63
+ offset: { type: "string", minLength: 1, maxLength: 4096 }
64
+ }, (result) => Array.isArray(result?.bases) && (result.offset === undefined || typeof result.offset === "string"))
65
+ }
66
+ });
67
+ export { airtableProvider };
@@ -0,0 +1,63 @@
1
+ import { createSchema } from "json-rest-schema";
2
+ import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
3
+ import { algoliaDefinition } from "../shared/algolia.js";
4
+ import { jsonOperation } from "./jsonOperation.js";
5
+
6
+ const origin = ({ applicationId }) => `https://${applicationId.toLowerCase()}.algolia.net`;
7
+ const searchSchema = createSchema({
8
+ indexName: { type: "string", required: true, minLength: 1, maxLength: 255,
9
+ validator: (value) => ![".", ".."].includes(value) || "Enter an index name." },
10
+ query: { type: "string", defaultTo: "", maxLength: 512,
11
+ validator: (value) => Buffer.byteLength(value, "utf8") <= 512 || "Use a query of at most 512 UTF-8 bytes." },
12
+ page: { type: "integer", min: 0, max: 2147483647, defaultTo: 0 },
13
+ hitsPerPage: { type: "integer", min: 1, max: 1000, defaultTo: 20 }
14
+ });
15
+ const indexField = { type: "string", required: true, minLength: 1, maxLength: 255,
16
+ validator: value => ![".", ".."].includes(value) || "Enter an index name." };
17
+ const objectField = { type: "string", required: true, minLength: 1, maxLength: 512,
18
+ validator: value => ![".", ".."].includes(value) || "Enter a record ID." };
19
+ const writeSchema = createSchema({ indexName: indexField, objectID: objectField,
20
+ attributes: { type: "object", required: true, additionalProperties: true,
21
+ validator: value => (Object.keys(value).length > 0 && !Object.hasOwn(value, "objectID")) || "Provide attributes without objectID; use the separate record ID." } });
22
+ const deleteSchema = createSchema({ indexName: indexField, objectID: objectField });
23
+ const taskSchema = createSchema({ indexName: indexField,
24
+ taskID: { type: "integer", required: true, min: 0, max: Number.MAX_SAFE_INTEGER } });
25
+ const taskResult = result => Number.isSafeInteger(result?.taskID) && result.taskID >= 0;
26
+ function writeOperation(method, suffix = "") {
27
+ return { scopes: [], request(input, settings) {
28
+ const { indexName, objectID, attributes } = validateSchemaPayload({ schema: writeSchema, mode: "replace" }, input, { statusCode: 422 });
29
+ return { method, url: `${origin(settings)}/1/indexes/${encodeURIComponent(indexName)}/${encodeURIComponent(objectID)}${suffix}`,
30
+ body: attributes };
31
+ }, validateResult: result => taskResult(result) && typeof result.objectID === "string" };
32
+ }
33
+ const algoliaProvider = Object.freeze({
34
+ ...algoliaDefinition,
35
+ apiOrigins: (settings) => [origin(settings)],
36
+ apiKey: { headers: (key, settings) => ({ "X-Algolia-Application-Id": settings.applicationId, "X-Algolia-API-Key": key }) },
37
+ checkOperation: "indices.list",
38
+ operations: {
39
+ "records.replace": writeOperation("PUT"),
40
+ "records.update": writeOperation("POST", "/partial?createIfNotExists=false"),
41
+ "records.delete": { scopes: [], request(input, settings) {
42
+ const { indexName, objectID } = validateSchemaPayload({ schema: deleteSchema, mode: "replace" }, input, { statusCode: 422 });
43
+ return { method: "DELETE", url: `${origin(settings)}/1/indexes/${encodeURIComponent(indexName)}/${encodeURIComponent(objectID)}` };
44
+ }, validateResult: taskResult },
45
+ "tasks.get": { scopes: [], request(input, settings) {
46
+ const { indexName, taskID } = validateSchemaPayload({ schema: taskSchema, mode: "replace" }, input, { statusCode: 422 });
47
+ return { method: "GET", url: `${origin(settings)}/1/indexes/${encodeURIComponent(indexName)}/task/${taskID}` };
48
+ }, validateResult: result => ["published", "notPublished"].includes(result?.status) },
49
+ "indices.list": jsonOperation((settings) => `${origin(settings)}/1/indexes`, {
50
+ page: { type: "integer", min: 0, max: 2147483647, defaultTo: 0 },
51
+ hitsPerPage: { type: "integer", min: 1, max: 1000, defaultTo: 100 }
52
+ }, (result) => Array.isArray(result?.items) && (result.nbPages === undefined || Number.isInteger(result.nbPages) && result.nbPages >= 0)),
53
+ "index.search": {
54
+ scopes: [],
55
+ request(input, settings) {
56
+ const { indexName, ...body } = validateSchemaPayload({ schema: searchSchema, mode: "replace" }, input, { statusCode: 422 });
57
+ return { method: "POST", url: `${origin(settings)}/1/indexes/${encodeURIComponent(indexName)}/query`, body };
58
+ },
59
+ validateResult: (result) => Array.isArray(result?.hits) && ["nbHits", "nbPages", "page", "hitsPerPage"].every((key) => Number.isInteger(result[key]) && result[key] >= 0)
60
+ }
61
+ }
62
+ });
63
+ export { algoliaProvider };
@@ -0,0 +1,96 @@
1
+ import { RedshiftDataClient, ListDatabasesCommand, ListSchemasCommand, ListTablesCommand, DescribeTableCommand, ExecuteStatementCommand, DescribeStatementCommand, GetStatementResultCommand, CancelStatementCommand } from "@aws-sdk/client-redshift-data";
2
+ import { ConnectorError } from "@jskit-ai/connectors-core/server";
3
+ import { createSchema } from "json-rest-schema";
4
+ import { amazonRedshiftDefinition } from "../shared/amazon-redshift.js";
5
+ import { awsOrigin, awsOperation, awsExchange } from "./awsTransport.js";
6
+
7
+ const statementPattern = "^[a-z0-9]{8}(-[a-z0-9]{4}){3}-[a-z0-9]{12}(:[0-9]{1,2})?$";
8
+ const statementId = { type: "string", required: true, pattern: statementPattern };
9
+ const text = { type: "string", noTrim: true, minLength: 1, maxLength: 1024 };
10
+ const nextToken = { ...text, maxLength: 8192 };
11
+ const maxResults = { type: "integer", min: 1, max: 1000, defaultTo: 100 };
12
+ const hasCursor = (r) => r?.NextToken === undefined || typeof r.NextToken === "string";
13
+ const validId = (r) => typeof r?.Id === "string" && new RegExp(statementPattern, "u").test(r.Id);
14
+ const validStatement = (r) => validId(r) && typeof r.Database === "string" &&
15
+ ["SUBMITTED", "PICKED", "STARTED", "FINISHED", "ABORTED", "FAILED"].includes(r.Status);
16
+ const validColumns = (columns) => Array.isArray(columns) && columns.every((c) => c && typeof c.name === "string" && typeof c.typeName === "string");
17
+ const target = (s) => ({ Database: s.database, ...(s.deploymentType === "provisioned"
18
+ ? { ClusterIdentifier: s.clusterIdentifier, ...(s.databaseUser ? { DbUser: s.databaseUser } : {}) }
19
+ : { WorkgroupName: s.workgroup }) });
20
+ const pageInput = (v) => ({ ...(v.nextToken ? { NextToken: v.nextToken } : {}), MaxResults: v.maxResults });
21
+
22
+ function assertTarget(result, settings) {
23
+ const matches = result.Database === settings.database && (settings.deploymentType === "provisioned"
24
+ ? result.ClusterIdentifier === settings.clusterIdentifier && !result.WorkgroupName && (!settings.databaseUser || result.DbUser === settings.databaseUser)
25
+ : result.WorkgroupName === settings.workgroup && !result.ClusterIdentifier);
26
+ if (!matches) throw new ConnectorError("connector_access_denied", "This statement belongs to a different Redshift target.", { statusCode: 403 });
27
+ }
28
+
29
+ function validCell(cell) {
30
+ if (!cell || Object.keys(cell).length !== 1) return false;
31
+ const [key, value] = Object.entries(cell)[0];
32
+ if (key === "isNull") return value === true;
33
+ if (key === "booleanValue") return typeof value === "boolean";
34
+ if (key === "longValue") return Number.isSafeInteger(value);
35
+ if (key === "doubleValue") return typeof value === "number" && Number.isFinite(value);
36
+ if (key === "stringValue") return typeof value === "string";
37
+ return key === "blobValue" && value instanceof Uint8Array;
38
+ }
39
+
40
+ const amazonRedshiftProvider = Object.freeze({
41
+ ...amazonRedshiftDefinition,
42
+ apiOrigins: (settings) => [awsOrigin("redshift-data", settings)], apiKey: { headers: () => ({}) }, checkOperation: "tables.list",
43
+ exchange: awsExchange("redshift-data", RedshiftDataClient, async (client, body, { settings, signal }) => {
44
+ const send = (command) => client.send(command, { abortSignal: signal });
45
+ if (body.command === "ListDatabases") return send(new ListDatabasesCommand(body.input));
46
+ if (body.command === "ListSchemas") return send(new ListSchemasCommand(body.input));
47
+ if (body.command === "ListTables") return send(new ListTablesCommand(body.input));
48
+ if (body.command === "DescribeTable") return send(new DescribeTableCommand(body.input));
49
+ if (body.command === "ExecuteStatement") {
50
+ const result = await send(new ExecuteStatementCommand(body.input));
51
+ if (!validId(result)) throw new ConnectorError("connector_response_invalid", "Redshift did not return a statement identifier.", { statusCode: 502 });
52
+ assertTarget(result, settings);
53
+ return result;
54
+ }
55
+ const statement = await send(new DescribeStatementCommand({ Id: body.input.Id }));
56
+ if (!validStatement(statement) || statement.Id !== body.input.Id) throw new ConnectorError("connector_response_invalid", "Redshift returned an unexpected statement.", { statusCode: 502 });
57
+ assertTarget(statement, settings);
58
+ if (body.command === "DescribeStatement") return statement;
59
+ if (body.command === "CancelStatement") return send(new CancelStatementCommand(body.input));
60
+ if (statement.Status !== "FINISHED" || !statement.HasResultSet) throw new ConnectorError("connector_result_unavailable", "This statement has no finished result set.", { statusCode: 409 });
61
+ return send(new GetStatementResultCommand(body.input));
62
+ }),
63
+ operations: {
64
+ "databases.list": awsOperation("redshift-data", "ListDatabases", { maxResults, nextToken },
65
+ (v, s) => ({ input: { ...target(s), ...pageInput(v) } }),
66
+ (r) => Array.isArray(r?.Databases) && r.Databases.length <= 1000 && r.Databases.every((name) => typeof name === "string") && hasCursor(r)),
67
+ "schemas.list": awsOperation("redshift-data", "ListSchemas", { maxResults, nextToken, schemaPattern: text },
68
+ (v, s) => ({ input: { ...target(s), ...pageInput(v), ...(v.schemaPattern ? { SchemaPattern: v.schemaPattern } : {}) } }),
69
+ (r) => Array.isArray(r?.Schemas) && r.Schemas.length <= 1000 && r.Schemas.every((name) => typeof name === "string") && hasCursor(r)),
70
+ "tables.list": awsOperation("redshift-data", "ListTables", { maxResults, nextToken, schemaPattern: text, tablePattern: text },
71
+ (v, s) => ({ input: { ...target(s), ...pageInput(v), ...(v.schemaPattern ? { SchemaPattern: v.schemaPattern } : {}), ...(v.tablePattern ? { TablePattern: v.tablePattern } : {}) } }),
72
+ (r) => Array.isArray(r?.Tables) && r.Tables.length <= 1000 && r.Tables.every((t) => typeof t?.name === "string" && typeof t.schema === "string" && typeof t.type === "string") && hasCursor(r)),
73
+ "table.describe": awsOperation("redshift-data", "DescribeTable", { table: { ...text, required: true }, schema: text, maxResults, nextToken },
74
+ (v, s) => ({ input: { ...target(s), ...pageInput(v), Table: v.table, ...(v.schema ? { Schema: v.schema } : {}) } }),
75
+ (r) => validColumns(r?.ColumnList) && r.ColumnList.length <= 1000 && typeof r.TableName === "string" && hasCursor(r)),
76
+ "query.start": awsOperation("redshift-data", "ExecuteStatement", {
77
+ sql: { type: "string", required: true, noTrim: true, minLength: 1, maxLength: 200000,
78
+ validator: (v) => (v.trim().length > 0 && Buffer.byteLength(v, "utf8") <= 200000) || "Enter SQL of at most 200000 UTF-8 bytes." },
79
+ clientToken: { ...text, required: true, maxLength: 64 },
80
+ parameters: { type: "array", validator: (v) => (v.length >= 1 && v.length <= 100) || "Supply between 1 and 100 SQL parameters, or omit parameters.", items: { type: "object", schema: createSchema({
81
+ name: { type: "string", required: true, pattern: "^[a-zA-Z0-9_]+$" }, value: { ...text, required: true, maxLength: 65536 }
82
+ }) } }
83
+ }, (v, s) => {
84
+ if (v.parameters && new Set(v.parameters.map((p) => p.name)).size !== v.parameters.length) throw new ConnectorError("connector_input_invalid", "Use each SQL parameter name only once.", { statusCode: 422 });
85
+ return { input: { ...target(s), Sql: v.sql, ClientToken: v.clientToken, ResultFormat: "JSON", ...(v.parameters ? { Parameters: v.parameters } : {}) } };
86
+ }, validId),
87
+ "query.status": awsOperation("redshift-data", "DescribeStatement", { statementId }, (v) => ({ input: { Id: v.statementId } }), validStatement),
88
+ "query.results": awsOperation("redshift-data", "GetStatementResult", { statementId, nextToken },
89
+ (v) => ({ input: { Id: v.statementId, ...(v.nextToken ? { NextToken: v.nextToken } : {}) } }),
90
+ (r) => validColumns(r?.ColumnMetadata) && Array.isArray(r.Records) && r.Records.every((row) => Array.isArray(row) && row.length === r.ColumnMetadata.length && row.every(validCell)) &&
91
+ (r.TotalNumRows === undefined || Number.isSafeInteger(r.TotalNumRows) && r.TotalNumRows >= 0) && hasCursor(r)),
92
+ "query.cancel": awsOperation("redshift-data", "CancelStatement", { statementId }, (v) => ({ input: { Id: v.statementId } }), (r) => typeof r?.Status === "boolean")
93
+ }
94
+ });
95
+
96
+ export { amazonRedshiftProvider };
@@ -0,0 +1,31 @@
1
+ import { createSchema } from "json-rest-schema";
2
+ import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
3
+ import { mcpRegistrationFields, registerMcpClient } from "./mcpRegistration.js";
4
+ import { amplitudeDefinition } from "../shared/amplitude.js";
5
+ import { mcpOperations, exchangeMcp } from "./mcpTransport.js";
6
+
7
+ const origin = (settings) => settings.region === "eu" ? "https://mcp.eu.amplitude.com" : "https://mcp.amplitude.com";
8
+ const oauth = (settings) => ({ issuer: origin(settings), authorization_endpoint: `${origin(settings)}/authorize`,
9
+ token_endpoint: `${origin(settings)}/token`, registration_endpoint: amplitudeDefinition.setup.clientRegistrationEndpoint(settings), response_types_supported: ["code"] });
10
+ const operations = mcpOperations((settings) => `${origin(settings)}/mcp`);
11
+ const amplitudeProvider = Object.freeze({
12
+ ...amplitudeDefinition, oauth, oauthResource: origin,
13
+ apiOrigins: (settings) => [origin(settings)], checkOperation: "tools.list", exchange: exchangeMcp,
14
+ operations: Object.fromEntries(Object.entries(operations).map(([name, operation]) => [name, { ...operation, scopes: ["mcp:read"] }]))
15
+ });
16
+
17
+ const registrationSchema = createSchema({
18
+ region: { type: "string", enum: ["us", "eu"], defaultTo: "us" },
19
+ ...mcpRegistrationFields,
20
+ scopes: { type: "array", required: true, minLength: 1,
21
+ items: { type: "string", enum: amplitudeDefinition.scopes.map((scope) => scope.value) },
22
+ validator: (scopes) => scopes.includes("mcp:read") && new Set(scopes).size === scopes.length || "Select read access and include each permission once." }
23
+ });
24
+
25
+ // Explicit setup operation. Callers own authorization and secure storage of its returned secret.
26
+ async function registerAmplitudeClient(input, options) {
27
+ const values = validateSchemaPayload({ schema: registrationSchema, mode: "replace" }, input, { statusCode: 422 });
28
+ return registerMcpClient("Amplitude", oauth(values), values, options);
29
+ }
30
+
31
+ export { amplitudeProvider, registerAmplitudeClient };
@@ -0,0 +1,68 @@
1
+ import { ConnectorError } from "@jskit-ai/connectors-core/server";
2
+ import { apifyDefinition } from "../shared/tokens.js";
3
+ import { jsonOperation, validatedOperation } from "./jsonOperation.js";
4
+
5
+ const id = { type: "string", required: true, minLength: 1, maxLength: 250, pattern: "^[A-Za-z0-9_~-]+$" };
6
+ const runResult = value => typeof value?.data?.id === "string" && typeof value.data.status === "string";
7
+ const apifyProvider = Object.freeze({
8
+ ...apifyDefinition, apiOrigins: ["https://api.apify.com"],
9
+ apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}` }) },
10
+ checkOperation: "actors.list",
11
+ async exchange(url, options, { request, fetchImpl }) {
12
+ if (!new URL(url).pathname.startsWith("/v2/key-value-stores/")) return request(url, options);
13
+ const response = await fetchImpl(url, { ...options, credentials: "omit", redirect: "error" });
14
+ if (!response.ok) {
15
+ await response.body?.cancel();
16
+ throw Object.assign(new Error("Apify storage request failed."), { status: response.status });
17
+ }
18
+ const reader = response.body?.getReader();
19
+ const chunks = [];
20
+ let size = 0;
21
+ if (reader) try {
22
+ while (true) {
23
+ const { done, value } = await reader.read();
24
+ if (done) break;
25
+ size += value.byteLength;
26
+ if (size > 8 * 1024 * 1024) {
27
+ await reader.cancel();
28
+ throw new ConnectorError("connector_response_too_large", "Use the framework's native Apify client to stream records larger than 8 MiB.", { statusCode: 413 });
29
+ }
30
+ chunks.push(value);
31
+ }
32
+ } finally { reader.releaseLock(); }
33
+ return { contentType: response.headers.get("content-type") || "application/octet-stream",
34
+ bodyBase64: Buffer.concat(chunks).toString("base64"), size };
35
+ },
36
+ operations: {
37
+ "actors.get": validatedOperation({ actorId: id }, ({ actorId }) => ({ method: "GET", url: `https://api.apify.com/v2/actors/${actorId}` }),
38
+ result => typeof result?.data?.id === "string"),
39
+ "runs.start": validatedOperation({ actorId: id,
40
+ input: { type: "object", required: true, additionalProperties: true },
41
+ timeout: { type: "integer", required: true, min: 1, max: 86400 },
42
+ maxTotalChargeUsd: { type: "number", required: true, min: 0.01, max: 100000 },
43
+ build: { type: "string", minLength: 1, maxLength: 100, pattern: "^[A-Za-z0-9_.-]+$" }
44
+ }, ({ actorId, input, ...options }) => {
45
+ const url = new URL(`https://api.apify.com/v2/actors/${actorId}/runs`);
46
+ for (const [name, value] of Object.entries(options)) url.searchParams.set(name, String(value));
47
+ url.searchParams.set("restartOnError", "false");
48
+ url.searchParams.set("waitForFinish", "0");
49
+ return { method: "POST", url: url.href, body: input };
50
+ }, runResult),
51
+ "runs.get": validatedOperation({ runId: id }, ({ runId }) => ({ method: "GET", url: `https://api.apify.com/v2/actor-runs/${runId}` }), runResult),
52
+ "runs.abort": validatedOperation({ runId: id }, ({ runId }) => ({ method: "POST", url: `https://api.apify.com/v2/actor-runs/${runId}/abort?gracefully=true` }), runResult),
53
+ "datasets.items": validatedOperation({ datasetId: id,
54
+ offset: { type: "integer", min: 0, defaultTo: 0 }, limit: { type: "integer", min: 1, max: 1000, defaultTo: 100 }
55
+ }, ({ datasetId, offset, limit }) => ({ method: "GET",
56
+ url: `https://api.apify.com/v2/datasets/${datasetId}/items?format=json&offset=${offset}&limit=${limit}` }), Array.isArray),
57
+ "stores.record": validatedOperation({ storeId: id, key: { type: "string", required: true, minLength: 1, maxLength: 250,
58
+ validator: value => ![".", ".."].includes(value) || "Enter a storage key." }
59
+ }, ({ storeId, key }) => ({ method: "GET", url: `https://api.apify.com/v2/key-value-stores/${storeId}/records/${encodeURIComponent(key)}` }),
60
+ result => typeof result?.bodyBase64 === "string" && typeof result.contentType === "string"),
61
+ "actors.list": jsonOperation("https://api.apify.com/v2/actors", {
62
+ limit: { type: "integer", min: 1, max: 1000, defaultTo: 100 },
63
+ offset: { type: "integer", min: 0, defaultTo: 0 },
64
+ desc: { type: "boolean", defaultTo: false }
65
+ }, (result) => Array.isArray(result?.data?.items) && Number.isInteger(result.data.total) && Number.isInteger(result.data.offset))
66
+ }
67
+ });
68
+ export { apifyProvider };
@@ -0,0 +1,85 @@
1
+ import { ConnectorError } from "@jskit-ai/connectors-core/server";
2
+ import { apolloIoDefinition } from "../shared/tokens.js";
3
+ import { jsonOperation, validatedOperation } from "./jsonOperation.js";
4
+
5
+ const text = { type: "string", minLength: 1, maxLength: 256 };
6
+ const id = { ...text, pattern: "^[A-Za-z0-9_-]+$" };
7
+ const page = { page: { type: "integer", min: 1, max: 500, defaultTo: 1 },
8
+ per_page: { type: "integer", min: 1, max: 100, defaultTo: 25 } };
9
+ const strings = { type: "array", items: text, validator: value => value.length <= 100 || "Use at most 100 filter values." };
10
+ const domain = { ...text, pattern: "^(?!www\\.)(?:[A-Za-z0-9-]+\\.)+[A-Za-z]{2,}$" };
11
+ const charge = { type: "boolean", required: true, validator: value => value === true || "Explicitly authorize provider credit consumption." };
12
+ const record = key => result => typeof result?.[key]?.id === "string";
13
+ const collection = key => result => Array.isArray(result?.[key]);
14
+ const accountFields = { name: text, domain, owner_id: id, account_stage_id: id, phone: text, raw_address: text };
15
+ const contactFields = { first_name: text, last_name: text, organization_name: text, title: text,
16
+ account_id: id, email: { ...text, pattern: "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$" },
17
+ label_names: strings, contact_stage_id: id, present_raw_address: text, direct_phone: text,
18
+ corporate_phone: text, mobile_phone: text };
19
+ const dealFields = { name: text, owner_id: id,
20
+ amount: { ...text, pattern: "^[0-9]+(?:\\.[0-9]+)?$" }, opportunity_stage_id: id,
21
+ closed_date: { ...text, pattern: "^\\d{4}-\\d{2}-\\d{2}$",
22
+ validator: value => { const date = new Date(value); return !Number.isNaN(date.valueOf()) && date.toISOString().slice(0, 10) === value || "Enter a real YYYY-MM-DD date."; } } };
23
+
24
+ function query(method, endpoint, input) {
25
+ const url = new URL(`https://api.apollo.io/api/v1/${endpoint}`);
26
+ for (const [key, value] of Object.entries(input)) {
27
+ if (key === "allowCreditConsumption") continue;
28
+ if (Array.isArray(value)) for (const entry of value) url.searchParams.append(`${key}[]`, entry);
29
+ else url.searchParams.set(key, String(value));
30
+ }
31
+ return { method, url: url.href };
32
+ }
33
+ function write(method, endpoint, fields, key, requiredNames = []) {
34
+ return validatedOperation({ ...(method === "PATCH" ? { id: { ...id, required: true } } : {}), ...fields },
35
+ ({ id: recordId, ...body }) => {
36
+ if (!Object.keys(body).length || (requiredNames.length && !requiredNames.some(name => body[name]))) {
37
+ throw new ConnectorError("connector_input_invalid", "Enter the record's required identifying fields or changes.", { statusCode: 422 });
38
+ }
39
+ return { method, url: `https://api.apollo.io/api/v1/${endpoint}${recordId ? `/${recordId}` : ""}`, body };
40
+ }, record(key));
41
+ }
42
+ const apolloIoProvider = Object.freeze({
43
+ ...apolloIoDefinition,
44
+ apiOrigins: ["https://api.apollo.io"],
45
+ apiKey: { headers: (key) => ({ "X-Api-Key": key }) },
46
+ checkOperation: "accounts.search",
47
+ operations: {
48
+ "accounts.search": jsonOperation("https://api.apollo.io/api/v1/accounts/search", {
49
+ ...page, q_organization_name: text
50
+ }, collection("accounts"), "POST"),
51
+ "people.search": validatedOperation({ ...page, q_person_name: text, q_keywords: text,
52
+ person_titles: strings, person_locations: strings, organization_ids: strings,
53
+ q_organization_domains_list: { ...strings, items: domain },
54
+ include_similar_titles: { type: "boolean" }
55
+ }, input => query("POST", "mixed_people/api_search", input), collection("people")),
56
+ "organizations.search": validatedOperation({ ...page, q_organization_name: text,
57
+ q_organization_domains_list: { ...strings, items: domain }, organization_locations: strings,
58
+ allowCreditConsumption: charge
59
+ }, input => query("POST", "mixed_companies/search", input), collection("organizations")),
60
+ "people.enrich": validatedOperation({ id: { ...id, required: true }, allowCreditConsumption: charge,
61
+ reveal_personal_emails: { type: "boolean", defaultTo: false }
62
+ }, input => query("POST", "people/match", { ...input, reveal_phone_number: false,
63
+ run_waterfall_email: false, run_waterfall_phone: false }),
64
+ result => result?.person === null || record("person")(result)),
65
+ "organizations.enrich": validatedOperation({ domain: { ...domain, required: true }, allowCreditConsumption: charge },
66
+ input => query("GET", "organizations/enrich", input),
67
+ result => result?.organization === null || record("organization")(result)),
68
+ "contacts.search": jsonOperation("https://api.apollo.io/api/v1/contacts/search", {
69
+ ...page, q_keywords: text, contact_stage_ids: strings, contact_label_ids: strings
70
+ }, collection("contacts"), "POST"),
71
+ "contacts.create": write("POST", "contacts", { ...contactFields,
72
+ run_dedupe: { type: "boolean", required: true }
73
+ }, "contact", ["first_name", "last_name", "email"]),
74
+ "contacts.update": write("PATCH", "contacts", contactFields, "contact"),
75
+ "accounts.create": write("POST", "accounts", accountFields, "account", ["name", "domain"]),
76
+ "accounts.update": write("PATCH", "accounts", accountFields, "account"),
77
+ "deals.list": validatedOperation({ ...page, sort_by_field: { ...text, enum: ["amount", "is_closed", "is_won"] } },
78
+ input => query("GET", "opportunities/search", input), collection("opportunities")),
79
+ "deals.create": write("POST", "opportunities", { ...dealFields, name: { ...text, required: true }, account_id: id }, "opportunity"),
80
+ "deals.update": write("PATCH", "opportunities", dealFields, "opportunity"),
81
+ "dealStages.list": jsonOperation("https://api.apollo.io/api/v1/opportunity_stages", {}, collection("opportunity_stages")),
82
+ "users.list": validatedOperation(page, input => query("GET", "users/search", input), collection("users"))
83
+ }
84
+ });
85
+ export { apolloIoProvider };
@@ -0,0 +1,64 @@
1
+ import { ConnectorError } from "@jskit-ai/connectors-core/server";
2
+ import { asanaDefinition } from "../shared/tokens.js";
3
+ import { jsonOperation, validatedOperation } from "./jsonOperation.js";
4
+
5
+ const gid = { type: "string", minLength: 1, maxLength: 100, pattern: "^[0-9]+$" };
6
+ const requiredGid = { ...gid, required: true };
7
+ const page = { limit: { type: "integer", min: 1, max: 100, defaultTo: 50 }, offset: { type: "string", maxLength: 4096 } };
8
+ const date = { type: "string", nullable: true, pattern: "^\\d{4}-\\d{2}-\\d{2}$",
9
+ validator: value => { if (value === null) return true; const parsed = new Date(value);
10
+ return !Number.isNaN(parsed.valueOf()) && parsed.toISOString().slice(0, 10) === value || "Enter a real YYYY-MM-DD date."; } };
11
+ const common = { name: { type: "string", minLength: 1, maxLength: 1000 },
12
+ notes: { type: "string", maxLength: 50000 }, due_on: date, start_on: date };
13
+ const task = { ...common, completed: { type: "boolean" }, assignee: { ...gid, nullable: true },
14
+ parent: { ...gid, nullable: true } };
15
+ const project = { ...common, archived: { type: "boolean" }, owner: { ...gid, nullable: true },
16
+ privacy_setting: { type: "string", enum: ["private", "public_to_workspace"] } };
17
+ const taskFields = "gid,name,notes,completed,assignee.gid,assignee.name,due_on,start_on,projects.gid,parent.gid";
18
+ const projectFields = "gid,name,notes,archived,owner.gid,privacy_setting,due_on,start_on,workspace.gid";
19
+ const record = result => typeof result?.data?.gid === "string";
20
+ const list = result => Array.isArray(result?.data);
21
+ function read(endpoint, input = {}, fields) {
22
+ const url = new URL(`https://app.asana.com/api/1.0/${endpoint}`);
23
+ for (const [key, value] of Object.entries(input)) url.searchParams.set(key, String(value));
24
+ if (fields) url.searchParams.set("opt_fields", fields);
25
+ return { method: "GET", url: url.href };
26
+ }
27
+ function write(method, endpoint, fields, resultFields) {
28
+ return validatedOperation({ ...(method === "PUT" ? { gid: requiredGid } : {}), ...fields }, ({ gid: resource, ...data }) => {
29
+ if (!Object.keys(data).length) throw new ConnectorError("connector_input_invalid", "Supply at least one change.", { statusCode: 422 });
30
+ if (Object.hasOwn(data, "start_on") && !Object.hasOwn(data, "due_on")) throw new ConnectorError("connector_input_invalid", "Include due_on when setting or clearing start_on.", { statusCode: 422 });
31
+ if (data.start_on && data.due_on && (data.start_on > data.due_on || endpoint === "projects" && data.start_on === data.due_on)) {
32
+ throw new ConnectorError("connector_input_invalid", "The start date must precede the project due date, or be no later than the task due date.", { statusCode: 422 });
33
+ }
34
+ const request = read(`${endpoint}${resource ? `/${resource}` : ""}`, {}, resultFields);
35
+ return { ...request, method, body: { data } };
36
+ }, record);
37
+ }
38
+ const asanaProvider = Object.freeze({
39
+ ...asanaDefinition, apiOrigins: ["https://app.asana.com"],
40
+ apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}` }) },
41
+ checkOperation: "workspaces.list",
42
+ operations: {
43
+ "workspaces.list": jsonOperation("https://app.asana.com/api/1.0/workspaces", page, list),
44
+ "projects.list": validatedOperation({ workspace: requiredGid, ...page, archived: { type: "boolean" } },
45
+ input => read("projects", input, projectFields), list),
46
+ "projects.get": validatedOperation({ gid: requiredGid }, ({ gid }) => read(`projects/${gid}`, {}, projectFields), record),
47
+ "projects.create": write("POST", "projects", { ...project, name: { ...common.name, required: true },
48
+ workspace: requiredGid, privacy_setting: { ...project.privacy_setting, required: true } }, projectFields),
49
+ "projects.update": write("PUT", "projects", project, projectFields),
50
+ "tasks.list": validatedOperation({ project: requiredGid, ...page, completed_since: { type: "string", maxLength: 100 } },
51
+ ({ project, ...input }) => read(`projects/${project}/tasks`, input, taskFields), list),
52
+ "tasks.get": validatedOperation({ gid: requiredGid }, ({ gid }) => read(`tasks/${gid}`, {}, taskFields), record),
53
+ "tasks.create": write("POST", "tasks", { ...task, name: { ...common.name, required: true }, workspace: requiredGid,
54
+ projects: { type: "array", items: gid, validator: values => values.length <= 100 || "Choose at most 100 projects." } }, taskFields),
55
+ "tasks.update": write("PUT", "tasks", task, taskFields),
56
+ "users.list": validatedOperation({ workspace: requiredGid, ...page }, ({ workspace, ...input }) => read(`workspaces/${workspace}/users`, input), list),
57
+ "teams.list": validatedOperation({ workspace: requiredGid, ...page }, ({ workspace, ...input }) => read(`workspaces/${workspace}/teams`, input), list),
58
+ "projectMembers.add": validatedOperation({ project: requiredGid, member: requiredGid,
59
+ access_level: { type: "string", required: true, enum: ["admin", "editor", "commenter"] } },
60
+ ({ project, member, access_level }) => ({ method: "POST", url: "https://app.asana.com/api/1.0/memberships",
61
+ body: { data: { parent: project, member, access_level } } }), record)
62
+ }
63
+ });
64
+ export { asanaProvider };