specific-ai-cli 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +177 -0
  3. package/README.ja.md +177 -0
  4. package/README.ko.md +177 -0
  5. package/README.md +174 -2
  6. package/README.zh-TW.md +177 -0
  7. package/assets/banner.txt +3 -0
  8. package/dist/cli/argv.js +98 -0
  9. package/dist/cli/commands/auth.js +11 -0
  10. package/dist/cli/commands/config.js +23 -0
  11. package/dist/cli/commands/doctor.js +134 -0
  12. package/dist/cli/commands/platforms.js +24 -0
  13. package/dist/cli/commands/version.js +4 -0
  14. package/dist/cli/confirm.js +22 -0
  15. package/dist/cli/dispatch.js +440 -0
  16. package/dist/cli/help.js +223 -0
  17. package/dist/cli/hints.js +60 -0
  18. package/dist/cli/main.js +18 -0
  19. package/dist/cli/output.js +66 -0
  20. package/dist/core/auth-handlers.js +41 -0
  21. package/dist/core/auth-store.js +119 -0
  22. package/dist/core/capabilities.js +163 -0
  23. package/dist/core/config.js +121 -0
  24. package/dist/core/cookies.js +164 -0
  25. package/dist/core/errors.js +37 -0
  26. package/dist/core/files.js +89 -0
  27. package/dist/core/fsutil.js +52 -0
  28. package/dist/core/hash.js +4 -0
  29. package/dist/core/http.js +331 -0
  30. package/dist/core/image.js +280 -0
  31. package/dist/core/log.js +40 -0
  32. package/dist/core/login.js +191 -0
  33. package/dist/core/mp4.js +130 -0
  34. package/dist/core/normalize.js +207 -0
  35. package/dist/core/options.js +110 -0
  36. package/dist/core/paths.js +38 -0
  37. package/dist/core/pb.js +59 -0
  38. package/dist/core/py.js +181 -0
  39. package/dist/core/rand.js +131 -0
  40. package/dist/core/registry.js +129 -0
  41. package/dist/core/schemas.js +41 -0
  42. package/dist/core/stream.js +104 -0
  43. package/dist/core/toolkit.js +35 -0
  44. package/dist/core/vm.js +73 -0
  45. package/dist/core/vocab.js +157 -0
  46. package/dist/platforms/_shared/impaas.js +410 -0
  47. package/dist/platforms/bilibili/index.js +216 -0
  48. package/dist/platforms/bilibili/web/api.js +806 -0
  49. package/dist/platforms/bilibili/web/client.js +311 -0
  50. package/dist/platforms/bilibili/web/commands.js +783 -0
  51. package/dist/platforms/bilibili/web/ddddocr.js +342 -0
  52. package/dist/platforms/bilibili/web/gaia.js +130 -0
  53. package/dist/platforms/bilibili/web/geetest-manual.js +154 -0
  54. package/dist/platforms/bilibili/web/geetest-vision.js +232 -0
  55. package/dist/platforms/bilibili/web/geetest.js +450 -0
  56. package/dist/platforms/bilibili/web/normalize.js +234 -0
  57. package/dist/platforms/bilibili/web/profile.js +135 -0
  58. package/dist/platforms/bilibili/web/resolve.js +112 -0
  59. package/dist/platforms/bilibili/web/sign.js +223 -0
  60. package/dist/platforms/bilibili/web/upos.js +102 -0
  61. package/dist/platforms/douyin/index.js +142 -0
  62. package/dist/platforms/douyin/web/api.js +863 -0
  63. package/dist/platforms/douyin/web/client.js +413 -0
  64. package/dist/platforms/douyin/web/commands.js +712 -0
  65. package/dist/platforms/douyin/web/creator.js +601 -0
  66. package/dist/platforms/douyin/web/crypto.js +247 -0
  67. package/dist/platforms/douyin/web/dtrait.js +190 -0
  68. package/dist/platforms/douyin/web/im.js +502 -0
  69. package/dist/platforms/douyin/web/image.js +5 -0
  70. package/dist/platforms/douyin/web/live.js +225 -0
  71. package/dist/platforms/douyin/web/mssdk.js +148 -0
  72. package/dist/platforms/douyin/web/normalize.js +326 -0
  73. package/dist/platforms/douyin/web/passport.js +1247 -0
  74. package/dist/platforms/douyin/web/profile.js +221 -0
  75. package/dist/platforms/douyin/web/proto.js +26 -0
  76. package/dist/platforms/douyin/web/resolve.js +88 -0
  77. package/dist/platforms/douyin/web/sign.js +436 -0
  78. package/dist/platforms/douyin/web/tos.js +78 -0
  79. package/dist/platforms/index.js +17 -0
  80. package/dist/platforms/jd/index.js +67 -0
  81. package/dist/platforms/jd/web/aks.js +115 -0
  82. package/dist/platforms/jd/web/api.js +549 -0
  83. package/dist/platforms/jd/web/chat.js +132 -0
  84. package/dist/platforms/jd/web/client.js +470 -0
  85. package/dist/platforms/jd/web/commands.js +557 -0
  86. package/dist/platforms/jd/web/jcap/helper.js +90 -0
  87. package/dist/platforms/jd/web/jcap/host.js +107 -0
  88. package/dist/platforms/jd/web/jcap/lsd.js +300 -0
  89. package/dist/platforms/jd/web/jcap/solver.js +995 -0
  90. package/dist/platforms/jd/web/jcap.js +150 -0
  91. package/dist/platforms/jd/web/js.js +420 -0
  92. package/dist/platforms/jd/web/login.js +775 -0
  93. package/dist/platforms/jd/web/normalize.js +297 -0
  94. package/dist/platforms/jd/web/profile.js +256 -0
  95. package/dist/platforms/jd/web/util.js +175 -0
  96. package/dist/platforms/jd/web/webm.js +133 -0
  97. package/dist/platforms/kuaishou/index.js +98 -0
  98. package/dist/platforms/kuaishou/web/api.js +694 -0
  99. package/dist/platforms/kuaishou/web/captcha-crypto.js +134 -0
  100. package/dist/platforms/kuaishou/web/captcha-fp.js +180 -0
  101. package/dist/platforms/kuaishou/web/captcha.js +273 -0
  102. package/dist/platforms/kuaishou/web/client.js +487 -0
  103. package/dist/platforms/kuaishou/web/commands.js +863 -0
  104. package/dist/platforms/kuaishou/web/gap.js +144 -0
  105. package/dist/platforms/kuaishou/web/gdfp.js +376 -0
  106. package/dist/platforms/kuaishou/web/gql.js +11 -0
  107. package/dist/platforms/kuaishou/web/live.js +125 -0
  108. package/dist/platforms/kuaishou/web/normalize.js +241 -0
  109. package/dist/platforms/kuaishou/web/oracle.js +329 -0
  110. package/dist/platforms/kuaishou/web/profile.js +259 -0
  111. package/dist/platforms/kuaishou/web/resolve.js +84 -0
  112. package/dist/platforms/kuaishou/web/session.js +592 -0
  113. package/dist/platforms/kuaishou/web/sign.js +568 -0
  114. package/dist/platforms/taobao/index.js +56 -0
  115. package/dist/platforms/taobao/web/api.js +96 -0
  116. package/dist/platforms/taobao/web/client.js +102 -0
  117. package/dist/platforms/taobao/web/commands.js +172 -0
  118. package/dist/platforms/taobao/web/im.js +68 -0
  119. package/dist/platforms/taobao/web/normalize.js +103 -0
  120. package/dist/platforms/taobao/web/profile.js +85 -0
  121. package/dist/platforms/taobao/web/resolve.js +51 -0
  122. package/dist/platforms/taobao/web/sign.js +35 -0
  123. package/dist/platforms/tiktok/index.js +120 -0
  124. package/dist/platforms/tiktok/web/api.js +1234 -0
  125. package/dist/platforms/tiktok/web/client.js +362 -0
  126. package/dist/platforms/tiktok/web/commands.js +821 -0
  127. package/dist/platforms/tiktok/web/im.js +133 -0
  128. package/dist/platforms/tiktok/web/jsrun.js +126 -0
  129. package/dist/platforms/tiktok/web/normalize.js +286 -0
  130. package/dist/platforms/tiktok/web/profile.js +169 -0
  131. package/dist/platforms/tiktok/web/resolve.js +156 -0
  132. package/dist/platforms/tiktok/web/sign.js +585 -0
  133. package/dist/platforms/tiktok/web/upload.js +805 -0
  134. package/dist/platforms/tiktok/web/wire.js +490 -0
  135. package/dist/platforms/weibo/index.js +91 -0
  136. package/dist/platforms/weibo/web/api.js +206 -0
  137. package/dist/platforms/weibo/web/client.js +155 -0
  138. package/dist/platforms/weibo/web/commands.js +202 -0
  139. package/dist/platforms/weibo/web/normalize.js +137 -0
  140. package/dist/platforms/weibo/web/profile.js +246 -0
  141. package/dist/platforms/weibo/web/resolve.js +60 -0
  142. package/dist/platforms/weibo/web/sign.js +85 -0
  143. package/dist/platforms/x/index.js +107 -0
  144. package/dist/platforms/x/web/api.js +443 -0
  145. package/dist/platforms/x/web/article.js +212 -0
  146. package/dist/platforms/x/web/client.js +202 -0
  147. package/dist/platforms/x/web/commands.js +363 -0
  148. package/dist/platforms/x/web/graphql.js +108 -0
  149. package/dist/platforms/x/web/normalize.js +235 -0
  150. package/dist/platforms/x/web/profile.js +99 -0
  151. package/dist/platforms/x/web/resolve.js +44 -0
  152. package/dist/platforms/x/web/transaction.js +171 -0
  153. package/dist/platforms/xhs/index.js +101 -0
  154. package/dist/platforms/xhs/web/api.js +278 -0
  155. package/dist/platforms/xhs/web/client.js +540 -0
  156. package/dist/platforms/xhs/web/commands.js +1046 -0
  157. package/dist/platforms/xhs/web/creator-api.js +609 -0
  158. package/dist/platforms/xhs/web/creator.js +203 -0
  159. package/dist/platforms/xhs/web/js.js +194 -0
  160. package/dist/platforms/xhs/web/login.js +230 -0
  161. package/dist/platforms/xhs/web/normalize.js +166 -0
  162. package/dist/platforms/xhs/web/profile.js +127 -0
  163. package/dist/platforms/xhs/web/push.js +361 -0
  164. package/dist/platforms/xhs/web/resolve.js +69 -0
  165. package/dist/platforms/xhs/web/state.js +368 -0
  166. package/dist/platforms/xianyu/index.js +59 -0
  167. package/dist/platforms/xianyu/web/api.js +312 -0
  168. package/dist/platforms/xianyu/web/client.js +134 -0
  169. package/dist/platforms/xianyu/web/commands.js +305 -0
  170. package/dist/platforms/xianyu/web/im.js +73 -0
  171. package/dist/platforms/xianyu/web/normalize.js +123 -0
  172. package/dist/platforms/xianyu/web/profile.js +146 -0
  173. package/dist/platforms/xianyu/web/resolve.js +48 -0
  174. package/dist/platforms/xianyu/web/sign.js +79 -0
  175. package/package.json +77 -4
  176. package/static/douyin/Live.proto +194 -0
  177. package/static/douyin/Request.proto +89 -0
  178. package/static/douyin/Response.proto +46 -0
  179. package/static/douyin/acrawler_runtime/ac_vm.js +2 -0
  180. package/static/douyin/acrawler_runtime/browser_window_shape.json +1 -0
  181. package/static/douyin/acrawler_runtime/canvas_actual_exact.json +1 -0
  182. package/static/douyin/acrawler_runtime/run_ac_node.js +1005 -0
  183. package/static/douyin/acrawler_runtime/runner_window_keys_baseline.txt +240 -0
  184. package/static/douyin/challenge_profile.json +801 -0
  185. package/static/douyin/challenge_template_runner.js +217 -0
  186. package/static/douyin/dtrait_profile.json +66 -0
  187. package/static/douyin/mstoken_common_profile.json +1 -0
  188. package/static/douyin/mstoken_profile.json +214 -0
  189. package/static/jd/h5st5_env.js +750 -0
  190. package/static/jd/h5st5_lib.js +5 -0
  191. package/static/jd/h5st5_server.js +49 -0
  192. package/static/jd/jcap/env/env_core.js +512 -0
  193. package/static/jd/jcap/env/run.js +1307 -0
  194. package/static/jd/jcap/run/jcap_ujb96b.js +39 -0
  195. package/static/jd/package.json +4 -0
  196. package/static/jd/pc_tk_lib.js +1 -0
  197. package/static/jd/pc_tk_server.js +63 -0
  198. package/static/jd/summer_cryptico_runner.js +50 -0
  199. package/static/jd/webm/env/run.js +453 -0
  200. package/static/jd/webm/run/jdwebm-riskhandle.js +49 -0
  201. package/static/kuaishou/bundles/weapon/kwf-0.0.2.2cee19b4b7dec496.js +1 -0
  202. package/static/kuaishou/bundles/weapon/kws-0-0.0.1-obfuscated.4b7f3de1ab0ddcc2.js +1 -0
  203. package/static/kuaishou/bundles/weapon/kws-1-0.0.1-obfuscated.346ebdb1c78df316.js +1 -0
  204. package/static/kuaishou/bundles/weapon/kws-10-0.0.1-obfuscated.a33db41897b983af.js +1 -0
  205. package/static/kuaishou/bundles/weapon/kws-11-0.0.1-obfuscated.5e0a90af726d8a7e.js +1 -0
  206. package/static/kuaishou/bundles/weapon/kws-12-0.0.1-obfuscated.20ea29345706ec8c.js +1 -0
  207. package/static/kuaishou/bundles/weapon/kws-13-0.0.1-obfuscated.6b74e9640ff18648.js +1 -0
  208. package/static/kuaishou/bundles/weapon/kws-14-0.0.1-obfuscated.551e4429d156bf66.js +1 -0
  209. package/static/kuaishou/bundles/weapon/kws-15-0.0.1-obfuscated.8625b087aaba4a60.js +1 -0
  210. package/static/kuaishou/bundles/weapon/kws-16-0.0.1-obfuscated.d5ffa4020f3be99a.js +1 -0
  211. package/static/kuaishou/bundles/weapon/kws-17-0.0.1-obfuscated.6d13e19b22f50a80.js +1 -0
  212. package/static/kuaishou/bundles/weapon/kws-18-0.0.1-obfuscated.6d31405044166289.js +1 -0
  213. package/static/kuaishou/bundles/weapon/kws-19-0.0.1-obfuscated.a9a8d925942f19e4.js +1 -0
  214. package/static/kuaishou/bundles/weapon/kws-2-0.0.1-obfuscated.b849a136ae4a6b3e.js +1 -0
  215. package/static/kuaishou/bundles/weapon/kws-3-0.0.1-obfuscated.f9e77ebf6186e2ac.js +1 -0
  216. package/static/kuaishou/bundles/weapon/kws-4-0.0.1-obfuscated.2c3c155aa52ceb0e.js +1 -0
  217. package/static/kuaishou/bundles/weapon/kws-5-0.0.1-obfuscated.4e553d17691b3c5a.js +1 -0
  218. package/static/kuaishou/bundles/weapon/kws-6-0.0.1-obfuscated.f73ddd3ff13e1217.js +1 -0
  219. package/static/kuaishou/bundles/weapon/kws-7-0.0.1-obfuscated.1628b8729beaab7e.js +1 -0
  220. package/static/kuaishou/bundles/weapon/kws-8-0.0.1-obfuscated.6da6baef7aa81fff.js +1 -0
  221. package/static/kuaishou/bundles/weapon/kws-9-0.0.1-obfuscated.8645d55e5323b743.js +1 -0
  222. package/static/kuaishou/fixtures/current_follow_script_813.network-response +2 -0
  223. package/static/kuaishou/fixtures/live_ws_maps.json +61 -0
  224. package/static/kuaishou/fixtures/live_ws_proto.json +1342 -0
  225. package/static/kuaishou/js/cp-kwf.js +1 -0
  226. package/static/kuaishou/tools/kwf_oracle.js +799 -0
  227. package/static/kuaishou/tools/kws_oracle.js +885 -0
  228. package/static/kuaishou/tools/like_token_oracle.js +70 -0
  229. package/static/taobao/taobao_js_20260407.js +570 -0
  230. package/static/tiktok/Tiktok_Request.json +597 -0
  231. package/static/tiktok/package.json +3 -0
  232. package/static/tiktok/shop_bsid/core.js +1 -0
  233. package/static/tiktok/shop_bsid/env_core.js +512 -0
  234. package/static/tiktok/shop_bsid/loader.js +1 -0
  235. package/static/tiktok/shop_bsid/run.js +753 -0
  236. package/static/tiktok/shop_bsid/sign.js +100 -0
  237. package/static/tiktok/shop_bsid/webmssdk.js +1 -0
  238. package/static/tiktok/shop_bsid/webmssdk_ex.js +1 -0
  239. package/static/tiktok/signing/env/env_core.js +512 -0
  240. package/static/tiktok/signing/env/sign.js +455 -0
  241. package/static/tiktok/signing/sdk_config.json +230 -0
  242. package/static/tiktok/signing/source/webmssdk.js +1 -0
  243. package/static/tiktok/signing/source/webmssdk_ex.js +1 -0
  244. package/static/tiktok/signing/source/webmssdk_legacy.js +1 -0
  245. package/static/x/graphql.json +7777 -0
  246. package/static/x/transaction_l1.json +12 -0
  247. package/static/xhs/package.json +3 -0
  248. package/static/xhs/xhs_core/js/b1.js +210 -0
  249. package/static/xhs/xhs_core/js/mns.js +340 -0
  250. package/static/xhs/xhs_core/js/mns_0101_keystream.json +5 -0
  251. package/static/xhs/xhs_core/js/mns_keystreams.json +1 -0
  252. package/static/xhs/xhs_core/js/sign.js +225 -0
  253. package/static/xhs/xhs_core/js/websectiga_cli.js +220 -0
  254. package/static/xhs/xhs_core/js/websectiga_env.js +512 -0
  255. package/static/xhs/xhs_core/js/xs_common.js +79 -0
  256. package/static/xhs/xhs_creator/js/profile.js +115 -0
  257. package/static/xhs/xhs_creator/js/rap_fingerprint_creator.json +6 -0
  258. package/static/xhs/xhs_creator/js/reference_profile.json +282 -0
  259. package/static/xhs/xhs_creator/js/xhs_creator_sign.js +29 -0
  260. package/static/xhs/xhs_creator/js/xhs_creator_signature.js +23 -0
  261. package/static/xhs/xhs_pc/js/aes_decrypt.js +22 -0
  262. package/static/xhs/xhs_pc/js/aes_encrypt.js +30 -0
  263. package/static/xhs/xhs_pc/js/b1.js +9 -0
  264. package/static/xhs/xhs_pc/js/deflate.js +320 -0
  265. package/static/xhs/xhs_pc/js/mns.js +4 -0
  266. package/static/xhs/xhs_pc/js/profile.js +141 -0
  267. package/static/xhs/xhs_pc/js/rap.js +173 -0
  268. package/static/xhs/xhs_pc/js/rap_cli.js +30 -0
  269. package/static/xhs/xhs_pc/js/rap_crypto.js +73 -0
  270. package/static/xhs/xhs_pc/js/rap_fingerprint_template.json +8 -0
  271. package/static/xhs/xhs_pc/js/reference_profile.json +262 -0
  272. package/static/xhs/xhs_pc/js/sign.js +6 -0
  273. package/static/xhs/xhs_pc/js/web_ssk.js +82 -0
  274. package/static/xhs/xhs_pc/js/xs_common.js +4 -0
  275. package/static/xianyu/et_f.js +2 -0
  276. package/static/xianyu/gen_tfstk.js +410 -0
  277. package/static/xianyu/goofish_js_version_2.js +568 -0
@@ -0,0 +1,234 @@
1
+ import * as n from '../../../core/normalize.js';
2
+ /** B 站原始对象 → 归一化类型(AGENTS 6.2)。 */
3
+ export const videoUrl = (bvid) => `https://www.bilibili.com/video/${bvid}`;
4
+ export const spaceUrl = (mid) => `https://space.bilibili.com/${mid}`;
5
+ export const liveUrl = (room) => `https://live.bilibili.com/${room}`;
6
+ export const articleUrl = (cvid) => `https://www.bilibili.com/read/cv${cvid}`;
7
+ export function ref(mid, name) {
8
+ return n.userRef({ id: mid, name, url: mid == null || mid === '' ? null : spaceUrl(mid) });
9
+ }
10
+ /** 稿件详情(x/web-interface/wbi/view 的 data)。 */
11
+ export function video(v) {
12
+ return n.item({
13
+ id: v.bvid,
14
+ kind: 'video',
15
+ url: videoUrl(v.bvid),
16
+ title: n.str(v.title),
17
+ text: n.str(v.desc),
18
+ author: ref(v.owner?.mid, v.owner?.name),
19
+ created_at: n.time(v.pubdate),
20
+ cover: n.url(v.pic),
21
+ stats: {
22
+ views: n.count(v.stat?.view),
23
+ likes: n.count(v.stat?.like),
24
+ comments: n.count(v.stat?.reply),
25
+ collects: n.count(v.stat?.favorite),
26
+ shares: n.count(v.stat?.share),
27
+ },
28
+ }, v);
29
+ }
30
+ /** 搜索结果里的视频卡片。 */
31
+ export function searchVideo(v) {
32
+ return n.item({
33
+ id: v.bvid,
34
+ kind: 'video',
35
+ url: videoUrl(v.bvid),
36
+ title: n.plainText(v.title),
37
+ text: n.str(v.description),
38
+ author: ref(v.mid, v.author),
39
+ created_at: n.time(v.pubdate),
40
+ cover: n.url(v.pic),
41
+ stats: { views: n.count(v.play), likes: n.count(v.like), comments: n.count(v.review), collects: n.count(v.favorites) },
42
+ }, v);
43
+ }
44
+ /** 专栏搜索结果(search_type=article)。id 用 `cv<id>`,可以直接传给 comment list。 */
45
+ export function searchArticle(v) {
46
+ return n.item({
47
+ id: `cv${v.id}`,
48
+ kind: 'article',
49
+ url: articleUrl(v.id),
50
+ title: n.plainText(v.title),
51
+ text: n.plainText(v.desc),
52
+ author: ref(v.mid, null),
53
+ created_at: n.time(v.pub_time),
54
+ cover: n.url(v.image_urls?.[0]),
55
+ stats: { views: n.count(v.view), likes: n.count(v.like), comments: n.count(v.reply) },
56
+ }, v);
57
+ }
58
+ /** 专栏草稿(article/creative/draft/view 的 data)。 */
59
+ export function articleDraft(v, id, mid) {
60
+ return n.item({
61
+ id: n.id(v.id ?? v.aid ?? id),
62
+ kind: 'article',
63
+ title: n.str(v.title),
64
+ text: n.str(v.content),
65
+ author: ref(v.author?.mid ?? mid, v.author?.name),
66
+ created_at: n.time(v.ctime ?? v.mtime),
67
+ cover: n.url(v.banner_url || v.image_urls?.[0]),
68
+ status: 'draft',
69
+ }, v);
70
+ }
71
+ /** 空间投稿列表(arc/search 的 vlist 项)。 */
72
+ export function spaceVideo(v) {
73
+ return n.item({
74
+ id: v.bvid,
75
+ kind: 'video',
76
+ url: videoUrl(v.bvid),
77
+ title: n.str(v.title),
78
+ text: n.str(v.description),
79
+ author: ref(v.mid, v.author),
80
+ created_at: n.time(v.created),
81
+ cover: n.url(v.pic),
82
+ stats: { views: n.count(v.play), comments: n.count(v.comment) },
83
+ }, v);
84
+ }
85
+ /** 首页推荐流的卡片。 */
86
+ export function feedVideo(v) {
87
+ return n.item({
88
+ id: v.bvid,
89
+ kind: 'video',
90
+ url: videoUrl(v.bvid),
91
+ title: n.str(v.title),
92
+ author: ref(v.owner?.mid, v.owner?.name),
93
+ created_at: n.time(v.pubdate),
94
+ cover: n.url(v.pic),
95
+ stats: { views: n.count(v.stat?.view), likes: n.count(v.stat?.like) },
96
+ }, v);
97
+ }
98
+ /** 收藏夹内容(fav/resource/list 的 medias 项)。 */
99
+ export function favVideo(v) {
100
+ return n.item({
101
+ id: v.bvid ?? n.id(v.id),
102
+ kind: 'video',
103
+ url: v.bvid ? videoUrl(v.bvid) : null,
104
+ title: n.str(v.title),
105
+ text: n.str(v.intro),
106
+ author: ref(v.upper?.mid, v.upper?.name),
107
+ created_at: n.time(v.pubtime),
108
+ cover: n.url(v.cover),
109
+ stats: { views: n.count(v.cnt_info?.play), collects: n.count(v.cnt_info?.collect) },
110
+ }, v);
111
+ }
112
+ /** 稿件审核状态(创作中心 state)。 */
113
+ function archiveStatus(state) {
114
+ const s = Number(state);
115
+ if (s >= 0)
116
+ return 'published';
117
+ if ([-2, -4, -16, -100].includes(s))
118
+ return 'rejected';
119
+ if ([-1, -6, -30, -40].includes(s))
120
+ return 'reviewing';
121
+ return null;
122
+ }
123
+ /** 我的稿件(x/web/archives 的 arc_audits 项)。 */
124
+ export function archive(v) {
125
+ const a = v.Archive ?? {};
126
+ return n.item({
127
+ id: a.bvid,
128
+ kind: 'video',
129
+ url: videoUrl(a.bvid),
130
+ title: n.str(a.title),
131
+ text: n.str(a.desc),
132
+ author: ref(a.mid, a.author),
133
+ created_at: n.time(a.ptime || a.ctime),
134
+ cover: n.url(a.cover),
135
+ stats: {
136
+ views: n.count(v.stat?.view),
137
+ likes: n.count(v.stat?.like),
138
+ comments: n.count(v.stat?.reply),
139
+ collects: n.count(v.stat?.favorite),
140
+ shares: n.count(v.stat?.share),
141
+ },
142
+ status: archiveStatus(a.state),
143
+ }, v);
144
+ }
145
+ export function spaceUser(v) {
146
+ return n.user({ id: n.id(v.mid), name: n.str(v.name), avatar: n.url(v.face), url: spaceUrl(v.mid), bio: n.str(v.sign) }, v);
147
+ }
148
+ export function searchUser(v) {
149
+ return n.user({
150
+ id: n.id(v.mid),
151
+ name: n.str(v.uname),
152
+ avatar: n.url(v.upic),
153
+ url: spaceUrl(v.mid),
154
+ bio: n.str(v.usign),
155
+ stats: { followers: n.count(v.fans), following: null, items: n.count(v.videos), likes: null },
156
+ }, v);
157
+ }
158
+ export function navUser(v) {
159
+ return n.user({ id: n.id(v.mid), name: n.str(v.uname), avatar: n.url(v.face), url: spaceUrl(v.mid) }, v);
160
+ }
161
+ export function reply(v, itemId) {
162
+ return n.comment({
163
+ id: n.id(v.rpid),
164
+ item_id: itemId,
165
+ parent_id: v.parent ? n.id(v.parent) : null,
166
+ author: ref(v.member?.mid, v.member?.uname),
167
+ text: String(v.content?.message ?? ''),
168
+ created_at: n.time(v.ctime),
169
+ stats: { likes: n.count(v.like), replies: n.count(v.rcount) },
170
+ }, v);
171
+ }
172
+ export function searchLive(v) {
173
+ return n.live({
174
+ id: n.id(v.roomid),
175
+ url: liveUrl(v.roomid),
176
+ title: n.plainText(v.title),
177
+ status: v.live_status === 1 ? 'live' : 'offline',
178
+ host: ref(v.uid, v.uname),
179
+ cover: n.url(v.user_cover || v.cover),
180
+ stats: { viewers: n.count(v.online) },
181
+ }, v);
182
+ }
183
+ /** 没有专门映射的 cmd:能读出内容的给内容,其余给 cmd 名(上游 live/server.py 的 on('*') 兜底)。 */
184
+ function otherText(cmd, d) {
185
+ switch (cmd) {
186
+ case 'WATCHED_CHANGE':
187
+ return n.str(d.text_large) ?? cmd;
188
+ case 'ROOM_CHANGE':
189
+ return d.title ? `直播间标题:${d.title}` : cmd;
190
+ case 'LIVE':
191
+ return '开播';
192
+ case 'PREPARING':
193
+ return '下播';
194
+ default:
195
+ return cmd || 'UNKNOWN';
196
+ }
197
+ }
198
+ /** 弹幕长连的心跳回复(op 3):人气值(上游 live/server.py 的 popularity)。 */
199
+ export function popularityEvent(value) {
200
+ return n.event({ type: 'other', text: `人气值 ${value}` }, { op: 3, popularity: value });
201
+ }
202
+ /** INTERACT_WORD 的 msg_type:1 进场,2 关注、4 特别关注、5 互相关注;3 分享没有对应的 Event 类型,和其余取值一样记为 other。 */
203
+ const INTERACT_TYPE = { 1: 'enter', 2: 'follow', 4: 'follow', 5: 'follow' };
204
+ /** 直播弹幕长连的业务消息 → Event:弹幕、礼物、进场、关注、点赞,其余 cmd 为 other。 */
205
+ export function liveEvent(msg) {
206
+ const cmd = String(msg?.cmd ?? '').split(':')[0];
207
+ const d = msg?.data ?? {};
208
+ switch (cmd) {
209
+ case 'DANMU_MSG': {
210
+ const info = msg.info ?? [];
211
+ return n.event({ type: 'chat', time: n.time(info[0]?.[4]) ?? undefined, user: ref(info[2]?.[0], info[2]?.[1]), text: String(info[1] ?? '') }, msg);
212
+ }
213
+ case 'SUPER_CHAT_MESSAGE':
214
+ return n.event({ type: 'chat', time: n.time(d.start_time) ?? undefined, user: ref(d.uid, d.user_info?.uname), text: n.str(d.message) }, msg);
215
+ case 'SEND_GIFT':
216
+ return n.event({ type: 'gift', time: n.time(d.timestamp) ?? undefined, user: ref(d.uid, d.uname), gift: { name: String(d.giftName ?? ''), count: Number(d.num ?? 1) } }, msg);
217
+ case 'INTERACT_WORD': {
218
+ const type = INTERACT_TYPE[Number(d.msg_type)] ?? 'other';
219
+ const text = type !== 'other' ? undefined : Number(d.msg_type) === 3 ? '分享直播间' : cmd;
220
+ return n.event({ type, time: n.time(d.timestamp) ?? undefined, user: ref(d.uid, d.uname), text }, msg);
221
+ }
222
+ case 'LIKE_INFO_V3_CLICK':
223
+ return n.event({ type: 'like', user: ref(d.uid, d.uname) }, msg);
224
+ case 'GUARD_BUY':
225
+ return n.event({ type: 'gift', time: n.time(d.start_time) ?? undefined, user: ref(d.uid, d.username), gift: { name: String(d.gift_name ?? ''), count: Number(d.num ?? 1) } }, msg);
226
+ default:
227
+ return n.event({ type: 'other', time: n.time(d.timestamp) ?? undefined, user: d.uid ? ref(d.uid, d.uname) : null, text: otherText(cmd, d) }, msg);
228
+ }
229
+ }
230
+ /** gethistory 的弹幕,timeline 是北京时间。 */
231
+ export function historyEvent(v) {
232
+ const t = typeof v.timeline === 'string' ? n.time(v.timeline.replace(' ', 'T') + '+08:00') : null;
233
+ return n.event({ type: 'chat', time: t ?? undefined, user: ref(v.uid, v.nickname), text: n.str(v.text) }, v);
234
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * 设备画像与请求头(上游 utils/fingerprint.py、builder/header.py)。
3
+ * UA 锁在 Chrome 146,与 TLS 指纹(wreq-js 的 chrome_146)一致。
4
+ */
5
+ export const PROFILE = {
6
+ ua: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36',
7
+ secChUa: '"Chromium";v="146", "Not-A.Brand";v="24", "Google Chrome";v="146"',
8
+ secChUaPlatform: '"Windows"',
9
+ acceptLanguage: 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7,ja;q=0.6',
10
+ screenWidth: 2560,
11
+ screenHeight: 1440,
12
+ browserResolution: '2560-1215',
13
+ colorDepth: 24,
14
+ deviceMemory: 8,
15
+ hardwareConcurrency: 20,
16
+ timezone: 'Asia/Shanghai',
17
+ webglVersion: 'WebGL 1.0 (OpenGL ES 2.0 Chromium)',
18
+ webglRenderer: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 5060 Ti (0x00002D04) Direct3D11 vs_5_0 ps_5_0, D3D11)',
19
+ webglVendor: 'Google Inc. (NVIDIA)',
20
+ };
21
+ export const BROWSER = 'chrome_146';
22
+ export const ORIGIN = {
23
+ main: 'https://www.bilibili.com',
24
+ live: 'https://live.bilibili.com',
25
+ passport: 'https://passport.bilibili.com',
26
+ member: 'https://member.bilibili.com',
27
+ dynamic: 'https://t.bilibili.com',
28
+ space: 'https://space.bilibili.com',
29
+ search: 'https://search.bilibili.com',
30
+ };
31
+ export const API = 'https://api.bilibili.com';
32
+ export const LIVE_API = 'https://api.live.bilibili.com';
33
+ export const PASSPORT = 'https://passport.bilibili.com';
34
+ export const MEMBER = 'https://member.bilibili.com';
35
+ const ACCEPT_ENCODING = 'gzip, deflate, br, zstd';
36
+ const XHR_ACCEPT = 'application/json, text/plain, */*';
37
+ const DOC_ACCEPT = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8';
38
+ /** 有序请求头,对应上游的 Header 类:改值不影响位置,新键追加到末尾。 */
39
+ export class Headers {
40
+ pairs = [];
41
+ set(key, value) {
42
+ const i = this.pairs.findIndex(([k]) => k === key);
43
+ if (i >= 0)
44
+ this.pairs[i] = [key, value];
45
+ else
46
+ this.pairs.push([key, value]);
47
+ return this;
48
+ }
49
+ referer(url) {
50
+ return this.set('referer', url);
51
+ }
52
+ remove(key) {
53
+ this.pairs = this.pairs.filter(([k]) => k !== key);
54
+ return this;
55
+ }
56
+ /** 在 anchor 之前插入;anchor 不存在时追加。 */
57
+ insertBefore(anchor, key, value) {
58
+ const i = this.pairs.findIndex(([k]) => k === anchor);
59
+ if (i < 0)
60
+ return this.set(key, value);
61
+ this.pairs = this.pairs.filter(([k]) => k !== key);
62
+ this.pairs.splice(this.pairs.findIndex(([k]) => k === anchor), 0, [key, value]);
63
+ return this;
64
+ }
65
+ get() {
66
+ return this.pairs;
67
+ }
68
+ }
69
+ /**
70
+ * 构造一组顺序与 Chrome 一致的请求头(上游 HeaderBuilder.build)。
71
+ * same_origin 时 `sec-fetch-site` 为 same-origin,且 GET 不带 origin。
72
+ */
73
+ export function headers(type = 'GET', options = {}) {
74
+ const origin = options.origin ?? ORIGIN.main;
75
+ const h = new Headers();
76
+ h.set('sec-ch-ua', PROFILE.secChUa);
77
+ h.set('sec-ch-ua-mobile', '?0');
78
+ h.set('sec-ch-ua-platform', PROFILE.secChUaPlatform);
79
+ if (type === 'DOC') {
80
+ h.set('upgrade-insecure-requests', '1');
81
+ h.set('user-agent', PROFILE.ua);
82
+ h.set('accept', options.accept ?? DOC_ACCEPT);
83
+ h.set('sec-fetch-site', 'none');
84
+ h.set('sec-fetch-mode', 'navigate');
85
+ h.set('sec-fetch-user', '?1');
86
+ h.set('sec-fetch-dest', 'document');
87
+ h.set('accept-encoding', ACCEPT_ENCODING);
88
+ h.set('accept-language', PROFILE.acceptLanguage);
89
+ h.set('priority', 'u=0, i');
90
+ return h;
91
+ }
92
+ h.set('user-agent', PROFILE.ua);
93
+ if (type === 'POST')
94
+ h.set('content-type', 'application/json');
95
+ else if (type === 'FORM')
96
+ h.set('content-type', 'application/x-www-form-urlencoded');
97
+ h.set('accept', options.accept ?? XHR_ACCEPT);
98
+ if (!(options.sameOrigin && type === 'GET'))
99
+ h.set('origin', origin);
100
+ h.set('sec-fetch-site', options.sameOrigin ? 'same-origin' : 'same-site');
101
+ h.set('sec-fetch-mode', 'cors');
102
+ h.set('sec-fetch-dest', 'empty');
103
+ h.set('referer', origin + '/');
104
+ h.set('accept-encoding', ACCEPT_ENCODING);
105
+ h.set('accept-language', PROFILE.acceptLanguage);
106
+ h.set('priority', 'u=1, i');
107
+ return h;
108
+ }
109
+ /** 浏览器实抓的 Cookie 顺序(上游 utils/device.py COOKIE_ORDER)。 */
110
+ export const COOKIE_ORDER = [
111
+ 'buvid3',
112
+ 'b_nut',
113
+ '_uuid',
114
+ 'home_feed_column',
115
+ 'buvid4',
116
+ 'browser_resolution',
117
+ 'buvid_fp',
118
+ 'LIVE_BUVID',
119
+ 'SESSDATA',
120
+ 'bili_jct',
121
+ 'DedeUserID',
122
+ 'DedeUserID__ckMd5',
123
+ 'theme-tip-show',
124
+ 'CURRENT_FNVAL',
125
+ 'sid',
126
+ 'rpdid',
127
+ 'PVID',
128
+ 'CURRENT_QUALITY',
129
+ 'bili_ticket',
130
+ 'bili_ticket_expires',
131
+ 'ogv_device_support_dolby',
132
+ 'ogv_device_support_hdr',
133
+ 'b_lsid',
134
+ ];
135
+ export const COOKIE_DOMAIN = '.bilibili.com';
@@ -0,0 +1,112 @@
1
+ import { SpecificAIError } from '../../../core/errors.js';
2
+ import * as api from './api.js';
3
+ import { av2bv, bv2av } from './sign.js';
4
+ /** 参数归一化(AGENTS 4.8):ID、URL、分享短链、BV 号、`me`。 */
5
+ const BV_RE = /BV1[0-9A-Za-z]{9}/;
6
+ const SHORT_RE = /^https?:\/\/(b23\.tv|bili2233\.cn)\//i;
7
+ /** 分享短链跟一次跳转,取 Location。 */
8
+ async function expand(b, url) {
9
+ if (!SHORT_RE.test(url))
10
+ return url;
11
+ const res = await b.http.request({ url, redirect: 'manual', cookies: false });
12
+ return res.headers.get('location') ?? url;
13
+ }
14
+ /** 稿件:BV 号、av 号、纯数字 aid、视频页 URL 或 b23.tv 短链。 */
15
+ export async function resolveItem(b, input) {
16
+ const s = (await expand(b, input.trim())).trim();
17
+ const bv = BV_RE.exec(s)?.[0];
18
+ if (bv)
19
+ return { bvid: bv, aid: bv2av(bv) };
20
+ const av = /(?:^|\/|\b)av(\d+)/i.exec(s)?.[1] ?? (/^\d+$/.test(s) ? s : undefined);
21
+ if (av)
22
+ return { bvid: av2bv(av), aid: av };
23
+ throw new SpecificAIError('USAGE', `无法识别的稿件:${input}`, { hint: '传 BV 号、av 号或视频链接,例如 BV1GJ411x7h7' });
24
+ }
25
+ /**
26
+ * 动态的评论区只有纯文字和转发动态是 type 17 + 动态 ID;图文动态(带图)的评论区挂在相簿上(type 11 + 相簿 rid),
27
+ * 专栏动态挂在专栏上(type 12 + cv 号)。从动态 ID 查 rid / cv 号要动态详情接口,上游没有,specific-ai 也不自己加。
28
+ */
29
+ export const DYNAMIC_REPLY_HINT = '动态只支持纯文字和转发:图文动态(带图)的评论区挂在相簿上,上游没有查相簿 ID 的接口;专栏请传 cv 号或专栏链接';
30
+ const ARTICLE_RE = [/(?:^|\/read\/)cv(\d+)/i, /\/read\/mobile(?:\/|\?id=)(\d+)/i];
31
+ const DYNAMIC_RE = [/t\.bilibili\.com\/(\d+)/i, /bilibili\.com\/opus\/(\d+)/i, /m\.bilibili\.com\/dynamic\/(\d+)/i, /^dyn(?:amic)?:(\d+)$/i];
32
+ /** aid 最大 2^51,16 位以内;动态 ID 是 18、19 位。更长的纯数字按动态处理。 */
33
+ const MIN_DYNAMIC_DIGITS = 17;
34
+ function firstMatch(res, s) {
35
+ for (const re of res) {
36
+ const m = re.exec(s)?.[1];
37
+ if (m)
38
+ return m;
39
+ }
40
+ }
41
+ /**
42
+ * 评论区:稿件(BV 号、av 号、视频链接)→ 1;专栏(cv 号、专栏链接)→ 12;
43
+ * 动态(动态链接、17 位以上的动态 ID、`dyn:<id>`)→ 17,只对纯文字和转发动态成立(见 DYNAMIC_REPLY_HINT)。
44
+ * opus 链接既可能是动态也可能是专栏,不发请求分不出来,按动态处理。
45
+ */
46
+ export async function resolveReplyTarget(b, input) {
47
+ const s = (await expand(b, input.trim())).trim();
48
+ const cv = firstMatch(ARTICLE_RE, s);
49
+ if (cv)
50
+ return { type: 12, oid: cv, itemId: `cv${cv}` };
51
+ const dyn = firstMatch(DYNAMIC_RE, s) ?? (/^\d+$/.test(s) && s.length >= MIN_DYNAMIC_DIGITS ? s : undefined);
52
+ if (dyn)
53
+ return { type: 17, oid: dyn, itemId: dyn };
54
+ try {
55
+ const { bvid, aid } = await resolveItem(b, s);
56
+ return { type: 1, oid: aid, itemId: bvid };
57
+ }
58
+ catch {
59
+ throw new SpecificAIError('USAGE', `无法识别的评论区:${input}`, {
60
+ hint: `传稿件(BV 号、av 号、视频链接)、专栏(cv 号或专栏链接)或动态(动态链接或动态 ID);${DYNAMIC_REPLY_HINT}`,
61
+ });
62
+ }
63
+ }
64
+ /** 动态:动态链接(t.bilibili.com、opus、m.bilibili.com/dynamic)、`dyn:<id>` 或纯数字的动态 ID。dynamic publish 输出的 id、url 都能直接用。 */
65
+ export async function resolveDynamic(b, input) {
66
+ const s = (await expand(b, input.trim())).trim();
67
+ const id = firstMatch(DYNAMIC_RE, s) ?? /^\d+$/.exec(s)?.[0];
68
+ if (id)
69
+ return id;
70
+ throw new SpecificAIError('USAGE', `无法识别的动态:${input}`, { hint: '传动态 ID 或动态链接 https://t.bilibili.com/<id>(dynamic publish 输出的 id、url 都可以)' });
71
+ }
72
+ /** 收藏夹:folder list 输出的 url(`…/favlist?fid=<id>`)、播放列表链接(`ml<id>`)或纯数字的收藏夹 ID。 */
73
+ export async function resolveFolder(b, input) {
74
+ const s = (await expand(b, input.trim())).trim();
75
+ const id = /[?&]fid=(\d+)/.exec(s)?.[1] ?? /(?:^|\/)ml(\d+)/.exec(s)?.[1] ?? /^\d+$/.exec(s)?.[0];
76
+ if (id)
77
+ return id;
78
+ throw new SpecificAIError('USAGE', `无法识别的收藏夹:${input}`, { hint: '传收藏夹 ID 或 folder list 输出的 url(https://space.bilibili.com/<mid>/favlist?fid=<id>)' });
79
+ }
80
+ /** 用户:mid、空间 URL、`me`。 */
81
+ export async function resolveUser(b, input) {
82
+ const s = (await expand(b, input.trim())).trim();
83
+ if (s === 'me') {
84
+ if (!b.mid)
85
+ throw new SpecificAIError('AUTH_REQUIRED', 'me 表示当前账号,需要先登录', { hint: 'specific-ai bilibili auth login' });
86
+ return b.mid;
87
+ }
88
+ const m = /space\.bilibili\.com\/(\d+)/.exec(s)?.[1] ?? /^(?:uid:?)?(\d+)$/i.exec(s)?.[1];
89
+ if (m)
90
+ return m;
91
+ throw new SpecificAIError('USAGE', `无法识别的用户:${input}`, { hint: '传用户 mid(数字)或空间链接 https://space.bilibili.com/<mid>' });
92
+ }
93
+ /**
94
+ * 直播间:房间号或直播间 URL(短号在调用 room_init 时换成真实房间号);
95
+ * 也可以传主播(空间链接、`uid:<mid>`、`me`),按上游 get_room_by_mid 换成房间号。纯数字按房间号处理。
96
+ */
97
+ export async function resolveRoom(b, input) {
98
+ const s = (await expand(b, input.trim())).trim();
99
+ const m = /live\.bilibili\.com\/(?:h5\/)?(\d+)/.exec(s)?.[1] ?? /^\d+$/.exec(s)?.[0];
100
+ if (m)
101
+ return m;
102
+ const mid = s === 'me' || /space\.bilibili\.com\/\d+/.test(s) || /^uid:?\d+$/i.test(s) ? await resolveUser(b, s) : null;
103
+ if (mid) {
104
+ const d = await api.roomByMid(b, mid);
105
+ if (!d?.roomid || d.roomStatus === 0)
106
+ throw new SpecificAIError('UPSTREAM', s === 'me' ? '这个账号还没有开通直播间' : `用户 ${mid} 没有开通直播间`, { detail: { mid } });
107
+ return String(d.roomid);
108
+ }
109
+ throw new SpecificAIError('USAGE', `无法识别的直播间:${input}`, {
110
+ hint: '传房间号、直播间链接 https://live.bilibili.com/<房间号>,或主播的空间链接 / uid:<mid>',
111
+ });
112
+ }