@oomol-lab/open-connector 1.6.4 → 1.7.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 (320) hide show
  1. package/assets/open-connector/catalog/apps/aisa.json +6394 -0
  2. package/assets/open-connector/catalog/apps/baizhi_mcp.json +135 -0
  3. package/assets/open-connector/catalog/apps/bilibili.json +2586 -0
  4. package/assets/open-connector/catalog/apps/bitwarden.json +1423 -0
  5. package/assets/open-connector/catalog/apps/cronfree.json +303 -0
  6. package/assets/open-connector/catalog/apps/cufinder.json +368 -0
  7. package/assets/open-connector/catalog/apps/discordbot.json +2888 -248
  8. package/assets/open-connector/catalog/apps/drawio_mcp.json +192 -0
  9. package/assets/open-connector/catalog/apps/firecrawl.json +176 -32
  10. package/assets/open-connector/catalog/apps/framejet.json +273 -0
  11. package/assets/open-connector/catalog/apps/futunn.json +2967 -0
  12. package/assets/open-connector/catalog/apps/gmail.json +298 -0
  13. package/assets/open-connector/catalog/apps/google_analytics.json +4 -1
  14. package/assets/open-connector/catalog/apps/google_bigquery.json +4 -1
  15. package/assets/open-connector/catalog/apps/google_search_console.json +4 -1
  16. package/assets/open-connector/catalog/apps/googlecalendar.json +4 -1
  17. package/assets/open-connector/catalog/apps/googlechat.json +694 -7
  18. package/assets/open-connector/catalog/apps/googledocs.json +4 -1
  19. package/assets/open-connector/catalog/apps/googlesheets.json +4 -1
  20. package/assets/open-connector/catalog/apps/granola.json +23 -0
  21. package/assets/open-connector/catalog/apps/investoday_mcp.json +22228 -197
  22. package/assets/open-connector/catalog/apps/ixspy.json +2274 -0
  23. package/assets/open-connector/catalog/apps/keepa.json +3534 -987
  24. package/assets/open-connector/catalog/apps/linear.json +17 -0
  25. package/assets/open-connector/catalog/apps/linear_mcp.json +510 -0
  26. package/assets/open-connector/catalog/apps/moneyforward_mcp.json +1432 -0
  27. package/assets/open-connector/catalog/apps/notion.json +446 -1
  28. package/assets/open-connector/catalog/apps/notion_mcp.json +490 -0
  29. package/assets/open-connector/catalog/apps/odoo.json +619 -0
  30. package/assets/open-connector/catalog/apps/osv.json +663 -0
  31. package/assets/open-connector/catalog/apps/outlook.json +30 -8
  32. package/assets/open-connector/catalog/apps/outlook_calendar.json +23 -2
  33. package/assets/open-connector/catalog/apps/pdf_vector.json +329 -0
  34. package/assets/open-connector/catalog/apps/postiz.json +334 -0
  35. package/assets/open-connector/catalog/apps/proworkflow.json +529 -0
  36. package/assets/open-connector/catalog/apps/pulsetic.json +406 -0
  37. package/assets/open-connector/catalog/apps/sellersprite_mcp.json +2 -1
  38. package/assets/open-connector/catalog/apps/shopee.json +1785 -0
  39. package/assets/open-connector/catalog/apps/slack.json +304 -0
  40. package/assets/open-connector/catalog/apps/slackbot.json +295 -0
  41. package/assets/open-connector/catalog/apps/speak_ai.json +360 -0
  42. package/assets/open-connector/catalog/apps/tencent_docs.json +3 -0
  43. package/assets/open-connector/catalog/apps/wisburg.json +1528 -0
  44. package/assets/open-connector/catalog/apps/youtube.json +4 -1
  45. package/assets/open-connector/catalog/apps/zerotier.json +3319 -0
  46. package/assets/open-connector/catalog/apps/zyhub.json +1585 -0
  47. package/assets/open-connector/catalog/apps-index.json +1 -1
  48. package/package.json +1 -1
  49. package/src/core/cast.d.ts +10 -0
  50. package/src/core/cast.js +14 -0
  51. package/src/core/guarded-fetch.d.ts +6 -3
  52. package/src/core/guarded-fetch.js +6 -3
  53. package/src/core/json-schema.d.ts +5 -1
  54. package/src/core/json-schema.js +14 -2
  55. package/src/core/provider-scenarios.js +67 -13
  56. package/src/core/request.js +4 -1
  57. package/src/core/types.d.ts +10 -1
  58. package/src/mcp.js +10 -7
  59. package/src/oauth/oauth-client-config-service.d.ts +16 -1
  60. package/src/oauth/oauth-client-config-service.js +59 -3
  61. package/src/oauth/oauth-credential-refresh-service.js +3 -1
  62. package/src/oauth/oauth-flow-service.d.ts +6 -0
  63. package/src/oauth/oauth-flow-service.js +21 -4
  64. package/src/oauth/oauth-token.d.ts +13 -0
  65. package/src/providers/17track/runtime.js +1 -1
  66. package/src/providers/action-contracts.generated.d.ts +32 -8
  67. package/src/providers/aifinmarket/runtime.js +1 -1
  68. package/src/providers/aisa/executors.d.ts +4 -0
  69. package/src/providers/aisa/executors.js +864 -0
  70. package/src/providers/aliyun_ack/runtime.js +1 -1
  71. package/src/providers/aliyun_sls/runtime.js +1 -1
  72. package/src/providers/app_store_server/runtime-helpers.js +1 -1
  73. package/src/providers/baizhi_mcp/actions.d.ts +2 -0
  74. package/src/providers/baizhi_mcp/actions.js +40 -0
  75. package/src/providers/baizhi_mcp/executors.d.ts +3 -0
  76. package/src/providers/baizhi_mcp/executors.js +102 -0
  77. package/src/providers/bazhuayu/runtime.js +2 -2
  78. package/src/providers/bilibili/articles.d.ts +3 -0
  79. package/src/providers/bilibili/articles.js +369 -0
  80. package/src/providers/bilibili/executors.d.ts +8 -0
  81. package/src/providers/bilibili/executors.js +262 -0
  82. package/src/providers/bilibili/runtime.d.ts +95 -0
  83. package/src/providers/bilibili/runtime.js +236 -0
  84. package/src/providers/bilibili/upload.d.ts +14 -0
  85. package/src/providers/bilibili/upload.js +107 -0
  86. package/src/providers/bitwarden/executors.d.ts +3 -0
  87. package/src/providers/bitwarden/executors.js +146 -0
  88. package/src/providers/cloudflare_browser_rendering/executors.js +1 -1
  89. package/src/providers/cloudflare_dns/executors.js +1 -1
  90. package/src/providers/{cloudflare-current-user.js → cloudflare_dns/runtime-user.js} +2 -2
  91. package/src/providers/cloudflare_dns/runtime.d.ts +1 -1
  92. package/src/providers/cloudflare_dns/runtime.js +1 -1
  93. package/src/providers/cloudflare_r2/executors.js +1 -1
  94. package/src/providers/cloudflare_r2/runtime.d.ts +1 -1
  95. package/src/providers/cloudflare_r2/runtime.js +1 -1
  96. package/src/providers/cloudflare_worker/executors.js +1 -1
  97. package/src/providers/cloudflare_worker/runtime.d.ts +1 -1
  98. package/src/providers/cloudflare_worker/runtime.js +1 -1
  99. package/src/providers/cronfree/executors.d.ts +4 -0
  100. package/src/providers/cronfree/executors.js +89 -0
  101. package/src/providers/cufinder/executors.d.ts +4 -0
  102. package/src/providers/cufinder/executors.js +105 -0
  103. package/src/providers/dingtalk_mcp/runtime.js +1 -1
  104. package/src/providers/discordbot/executors.d.ts +0 -9
  105. package/src/providers/discordbot/executors.js +9 -101
  106. package/src/providers/discordbot/runtime-guilds.d.ts +3 -0
  107. package/src/providers/discordbot/runtime-guilds.js +464 -0
  108. package/src/providers/discordbot/runtime.d.ts +42 -0
  109. package/src/providers/discordbot/runtime.js +124 -0
  110. package/src/providers/drawio_mcp/executors.d.ts +3 -0
  111. package/src/providers/drawio_mcp/executors.js +19 -0
  112. package/src/providers/drawio_mcp/runtime.d.ts +8 -0
  113. package/src/providers/drawio_mcp/runtime.js +175 -0
  114. package/src/providers/elevenreader/runtime.js +6 -5
  115. package/src/providers/fastmoss_mcp/executors.js +2 -2
  116. package/src/providers/finchina/executors.js +1 -1
  117. package/src/providers/framejet/executors.d.ts +4 -0
  118. package/src/providers/framejet/executors.js +19 -0
  119. package/src/providers/framejet/runtime.d.ts +16 -0
  120. package/src/providers/framejet/runtime.js +148 -0
  121. package/src/providers/futunn/actions.d.ts +2 -0
  122. package/src/providers/futunn/actions.js +152 -0
  123. package/src/providers/futunn/actions.research.d.ts +2 -0
  124. package/src/providers/futunn/actions.research.js +423 -0
  125. package/src/providers/futunn/actions.trading.d.ts +2 -0
  126. package/src/providers/futunn/actions.trading.js +204 -0
  127. package/src/providers/futunn/executors.d.ts +3 -0
  128. package/src/providers/futunn/executors.js +24 -0
  129. package/src/providers/futunn/runtime-research.d.ts +3 -0
  130. package/src/providers/futunn/runtime-research.js +133 -0
  131. package/src/providers/futunn/runtime-trading.d.ts +3 -0
  132. package/src/providers/futunn/runtime-trading.js +39 -0
  133. package/src/providers/futunn/runtime.d.ts +2 -0
  134. package/src/providers/futunn/runtime.js +50 -0
  135. package/src/providers/futunn/transport.d.ts +11 -0
  136. package/src/providers/futunn/transport.js +76 -0
  137. package/src/providers/gildata/executors.js +1 -1
  138. package/src/providers/gmail/attachment-stream.d.ts +2 -0
  139. package/src/providers/gmail/attachment-stream.js +162 -0
  140. package/src/providers/gmail/executors.d.ts +3 -1
  141. package/src/providers/gmail/executors.js +54 -26
  142. package/src/providers/gmail/message.d.ts +6 -8
  143. package/src/providers/gmail/message.js +4 -0
  144. package/src/providers/gmail/scopes.d.ts +32 -0
  145. package/src/providers/gmail/scopes.js +37 -0
  146. package/src/providers/google_analytics/executors.js +2 -2
  147. package/src/providers/google_analytics/scopes.js +6 -1
  148. package/src/providers/google_bigquery/executors.js +2 -2
  149. package/src/providers/google_bigquery/scopes.js +2 -0
  150. package/src/providers/google_search_console/executors.js +2 -2
  151. package/src/providers/google_search_console/scopes.js +2 -0
  152. package/src/providers/googleads/executors.js +1 -1
  153. package/src/providers/googleads/scopes.d.ts +1 -3
  154. package/src/providers/googleads/scopes.js +2 -4
  155. package/src/providers/googlecalendar/executors.js +1 -1
  156. package/src/providers/googlecalendar/runtime-shared.js +2 -2
  157. package/src/providers/googlecalendar/scopes.js +2 -0
  158. package/src/providers/googlechat/executors.d.ts +1 -3
  159. package/src/providers/googlechat/executors.js +219 -28
  160. package/src/providers/googlechat/members.d.ts +55 -0
  161. package/src/providers/googlechat/members.js +293 -0
  162. package/src/providers/googlechat/runtime.d.ts +16 -0
  163. package/src/providers/googlechat/runtime.js +25 -0
  164. package/src/providers/googlechat/scopes.d.ts +22 -3
  165. package/src/providers/googlechat/scopes.js +30 -7
  166. package/src/providers/googledocs/executors.js +1 -1
  167. package/src/providers/googledrive/executors.js +2 -2
  168. package/src/providers/{google-auth.d.ts → googledrive/runtime-auth.d.ts} +2 -2
  169. package/src/providers/{google-auth.js → googledrive/runtime-auth.js} +3 -3
  170. package/src/providers/googledrive/runtime-collaboration.js +1 -1
  171. package/src/providers/{google-runtime.d.ts → googledrive/runtime-request.d.ts} +1 -1
  172. package/src/providers/{google-runtime.js → googledrive/runtime-request.js} +2 -2
  173. package/src/providers/googleforms/executors.js +2 -2
  174. package/src/providers/googleforms/scopes.d.ts +0 -3
  175. package/src/providers/googleforms/scopes.js +2 -6
  176. package/src/providers/googlemeet/executors.js +2 -2
  177. package/src/providers/googlemeet/scopes.d.ts +0 -3
  178. package/src/providers/googlemeet/scopes.js +2 -6
  179. package/src/providers/googlesheets/executors.js +1 -1
  180. package/src/providers/googlesheets/scopes.js +2 -0
  181. package/src/providers/googleslides/executors.js +2 -2
  182. package/src/providers/googleslides/scopes.d.ts +0 -3
  183. package/src/providers/googleslides/scopes.js +2 -6
  184. package/src/providers/googletasks/executors.js +2 -2
  185. package/src/providers/googletasks/scopes.d.ts +0 -3
  186. package/src/providers/googletasks/scopes.js +2 -6
  187. package/src/providers/granola/actions.js +2 -0
  188. package/src/providers/granola/runtime-mcp.js +2 -0
  189. package/src/providers/granola/runtime.js +2 -2
  190. package/src/providers/indiegogo/executors.js +1 -1
  191. package/src/providers/investoday_mcp/executors.js +2 -2
  192. package/src/providers/investoday_mcp/official-actions.js +19 -372
  193. package/src/providers/investoday_mcp/official-tool-types.d.ts +10 -0
  194. package/src/providers/investoday_mcp/official-tool-types.js +4 -0
  195. package/src/providers/investoday_mcp/official-tools/charts.d.ts +2 -0
  196. package/src/providers/investoday_mcp/official-tools/charts.js +32 -0
  197. package/src/providers/investoday_mcp/official-tools/fixed-income.d.ts +2 -0
  198. package/src/providers/investoday_mcp/official-tools/fixed-income.js +326 -0
  199. package/src/providers/investoday_mcp/official-tools/funds.d.ts +2 -0
  200. package/src/providers/investoday_mcp/official-tools/funds.js +1085 -0
  201. package/src/providers/investoday_mcp/official-tools/hong-kong.d.ts +2 -0
  202. package/src/providers/investoday_mcp/official-tools/hong-kong.js +275 -0
  203. package/src/providers/investoday_mcp/official-tools/indices-industries.d.ts +2 -0
  204. package/src/providers/investoday_mcp/official-tools/indices-industries.js +642 -0
  205. package/src/providers/investoday_mcp/official-tools/macro-calendar.d.ts +2 -0
  206. package/src/providers/investoday_mcp/official-tools/macro-calendar.js +398 -0
  207. package/src/providers/investoday_mcp/official-tools/other.d.ts +2 -0
  208. package/src/providers/investoday_mcp/official-tools/other.js +131 -0
  209. package/src/providers/investoday_mcp/official-tools/research-news.d.ts +2 -0
  210. package/src/providers/investoday_mcp/official-tools/research-news.js +302 -0
  211. package/src/providers/investoday_mcp/official-tools/stocks.d.ts +2 -0
  212. package/src/providers/investoday_mcp/official-tools/stocks.js +2829 -0
  213. package/src/providers/ixspy/executors.d.ts +3 -0
  214. package/src/providers/ixspy/executors.js +220 -0
  215. package/src/providers/iyunbiao/runtime.js +2 -2
  216. package/src/providers/keepa/actions.d.ts +1 -1
  217. package/src/providers/keepa/actions.js +371 -149
  218. package/src/providers/keepa/deal-attributes.d.ts +1 -0
  219. package/src/providers/keepa/deal-attributes.js +26 -0
  220. package/src/providers/keepa/runtime.js +212 -18
  221. package/src/providers/kingdee/executors.js +1 -1
  222. package/src/providers/kingdee/runtime.js +2 -2
  223. package/src/providers/kuaidi100/runtime.js +1 -1
  224. package/src/providers/linear/executors.js +12 -9
  225. package/src/providers/linear_mcp/actions.d.ts +47 -0
  226. package/src/providers/linear_mcp/actions.js +102 -0
  227. package/src/providers/linear_mcp/endpoints.d.ts +3 -0
  228. package/src/providers/linear_mcp/endpoints.js +3 -0
  229. package/src/providers/linear_mcp/executors.d.ts +3 -0
  230. package/src/providers/linear_mcp/executors.js +9 -0
  231. package/src/providers/linear_mcp/runtime-mcp.d.ts +15 -0
  232. package/src/providers/linear_mcp/runtime-mcp.js +238 -0
  233. package/src/providers/lingxing_mcp/runtime.js +1 -1
  234. package/src/providers/mcdonalds_cn_mcp/runtime.js +1 -1
  235. package/src/providers/metabase/mcp-runtime.js +1 -1
  236. package/src/providers/moneyforward_mcp/executors.d.ts +3 -0
  237. package/src/providers/moneyforward_mcp/executors.js +11 -0
  238. package/src/providers/moneyforward_mcp/manifest.d.ts +31 -0
  239. package/src/providers/moneyforward_mcp/manifest.js +204 -0
  240. package/src/providers/moneyforward_mcp/runtime.d.ts +18 -0
  241. package/src/providers/moneyforward_mcp/runtime.js +296 -0
  242. package/src/providers/notion/executors.js +39 -3
  243. package/src/providers/notion_mcp/endpoints.d.ts +3 -0
  244. package/src/providers/notion_mcp/endpoints.js +3 -0
  245. package/src/providers/notion_mcp/executors.d.ts +3 -0
  246. package/src/providers/notion_mcp/executors.js +176 -0
  247. package/src/providers/notion_mcp/mcp-response.d.ts +86 -0
  248. package/src/providers/notion_mcp/mcp-response.js +314 -0
  249. package/src/providers/odoo/executors.d.ts +3 -0
  250. package/src/providers/odoo/executors.js +32 -0
  251. package/src/providers/odoo/runtime.d.ts +16 -0
  252. package/src/providers/odoo/runtime.js +140 -0
  253. package/src/providers/osv/executors.d.ts +2 -0
  254. package/src/providers/osv/executors.js +11 -0
  255. package/src/providers/osv/runtime.d.ts +9 -0
  256. package/src/providers/osv/runtime.js +66 -0
  257. package/src/providers/outlook/executors.js +27 -6
  258. package/src/providers/outlook/microsoft-graph.js +17 -6
  259. package/src/providers/outlook_calendar/executors.js +24 -7
  260. package/src/providers/patsnap_mcp/executors.js +2 -2
  261. package/src/providers/pdf_vector/executors.d.ts +4 -0
  262. package/src/providers/pdf_vector/executors.js +108 -0
  263. package/src/providers/postiz/executors.d.ts +4 -0
  264. package/src/providers/postiz/executors.js +83 -0
  265. package/src/providers/provider-runtime.d.ts +18 -0
  266. package/src/providers/provider-runtime.js +60 -4
  267. package/src/providers/proworkflow/executors.d.ts +4 -0
  268. package/src/providers/proworkflow/executors.js +135 -0
  269. package/src/providers/pulsetic/executors.d.ts +4 -0
  270. package/src/providers/pulsetic/executors.js +126 -0
  271. package/src/providers/registry.generated.js +24 -0
  272. package/src/providers/scrape_creators/catalog.js +1 -1
  273. package/src/providers/scrape_creators/runtime.js +1 -1
  274. package/src/providers/sellersprite_mcp/executors.js +2 -2
  275. package/src/providers/sellersprite_mcp/official-actions.js +3 -2
  276. package/src/providers/sentry/runtime.js +6 -7
  277. package/src/providers/shopee/actions.d.ts +2 -0
  278. package/src/providers/shopee/actions.js +474 -0
  279. package/src/providers/shopee/executors.d.ts +3 -0
  280. package/src/providers/shopee/executors.js +85 -0
  281. package/src/providers/shopee/runtime.d.ts +79 -0
  282. package/src/providers/shopee/runtime.js +693 -0
  283. package/src/providers/slack/actions.js +69 -3
  284. package/src/providers/slack/runtime.js +116 -21
  285. package/src/providers/slickdeals/runtime.js +1 -1
  286. package/src/providers/sorftime_mcp/executors.js +2 -2
  287. package/src/providers/speak_ai/executors.d.ts +3 -0
  288. package/src/providers/speak_ai/executors.js +187 -0
  289. package/src/providers/tikhub/bounded-response.d.ts +1 -1
  290. package/src/providers/tikhub/bounded-response.js +8 -34
  291. package/src/providers/tikhub/endpoint-catalog.js +1 -1
  292. package/src/providers/tikhub/runtime.js +2 -2
  293. package/src/providers/tongdaxin/executors.js +79 -15
  294. package/src/providers/tushare/runtime.js +22 -9
  295. package/src/providers/uspto/executors.js +1 -1
  296. package/src/providers/waffo/executors.js +1 -1
  297. package/src/providers/waffo/runtime.js +1 -1
  298. package/src/providers/wecom_mcp/runtime.js +1 -1
  299. package/src/providers/wisburg/executors.d.ts +4 -0
  300. package/src/providers/wisburg/executors.js +13 -0
  301. package/src/providers/wisburg/runtime.d.ts +12 -0
  302. package/src/providers/wisburg/runtime.js +133 -0
  303. package/src/providers/xydc_mcp/executors.js +2 -2
  304. package/src/providers/youtube/executors.js +1 -1
  305. package/src/providers/youtube/runtime.js +1 -1
  306. package/src/providers/zerotier/executors.d.ts +9 -0
  307. package/src/providers/zerotier/executors.js +629 -0
  308. package/src/providers/zerotier/runtime.d.ts +54 -0
  309. package/src/providers/zerotier/runtime.js +207 -0
  310. package/src/providers/zyhub/executors.d.ts +3 -0
  311. package/src/providers/zyhub/executors.js +240 -0
  312. package/src/server/api/openapi.js +12 -2
  313. package/src/server/api/runtime-api.d.ts +8 -1
  314. package/src/server/api/runtime-api.js +19 -1
  315. package/src/server/connect-server.js +10 -2
  316. package/src/server/files/transit-files.d.ts +3 -2
  317. package/src/server/files/transit-files.js +31 -35
  318. package/src/providers/sentry/scopes.d.ts +0 -1
  319. package/src/providers/sentry/scopes.js +0 -7
  320. /package/src/providers/{cloudflare-current-user.d.ts → cloudflare_dns/runtime-user.d.ts} +0 -0
@@ -0,0 +1,207 @@
1
+ import { looseArray, optionalRecord, optionalString } from "../../core/cast.js";
2
+ import { encodePathSegment } from "../../core/request.js";
3
+ import { providerInputError, providerUserAgent, ProviderRequestError, readProviderJsonBody, runProviderRequest, } from "../provider-runtime.js";
4
+ /** Legacy Central base URL; requests authenticate with `Authorization: token <key>`. */
5
+ export const zerotierV1BaseUrl = "https://api.zerotier.com/api/v1";
6
+ /** New Central base URL; requests authenticate with `Authorization: Bearer <key>`. */
7
+ export const zerotierV2BaseUrl = "https://central.zerotier.com/api/v2";
8
+ const zerotierV2BetaBaseUrl = "https://central.zerotier.com/api/v2beta";
9
+ export function parseZerotierApiVersion(value) {
10
+ const version = optionalString(value)?.toLowerCase();
11
+ if (version === "v1" || version === "v2") {
12
+ return version;
13
+ }
14
+ throw providerInputError('zerotier apiVersion must be "v1" (Legacy Central) or "v2" (New Central).');
15
+ }
16
+ export function createZerotierContext(values, fetcher, signal) {
17
+ const apiVersion = parseZerotierApiVersion(values.apiVersion);
18
+ const apiKey = optionalString(values.apiKey);
19
+ if (!apiKey) {
20
+ throw providerInputError("zerotier apiKey is required.");
21
+ }
22
+ return {
23
+ apiVersion,
24
+ apiKey,
25
+ orgId: optionalString(values.orgId),
26
+ fetcher,
27
+ signal,
28
+ };
29
+ }
30
+ /**
31
+ * Encode one ID as a single path segment. encodeURIComponent leaves "." and
32
+ * ".." intact and new URL() collapses them, so memberId ".." would turn
33
+ * delete_member into DELETE /network/{id}; reject dot segments and separators.
34
+ */
35
+ export function zerotierPathSegment(value, field) {
36
+ if (value === "." || value === ".." || value.includes("/") || value.includes("\\")) {
37
+ throw providerInputError(`${field} must be a single path segment.`);
38
+ }
39
+ return encodePathSegment(value);
40
+ }
41
+ export function zerotierAuthorizationHeader(credential) {
42
+ return credential.apiVersion === "v2" ? `Bearer ${credential.apiKey}` : `token ${credential.apiKey}`;
43
+ }
44
+ function zerotierBaseUrl(context, beta) {
45
+ if (context.apiVersion === "v1") {
46
+ if (beta) {
47
+ throw providerInputError("ZeroTier beta endpoints are only available on the v2 (New Central) API.");
48
+ }
49
+ return zerotierV1BaseUrl;
50
+ }
51
+ return beta ? zerotierV2BetaBaseUrl : zerotierV2BaseUrl;
52
+ }
53
+ /** Run one ZeroTier Central API request and return the parsed JSON payload, or null for empty bodies. */
54
+ export async function zerotierRequest(context, options) {
55
+ return runProviderRequest({ signal: context.signal, label: "ZeroTier" }, async (signal) => {
56
+ const url = new URL(zerotierBaseUrl(context, options.beta) + options.path);
57
+ if (options.query) {
58
+ for (const [key, value] of Object.entries(options.query)) {
59
+ if (value === undefined)
60
+ continue;
61
+ if (Array.isArray(value)) {
62
+ for (const item of value) {
63
+ url.searchParams.append(key, item);
64
+ }
65
+ }
66
+ else {
67
+ url.searchParams.set(key, value);
68
+ }
69
+ }
70
+ }
71
+ const response = await context.fetcher(url, {
72
+ method: options.method ?? "GET",
73
+ headers: {
74
+ accept: "application/json",
75
+ authorization: zerotierAuthorizationHeader(context),
76
+ "content-type": "application/json",
77
+ "user-agent": providerUserAgent,
78
+ },
79
+ body: options.body === undefined ? undefined : JSON.stringify(options.body),
80
+ signal,
81
+ });
82
+ const payload = await readProviderJsonBody(response, {
83
+ emptyBody: null,
84
+ invalidJsonMessage: "ZeroTier returned invalid JSON",
85
+ // A non-JSON error body (such as a proxy's HTML page) keeps the upstream
86
+ // status instead of becoming a 502 parse failure; only its start is kept.
87
+ invalidJsonFallback: response.ok ? undefined : (text) => ({ message: text.trim().slice(0, 500) }),
88
+ });
89
+ if (!response.ok) {
90
+ const record = optionalRecord(payload);
91
+ const summary = optionalString(record?.message) ?? `ZeroTier request failed with status ${response.status}`;
92
+ // v2 validation failures list the offending fields under details[{field, message}].
93
+ const details = looseArray(record?.details).flatMap((item) => {
94
+ const detail = optionalRecord(item);
95
+ const field = optionalString(detail?.field);
96
+ const reason = optionalString(detail?.message);
97
+ return reason ? [field ? `${field}: ${reason}` : reason] : [];
98
+ });
99
+ const message = details.length > 0 ? `${summary}: ${details.join("; ")}` : summary;
100
+ throw new ProviderRequestError(response.status, message);
101
+ }
102
+ return payload;
103
+ });
104
+ }
105
+ /** Require a specific ZeroTier API version on the connection. */
106
+ export function requireZerotierApiVersion(context, version) {
107
+ if (context.apiVersion !== version) {
108
+ throw providerInputError(`This action requires a ${version} ZeroTier connection; the connection is configured for ${context.apiVersion}.`);
109
+ }
110
+ }
111
+ /**
112
+ * Wrap a ZeroTier array response into the action list envelope. v2 list
113
+ * endpoints answer `{ items, stats? }`; the aggregate `stats` requested with
114
+ * `stats=true` is kept next to the items.
115
+ */
116
+ export function zerotierList(payload) {
117
+ if (payload === null || payload === undefined) {
118
+ return { items: [] };
119
+ }
120
+ if (Array.isArray(payload)) {
121
+ return { items: payload };
122
+ }
123
+ const record = optionalRecord(payload);
124
+ if (record && Array.isArray(record.items)) {
125
+ return { items: record.items, stats: record.stats };
126
+ }
127
+ return { items: [payload] };
128
+ }
129
+ /** Wrap a ZeroTier object response into the action single-resource envelope. */
130
+ export function zerotierResult(payload) {
131
+ return { result: payload };
132
+ }
133
+ /** Wrap a mutating ZeroTier response that may have an empty body. */
134
+ export function zerotierStatus(payload) {
135
+ return { ok: true, result: payload };
136
+ }
137
+ /** Organization ID from the action input, falling back to the connection's orgId field. */
138
+ export function zerotierOrgId(context, input) {
139
+ return optionalString(input.orgId) ?? context.orgId;
140
+ }
141
+ const v1ValidationEndpoint = "/status";
142
+ const v2ValidationEndpoint = "/org";
143
+ /**
144
+ * Legacy Central reports the token's user under `user` on GET /status. The
145
+ * status document itself is not an authorization check, so a response without
146
+ * a user means the token did not authenticate.
147
+ */
148
+ function readV1Account(payload) {
149
+ const user = optionalRecord(optionalRecord(payload)?.user);
150
+ const accountId = optionalString(user?.id);
151
+ if (!accountId) {
152
+ throw new ProviderRequestError(401, "ZeroTier did not return a user for this API token.");
153
+ }
154
+ return {
155
+ accountId,
156
+ displayName: optionalString(user?.displayName) ?? optionalString(user?.email) ?? "ZeroTier Central v1",
157
+ };
158
+ }
159
+ /**
160
+ * New Central GET /org lists the organizations the service account can reach.
161
+ * A service account belongs to one organization, which identifies the account.
162
+ * A configured orgId must name an organization the key can reach, because the
163
+ * list actions and get_org keep using it; one missing from the list is looked
164
+ * up directly so the upstream 403/404 rejects a mistyped id. Without an orgId,
165
+ * a single listed organization identifies the account, otherwise the runtime
166
+ * default account id applies.
167
+ */
168
+ async function readV2Account(context, payload) {
169
+ const orgs = looseArray(optionalRecord(payload)?.items).map((item) => optionalRecord(item));
170
+ const orgId = context.orgId;
171
+ if (!orgId) {
172
+ const onlyOrg = orgs.length === 1 ? orgs[0] : undefined;
173
+ return {
174
+ accountId: optionalString(onlyOrg?.id),
175
+ displayName: optionalString(onlyOrg?.name) ?? "ZeroTier New Central v2",
176
+ };
177
+ }
178
+ const org = orgs.find((item) => optionalString(item?.id) === orgId) ??
179
+ optionalRecord(await zerotierRequest(context, { path: `/org/${zerotierPathSegment(orgId, "orgId")}` }));
180
+ return {
181
+ accountId: optionalString(org?.id) ?? orgId,
182
+ displayName: optionalString(org?.name) ?? "ZeroTier New Central v2",
183
+ };
184
+ }
185
+ /**
186
+ * Verify a ZeroTier credential against the API generation it selects: v1 calls
187
+ * GET /status on Legacy Central, v2 calls GET /org on New Central (plus
188
+ * GET /org/{orgId} when the configured orgId is not in that list).
189
+ */
190
+ export async function validateZerotierCredential(values, fetcher, signal) {
191
+ const context = createZerotierContext(values, fetcher, signal);
192
+ const path = context.apiVersion === "v1" ? v1ValidationEndpoint : v2ValidationEndpoint;
193
+ const payload = await zerotierRequest(context, { path });
194
+ const account = context.apiVersion === "v1" ? readV1Account(payload) : await readV2Account(context, payload);
195
+ return {
196
+ profile: {
197
+ accountId: account.accountId,
198
+ displayName: account.displayName,
199
+ },
200
+ grantedScopes: [],
201
+ metadata: {
202
+ apiVersion: context.apiVersion,
203
+ apiBaseUrl: context.apiVersion === "v1" ? zerotierV1BaseUrl : zerotierV2BaseUrl,
204
+ validationEndpoint: path,
205
+ },
206
+ };
207
+ }
@@ -0,0 +1,3 @@
1
+ import type { CredentialValidators, ProviderExecutors } from "../../core/types.ts";
2
+ export declare const executors: ProviderExecutors;
3
+ export declare const credentialValidators: CredentialValidators;
@@ -0,0 +1,240 @@
1
+ import { looseArray, optionalBoolean, optionalInteger, optionalRecord, optionalString } from "../../core/cast.js";
2
+ import { readBoundedResponseBytes } from "../../core/request.js";
3
+ import { defaultProviderJsonMaxResponseBytes, defineApiKeyProviderExecutors, ProviderRequestError, providerUserAgent, readProviderTextBody, runProviderRequest, } from "../provider-runtime.js";
4
+ const service = "zyhub";
5
+ const baseUrl = "https://mcp.finance.sina.com.cn/api-call/";
6
+ const operations = {
7
+ search_symbols: {
8
+ code: "globalStockSearchSymbols",
9
+ query: (i) => ({ type: i.marketTypes, key: i.query, format: "text", num: 4 }),
10
+ },
11
+ get_stock_quote: { code: "globalStockQuoteRealtime", query: (i) => pick(i, { market: "market", symbol: "symbol" }) },
12
+ get_daily_kline: {
13
+ code: "globalStockKlineDaily",
14
+ query: (i) => ({
15
+ market: i.market,
16
+ symbol: i.symbol,
17
+ fq_type: i.adjustment,
18
+ compare: i.comparison,
19
+ in_date: i.date,
20
+ num: i.limit,
21
+ }),
22
+ },
23
+ get_company_profile: { code: "cnCompanyBasicInfo", query: symbol },
24
+ get_forex_quote: { code: "forexQuoteLatest", query: symbol },
25
+ search_news: { code: "newsSearch", query: (i) => ({ keyword: join(i.keywords), page: i.page, num: i.pageSize }) },
26
+ get_intraday_kline: {
27
+ code: "cnStockKLine",
28
+ query: (i) => ({ symbol: i.symbol, scale: i.intervalMinutes, datalen: i.limit }),
29
+ },
30
+ list_financial_report_dates: { code: "cnFinanceReportDateList", query: (i) => ({ paperCode: i.symbol }) },
31
+ get_financial_report: {
32
+ code: "cnFinanceReportsFull",
33
+ query: (i) => ({
34
+ paperCode: i.symbol,
35
+ source: map(i.reportType, {
36
+ income_statement: "lrb",
37
+ balance_sheet: "fzb",
38
+ cash_flow: "llb",
39
+ key_indicators: "gjzb",
40
+ special_indicators: "zxzb",
41
+ }),
42
+ rDate: i.reportDate,
43
+ }),
44
+ },
45
+ get_revenue_composition: {
46
+ code: "cnFinanceRevenueComposition",
47
+ query: (i) => ({ paperCode: i.symbol, frDate: i.reportDate }),
48
+ },
49
+ get_valuation_history: {
50
+ code: "cnStockValuationDetail",
51
+ query: (i) => ({
52
+ symbol: i.symbol,
53
+ rank: map(i.period, { "1y": "y1", "3y": "y3", "5y": "y5", "10y": "y10", all: "all" }),
54
+ type: map(i.metric, { pe_ttm: "syl", pb: "sjl", pcf: "sxl", dividend_yield: "gxl", market_cap: "zsz" }),
55
+ }),
56
+ },
57
+ get_major_events: {
58
+ code: "globalStockMajorEvents",
59
+ query: (i) => ({ market: map(i.market, { cn: "0", hk: "1", us: "2" }), symbols: i.symbol, pageSize: i.limit }),
60
+ },
61
+ get_margin_trading: { code: "cnStockTradingMarginList", query: pageSymbol },
62
+ get_block_trades: { code: "cnTradingBlockList", query: pageSymbol },
63
+ list_stock_connect_holdings: {
64
+ code: "cnStockConnectHoldings",
65
+ query: (i) => ({ type: i.channel, sort: i.sortBy, asc: direction(i.sortDirection), page: i.page, num: i.pageSize }),
66
+ },
67
+ get_stock_industry: { code: "swSymbolList", query: symbol },
68
+ list_sector_rankings: {
69
+ code: "cnVirtualSectorRanking",
70
+ query: (i) => ({
71
+ index_type: map(i.sectorType, {
72
+ all: "",
73
+ shenwan_level_1: "hy1",
74
+ shenwan_level_2: "hy",
75
+ shenwan_level_3: "hy3",
76
+ concept: "gn",
77
+ region: "dy",
78
+ }),
79
+ sort: i.sortBy,
80
+ asc: direction(i.sortDirection),
81
+ page: i.page,
82
+ num: i.pageSize,
83
+ }),
84
+ },
85
+ get_sector_constituents: {
86
+ code: "cnSectorComponentsRanking",
87
+ query: (i) => ({
88
+ node: i.sectorSymbol,
89
+ ret: join(i.fields),
90
+ sort: i.sortBy,
91
+ asc: direction(i.sortDirection),
92
+ page: i.page,
93
+ num: i.pageSize,
94
+ hnew: flag(i.includeNewStocks),
95
+ hcnew: flag(i.includeRecentStocks),
96
+ }),
97
+ },
98
+ list_strong_sectors: {
99
+ code: "cnMarketStrongSectors",
100
+ query: (i) => ({
101
+ type: map(i.boardGroup, { all: "all", growth: "ck", other: "other" }),
102
+ bk: map(i.sectorType, { concept: "gn", industry: "hy", region: "dy" }),
103
+ isNotSt: flag(i.excludeSt),
104
+ }),
105
+ },
106
+ list_limit_up_stocks: { code: "cnMarketLimitUpPool" },
107
+ list_consecutive_limit_up_stocks: { code: "cnStockLianBC" },
108
+ list_hot_stocks: {
109
+ code: "globalStockHotBoard",
110
+ query: (i) => ({ type: map(i.period, { day: "d", hour: "h" }), market: i.market, page: i.page, num: i.pageSize }),
111
+ },
112
+ get_batch_forex_quotes: {
113
+ code: "forexQuotesBatch",
114
+ query: (i) => ({ from: i.baseCurrency, to: join(i.targetCurrencies) }),
115
+ },
116
+ get_futures_quote: {
117
+ code: "future_quotes",
118
+ query: (i) => ({ market: map(i.market, { domestic: "gn", global: "global", financial: "cff" }), symbol: i.symbol }),
119
+ },
120
+ search_stock_news: {
121
+ code: "stockNewsSearch",
122
+ query: (i) => ({ market: i.market, symbol: i.symbol, page: i.page, num: i.pageSize }),
123
+ },
124
+ search_flash_news: {
125
+ code: "qNewsSearch",
126
+ query: (i) => ({ keyword: join(i.keywords), page: i.page, num: i.pageSize }),
127
+ },
128
+ list_flash_news: {
129
+ code: "newsFlashList",
130
+ query: (i) => ({
131
+ id: i.documentId,
132
+ type: map(i.direction, { after: "0", before: "1" }),
133
+ page: i.page,
134
+ num: i.pageSize,
135
+ }),
136
+ },
137
+ get_news_article: { code: "newsArticleDetail", query: (i) => ({ docid: i.documentId }) },
138
+ get_fund_profile: { code: "fund_info", query: symbol },
139
+ get_fund_net_values: { code: "fund_networth", query: symbol },
140
+ get_fund_returns: { code: "fund_cum_return", query: symbol },
141
+ get_fund_metrics: { code: "fund_core_metrics", query: symbol },
142
+ get_fund_stock_holdings: { code: "fund_heavy_stock", query: pageSymbol },
143
+ get_fund_industry_allocation: { code: "fund_industry_alloc", query: symbol },
144
+ get_fund_asset_allocation: { code: "fund_asset_alloc", query: symbol },
145
+ get_fund_manager: { code: "fund_manager_detail", query: (i) => ({ pscode: i.managerCode }) },
146
+ };
147
+ const handlers = Object.fromEntries(Object.entries(operations).map(([name, operation]) => [
148
+ name,
149
+ (input, context) => execute(operation, input, context),
150
+ ]));
151
+ export const executors = defineApiKeyProviderExecutors(service, handlers, {
152
+ skipDnsValidation: true,
153
+ });
154
+ export const credentialValidators = {
155
+ async apiKey(input, context) {
156
+ await request("cnMarketUpdownDistribution", {}, input.apiKey, context.fetcher, context.signal, true);
157
+ return {
158
+ profile: { accountId: "zyhub-api-key", displayName: "Sina Finance ZYHub API Key" },
159
+ grantedScopes: [],
160
+ metadata: { apiBaseUrl: baseUrl, validationEndpoint: "cnMarketUpdownDistribution" },
161
+ };
162
+ },
163
+ };
164
+ async function execute(operation, input, context) {
165
+ return {
166
+ response: await request(operation.code, operation.query?.(input) ?? {}, context.apiKey, context.fetcher, context.signal, false),
167
+ };
168
+ }
169
+ async function request(code, query, apiKey, fetcher, parentSignal, validating) {
170
+ return runProviderRequest({ signal: parentSignal, label: "Sina Finance ZYHub" }, async (signal) => {
171
+ const url = new URL(code, baseUrl);
172
+ for (const [key, value] of Object.entries(query))
173
+ if (value != null)
174
+ url.searchParams.set(key, String(value));
175
+ const response = await fetcher(url, {
176
+ headers: { accept: "application/json", "user-agent": providerUserAgent, "X-Auth-Token": apiKey },
177
+ signal,
178
+ });
179
+ const text = code === "globalStockSearchSymbols" && response.ok
180
+ ? new TextDecoder("gb18030").decode(await readBoundedResponseBytes(response, {
181
+ maxBytes: defaultProviderJsonMaxResponseBytes,
182
+ fieldName: "ZYHub response",
183
+ createError: (message) => new ProviderRequestError(413, message),
184
+ }))
185
+ : await readProviderTextBody(response, "ZYHub response");
186
+ const payload = parse(text);
187
+ if (!response.ok) {
188
+ const message = errorMessage(payload) ?? `ZYHub request failed with status ${response.status}`;
189
+ if (response.status === 429)
190
+ throw new ProviderRequestError(429, message, payload);
191
+ if (validating && response.status === 401)
192
+ throw new ProviderRequestError(400, message, payload);
193
+ throw new ProviderRequestError(response.status, message, payload);
194
+ }
195
+ return payload;
196
+ });
197
+ }
198
+ function parse(text) {
199
+ if (!text.trim())
200
+ return null;
201
+ try {
202
+ return JSON.parse(text);
203
+ }
204
+ catch {
205
+ return text;
206
+ }
207
+ }
208
+ function errorMessage(value) {
209
+ if (typeof value === "string" && value.trim())
210
+ return value.trim();
211
+ const body = optionalRecord(value);
212
+ return optionalString(body?.message) ?? optionalString(body?.msg) ?? optionalString(body?.error);
213
+ }
214
+ function map(value, values) {
215
+ const key = optionalString(value);
216
+ return key == null ? undefined : values[key];
217
+ }
218
+ function direction(value) {
219
+ return map(value, { desc: "0", asc: "1" });
220
+ }
221
+ function flag(value) {
222
+ const result = optionalBoolean(value);
223
+ return result == null ? undefined : result ? 1 : 0;
224
+ }
225
+ function join(value) {
226
+ return value == null ? undefined : looseArray(value).join(",");
227
+ }
228
+ function symbol(input) {
229
+ return { symbol: optionalString(input.symbol) };
230
+ }
231
+ function pageSymbol(input) {
232
+ return {
233
+ symbol: optionalString(input.symbol),
234
+ page: optionalInteger(input.page),
235
+ num: optionalInteger(input.pageSize),
236
+ };
237
+ }
238
+ function pick(input, fields) {
239
+ return Object.fromEntries(Object.entries(fields).map(([to, from]) => [to, input[from]]));
240
+ }
@@ -42,6 +42,9 @@ const oauthClientConfigRequestSchema = jsonSchema.object({
42
42
  minItems: 1,
43
43
  description: "Non-empty provider-declared scope subset to request. Omit to use every provider default.",
44
44
  }),
45
+ redirectUri: jsonSchema.string({
46
+ description: "Absolute redirect URI registered with the provider instead of the runtime callback, such as a native app's custom scheme. Must not carry user info or a fragment; javascript, vbscript, data, file, blob, and about schemes are rejected. Omit or send an empty string to use the runtime callback.",
47
+ }),
45
48
  extra: {
46
49
  type: "object",
47
50
  additionalProperties: { type: "string" },
@@ -457,8 +460,11 @@ export function createOpenApiDocument(providers, options = {}) {
457
460
  }),
458
461
  clientId: jsonSchema.nullable(jsonSchema.string({ description: "Configured OAuth client id." })),
459
462
  expectedRedirectUri: jsonSchema.string({
460
- description: "Callback URL to configure in the provider OAuth app.",
463
+ description: "Callback URL to configure in the provider OAuth app: the configured override, else the runtime callback.",
461
464
  }),
465
+ redirectUri: jsonSchema.nullable(jsonSchema.string({
466
+ description: "Configured redirect URI override, or null when the runtime callback is used.",
467
+ })),
462
468
  auth: jsonSchema.unknownObject("Provider OAuth capability metadata."),
463
469
  requestedScopes: jsonSchema.nullable(jsonSchema.array(jsonSchema.string(), {
464
470
  description: "Configured scope subset, or null when provider defaults are used.",
@@ -473,6 +479,7 @@ export function createOpenApiDocument(providers, options = {}) {
473
479
  "customClientAvailable",
474
480
  "clientId",
475
481
  "expectedRedirectUri",
482
+ "redirectUri",
476
483
  "auth",
477
484
  "requestedScopes",
478
485
  "effectiveScopes",
@@ -1082,6 +1089,9 @@ function createOAuthAuthorizationPath() {
1082
1089
  minItems: 1,
1083
1090
  description: "Optional non-empty provider-declared scope subset to request.",
1084
1091
  }),
1092
+ redirectUri: jsonSchema.string({
1093
+ description: "Optional redirect URI registered with the connection-scoped OAuth app instead of the runtime callback. Same rules as OAuthClientConfigRequest.redirectUri.",
1094
+ }),
1085
1095
  authorizationOptionIds: jsonSchema.array(jsonSchema.string(), {
1086
1096
  description: "Optional provider authorization option ids selected for this connection.",
1087
1097
  }),
@@ -1342,7 +1352,7 @@ function connectionManagementPaths() {
1342
1352
  oauthClient: jsonSchema.optional(jsonSchema.object("OAuth client configuration state; present when the provider supports OAuth.", {
1343
1353
  configured: jsonSchema.boolean(),
1344
1354
  customClientAvailable: jsonSchema.boolean("Whether connections may carry their own OAuth client."),
1345
- expectedRedirectUri: jsonSchema.string("Callback URL to register with the provider."),
1355
+ expectedRedirectUri: jsonSchema.string("Callback URL to register with the provider: the configured override, else the runtime callback."),
1346
1356
  missingFields: jsonSchema.stringArray("Required client inputs absent from the stored configuration."),
1347
1357
  })),
1348
1358
  });
@@ -100,7 +100,13 @@ export declare function serializeRuntimeFailure(input: RuntimeFailureInput): Run
100
100
  export declare function serializeRuntimeActionResult(input: RuntimeActionResultInput): RuntimeActionHttpResult;
101
101
  /** Validate an action response decoded from persistent storage. */
102
102
  export declare function parseRuntimeActionHttpResult(value: unknown): RuntimeActionHttpResult;
103
- /** Write a newly serialized or replayed action response. */
103
+ /**
104
+ * Write a newly serialized or replayed action response; runtime failures,
105
+ * including proxy failures, go through here as well. A 429 whose provider
106
+ * details carry `retryAfterSeconds` also answers with the `Retry-After`
107
+ * header, so HTTP callers pace on the provider's own hint; the body keeps it,
108
+ * which is what an idempotent replay re-emits the header from.
109
+ */
104
110
  export declare function writeRuntimeActionHttpResult(context: Context, result: RuntimeActionHttpResult): Response;
105
111
  export declare function mapConnectionErrorStatus(error: ConnectionError): 400 | 404 | 409;
106
112
  /**
@@ -129,6 +135,7 @@ export interface RuntimeProviderSetup {
129
135
  interface RuntimeOAuthClientSetup {
130
136
  configured: boolean;
131
137
  customClientAvailable: boolean;
138
+ /** Redirect URI to register with the provider: the configured override, else the runtime callback. */
132
139
  expectedRedirectUri: string;
133
140
  missingFields: string[];
134
141
  }
@@ -128,10 +128,28 @@ export function parseRuntimeActionHttpResult(value) {
128
128
  }
129
129
  throw invalid("status and body envelope do not match");
130
130
  }
131
- /** Write a newly serialized or replayed action response. */
131
+ /**
132
+ * Write a newly serialized or replayed action response; runtime failures,
133
+ * including proxy failures, go through here as well. A 429 whose provider
134
+ * details carry `retryAfterSeconds` also answers with the `Retry-After`
135
+ * header, so HTTP callers pace on the provider's own hint; the body keeps it,
136
+ * which is what an idempotent replay re-emits the header from.
137
+ */
132
138
  export function writeRuntimeActionHttpResult(context, result) {
139
+ const retryAfterSeconds = readRuntimeRetryAfterSeconds(result);
140
+ if (retryAfterSeconds !== undefined) {
141
+ context.header("Retry-After", String(retryAfterSeconds));
142
+ }
133
143
  return context.json(result.body, result.status);
134
144
  }
145
+ function readRuntimeRetryAfterSeconds(result) {
146
+ if (result.status !== 429) {
147
+ return undefined;
148
+ }
149
+ const seconds = optionalInteger(optionalRecord(optionalRecord(result.body.data)?.details)?.retryAfterSeconds);
150
+ // A safe integer is what keeps String() in plain delay-seconds digits: 1e21 would print as "1e+21".
151
+ return seconds !== undefined && Number.isSafeInteger(seconds) && seconds >= 0 ? seconds : undefined;
152
+ }
135
153
  export function mapConnectionErrorStatus(error) {
136
154
  if (error.code === "unknown_service" || error.code === "connection_not_found") {
137
155
  return 404;
@@ -3,7 +3,7 @@ import { compress } from "hono/compress";
3
3
  import { ConnectionError, defaultConnectionName } from "../connection-service.js";
4
4
  import { ActionPolicyService, emptyPolicyRules } from "../core/action-policy.js";
5
5
  import { DEFAULT_ACTION_SEARCH_LIMIT, createActionSearchIndexProvider, searchActions } from "../core/action-search.js";
6
- import { optionalRecord, optionalString, requiredString, requiredStringArray } from "../core/cast.js";
6
+ import { optionalRecord, optionalString, requiredRawString, requiredString, requiredStringArray, } from "../core/cast.js";
7
7
  import { PromiseCache } from "../core/promise-cache.js";
8
8
  import { MarketplaceError } from "../marketplace/marketplace-service.js";
9
9
  import { OAuthClientConfigError, OAuthClientConfigService } from "../oauth/oauth-client-config-service.js";
@@ -845,6 +845,7 @@ export class ConnectServer {
845
845
  clientId: optionalString(body.clientId) ?? "",
846
846
  clientSecret: optionalString(body.clientSecret) ?? "",
847
847
  requestedScopes: readOptionalStringArray(body, "requestedScopes"),
848
+ redirectUri: readOptionalRawString(body, "redirectUri"),
848
849
  extra: optionalRecord(body.extra),
849
850
  secretExtra: optionalRecord(body.secretExtra),
850
851
  }));
@@ -981,7 +982,7 @@ export class ConnectServer {
981
982
  }
982
983
  }
983
984
  function readOAuthClientConfigInput(body) {
984
- const keys = ["clientId", "clientSecret", "requestedScopes", "extra", "secretExtra"];
985
+ const keys = ["clientId", "clientSecret", "requestedScopes", "redirectUri", "extra", "secretExtra"];
985
986
  if (!keys.some((key) => key in body)) {
986
987
  return undefined;
987
988
  }
@@ -989,10 +990,17 @@ function readOAuthClientConfigInput(body) {
989
990
  clientId: optionalString(body.clientId) ?? "",
990
991
  clientSecret: optionalString(body.clientSecret) ?? "",
991
992
  requestedScopes: readOptionalStringArray(body, "requestedScopes"),
993
+ redirectUri: readOptionalRawString(body, "redirectUri"),
992
994
  extra: optionalRecord(body.extra),
993
995
  secretExtra: optionalRecord(body.secretExtra),
994
996
  };
995
997
  }
998
+ /** An absent field is undefined; a present one, null included, must be a string (blank is kept for the caller to read as unset). */
999
+ function readOptionalRawString(body, fieldName) {
1000
+ if (!(fieldName in body))
1001
+ return undefined;
1002
+ return requiredRawString(body[fieldName], fieldName, (message) => new HttpRequestError("invalid_input", `${message}.`));
1003
+ }
996
1004
  function readOptionalStringArray(body, fieldName) {
997
1005
  if (!(fieldName in body))
998
1006
  return undefined;
@@ -1,4 +1,4 @@
1
- import type { TransitFileRead, TransitFileUpload } from "../../core/types.ts";
1
+ import type { TransitFileRead, TransitFileStream, TransitFileUpload } from "../../core/types.ts";
2
2
  import type { IStagedTransitFileService, StagedTransitFile } from "./transit-file-store.ts";
3
3
  export interface TransitFileOptions {
4
4
  rootDir: string;
@@ -13,6 +13,7 @@ export declare class TransitFileService implements IStagedTransitFileService {
13
13
  readonly maxBytes: number;
14
14
  constructor(options: TransitFileOptions);
15
15
  create(file: File): Promise<TransitFileUpload>;
16
+ createFromStream(file: TransitFileStream): Promise<TransitFileUpload>;
16
17
  createFromPath(file: StagedTransitFile): Promise<TransitFileUpload>;
17
18
  read(fileId: string): Promise<TransitFileRead>;
18
19
  response(fileId: string): Promise<Response>;
@@ -21,5 +22,5 @@ export declare class TransitFileService implements IStagedTransitFileService {
21
22
  /** Resolve a live, unexpired file on disk together with its side-car metadata, or report it as missing. */
22
23
  private locate;
23
24
  private readMetadata;
24
- private writeFile;
25
+ private writeStream;
25
26
  }