@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,262 @@
1
+ import { compactObject, looseArray, optionalBoolean, optionalNumber, optionalRecord, optionalString, optionalStringArray, requiredString, } from "../../core/cast.js";
2
+ import { combineProviderActionHandlers, defineProviderExecutors, providerInputError, providerResponseError, readTransitFileInput, requiredInputNumber, requiredInputString, requiredResponseRecord, } from "../provider-runtime.js";
3
+ import { bilibiliArticleActionHandlers } from "./articles.js";
4
+ import { assertAnyInputField, bilibiliApiRequest, bilibiliOAuthRefreshTokenUrl, createBilibiliContext, readBilibiliReplyIncrement, readBilibiliSigningMaterial, requestBilibiliOAuthToken, } from "./runtime.js";
5
+ import { uploadBilibiliCover, uploadBilibiliVideo } from "./upload.js";
6
+ const service = "bilibili";
7
+ const bilibiliAccountActionHandlers = {
8
+ async get_user_info(_input, context) {
9
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, { path: "/user/account/info", label: "Bilibili user info" }), "Bilibili user info");
10
+ return compactObject({
11
+ name: optionalString(data.name),
12
+ face: optionalString(data.face),
13
+ openid: optionalString(data.openid),
14
+ });
15
+ },
16
+ async get_user_union_id(_input, context) {
17
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
18
+ method: "POST",
19
+ path: "/user/account/union_id",
20
+ label: "Bilibili user union id",
21
+ }), "Bilibili user union id");
22
+ return compactObject({ unionId: optionalString(data.union_id) });
23
+ },
24
+ async get_user_scopes(_input, context) {
25
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, { path: "/user/account/scopes", label: "Bilibili user scopes" }), "Bilibili user scopes");
26
+ return compactObject({
27
+ openid: optionalString(data.openid),
28
+ scopes: optionalStringArray(data.scopes) ?? [],
29
+ });
30
+ },
31
+ async get_user_stat(_input, context) {
32
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, { path: "/data/user/stat", label: "Bilibili user stat" }), "Bilibili user stat");
33
+ return compactObject({
34
+ following: optionalNumber(data.following),
35
+ follower: optionalNumber(data.follower),
36
+ arcPassedTotal: optionalNumber(data.arc_passed_total),
37
+ });
38
+ },
39
+ async list_archive_types(_input, context) {
40
+ const data = await bilibiliApiRequest(context, { path: "/archive/type/list", label: "Bilibili archive type list" });
41
+ return { types: looseArray(data) };
42
+ },
43
+ async upload_video(input, context) {
44
+ const title = requiredInputString(input.title, "title");
45
+ const tid = requiredInputNumber(input.tid, "tid");
46
+ const tag = requiredInputString(input.tag, "tag");
47
+ const copyright = requiredInputNumber(input.copyright, "copyright");
48
+ const source = optionalString(input.source);
49
+ if (copyright === 2 && !source) {
50
+ throw providerInputError("source is required when copyright is 2 (reposted content).");
51
+ }
52
+ const video = await readTransitFileInput(input.file, context);
53
+ const coverInput = input.cover === undefined ? undefined : await readTransitFileInput(input.cover, context);
54
+ const coverUrl = coverInput ? await uploadBilibiliCover(context, coverInput.file) : undefined;
55
+ const uploadToken = await uploadBilibiliVideo(context, video.file);
56
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
57
+ method: "POST",
58
+ path: "/archive/add-by-utoken",
59
+ query: { upload_token: uploadToken },
60
+ body: compactObject({
61
+ title,
62
+ tid,
63
+ tag,
64
+ copyright,
65
+ cover: coverUrl,
66
+ desc: optionalString(input.desc),
67
+ no_reprint: optionalBoolean(input.noReprint) === true ? 1 : 0,
68
+ source,
69
+ }),
70
+ label: "Bilibili archive submit",
71
+ }), "Bilibili archive submit");
72
+ return compactObject({
73
+ resourceId: requiredString(data.resource_id, "resource_id", providerResponseError),
74
+ coverUrl,
75
+ });
76
+ },
77
+ async list_archives(input, context) {
78
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
79
+ path: "/archive/viewlist",
80
+ query: {
81
+ pn: String(optionalNumber(input.pn) ?? 1),
82
+ ps: String(optionalNumber(input.ps) ?? 20),
83
+ status: optionalString(input.status),
84
+ },
85
+ label: "Bilibili archive list",
86
+ }), "Bilibili archive list");
87
+ const page = optionalRecord(data.page);
88
+ return {
89
+ archives: looseArray(data.list).map(normalizeBilibiliArchive),
90
+ page: compactObject({
91
+ pn: optionalNumber(page?.pn),
92
+ ps: optionalNumber(page?.ps),
93
+ total: optionalNumber(page?.total),
94
+ }),
95
+ };
96
+ },
97
+ async get_archive(input, context) {
98
+ const resourceId = requiredInputString(input.resourceId, "resourceId");
99
+ const data = await bilibiliApiRequest(context, {
100
+ path: "/archive/view",
101
+ query: { resource_id: resourceId },
102
+ label: "Bilibili archive view",
103
+ });
104
+ return normalizeBilibiliArchive(data);
105
+ },
106
+ async edit_archive(input, context) {
107
+ const resourceId = requiredInputString(input.resourceId, "resourceId");
108
+ assertAnyInputField(input, ["title", "tid", "coverUrl", "desc", "noReprint"]);
109
+ // Bilibili replaces every descriptive field on edit, so unchanged fields
110
+ // are refilled from the current archive before submitting.
111
+ const current = requiredResponseRecord(await bilibiliApiRequest(context, {
112
+ path: "/archive/view",
113
+ query: { resource_id: resourceId },
114
+ label: "Bilibili archive view",
115
+ }), "Bilibili archive view");
116
+ const title = optionalString(input.title) ?? optionalString(current.title);
117
+ const tid = optionalNumber(input.tid) ?? optionalNumber(current.tid);
118
+ if (!title || tid === undefined) {
119
+ throw providerInputError("title and tid are required when the current archive does not provide them.");
120
+ }
121
+ const noReprint = optionalBoolean(input.noReprint);
122
+ await bilibiliApiRequest(context, {
123
+ method: "POST",
124
+ path: "/archive/edit",
125
+ body: compactObject({
126
+ resource_id: resourceId,
127
+ title,
128
+ tid,
129
+ cover: optionalString(input.coverUrl) ?? optionalString(current.cover),
130
+ desc: optionalString(input.desc) ?? optionalString(current.desc),
131
+ no_reprint: noReprint === undefined ? optionalNumber(current.no_reprint) : noReprint ? 1 : 0,
132
+ }),
133
+ label: "Bilibili archive edit",
134
+ });
135
+ return { resourceId };
136
+ },
137
+ async delete_archive(input, context) {
138
+ const resourceId = requiredInputString(input.resourceId, "resourceId");
139
+ await bilibiliApiRequest(context, {
140
+ method: "POST",
141
+ path: "/archive/delete",
142
+ body: { resource_id: resourceId },
143
+ label: "Bilibili archive delete",
144
+ });
145
+ return { resourceId, deleted: true };
146
+ },
147
+ async get_archive_stat(input, context) {
148
+ const resourceId = requiredInputString(input.resourceId, "resourceId");
149
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
150
+ path: "/data/arc/stat",
151
+ query: { resource_id: resourceId },
152
+ label: "Bilibili archive stat",
153
+ }), "Bilibili archive stat");
154
+ return compactObject({
155
+ title: optionalString(data.title),
156
+ ptime: optionalNumber(data.ptime),
157
+ view: optionalNumber(data.view),
158
+ danmaku: optionalNumber(data.danmaku),
159
+ reply: optionalNumber(data.reply),
160
+ favorite: optionalNumber(data.favorite),
161
+ coin: optionalNumber(data.coin),
162
+ share: optionalNumber(data.share),
163
+ like: optionalNumber(data.like),
164
+ });
165
+ },
166
+ async get_archive_inc_stats(_input, context) {
167
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, { path: "/data/arc/inc-stats", label: "Bilibili archive incremental stats" }), "Bilibili archive incremental stats");
168
+ return compactObject({
169
+ incClick: optionalNumber(data.inc_click),
170
+ incDanmaku: optionalNumber(data.inc_dm),
171
+ incReply: readBilibiliReplyIncrement(data),
172
+ incFavorite: optionalNumber(data.inc_fav),
173
+ incCoin: optionalNumber(data.inc_coin),
174
+ incShare: optionalNumber(data.inc_share),
175
+ incLike: optionalNumber(data.inc_like),
176
+ incElec: optionalNumber(data.inc_elec),
177
+ });
178
+ },
179
+ };
180
+ function normalizeBilibiliArchive(value) {
181
+ const record = optionalRecord(value) ?? {};
182
+ const review = optionalRecord(record.addit_info);
183
+ const video = optionalRecord(record.video_info);
184
+ return compactObject({
185
+ resourceId: optionalString(record.resource_id),
186
+ title: optionalString(record.title),
187
+ cover: optionalString(record.cover),
188
+ tid: optionalNumber(record.tid),
189
+ tag: optionalString(record.tag),
190
+ desc: optionalString(record.desc),
191
+ copyright: optionalNumber(record.copyright),
192
+ noReprint: optionalNumber(record.no_reprint),
193
+ state: optionalNumber(review?.state),
194
+ stateDesc: optionalString(review?.state_desc),
195
+ rejectReason: optionalString(review?.reject_reason),
196
+ ctime: optionalNumber(record.ctime),
197
+ ptime: optionalNumber(record.ptime),
198
+ video: video === undefined
199
+ ? undefined
200
+ : compactObject({
201
+ cid: optionalNumber(video.cid),
202
+ filename: optionalString(video.filename),
203
+ duration: optionalNumber(video.duration),
204
+ shareUrl: optionalString(video.share_url),
205
+ iframeUrl: optionalString(video.iframe_url),
206
+ }),
207
+ });
208
+ }
209
+ export const bilibiliActionHandlers = combineProviderActionHandlers(service, bilibiliAccountActionHandlers, bilibiliArticleActionHandlers);
210
+ export const executors = defineProviderExecutors({
211
+ service,
212
+ handlers: bilibiliActionHandlers,
213
+ // Both API hosts are hardcoded literals: member.bilibili.com and openupos.bilivideo.com.
214
+ skipDnsValidation: true,
215
+ createContext: createBilibiliContext,
216
+ });
217
+ export const oauth = {
218
+ async exchangeCode(input) {
219
+ return requestBilibiliOAuthToken({
220
+ url: input.tokenUrl,
221
+ fields: {
222
+ client_id: input.clientConfig.clientId,
223
+ client_secret: input.clientConfig.clientSecret,
224
+ grant_type: "authorization_code",
225
+ code: input.code,
226
+ },
227
+ fetcher: input.fetcher,
228
+ signal: input.signal,
229
+ createError: input.createError,
230
+ });
231
+ },
232
+ async refreshAccessToken(input) {
233
+ return requestBilibiliOAuthToken({
234
+ url: bilibiliOAuthRefreshTokenUrl,
235
+ fields: {
236
+ client_id: input.clientConfig.clientId,
237
+ client_secret: input.clientConfig.clientSecret,
238
+ grant_type: "refresh_token",
239
+ refresh_token: input.refreshToken,
240
+ },
241
+ fetcher: input.fetcher,
242
+ createError: input.createError,
243
+ });
244
+ },
245
+ };
246
+ export const credentialValidators = {
247
+ async oauth2(input, { fetcher, signal }) {
248
+ const signing = readBilibiliSigningMaterial(input.metadata);
249
+ if (!signing) {
250
+ return;
251
+ }
252
+ const data = requiredResponseRecord(await bilibiliApiRequest({ accessToken: input.accessToken, clientId: signing.clientId, appSecret: signing.appSecret, fetcher, signal }, { path: "/user/account/info", label: "Bilibili credential validation" }), "Bilibili credential validation");
253
+ const openid = optionalString(data.openid);
254
+ return {
255
+ profile: {
256
+ accountId: openid ?? "bilibili:oauth2",
257
+ displayName: optionalString(data.name) ?? openid ?? "Bilibili OAuth Credential",
258
+ grantedScopes: optionalStringArray(input.metadata.scopes) ?? [],
259
+ },
260
+ };
261
+ },
262
+ };
@@ -0,0 +1,95 @@
1
+ import type { ExecutionContext, TransitFileWriter } from "../../core/types.ts";
2
+ import type { OAuthTokenResult } from "../../oauth/oauth-token.ts";
3
+ import type { ProviderFetch, ProviderRuntimeHandler } from "../provider-runtime.ts";
4
+ export declare const bilibiliUposBaseUrl = "https://openupos.bilivideo.com";
5
+ export declare const bilibiliOAuthRefreshTokenUrl = "https://api.bilibili.com/x/account-oauth2/v1/refresh_token";
6
+ export interface BilibiliActionContext {
7
+ accessToken: string;
8
+ clientId: string;
9
+ appSecret: string;
10
+ fetcher: ProviderFetch;
11
+ transitFiles?: TransitFileWriter;
12
+ signal?: AbortSignal;
13
+ }
14
+ /** Provider-native handler shape for Bilibili actions. */
15
+ export type BilibiliActionHandler = ProviderRuntimeHandler<BilibiliActionContext>;
16
+ /**
17
+ * Reject an edit call that carries no editable field: it would resubmit
18
+ * unchanged content and still cost a provider-side review round.
19
+ */
20
+ export declare function assertAnyInputField(input: Record<string, unknown>, fields: readonly string[]): void;
21
+ /** Read the signing material stored on the connection metadata by the OAuth client config. */
22
+ export declare function readBilibiliSigningMaterial(metadata: Record<string, unknown>): {
23
+ clientId: string;
24
+ appSecret: string;
25
+ } | undefined;
26
+ /**
27
+ * Resolve the access token plus the signing material of the configured OAuth
28
+ * client. The Bilibili signature signs with the application App Secret, which
29
+ * the OAuth client config stores as the `appSecret` secretExtra field.
30
+ */
31
+ export declare function createBilibiliContext(context: ExecutionContext, fetcher: ProviderFetch): Promise<BilibiliActionContext>;
32
+ export interface BilibiliSignedHeadersInput {
33
+ clientId: string;
34
+ appSecret: string;
35
+ accessToken: string;
36
+ /** Exact request body string; GET and empty-body requests sign the MD5 of "". */
37
+ body?: string;
38
+ }
39
+ /**
40
+ * Create the signed common header set (signature version 2.0) for one arcopen
41
+ * request. The HMAC-SHA256 signature covers the sorted x-bili-* header lines
42
+ * joined by "\n"; per Bilibili's official demos the trailing newline of the
43
+ * last line is trimmed before signing.
44
+ */
45
+ export declare function createBilibiliSignedHeaders(input: BilibiliSignedHeadersInput): Record<string, string>;
46
+ export interface BilibiliApiRequestOptions {
47
+ method?: "GET" | "POST";
48
+ /** Endpoint path under https://member.bilibili.com/arcopen/fn, for example /archive/viewlist. */
49
+ path: string;
50
+ query?: Record<string, string | undefined>;
51
+ body?: Record<string, unknown>;
52
+ /** Short request label used in error messages. */
53
+ label: string;
54
+ }
55
+ /** Call one signed member.bilibili.com arcopen endpoint and unwrap its data envelope. */
56
+ export declare function bilibiliApiRequest(context: BilibiliActionContext, options: BilibiliApiRequestOptions): Promise<unknown>;
57
+ export interface BilibiliFormRequestOptions {
58
+ /** Endpoint path under https://member.bilibili.com/arcopen/fn. */
59
+ path: string;
60
+ /** Form fields; undefined values are skipped. */
61
+ fields?: Record<string, string | undefined>;
62
+ /** Optional file part, sent as the `file` form field. */
63
+ file?: File;
64
+ /** Short request label used in error messages. */
65
+ label: string;
66
+ }
67
+ /**
68
+ * POST one arcopen endpoint as multipart/form-data. Bilibili article
69
+ * endpoints take form fields instead of JSON, and per the signing rules the
70
+ * content MD5 of a form body is computed over an empty string.
71
+ */
72
+ export declare function bilibiliFormRequest(context: BilibiliActionContext, options: BilibiliFormRequestOptions): Promise<unknown>;
73
+ /** Read one arcopen/openupos response into its envelope data, mapping HTTP and envelope failures. */
74
+ export declare function readBilibiliApiResponse(response: Response, label: string): Promise<unknown>;
75
+ /** Unwrap the shared Bilibili { code, message, data } envelope of arcopen and openupos responses. */
76
+ export declare function readBilibiliEnvelopeData(payload: unknown, label: string): unknown;
77
+ /**
78
+ * Read the comment increment of an inc-stats response. The field table of the
79
+ * official document spells this counter icn_reply; the sample payload uses inc_reply.
80
+ */
81
+ export declare function readBilibiliReplyIncrement(data: Record<string, unknown>): number | undefined;
82
+ export interface BilibiliOAuthTokenRequest {
83
+ url: string;
84
+ fields: Record<string, string>;
85
+ fetcher: ProviderFetch;
86
+ signal?: AbortSignal;
87
+ createError: (message: string) => Error;
88
+ }
89
+ /**
90
+ * Exchange or refresh a Bilibili OAuth token. Two Bilibili quirks force this
91
+ * provider-local request: the parameters travel in the URL query (the
92
+ * documents list them as url 参数), and `expires_in` is an absolute UTC Unix
93
+ * timestamp rather than a lifetime in seconds.
94
+ */
95
+ export declare function requestBilibiliOAuthToken(input: BilibiliOAuthTokenRequest): Promise<OAuthTokenResult>;
@@ -0,0 +1,236 @@
1
+ import { createHash, createHmac, randomUUID } from "node:crypto";
2
+ import { compactObject, optionalNumber, optionalRecord, optionalString, requiredString } from "../../core/cast.js";
3
+ import { ProviderRequestError, createProviderTimeout, isAbortLikeError, providerInputError, providerResponseError, providerUserAgent, readProviderErrorTextBody, readProviderJsonBody, requireOAuthCredential, requiredResponseRecord, runProviderRequest, setSearchParams, } from "../provider-runtime.js";
4
+ const bilibiliMemberBaseUrl = "https://member.bilibili.com";
5
+ export const bilibiliUposBaseUrl = "https://openupos.bilivideo.com";
6
+ export const bilibiliOAuthRefreshTokenUrl = "https://api.bilibili.com/x/account-oauth2/v1/refresh_token";
7
+ const bilibiliApiBasePath = "/arcopen/fn";
8
+ const bilibiliSignatureMethod = "HMAC-SHA256";
9
+ const bilibiliSignatureVersion = "2.0";
10
+ // Business codes from the 接口签名实现标准和状态码 document. -101 is the
11
+ // main-site "not logged in" code that arcopen also returns for a dead token.
12
+ const bilibiliCredentialErrorCodes = new Set([-101, 127000, 127001, 127002, 127004]);
13
+ const bilibiliPermissionErrorCodes = new Set([123001, 127005, 127006, 127007, 127011, 127304, 127305]);
14
+ const bilibiliRateLimitErrorCodes = new Set([127009, 127306]);
15
+ const bilibiliInvalidInputErrorCodes = new Set([
16
+ 4000, 123003, 123004, 123005, 123008, 123009, 123010, 123012, 123013, 123014, 123015, 123016, 123017, 123018, 123024,
17
+ 123029, 123030, 123038, 123040, 123041, 129000, 129001, 129002, 129003, 129004, 129005, 129006, 129009, 129010,
18
+ 129012, 129015, 129018,
19
+ ]);
20
+ /**
21
+ * Reject an edit call that carries no editable field: it would resubmit
22
+ * unchanged content and still cost a provider-side review round.
23
+ */
24
+ export function assertAnyInputField(input, fields) {
25
+ if (!fields.some((field) => input[field] !== undefined)) {
26
+ throw providerInputError(`Provide at least one field to edit: ${fields.join(", ")}.`);
27
+ }
28
+ }
29
+ /** Read the signing material stored on the connection metadata by the OAuth client config. */
30
+ export function readBilibiliSigningMaterial(metadata) {
31
+ const clientId = optionalString(metadata.oauthClientId);
32
+ const appSecret = optionalString(optionalRecord(metadata.oauthClientSecretExtra)?.appSecret);
33
+ return clientId && appSecret ? { clientId, appSecret } : undefined;
34
+ }
35
+ /**
36
+ * Resolve the access token plus the signing material of the configured OAuth
37
+ * client. The Bilibili signature signs with the application App Secret, which
38
+ * the OAuth client config stores as the `appSecret` secretExtra field.
39
+ */
40
+ export async function createBilibiliContext(context, fetcher) {
41
+ const credential = await requireOAuthCredential(context, "bilibili");
42
+ const signing = readBilibiliSigningMaterial(credential.metadata);
43
+ if (!signing) {
44
+ throw new ProviderRequestError(401, "Bilibili OAuth client is missing clientId or appSecret. Reconfigure the Bilibili OAuth client first.");
45
+ }
46
+ return {
47
+ accessToken: credential.accessToken,
48
+ clientId: signing.clientId,
49
+ appSecret: signing.appSecret,
50
+ fetcher,
51
+ transitFiles: context.transitFiles,
52
+ signal: context.signal,
53
+ };
54
+ }
55
+ /**
56
+ * Create the signed common header set (signature version 2.0) for one arcopen
57
+ * request. The HMAC-SHA256 signature covers the sorted x-bili-* header lines
58
+ * joined by "\n"; per Bilibili's official demos the trailing newline of the
59
+ * last line is trimmed before signing.
60
+ */
61
+ export function createBilibiliSignedHeaders(input) {
62
+ const contentMd5 = createHash("md5")
63
+ .update(input.body ?? "")
64
+ .digest("hex");
65
+ const nonce = randomUUID();
66
+ const timestamp = Math.floor(Date.now() / 1000).toString();
67
+ const signedLines = [
68
+ `x-bili-accesskeyid:${input.clientId}`,
69
+ `x-bili-content-md5:${contentMd5}`,
70
+ `x-bili-signature-method:${bilibiliSignatureMethod}`,
71
+ `x-bili-signature-nonce:${nonce}`,
72
+ `x-bili-signature-version:${bilibiliSignatureVersion}`,
73
+ `x-bili-timestamp:${timestamp}`,
74
+ ];
75
+ const authorization = createHmac("sha256", input.appSecret).update(signedLines.join("\n")).digest("hex");
76
+ return {
77
+ accept: "application/json",
78
+ "access-token": input.accessToken,
79
+ authorization,
80
+ "x-bili-accesskeyid": input.clientId,
81
+ "x-bili-content-md5": contentMd5,
82
+ "x-bili-signature-method": bilibiliSignatureMethod,
83
+ "x-bili-signature-nonce": nonce,
84
+ "x-bili-signature-version": bilibiliSignatureVersion,
85
+ "x-bili-timestamp": timestamp,
86
+ };
87
+ }
88
+ /** Call one signed member.bilibili.com arcopen endpoint and unwrap its data envelope. */
89
+ export async function bilibiliApiRequest(context, options) {
90
+ const method = options.method ?? "GET";
91
+ const url = new URL(`${bilibiliMemberBaseUrl}${bilibiliApiBasePath}${options.path}`);
92
+ setSearchParams(url, options.query ?? {});
93
+ const body = options.body === undefined ? undefined : JSON.stringify(options.body);
94
+ const headers = createBilibiliSignedHeaders({
95
+ clientId: context.clientId,
96
+ appSecret: context.appSecret,
97
+ accessToken: context.accessToken,
98
+ body,
99
+ });
100
+ // The document's samples send Content-Type: application/json on every
101
+ // JSON-channel request, including GETs and body-less POSTs.
102
+ headers["content-type"] = "application/json";
103
+ return runProviderRequest({ signal: context.signal, label: options.label }, async (signal) => readBilibiliApiResponse(await context.fetcher(url.toString(), { method, headers, body, signal }), options.label));
104
+ }
105
+ /**
106
+ * POST one arcopen endpoint as multipart/form-data. Bilibili article
107
+ * endpoints take form fields instead of JSON, and per the signing rules the
108
+ * content MD5 of a form body is computed over an empty string.
109
+ */
110
+ export async function bilibiliFormRequest(context, options) {
111
+ const url = new URL(`${bilibiliMemberBaseUrl}${bilibiliApiBasePath}${options.path}`);
112
+ const form = new FormData();
113
+ for (const [key, value] of Object.entries(options.fields ?? {})) {
114
+ if (value !== undefined) {
115
+ form.set(key, value);
116
+ }
117
+ }
118
+ if (options.file) {
119
+ form.set("file", options.file, options.file.name);
120
+ }
121
+ const headers = createBilibiliSignedHeaders({
122
+ clientId: context.clientId,
123
+ appSecret: context.appSecret,
124
+ accessToken: context.accessToken,
125
+ });
126
+ return runProviderRequest({ signal: context.signal, label: options.label }, async (signal) => readBilibiliApiResponse(await context.fetcher(url.toString(), { method: "POST", headers, body: form, signal }), options.label));
127
+ }
128
+ /** Read one arcopen/openupos response into its envelope data, mapping HTTP and envelope failures. */
129
+ export async function readBilibiliApiResponse(response, label) {
130
+ if (!response.ok) {
131
+ const text = await readProviderErrorTextBody(response, `${label} error response`);
132
+ throw new ProviderRequestError(response.status >= 500 ? 502 : response.status, text || `${label} failed with HTTP ${response.status}`);
133
+ }
134
+ const payload = await readProviderJsonBody(response, {
135
+ emptyBody: {},
136
+ invalidJsonMessage: `${label} returned invalid JSON`,
137
+ });
138
+ return readBilibiliEnvelopeData(payload, label);
139
+ }
140
+ /** Unwrap the shared Bilibili { code, message, data } envelope of arcopen and openupos responses. */
141
+ export function readBilibiliEnvelopeData(payload, label) {
142
+ const envelope = requiredResponseRecord(payload, label);
143
+ const code = optionalNumber(envelope.code);
144
+ if (code === 0) {
145
+ return envelope.data;
146
+ }
147
+ const message = optionalString(envelope.message) ?? "unknown error";
148
+ if (code !== undefined && bilibiliCredentialErrorCodes.has(code)) {
149
+ throw new ProviderRequestError(401, `Bilibili credential expired or invalid (code ${code}): ${message}`, envelope);
150
+ }
151
+ const status = code === undefined ? 502 : mapBilibiliErrorStatus(code);
152
+ if (status === 502) {
153
+ throw providerResponseError(`${label} failed (code ${code ?? "unknown"}): ${message}`);
154
+ }
155
+ throw new ProviderRequestError(status, `${label} failed (code ${code}): ${message}`, envelope);
156
+ }
157
+ /** Map a documented non-zero business code to the runtime status it means; unknown codes stay upstream failures. */
158
+ function mapBilibiliErrorStatus(code) {
159
+ if (bilibiliPermissionErrorCodes.has(code)) {
160
+ return 403;
161
+ }
162
+ if (bilibiliRateLimitErrorCodes.has(code)) {
163
+ return 429;
164
+ }
165
+ if (bilibiliInvalidInputErrorCodes.has(code)) {
166
+ return 400;
167
+ }
168
+ return 502;
169
+ }
170
+ /**
171
+ * Read the comment increment of an inc-stats response. The field table of the
172
+ * official document spells this counter icn_reply; the sample payload uses inc_reply.
173
+ */
174
+ export function readBilibiliReplyIncrement(data) {
175
+ return optionalNumber(data.inc_reply) ?? optionalNumber(data.icn_reply);
176
+ }
177
+ /**
178
+ * Exchange or refresh a Bilibili OAuth token. Two Bilibili quirks force this
179
+ * provider-local request: the parameters travel in the URL query (the
180
+ * documents list them as url 参数), and `expires_in` is an absolute UTC Unix
181
+ * timestamp rather than a lifetime in seconds.
182
+ */
183
+ export async function requestBilibiliOAuthToken(input) {
184
+ const url = new URL(input.url);
185
+ setSearchParams(url, input.fields);
186
+ const timeout = createProviderTimeout(input.signal);
187
+ let response;
188
+ try {
189
+ response = await input.fetcher(url.toString(), {
190
+ method: "POST",
191
+ headers: {
192
+ accept: "application/json",
193
+ "content-type": "application/x-www-form-urlencoded",
194
+ "user-agent": providerUserAgent,
195
+ },
196
+ redirect: "manual",
197
+ signal: timeout.signal,
198
+ });
199
+ }
200
+ catch (error) {
201
+ timeout.cleanup();
202
+ if (input.signal?.aborted) {
203
+ throw input.createError("Bilibili OAuth token request was cancelled.");
204
+ }
205
+ if (timeout.didTimeout() || isAbortLikeError(error)) {
206
+ throw input.createError("Bilibili OAuth token request timed out.");
207
+ }
208
+ throw input.createError("Bilibili OAuth token request failed without an HTTP response.");
209
+ }
210
+ try {
211
+ const payload = optionalRecord(await readProviderJsonBody(response, {
212
+ emptyBody: {},
213
+ invalidJsonMessage: "Bilibili OAuth token request returned invalid JSON",
214
+ }));
215
+ const code = optionalNumber(payload?.code);
216
+ if (!response.ok || code !== 0) {
217
+ throw input.createError(optionalString(payload?.message) ?? `Bilibili OAuth token request failed (HTTP ${response.status}).`);
218
+ }
219
+ const data = optionalRecord(payload?.data);
220
+ const accessToken = requiredString(data?.access_token, "access_token", input.createError);
221
+ return {
222
+ accessToken,
223
+ refreshToken: optionalString(data?.refresh_token),
224
+ tokenType: "Bearer",
225
+ expiresAt: readBilibiliExpiresAt(optionalNumber(data?.expires_in)),
226
+ metadata: compactObject({ scopes: data?.scopes }),
227
+ };
228
+ }
229
+ finally {
230
+ timeout.cleanup();
231
+ }
232
+ }
233
+ function readBilibiliExpiresAt(expiresAtUnixSeconds) {
234
+ const millis = (expiresAtUnixSeconds ?? 0) * 1000;
235
+ return millis > 0 && millis <= 8.64e15 ? new Date(millis).toISOString() : undefined;
236
+ }
@@ -0,0 +1,14 @@
1
+ import type { BilibiliActionContext } from "./runtime.ts";
2
+ /**
3
+ * Upload one video through Bilibili's upload-token flow: initialize, then a
4
+ * single openupos upload (files up to 100MB) or equal-sized 10MB parts plus
5
+ * the merge call. Returns the upload token used by archive submission.
6
+ */
7
+ export declare function uploadBilibiliVideo(context: BilibiliActionContext, file: File): Promise<string>;
8
+ /** Upload one cover image (jpeg/png, up to 5MB) and return its Bilibili-hosted URL. */
9
+ export declare function uploadBilibiliCover(context: BilibiliActionContext, file: File): Promise<string>;
10
+ /** Upload one article image (jpg/png, up to 5MB) and return its Bilibili-hosted URL. */
11
+ export declare function uploadBilibiliArticleImage(context: BilibiliActionContext, file: File, watermark?: boolean): Promise<{
12
+ url: string;
13
+ size?: number;
14
+ }>;