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,540 @@
1
+ import { SpecificAIError } from '../../../core/errors.js';
2
+ import { parseJson } from '../../../core/http.js';
3
+ import { compactJson, urlencode } from '../../../core/py.js';
4
+ import * as rand from '../../../core/rand.js';
5
+ import { authError, httpClient } from '../../../core/toolkit.js';
6
+ import { checkSign, rapParam, signFull } from './js.js';
7
+ import { AS, BROWSER, BUSINESS_LANG, COOKIE_DOMAIN, EDITH, LOGIN_LANG, orderedHeaders, PC_ORDER, SEC_CH_UA, UA, WEB, XHR_ACCEPT, } from './profile.js';
8
+ import { PcState } from './state.js';
9
+ /**
10
+ * PC 主站的会话(上游 xhs_utils/xhs_pc:XHSPcAuth + PcDeviceProfile + params.generate_request_params)。
11
+ *
12
+ * cookie:凭证 scope 里的 cookie 就是上游的 cookie_map,除 `acw_tc` 外都放在 `.xiaohongshu.com`;
13
+ * `acw_tc` 与上游 HostCookieStore 一样只对下发它的主机生效。发往各主机的 cookie 按存入顺序排列。
14
+ */
15
+ const HOST_ONLY = new Set(['acw_tc']);
16
+ const PC_TIERS = { '0101': [205], '0201': [208], '0301': [200] };
17
+ /** 带 RAP 的接口(浏览器实抓:需要 x-rap-param)与带 xy-direction 的接口。 */
18
+ const RAP_PATHS = ['api/sns/web/v1/homefeed', 'api/sns/web/v1/search/notes', 'api/sns/web/v2/search/notes', 'api/sns/web/v1/user_posted', 'api/sns/web/v1/feed', 'api/sns/web/v1/comment/post'];
19
+ const XY_PATHS = ['api/sns/web/v1/homefeed', 'api/sns/web/v1/feed'];
20
+ const CACHE_PATHS = ['/api/sns/web/v1/config', '/api/sns/web/v1/system/config', '/api/sns/web/v2/user/me'];
21
+ const pathOf = (api) => api.split('?', 1)[0].replace(/\/+$/, '').replace(/^\/+/, '');
22
+ export const needsRap = (api) => RAP_PATHS.some((m) => (m.includes('user_posted') ? pathOf(api).includes(m) : pathOf(api) === m));
23
+ export const needsXy = (api) => XY_PATHS.includes(pathOf(api));
24
+ /** 上游 splice_str:`api?urlencode(params)`,None 写成空串。 */
25
+ export function splice(api, params, safe = '') {
26
+ return `${api}?${urlencode(Object.entries(params).map(([k, v]) => [k, v == null ? '' : v]), { safe })}`;
27
+ }
28
+ // ---------------------------------------------------------------- trace id
29
+ let pcXraySeq = Math.floor(rand.random() * 0x7fffff);
30
+ let coreXraySeq = Math.floor(rand.random() * 0x7fffff);
31
+ /** 对拍用:上游两份模块级自增序号(xhs_pc/params.py、xhs_core/params.py)。 */
32
+ export function resetXraySeq(n) {
33
+ pcXraySeq = coreXraySeq = n;
34
+ }
35
+ /** x-xray-traceid:hex16((now<<23)|seq) + hex16(random64)。 */
36
+ export function xrayTraceId(which = 'pc') {
37
+ const seq = which === 'pc' ? (pcXraySeq = (pcXraySeq + 1) & 0x7fffff) : (coreXraySeq = (coreXraySeq + 1) & 0x7fffff);
38
+ const part1 = ((BigInt(rand.now()) << 23n) | BigInt(seq)) & 0xffffffffffffffffn;
39
+ const hi = BigInt(Math.floor(rand.random() * 2 ** 32));
40
+ const lo = BigInt(Math.floor(rand.random() * 2 ** 32));
41
+ return part1.toString(16).padStart(16, '0') + ((hi << 32n) | lo).toString(16).padStart(16, '0');
42
+ }
43
+ export const b3TraceId = () => rand.string(16, 'abcdef0123456789');
44
+ /** PC 端 xy-direction = murmurHash3_32(userId, 151488) % 100 + 1。 */
45
+ export function xyDirection(userId) {
46
+ if (!userId)
47
+ return 0;
48
+ const data = Buffer.from(userId, 'utf8');
49
+ let h = 151488;
50
+ const mul = Math.imul;
51
+ const nblocks = data.length >> 2;
52
+ for (let i = 0; i < nblocks; i++) {
53
+ let k = data.readUInt32LE(i * 4);
54
+ k = mul(k, 0xcc9e2d51);
55
+ k = (k << 15) | (k >>> 17);
56
+ k = mul(k, 0x1b873593);
57
+ h ^= k;
58
+ h = (h << 13) | (h >>> 19);
59
+ h = (mul(h, 5) + 0xe6546b64) | 0;
60
+ }
61
+ let k = 0;
62
+ const t = nblocks * 4;
63
+ const rem = data.length & 3;
64
+ if (rem === 3)
65
+ k ^= data[t + 2] << 16;
66
+ if (rem >= 2)
67
+ k ^= data[t + 1] << 8;
68
+ if (rem >= 1) {
69
+ k ^= data[t];
70
+ k = mul(k, 0xcc9e2d51);
71
+ k = (k << 15) | (k >>> 17);
72
+ k = mul(k, 0x1b873593);
73
+ h ^= k;
74
+ }
75
+ h ^= data.length;
76
+ h ^= h >>> 16;
77
+ h = mul(h, 0x85ebca6b);
78
+ h ^= h >>> 13;
79
+ h = mul(h, 0xc2b2ae35);
80
+ h ^= h >>> 16;
81
+ return ((h >>> 0) % 100) + 1;
82
+ }
83
+ /** 上游 get_request_headers_template。 */
84
+ function template(context, hasBody, clientHints, trace) {
85
+ const h = {
86
+ referer: `${WEB}/`,
87
+ 'x-t': '',
88
+ 'x-s-common': '',
89
+ 'user-agent': context.userAgent ?? UA,
90
+ accept: XHR_ACCEPT,
91
+ 'x-s': '',
92
+ 'accept-language': BUSINESS_LANG,
93
+ origin: WEB,
94
+ priority: 'u=1, i',
95
+ 'sec-fetch-dest': 'empty',
96
+ 'sec-fetch-mode': 'cors',
97
+ 'sec-fetch-site': 'same-site',
98
+ };
99
+ if (trace) {
100
+ h['x-xray-traceid'] = xrayTraceId('pc');
101
+ h['x-b3-traceid'] = '';
102
+ }
103
+ if (clientHints) {
104
+ h['sec-ch-ua-platform'] = '"Windows"';
105
+ h['sec-ch-ua'] = context.secChUa ?? SEC_CH_UA;
106
+ h['sec-ch-ua-mobile'] = '?0';
107
+ }
108
+ if (hasBody)
109
+ h['content-type'] = 'application/json;charset=UTF-8';
110
+ return h;
111
+ }
112
+ /**
113
+ * X-s / X-t / X-S-Common + 请求头模板(上游 generate_headers)。data 为对象时按 JSON 签名,
114
+ * 返回的 body 是紧凑 JSON(ensure_ascii=False)。
115
+ */
116
+ export function pcSignedHeaders(state, api, data, o) {
117
+ const a1 = state.cookies.a1;
118
+ if (!a1)
119
+ throw new SpecificAIError('ERROR', '小红书 cookie 缺少 a1');
120
+ const tier = o.context.tier;
121
+ const input = { api, data: data ?? '', cookie: state.documentCookie, a1, b1: o.b1, dslPair: o.dslPair, tier };
122
+ for (const k of ['now', 'version', 'loadts', 'seq', 'envConst', 'envFpTail', 'webBuild', 'signVersion', 'appId', 'platform', 'deviceTag', 'webSsk']) {
123
+ if (k in o.context)
124
+ input[k] = o.context[k];
125
+ }
126
+ const r = checkSign(signFull(input), tier, PC_TIERS[tier]);
127
+ if (!r.xs_common)
128
+ throw new SpecificAIError('ERROR', '小红书 X-S-Common 生成失败');
129
+ const b3 = b3TraceId();
130
+ const hasBody = data !== '' && data != null;
131
+ const headers = template(o.context, hasBody, o.clientHints ?? true, o.trace ?? true);
132
+ headers['x-s'] = r.xs;
133
+ headers['x-t'] = String(r.xt);
134
+ headers['x-s-common'] = r.xs_common;
135
+ headers['x-b3-traceid'] = b3;
136
+ if (o.xy) {
137
+ if (!o.userId)
138
+ throw new SpecificAIError('ERROR', '该接口需要 xy-direction,缺少 user_id');
139
+ headers['xy-direction'] = String(xyDirection(o.userId));
140
+ }
141
+ const body = !hasBody ? '' : typeof data === 'string' ? data : compactJson(data);
142
+ return { headers, body };
143
+ }
144
+ /** 上游 build_pc_business_headers:按有无 client hints / RAP / xy-direction / c_device_id 选实抓顺序。 */
145
+ export function businessOrder(headers, method) {
146
+ const has = (k) => Object.keys(headers).some((x) => x.toLowerCase() === k);
147
+ const get = method.toUpperCase() === 'GET';
148
+ const hints = has('sec-ch-ua-platform');
149
+ let order;
150
+ if (!hints && has('c_device_id'))
151
+ order = PC_ORDER.cdeviceGet;
152
+ else if (has('xy-direction'))
153
+ order = PC_ORDER.xyRapPost;
154
+ else if (has('x-rap-param'))
155
+ order = get ? PC_ORDER.rapGet : PC_ORDER.rapPost;
156
+ else if (!hints)
157
+ order = get ? PC_ORDER.businessGet : PC_ORDER.businessPost;
158
+ else
159
+ order = get ? PC_ORDER.signedGet : PC_ORDER.signedPost;
160
+ if (!has('content-type'))
161
+ order = order.filter((k) => k !== 'content-type');
162
+ order = [...order];
163
+ if (has('cache-control') && !order.includes('cache-control'))
164
+ order.splice(order.indexOf(order.includes('cookie') ? 'cookie' : 'origin'), 0, 'cache-control');
165
+ if (has('pragma') && !order.includes('pragma'))
166
+ order.splice(order.includes('priority') ? order.indexOf('priority') : order.length, 0, 'pragma');
167
+ return order;
168
+ }
169
+ const DSL_TTL = 300_000;
170
+ const GETDSS = /function\s+getdss\s*\(\s*\)\s*\{\s*return\s+'(\d+)'/;
171
+ /** 安全程序里 `function getdss() { return '<数字>' }` 的值(window._dsl),没有时为 undefined。 */
172
+ export const getdss = (code) => GETDSS.exec(code)?.[1];
173
+ /** 发请求并按上游规则合并响应 cookie 的基类(PC、Creator 共用)。 */
174
+ export class Session {
175
+ ctx;
176
+ scopeName;
177
+ http;
178
+ jar;
179
+ constructor(ctx, scopeName = 'main', browser = BROWSER, apiOrigin = EDITH) {
180
+ this.ctx = ctx;
181
+ this.scopeName = scopeName;
182
+ this.http = httpClient(ctx, { browser: browser, os: 'windows', scope: scopeName });
183
+ this.jar = this.http.jar;
184
+ // 导入的整串 cookie 都落在默认域上;acw_tc 按上游归到 api 源(cookie_source_url 默认值)
185
+ for (const c of this.jar.cookies)
186
+ if (HOST_ONLY.has(c.name) && c.domain.startsWith('.'))
187
+ c.domain = new URL(apiOrigin).hostname;
188
+ }
189
+ /** 换掉整组共享 cookie(上游用新的 cookie dict 重建 profile):已有的名字留在原位,host-only 的保留,其余追加。 */
190
+ replaceShared(values) {
191
+ const kept = this.jar.cookies.filter((c) => HOST_ONLY.has(c.name) || c.name in values);
192
+ for (const c of kept)
193
+ if (!HOST_ONLY.has(c.name))
194
+ c.value = values[c.name];
195
+ const names = new Set(kept.map((c) => c.name));
196
+ this.jar.cookies.splice(0, this.jar.cookies.length, ...kept);
197
+ for (const [k, v] of Object.entries(values))
198
+ if (!names.has(k))
199
+ this.jar.set(k, v, COOKIE_DOMAIN);
200
+ }
201
+ get credential() {
202
+ return this.ctx.credential;
203
+ }
204
+ /** 共享 cookie(上游 cookie_map),按存入顺序。 */
205
+ shared() {
206
+ const out = {};
207
+ for (const c of this.jar.cookies)
208
+ if (!HOST_ONLY.has(c.name) && !(c.name in out))
209
+ out[c.name] = c.value;
210
+ return out;
211
+ }
212
+ /** 发往某个 URL 的 cookie(上游 cookies_for_url)。 */
213
+ wire(url) {
214
+ return this.jar.toObject(url);
215
+ }
216
+ setCookie(name, value) {
217
+ const existing = this.jar.cookies.find((c) => c.name === name && !HOST_ONLY.has(name));
218
+ if (existing)
219
+ existing.value = value;
220
+ else
221
+ this.jar.set(name, value, COOKIE_DOMAIN);
222
+ }
223
+ /** 发送;响应的 Set-Cookie 除 acw_tc 外一律归到 `.xiaohongshu.com`(上游 merge_response)。 */
224
+ /**
225
+ * 发送。bare 为 true 时去掉 core/http 自动补的 `Content-Type: application/octet-stream`
226
+ * (上游 requests 发 `data=str` 且头里没有 content-type 时不补)。
227
+ */
228
+ async send(req, appendShared = [], bare = false) {
229
+ const before = this.jar.cookies.length;
230
+ // 请求头里的 cookie 由调用方按上游规则组好;没有 cookie 头时也不从罐子里补
231
+ const res = bare ? await this.bareRequest({ cookies: false, ...req }) : await this.http.request({ cookies: false, ...req });
232
+ const host = new URL(res.url || req.url).hostname;
233
+ const added = this.jar.cookies.splice(before);
234
+ // 已有 cookie 被 Set-Cookie 原地更新的情况:jar 已处理;新增的按上游规则归域
235
+ for (const c of added) {
236
+ if (HOST_ONLY.has(c.name)) {
237
+ c.domain = host;
238
+ const i = this.jar.cookies.findIndex((x) => x.name === c.name && x.domain === host);
239
+ if (i >= 0)
240
+ this.jar.cookies[i] = c;
241
+ else
242
+ this.jar.cookies.push(c);
243
+ continue;
244
+ }
245
+ const i = this.jar.cookies.findIndex((x) => x.name === c.name && !HOST_ONLY.has(x.name));
246
+ if (i >= 0) {
247
+ if (appendShared.includes(c.name)) {
248
+ this.jar.cookies.splice(i, 1);
249
+ this.jar.cookies.push({ ...c, domain: COOKIE_DOMAIN });
250
+ }
251
+ else
252
+ this.jar.cookies[i].value = c.value;
253
+ }
254
+ else
255
+ this.jar.cookies.push({ ...c, domain: COOKIE_DOMAIN });
256
+ }
257
+ this.afterResponse();
258
+ return res;
259
+ }
260
+ async bareRequest(req) {
261
+ const http = this.http;
262
+ const prepare = http.prepare;
263
+ http.prepare = (r) => {
264
+ const p = prepare.call(this.http, r);
265
+ p.headers = p.headers.filter(([k, v]) => !(k === 'Content-Type' && v === 'application/octet-stream'));
266
+ return p;
267
+ };
268
+ try {
269
+ return await this.http.request(req);
270
+ }
271
+ finally {
272
+ http.prepare = prepare;
273
+ }
274
+ }
275
+ /** 共享 cookie 变化同步到签名状态(上游 update_cookies 的副作用:loadts / ets / websectiga / gid)。 */
276
+ sync() {
277
+ const updates = {};
278
+ for (const [k, v] of Object.entries(this.shared()))
279
+ if (this.state.cookies[k] !== v)
280
+ updates[k] = v;
281
+ this.state.updateCookies(updates);
282
+ }
283
+ /** 响应 cookie 合并后同步签名状态。 */
284
+ afterResponse() {
285
+ this.sync();
286
+ }
287
+ /**
288
+ * as.xiaohongshu.com 的 ds 接口(上游 xhs_pc/dsl.py、xhs_creator/dsl.py):返回整段程序和其中 getdss() 的值。
289
+ * 缓存由调用方做(PC 只存 _dsl,创作者中心连程序一起存)。
290
+ */
291
+ async fetchDs(appId, referer) {
292
+ const res = await this.send({
293
+ url: `${AS}/api/sec/v1/ds?appId=${appId}`,
294
+ headers: [
295
+ ['User-Agent', UA],
296
+ ['Referer', referer],
297
+ ['Accept', '*/*'],
298
+ ['accept-encoding', 'gzip, deflate, br, zstd'],
299
+ ],
300
+ });
301
+ const program = await res.text();
302
+ return { dsl: getdss(program), program };
303
+ }
304
+ async json(req, appendShared = [], bare = false) {
305
+ return jsonOf(await this.send(req, appendShared, bare));
306
+ }
307
+ /** 业务码检查(AGENTS 6.4),返回 data。 */
308
+ check(body) {
309
+ if (body?.success !== false && (body?.code === undefined || body.code === 0 || body.code === 1000))
310
+ return body?.data;
311
+ const code = body.code;
312
+ const message = String(body.msg ?? body.message ?? '');
313
+ if (isAuthFailure(body))
314
+ throw authError(this.ctx, message || undefined);
315
+ const kind = riskOf(code, message);
316
+ if (kind)
317
+ throw new SpecificAIError('RISK_CONTROL', `小红书风控:${message || code}`, { detail: { kind, code, message } });
318
+ throw new SpecificAIError('UPSTREAM', message || `小红书返回错误 ${code}`, { detail: { code, message } });
319
+ }
320
+ }
321
+ /** 风控业务码(AGENTS 6.4):300012 / 300013 / 300015 与 461 / 471 要人机验证,300011 是账号被限制。 */
322
+ const RISK_CODES = { 300012: 'captcha', 300013: 'captcha', 300015: 'captcha', 461: 'captcha', 471: 'captcha', 300011: 'blocked' };
323
+ /**
324
+ * 没有风控业务码时只认明确的措辞:宽泛的「异常」「验证」「频繁」也出现在普通业务错误里(「服务异常」「验证码错误」),
325
+ * 会把本该降级处理的 UPSTREAM 错误当成风控(例如群聊列表取不到时只列单聊)。
326
+ */
327
+ const RISK_WORDS = [
328
+ [/人机验证|滑块验证|安全验证|captcha/i, 'captcha'],
329
+ [/访问频次异常|访问(太|过于)频繁|操作(太|过于)频繁|请勿频繁操作/, 'blocked'],
330
+ ];
331
+ /** 业务错误是不是风控,是的话返回 detail.kind。 */
332
+ export function riskOf(code, message) {
333
+ const byCode = typeof code === 'number' ? RISK_CODES[code] : undefined;
334
+ return byCode ?? RISK_WORDS.find(([re]) => re.test(message))?.[1] ?? null;
335
+ }
336
+ /** 登录墙 / 登录失效的业务响应:-100 / -101 / -104,或提示里说要登录。 */
337
+ export function isAuthFailure(body) {
338
+ const code = body?.code;
339
+ return code === -100 || code === -101 || code === -104 || /登录|未登录|login/i.test(String(body?.msg ?? body?.message ?? ''));
340
+ }
341
+ /**
342
+ * 网页端遇到 461 / 471 时跳转的验证页。在登录了同一账号的浏览器里打开、完成验证后,标记解除,命令可以重试。
343
+ * 格式照网页端的跳转:redirectPath 是验证完回到的页面,verifyBiz 是触发验证的状态码。
344
+ */
345
+ export function verifyUrl(status, verifyUuid, verifyType) {
346
+ const q = new URLSearchParams({ redirectPath: `${WEB}/explore`, verifyUuid, ...(verifyType ? { verifyType } : {}), verifyBiz: String(status) });
347
+ return `${WEB}/website-login/captcha?${q}`;
348
+ }
349
+ /**
350
+ * 风控状态码(AGENTS 6.4):461 / 471 要求人机验证(响应体照样是 success,data 为空),406 / 429 是限流。
351
+ * 响应头里的 verifytype / verifyuuid 放进 detail;有 verifyuuid 时拼出网页验证链接(detail.verify_url),
352
+ * 调用方没给 hint 时 hint 就是让用户去浏览器里验证。
353
+ */
354
+ export function checkStatus(res, hint) {
355
+ if (res.status === 461 || res.status === 471) {
356
+ const type = res.headers.get('verifytype');
357
+ const uuid = res.headers.get('verifyuuid');
358
+ const url = uuid ? verifyUrl(res.status, uuid, type) : null;
359
+ throw new SpecificAIError('RISK_CONTROL', `小红书要求人机验证(HTTP ${res.status})`, {
360
+ hint: hint ?? (url ? `在登录了同一个小红书账号的浏览器里打开链接完成验证,再重试:${url}` : '过几分钟再试,或在浏览器里打开小红书完成验证'),
361
+ detail: {
362
+ kind: 'captcha',
363
+ status: res.status,
364
+ ...(type ? { verify_type: type } : {}),
365
+ ...(uuid ? { verify_uuid: uuid, verify_url: url } : {}),
366
+ },
367
+ });
368
+ }
369
+ if (res.status === 406 || res.status === 429) {
370
+ throw new SpecificAIError('RISK_CONTROL', `小红书拒绝了请求(HTTP ${res.status}),请稍后再试`, { hint, detail: { kind: 'rate_limit', status: res.status } });
371
+ }
372
+ }
373
+ /**
374
+ * 响应 → JSON:先按状态码判风控(checkStatus),再解析,不是 JSON 时报 UPSTREAM。
375
+ * 业务请求(Session.json)、主站登录、创作者中心登录共用。
376
+ */
377
+ export async function jsonOf(res, hint) {
378
+ checkStatus(res, hint);
379
+ return parseJson(res);
380
+ }
381
+ export class Pc extends Session {
382
+ state;
383
+ constructor(ctx) {
384
+ super(ctx, 'main');
385
+ const device = (ctx.credential.device.pc ?? {});
386
+ this.state = new PcState(this.shared(), device.local, device.session);
387
+ }
388
+ /** 用当前共享 cookie 重建签名状态(上游 _new_profile),不带已保存的 storage。 */
389
+ resetState() {
390
+ this.state = new PcState(this.shared());
391
+ }
392
+ get isLogin() {
393
+ return Boolean(this.state.cookies.a1 && this.state.cookies.web_session);
394
+ }
395
+ /** 当前用户 id:登录账号取凭证里的 user,游客取游客初始化时 user/me 的结果。 */
396
+ get userId() {
397
+ return this.credential.user?.id ?? String(this.credential.extra.user_id ?? '');
398
+ }
399
+ set userId(id) {
400
+ this.credential.extra.user_id = id;
401
+ }
402
+ /** 持久化签名状态(localStorage / sessionStorage 的等价物)。 */
403
+ save() {
404
+ this.credential.device.pc = this.state.storage();
405
+ }
406
+ // ---------------------------------------------------------------- _dsl
407
+ /** window._dsl:as.xiaohongshu.com/api/sec/v1/ds 的 getdss(),5 分钟缓存(上游 xhs_pc/dsl.py)。 */
408
+ async dsl(force = false) {
409
+ const cached = this.credential.extra.pc_dsl;
410
+ if (!force && cached?.value && rand.now() - cached.at < DSL_TTL)
411
+ return cached.value;
412
+ const value = (await this.fetchDs('xhs-pc-web', `${WEB}/`)).dsl;
413
+ if (!value) {
414
+ if (cached?.value)
415
+ return cached.value;
416
+ throw new SpecificAIError('UPSTREAM', '小红书 ds 接口没有返回 getdss(),无法签名');
417
+ }
418
+ this.credential.extra.pc_dsl = { value, at: rand.now() };
419
+ return value;
420
+ }
421
+ // ---------------------------------------------------------------- 业务请求(上游 XHS_Apis._request_params)
422
+ /** 登录态检查(上游 validate):没有 a1 / web_session 时,游客去做初始化,账号报登录失效。 */
423
+ requireSession() {
424
+ if (!this.state.cookies.a1 || !this.state.cookies.web_session)
425
+ throw authError(this.ctx, '小红书 cookie 缺少 a1 或 web_session');
426
+ // 签名把 a1 打进定长的 mns 包里,长度不对的 a1 必然签不出来(上游同样卡在 mns 长度门禁)
427
+ if (this.state.cookies.a1.length !== 52)
428
+ throw authError(this.ctx, `小红书 cookie 里的 a1 长度不对(${this.state.cookies.a1.length},应为 52)`);
429
+ }
430
+ async signedRequest(api, data, method, o = {}) {
431
+ this.sync();
432
+ this.requireSession();
433
+ const xy = needsXy(api);
434
+ if (xy && !this.userId)
435
+ await this.bootstrap();
436
+ const context = this.state.nextSignContext(api);
437
+ const b1 = this.state.currentB1(context.now);
438
+ const dslPair = this.state.dslPair(await this.dsl(), rand.now());
439
+ const signed = pcSignedHeaders(this.state, api, data, { b1, dslPair, context, userId: this.userId, xy, clientHints: false });
440
+ let headers = signed.headers;
441
+ if (CACHE_PATHS.includes(api.split('?', 1)[0])) {
442
+ headers['cache-control'] = 'no-cache';
443
+ headers.pragma = 'no-cache';
444
+ }
445
+ if (needsRap(api))
446
+ headers['x-rap-param'] = rapParam(api, signed.body);
447
+ for (const [k, v] of Object.entries(o.extra ?? {}))
448
+ headers[k] = v;
449
+ if (o.contentType)
450
+ headers['content-type'] = o.contentType;
451
+ const url = (o.origin ?? EDITH) + api;
452
+ const cookies = this.wire(url);
453
+ const pairs = orderedHeaders(headers, businessOrder(headers, method), cookies);
454
+ const ua = pairs.findIndex(([k]) => k === 'user-agent');
455
+ pairs[ua] = ['user-agent', context.userAgent ?? UA];
456
+ return { url, headers: pairs, body: signed.body };
457
+ }
458
+ /** 签名、发送、解析 JSON(不检查业务码)。 */
459
+ async request(api, data = '', method = 'GET', o = {}) {
460
+ const r = await this.signedRequest(api, data, method, o);
461
+ return this.json({ method, url: r.url, headers: r.headers, ...(method === 'POST' ? { body: r.body } : {}) });
462
+ }
463
+ /** 签名、发送并检查业务码,返回 data。 */
464
+ async call(api, data = '', method = 'GET', o = {}) {
465
+ return this.check(await this.request(api, data, method, o));
466
+ }
467
+ /** 上游 bootstrap:user/me 写入 user_id(算 xy-direction 用)。 */
468
+ async bootstrap() {
469
+ const me = await this.call('/api/sns/web/v2/user/me');
470
+ const id = String(me?.user_id ?? '');
471
+ if (!id)
472
+ throw new SpecificAIError('UPSTREAM', 'user/me 没有返回 user_id');
473
+ if (!this.credential.user)
474
+ this.userId = id;
475
+ return id;
476
+ }
477
+ // ---------------------------------------------------------------- 登录链路的签名(上游 XHSLoginApi._signed_request_params)
478
+ loginB1 = '';
479
+ webprofileReported = false;
480
+ loginDsl = '';
481
+ async loginSigned(api, data = '', method = 'POST', o = {}) {
482
+ this.sync();
483
+ const context = this.state.nextSignContext(api, { tier: o.tier, mnsProfile: o.mnsProfile });
484
+ const includeB1 = o.includeB1 ?? Boolean(this.loginB1 || this.webprofileReported);
485
+ if (includeB1 && !this.loginB1)
486
+ this.loginB1 = this.state.currentB1(context.now, 'login');
487
+ const b1 = includeB1 ? this.loginB1 : '';
488
+ if (!this.loginDsl)
489
+ this.loginDsl = await this.dsl();
490
+ const dslPair = this.state.lastTiga ? this.state.dslPair(this.loginDsl, context.now) : `null;${this.loginDsl}`;
491
+ const signed = pcSignedHeaders(this.state, api, data, { b1, dslPair, context });
492
+ signed.headers['accept-language'] = LOGIN_LANG;
493
+ if (o.secDomain) {
494
+ const names = ['x-s', 'x-t', 'x-s-common', ...((o.trace ?? true) ? ['x-b3-traceid', 'x-xray-traceid'] : [])];
495
+ const keep = Object.fromEntries(names.map((k) => [k, signed.headers[k]]));
496
+ signed.headers = { ...secHeaders(context, method), ...keep };
497
+ }
498
+ return signed;
499
+ }
500
+ setLoginDsl(value) {
501
+ this.loginDsl = value;
502
+ }
503
+ }
504
+ /** 上游 XHSLoginApi._get_sec_headers。 */
505
+ export function secHeaders(context, method) {
506
+ const h = {
507
+ 'sec-ch-ua-platform': '"Windows"',
508
+ referer: `${WEB}/`,
509
+ 'sec-ch-ua': context.secChUa ?? SEC_CH_UA,
510
+ 'sec-ch-ua-mobile': '?0',
511
+ 'user-agent': context.userAgent ?? UA,
512
+ accept: XHR_ACCEPT,
513
+ 'accept-language': LOGIN_LANG,
514
+ origin: WEB,
515
+ priority: 'u=1, i',
516
+ 'sec-fetch-dest': 'empty',
517
+ 'sec-fetch-mode': 'cors',
518
+ 'sec-fetch-site': 'same-site',
519
+ };
520
+ if (method.toUpperCase() !== 'GET')
521
+ h['content-type'] = 'application/json;charset=UTF-8';
522
+ return h;
523
+ }
524
+ /** 上游 build_pc_login_headers。 */
525
+ export function loginOrder(headers, kind) {
526
+ let order = kind === 'honeypot'
527
+ ? PC_ORDER.honeypot
528
+ : kind === 'security'
529
+ ? PC_ORDER.security
530
+ : kind === 'sem'
531
+ ? PC_ORDER.sem
532
+ : kind === 'get'
533
+ ? PC_ORDER.signedGet
534
+ : kind === 'get-login-mode'
535
+ ? ['sec-ch-ua-platform', 'x-login-mode', ...PC_ORDER.signedGet.slice(1)]
536
+ : PC_ORDER.signedPost;
537
+ if (!Object.keys(headers).some((k) => k.toLowerCase() === 'content-type'))
538
+ order = order.filter((k) => k !== 'content-type');
539
+ return order;
540
+ }