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,320 @@
1
+ 'use strict';
2
+ // Full DEFLATE encoder matching browser: simple greedy LZ77 + dynamic Huffman
3
+ // Verify byte-exact against browser samples
4
+
5
+ const LEN_BASE = [3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258];
6
+ const LEN_EXTRA = [0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0];
7
+ const DIST_BASE = [1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577];
8
+ const DIST_EXTRA = [0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];
9
+ const CLC_ORDER = [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];
10
+
11
+ function lenToCode(l) {
12
+ for (let i = 0; i < LEN_BASE.length; i++) {
13
+ if (l >= LEN_BASE[i] && (i === LEN_BASE.length - 1 || l < LEN_BASE[i + 1])) {
14
+ return { code: 257 + i, extra: LEN_EXTRA[i], extraBits: l - LEN_BASE[i] };
15
+ }
16
+ }
17
+ throw new Error('bad len ' + l);
18
+ }
19
+
20
+ function distToCode(d) {
21
+ for (let i = 0; i < DIST_BASE.length; i++) {
22
+ if (d >= DIST_BASE[i] && (i === DIST_BASE.length - 1 || d < DIST_BASE[i + 1])) {
23
+ return { code: i, extra: DIST_EXTRA[i], extraBits: d - DIST_BASE[i] };
24
+ }
25
+ }
26
+ throw new Error('bad dist ' + d);
27
+ }
28
+
29
+ function lz77Tokenize(input, maxMatchLen = 5, minMatchLen = 3, windowSize = 128) {
30
+ const tokens = [];
31
+ let pos = 0;
32
+ while (pos < input.length) {
33
+ let bestLen = 0, bestDist = 0;
34
+ const searchStart = Math.max(0, pos - windowSize);
35
+ for (let back = pos - 1; back >= searchStart; back--) {
36
+ const dist = pos - back;
37
+ let len = 0;
38
+ const maxLen = Math.min(maxMatchLen, input.length - pos);
39
+ while (len < maxLen && input[back + len] === input[pos + len]) len++;
40
+ if (len >= minMatchLen && len > bestLen) {
41
+ bestLen = len; bestDist = dist;
42
+ if (len === maxMatchLen) break;
43
+ }
44
+ }
45
+ if (bestLen >= minMatchLen) {
46
+ tokens.push({ t: 'M', len: bestLen, dist: bestDist });
47
+ pos += bestLen;
48
+ } else {
49
+ tokens.push({ t: 'L', v: input[pos] });
50
+ pos++;
51
+ }
52
+ }
53
+ tokens.push({ t: 'END' });
54
+ return tokens;
55
+ }
56
+
57
+ // Standard Huffman with length limit — package-merge would be more accurate, but zlib uses a specific heap-based algorithm
58
+ function buildHuffmanLengths(freqs, maxBits = 15) {
59
+ // Build heap of (freq, sym) leaves; construct Huffman tree
60
+ const n = freqs.length;
61
+ const nodes = [];
62
+ for (let i = 0; i < n; i++) if (freqs[i] > 0) nodes.push({ freq: freqs[i], sym: i, left: null, right: null });
63
+ if (nodes.length === 0) return new Array(n).fill(0);
64
+ if (nodes.length === 1) {
65
+ const lens = new Array(n).fill(0);
66
+ lens[nodes[0].sym] = 1;
67
+ return lens;
68
+ }
69
+ // Build tree
70
+ const heap = nodes.slice();
71
+ const cmp = (a, b) => a.freq - b.freq;
72
+ heap.sort(cmp);
73
+ while (heap.length > 1) {
74
+ const a = heap.shift();
75
+ const b = heap.shift();
76
+ const parent = { freq: a.freq + b.freq, sym: -1, left: a, right: b };
77
+ let idx = 0;
78
+ while (idx < heap.length && heap[idx].freq <= parent.freq) idx++;
79
+ heap.splice(idx, 0, parent);
80
+ }
81
+ const lens = new Array(n).fill(0);
82
+ const depths = new Array(n).fill(0);
83
+ function walk(node, depth) {
84
+ if (node.sym >= 0) { lens[node.sym] = depth || 1; depths[node.sym] = depth || 1; return; }
85
+ walk(node.left, depth + 1);
86
+ walk(node.right, depth + 1);
87
+ }
88
+ walk(heap[0], 0);
89
+
90
+ // Enforce max bits limit(pako/zlib gen_bitlen 溢出修正,2026-07-25 移植)
91
+ let overflow = 0;
92
+ for (let i = 0; i < n; i++) if (lens[i] > maxBits) overflow++;
93
+ if (overflow === 0) return lens;
94
+
95
+ const bl_count = new Array(maxBits + 1).fill(0);
96
+ for (let i = 0; i < n; i++) {
97
+ if (lens[i]) bl_count[Math.min(lens[i], maxBits)]++;
98
+ }
99
+ do {
100
+ let bits = maxBits - 1;
101
+ while (bl_count[bits] === 0) bits--;
102
+ bl_count[bits]--;
103
+ bl_count[bits + 1] += 2;
104
+ bl_count[maxBits]--;
105
+ overflow -= 2;
106
+ } while (overflow > 0);
107
+
108
+ // 按 pako 的 smaller() 顺序(freq 升序、depth 升序、sym 升序)把叶子排好;
109
+ // pako 的 heap 尾删序使最长码从最低频叶子开始分配(heap[--h] 从最小端扫)。
110
+ const leaves = nodes
111
+ .map(node => ({ sym: node.sym, freq: node.freq, depth: depths[node.sym] }))
112
+ .sort((a, b) => (a.freq - b.freq) || (a.depth - b.depth) || (a.sym - b.sym));
113
+ const out = new Array(n).fill(0);
114
+ let h = 0;
115
+ for (let bits = maxBits; bits !== 0; bits--) {
116
+ let cnt = bl_count[bits];
117
+ while (cnt !== 0) {
118
+ const leaf = leaves[h++];
119
+ out[leaf.sym] = bits;
120
+ cnt--;
121
+ }
122
+ }
123
+ return out;
124
+ }
125
+
126
+ // Canonical Huffman codes from lengths
127
+ function buildCanonical(lens) {
128
+ const maxLen = Math.max(...lens, 0);
129
+ const bl_count = new Array(maxLen + 1).fill(0);
130
+ for (const l of lens) if (l) bl_count[l]++;
131
+ const next_code = new Array(maxLen + 1).fill(0);
132
+ let code = 0;
133
+ for (let bits = 1; bits <= maxLen; bits++) {
134
+ code = (code + bl_count[bits - 1]) << 1;
135
+ next_code[bits] = code;
136
+ }
137
+ const codes = new Array(lens.length).fill(0);
138
+ for (let n = 0; n < lens.length; n++) {
139
+ if (lens[n]) {
140
+ codes[n] = next_code[lens[n]];
141
+ next_code[lens[n]]++;
142
+ }
143
+ }
144
+ return codes;
145
+ }
146
+
147
+ class BitWriter {
148
+ constructor() { this.chunks = []; this.buf = 0; this.nbits = 0; }
149
+ writeBits(val, n) {
150
+ this.buf |= (val << this.nbits) >>> 0;
151
+ this.nbits += n;
152
+ while (this.nbits >= 8) {
153
+ this.chunks.push(this.buf & 0xff);
154
+ this.buf >>>= 8;
155
+ this.nbits -= 8;
156
+ }
157
+ }
158
+ writeCode(code, len) {
159
+ // DEFLATE Huffman codes emitted MSB-first
160
+ let rev = 0;
161
+ for (let i = 0; i < len; i++) {
162
+ rev |= (((code >> (len - 1 - i)) & 1) << i);
163
+ }
164
+ this.writeBits(rev, len);
165
+ }
166
+ flush() {
167
+ if (this.nbits > 0) {
168
+ this.chunks.push(this.buf & 0xff);
169
+ this.buf = 0; this.nbits = 0;
170
+ }
171
+ return Buffer.from(this.chunks);
172
+ }
173
+ }
174
+
175
+ // Encode code-length sequence with RLE for the code-length alphabet
176
+ function rleCodeLens(lens) {
177
+ const rle = [];
178
+ let i = 0;
179
+ while (i < lens.length) {
180
+ const cur = lens[i];
181
+ let run = 1;
182
+ while (i + run < lens.length && lens[i + run] === cur) run++;
183
+ if (cur === 0) {
184
+ // Encode run of zeros
185
+ while (run >= 11) {
186
+ const r = Math.min(run, 138);
187
+ rle.push({ sym: 18, extra: r - 11, extraBits: 7 });
188
+ run -= r;
189
+ }
190
+ while (run >= 3) {
191
+ const r = Math.min(run, 10);
192
+ rle.push({ sym: 17, extra: r - 3, extraBits: 3 });
193
+ run -= r;
194
+ }
195
+ while (run > 0) { rle.push({ sym: 0 }); run--; }
196
+ } else {
197
+ // Emit first, then use 16 (repeat prev) for subsequent
198
+ rle.push({ sym: cur });
199
+ run--;
200
+ while (run >= 3) {
201
+ const r = Math.min(run, 6);
202
+ rle.push({ sym: 16, extra: r - 3, extraBits: 2 });
203
+ run -= r;
204
+ }
205
+ while (run > 0) { rle.push({ sym: cur }); run--; }
206
+ }
207
+ i += (run === 0 ? (i + run + 1 === i ? 1 : 0) : 0); // this loop advance
208
+ // Actually: re-set i to start of next different value
209
+ let jmp = 1;
210
+ while (i + jmp < lens.length && lens[i + jmp] === cur) jmp++;
211
+ i += jmp;
212
+ }
213
+ return rle;
214
+ }
215
+
216
+ function encodeDeflate(input) {
217
+ const tokens = lz77Tokenize(input);
218
+
219
+ // Count frequencies
220
+ const litFreq = new Array(286).fill(0);
221
+ const distFreq = new Array(30).fill(0);
222
+ litFreq[256] = 1; // END
223
+ for (const t of tokens) {
224
+ if (t.t === 'L') litFreq[t.v]++;
225
+ else if (t.t === 'M') {
226
+ const lc = lenToCode(t.len);
227
+ litFreq[lc.code]++;
228
+ const dc = distToCode(t.dist);
229
+ distFreq[dc.code]++;
230
+ }
231
+ }
232
+
233
+ // Build Huffman code lengths
234
+ const litLens = buildHuffmanLengths(litFreq, 15);
235
+ const distLens = buildHuffmanLengths(distFreq, 15);
236
+
237
+ // Find HLIT, HDIST
238
+ let hlitMax = 256;
239
+ for (let i = 285; i > 256; i--) if (litLens[i]) { hlitMax = i; break; }
240
+ const HLIT = hlitMax - 256; // HLIT + 257 = alphabet size
241
+ let hdistMax = 0;
242
+ for (let i = 29; i > 0; i--) if (distLens[i]) { hdistMax = i; break; }
243
+ const HDIST = hdistMax; // HDIST + 1 = alphabet size
244
+
245
+ // Trim to actual size
246
+ const litLensTrim = litLens.slice(0, HLIT + 257);
247
+ const distLensTrim = distLens.slice(0, HDIST + 1);
248
+
249
+ // RLE encode the lengths
250
+ const combined = litLensTrim.concat(distLensTrim);
251
+ const rle = rleCodeLens(combined);
252
+
253
+ // Build code-length alphabet Huffman
254
+ const clenFreq = new Array(19).fill(0);
255
+ for (const r of rle) clenFreq[r.sym]++;
256
+ const clenLens = buildHuffmanLengths(clenFreq, 7);
257
+
258
+ // Find HCLEN (minimum needed by CLC_ORDER)
259
+ let HCLEN = 0;
260
+ for (let i = 18; i >= 4; i--) if (clenLens[CLC_ORDER[i]]) { HCLEN = i - 3; break; }
261
+
262
+ // Build canonical codes for all three Huffman tables
263
+ const litCodes = buildCanonical(litLensTrim);
264
+ const distCodes = buildCanonical(distLensTrim);
265
+ const clenCodes = buildCanonical(clenLens);
266
+
267
+ // Write DEFLATE stream
268
+ const bw = new BitWriter();
269
+ bw.writeBits(1, 1); // BFINAL
270
+ bw.writeBits(2, 2); // BTYPE=10 (dynamic)
271
+ bw.writeBits(HLIT, 5);
272
+ bw.writeBits(HDIST, 5);
273
+ bw.writeBits(HCLEN, 4);
274
+ for (let i = 0; i < HCLEN + 4; i++) bw.writeBits(clenLens[CLC_ORDER[i]], 3);
275
+
276
+ // Write encoded code lengths using clen Huffman
277
+ for (const r of rle) {
278
+ bw.writeCode(clenCodes[r.sym], clenLens[r.sym]);
279
+ if (r.sym === 16) bw.writeBits(r.extra, 2);
280
+ else if (r.sym === 17) bw.writeBits(r.extra, 3);
281
+ else if (r.sym === 18) bw.writeBits(r.extra, 7);
282
+ }
283
+
284
+ // Write actual data
285
+ for (const t of tokens) {
286
+ if (t.t === 'L') {
287
+ bw.writeCode(litCodes[t.v], litLensTrim[t.v]);
288
+ } else if (t.t === 'M') {
289
+ const lc = lenToCode(t.len);
290
+ bw.writeCode(litCodes[lc.code], litLensTrim[lc.code]);
291
+ if (lc.extra) bw.writeBits(lc.extraBits, lc.extra);
292
+ const dc = distToCode(t.dist);
293
+ bw.writeCode(distCodes[dc.code], distLensTrim[dc.code]);
294
+ if (dc.extra) bw.writeBits(dc.extraBits, dc.extra);
295
+ } else if (t.t === 'END') {
296
+ bw.writeCode(litCodes[256], litLensTrim[256]);
297
+ }
298
+ }
299
+
300
+ return bw.flush();
301
+ }
302
+
303
+ // Wrap in gzip format
304
+ function encodeGzip(input, mtime = 0) {
305
+ const deflate = encodeDeflate(input);
306
+ const gzip = Buffer.alloc(10 + deflate.length + 8);
307
+ gzip[0] = 0x1f; gzip[1] = 0x8b;
308
+ gzip[2] = 0x08; // CM = DEFLATE
309
+ gzip[3] = 0x00; // FLG = 0
310
+ gzip.writeUInt32LE(mtime, 4); // MTIME
311
+ gzip[8] = 0x00; // XFL
312
+ gzip[9] = 0x03; // OS = Unix
313
+ deflate.copy(gzip, 10);
314
+ const crc = require('zlib').crc32(input);
315
+ gzip.writeUInt32LE(crc, 10 + deflate.length);
316
+ gzip.writeUInt32LE(input.length & 0xffffffff, 14 + deflate.length);
317
+ return gzip;
318
+ }
319
+
320
+ module.exports = { encodeDeflate, encodeGzip, lz77Tokenize };
@@ -0,0 +1,4 @@
1
+ 'use strict';
2
+
3
+ // Legacy PC import path for the current pure MNS implementation.
4
+ module.exports = require('../../xhs_core/js/mns.js');
@@ -0,0 +1,141 @@
1
+ 'use strict';
2
+
3
+ const CryptoJS = require('crypto-js');
4
+ const reference = require('./reference_profile.json');
5
+
6
+ const DES_KEY = CryptoJS.enc.Utf8.parse('zbp30y86');
7
+ const I12_DYNAMIC_INDEXES = [18, 53, 69, 86, 103, 120];
8
+
9
+ function clone(value) {
10
+ return JSON.parse(JSON.stringify(value));
11
+ }
12
+
13
+ function integerOption(value, fallback, minimum, maximum) {
14
+ const resolved = value == null ? fallback : Number(value);
15
+ if (!Number.isInteger(resolved) || resolved < minimum || resolved > maximum) {
16
+ throw new TypeError(`integer option must be in [${minimum}, ${maximum}]`);
17
+ }
18
+ return resolved;
19
+ }
20
+
21
+ function buildI12Data(seed, templateValue, dynamicIndexes = I12_DYNAMIC_INDEXES) {
22
+ const decoded = JSON.parse(Buffer.from(String(templateValue), 'base64').toString('utf8'));
23
+ for (const index of dynamicIndexes) decoded.d[String(index)] = seed;
24
+ return Buffer.from(JSON.stringify(decoded), 'utf8').toString('base64');
25
+ }
26
+
27
+ function buildTelemetryValue(fi, options = {}, profileReference = {}) {
28
+ if (profileReference.telemetryTemplate) {
29
+ return String(profileReference.telemetryTemplate).replace(
30
+ '__TIME_ORIGIN__',
31
+ String(options.timeOrigin ?? ''),
32
+ );
33
+ }
34
+ return '{mt:{},m:{me:1},k:{},p:{ulr:0,ps:1,f:1,rs:1,sc:1,vc:1},'
35
+ + `st:{h:0,f:1,kr:0},ft:{ae:null,ak:null,cdr:null,bf:null,fi:${fi}}}`;
36
+ }
37
+
38
+ function buildProfileFields(options = {}) {
39
+ const profileReference = reference.webProfile;
40
+ if (!profileReference || !profileReference.fields) {
41
+ throw new Error('webProfile reference fields are missing');
42
+ }
43
+
44
+ const fields = clone(profileReference.fields);
45
+ // A complete DevTools capture can be replayed byte-for-byte. In this
46
+ // mode every field is required and preserved; no local timestamp, cookie,
47
+ // telemetry, or i12 value is substituted.
48
+ if (options.exactFields === true) {
49
+ const captured = options.fields;
50
+ if (!captured || typeof captured !== 'object' || Array.isArray(captured)) {
51
+ throw new TypeError('exactFields requires captured fields');
52
+ }
53
+ const expected = Object.keys(fields);
54
+ const missing = expected.filter((key) => !Object.prototype.hasOwnProperty.call(captured, key));
55
+ const extra = Object.keys(captured).filter((key) => !Object.prototype.hasOwnProperty.call(fields, key));
56
+ if (missing.length || extra.length) {
57
+ throw new TypeError(`exactFields schema mismatch; missing=${missing.join(',')}; extra=${extra.join(',')}`);
58
+ }
59
+ return clone(captured);
60
+ }
61
+ // Keep historical 6.32.2 fixtures reproducible while using the current
62
+ // 6.47.2 browser profile by default. Cookie input selects old fixtures.
63
+ const cookie = String(options.documentCookie || '');
64
+ if (/webBuild=6\.32\.2(?:;|$)/.test(cookie)) {
65
+ fields.x1 = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36';
66
+ }
67
+ const timestampMs = integerOption(options.timestampMs, Date.now(), 1, Number.MAX_SAFE_INTEGER);
68
+ const ets = integerOption(options.ets, timestampMs, 1, Number.MAX_SAFE_INTEGER);
69
+ const i12Seed = integerOption(
70
+ options.i12Seed,
71
+ profileReference.i12Seed,
72
+ 0,
73
+ 255,
74
+ );
75
+ const telemetryFi = integerOption(
76
+ options.telemetryFi,
77
+ profileReference.telemetryFi,
78
+ 0,
79
+ 1000000,
80
+ );
81
+
82
+ fields.x44 = String(timestampMs);
83
+ fields.x57 = options.documentCookie == null
84
+ ? `ets=${ets}; domain=.xiaohongshu.com; path=/; max-age=2592000`
85
+ : String(options.documentCookie);
86
+ fields.x83 = buildI12Data(
87
+ i12Seed,
88
+ fields.x83,
89
+ profileReference.i12DynamicIndexes ?? I12_DYNAMIC_INDEXES,
90
+ );
91
+ fields.x84 = buildTelemetryValue(telemetryFi, options, profileReference);
92
+
93
+ const overrides = options.fields || {};
94
+ for (const [key, value] of Object.entries(overrides)) {
95
+ if (!Object.prototype.hasOwnProperty.call(fields, key)) {
96
+ throw new TypeError(`unsupported webProfile field: ${key}`);
97
+ }
98
+ fields[key] = value;
99
+ }
100
+ return fields;
101
+ }
102
+
103
+ function encodeProfileData(fields) {
104
+ if (!fields || typeof fields !== 'object' || Array.isArray(fields)) {
105
+ throw new TypeError('profile fields must be an object');
106
+ }
107
+ const serialized = JSON.stringify(fields);
108
+ const base64 = Buffer.from(serialized, 'utf8').toString('base64');
109
+ const encrypted = CryptoJS.DES.encrypt(
110
+ CryptoJS.enc.Latin1.parse(base64),
111
+ DES_KEY,
112
+ { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding },
113
+ );
114
+ return encrypted.ciphertext.toString(CryptoJS.enc.Hex);
115
+ }
116
+
117
+ function generateProfileData(options = {}) {
118
+ return encodeProfileData(buildProfileFields(options));
119
+ }
120
+
121
+ if (require.main === module) {
122
+ try {
123
+ const options = process.argv[2] ? JSON.parse(process.argv[2]) : {};
124
+ const profileData = generateProfileData(options);
125
+ process.stdout.write(`${JSON.stringify({
126
+ profileData,
127
+ length: profileData.length,
128
+ sdkVersion: reference.release.webProfileSdkVersion,
129
+ algorithm: 'base64-des-ecb-zero-padding-hex',
130
+ })}\n`);
131
+ } catch (error) {
132
+ process.stderr.write(`${error && error.stack ? error.stack : error}\n`);
133
+ process.exit(1);
134
+ }
135
+ }
136
+
137
+ module.exports = {
138
+ buildProfileFields,
139
+ encodeProfileData,
140
+ generateProfileData,
141
+ };
@@ -0,0 +1,173 @@
1
+ /**
2
+ * x-rap-param algorithm — zero VMP execution.
3
+ *
4
+ * Reversed 2026-07-15 from captured Sanji/Garp VMP browser evidence.
5
+ *
6
+ * Discoveries:
7
+ * - Xorkeyverifyvalue = xxHash32([mask_byte], seed=0)
8
+ * - RequestHash = xxHash32(url_pathAndBeyond + jsonBody, seed=0)
9
+ * - Payload wire format: header(16B raw) + body(N B XOR-masked with 1B mask)
10
+ * header: [0:2]=0x03e8 op, [2:10]=8B ts BE, [10:12]=0x03e9 op, [12:16]=4B xorKeyVerify
11
+ * body: TLV opcodes 0x3ea..0x484, each XOR'd with mask
12
+ * - Random ASCII generation: bytes → alphabet36[b % 36]
13
+ * - AES key: "kqI1DTcwKX90ZtAy" (fixed)
14
+ * - Envelope: rap_crypto.js
15
+ *
16
+ * Fingerprint (~200B of behavior/detection data) is captured once per session
17
+ * and reused (same approach as mns envConst/envFpTail).
18
+ */
19
+ 'use strict';
20
+ const crypto = require('crypto');
21
+ const { encryptPayload } = require('./rap_crypto.js');
22
+
23
+ const ALPHABET36 = '0123456789abcdefghijklmnopqrstuvwxyz';
24
+
25
+ /** xxHash32 (32-bit variant of xxHash). Byte-exact vs reference. */
26
+ function xxHash32(buf, seed = 0) {
27
+ const P1 = 0x9E3779B1 >>> 0, P2 = 0x85EBCA77 >>> 0, P3 = 0xC2B2AE3D >>> 0;
28
+ const P4 = 0x27D4EB2F >>> 0, P5 = 0x165667B1 >>> 0;
29
+ const M = Math.imul;
30
+ const rotl = (x, n) => ((x << n) | (x >>> (32 - n))) >>> 0;
31
+ let h, p = 0; const len = buf.length;
32
+ if (len >= 16) {
33
+ let v1 = (seed + P1 + P2) >>> 0, v2 = (seed + P2) >>> 0, v3 = seed >>> 0, v4 = (seed - P1) >>> 0;
34
+ while (p + 16 <= len) {
35
+ const l1 = (buf[p] | buf[p+1]<<8 | buf[p+2]<<16 | buf[p+3]<<24) >>> 0;
36
+ const l2 = (buf[p+4] | buf[p+5]<<8 | buf[p+6]<<16 | buf[p+7]<<24) >>> 0;
37
+ const l3 = (buf[p+8] | buf[p+9]<<8 | buf[p+10]<<16 | buf[p+11]<<24) >>> 0;
38
+ const l4 = (buf[p+12] | buf[p+13]<<8 | buf[p+14]<<16 | buf[p+15]<<24) >>> 0;
39
+ v1 = M(rotl((v1 + M(l1, P2)) >>> 0, 13), P1) >>> 0;
40
+ v2 = M(rotl((v2 + M(l2, P2)) >>> 0, 13), P1) >>> 0;
41
+ v3 = M(rotl((v3 + M(l3, P2)) >>> 0, 13), P1) >>> 0;
42
+ v4 = M(rotl((v4 + M(l4, P2)) >>> 0, 13), P1) >>> 0;
43
+ p += 16;
44
+ }
45
+ h = (rotl(v1, 1) + rotl(v2, 7) + rotl(v3, 12) + rotl(v4, 18)) >>> 0;
46
+ } else h = (seed + P5) >>> 0;
47
+ h = (h + len) >>> 0;
48
+ while (p + 4 <= len) {
49
+ const l = (buf[p] | buf[p+1]<<8 | buf[p+2]<<16 | buf[p+3]<<24) >>> 0;
50
+ h = M(rotl((h + M(l, P3)) >>> 0, 17), P4) >>> 0; p += 4;
51
+ }
52
+ while (p < len) { h = M(rotl((h + M(buf[p], P5)) >>> 0, 11), P1) >>> 0; p++; }
53
+ h ^= h >>> 15; h = M(h, P2) >>> 0; h ^= h >>> 13; h = M(h, P3) >>> 0; h ^= h >>> 16;
54
+ return h >>> 0;
55
+ }
56
+
57
+ /** Map N raw random bytes to ASCII: alphabet36[b % 36] for each byte. */
58
+ function randomAscii(n) {
59
+ const buf = crypto.randomBytes(n);
60
+ let out = '';
61
+ for (const b of buf) out += ALPHABET36[b % 36];
62
+ return out;
63
+ }
64
+
65
+ /** Build a fresh 235B payload (no Uuid; the minimal common form).
66
+ * Returns Uint8Array of the plaintext payload (before gzip).
67
+ * fingerprint is a 201-byte Buffer captured from browser (or all-zeros for a bare minimum). */
68
+ function buildPayload({ ts, url, body, fingerprint, mask }) {
69
+ // Layout for 235B payload (Uuid absent):
70
+ // [0:16] header (raw)
71
+ // [16:38] Uuid section — for 235B we still need SOMETHING at [16:] starting with 0x03ea
72
+ // ... 219B body ...
73
+ // Simpler: use the exact template we captured and only overwrite dynamic bytes.
74
+ // Header: 03e8 <8B ts BE> 03e9 <4B xorKeyVerify>
75
+ const p = new Uint8Array(fingerprint.length + 16);
76
+ // Header
77
+ p[0] = 0x03; p[1] = 0xe8;
78
+ // 8B ts BE
79
+ const tsHi = Math.floor(ts / 0x100000000);
80
+ const tsLo = ts >>> 0;
81
+ p[2] = (tsHi >>> 24) & 0xff; p[3] = (tsHi >>> 16) & 0xff; p[4] = (tsHi >>> 8) & 0xff; p[5] = tsHi & 0xff;
82
+ p[6] = (tsLo >>> 24) & 0xff; p[7] = (tsLo >>> 16) & 0xff; p[8] = (tsLo >>> 8) & 0xff; p[9] = tsLo & 0xff;
83
+ p[10] = 0x03; p[11] = 0xe9;
84
+ // xorKeyVerify = xxHash32([mask])
85
+ const verify = xxHash32(Buffer.from([mask]));
86
+ p[12] = (verify >>> 24) & 0xff; p[13] = (verify >>> 16) & 0xff; p[14] = (verify >>> 8) & 0xff; p[15] = verify & 0xff;
87
+ // Body: copy fingerprint template + write dynamic fields, then XOR with mask
88
+ const bodyUnmasked = Buffer.from(fingerprint);
89
+ // Fingerprint template must already have 0x03ea at [0] (Uuid) or 0x03eb at [0] (RequestHash).
90
+ // Locate RequestHash slot (opcode 0x03eb) and write current URL+body hash.
91
+ // Search for 03eb marker in template.
92
+ let reqHashOff = -1;
93
+ for (let i = 0; i < bodyUnmasked.length - 5; i++) {
94
+ if (bodyUnmasked[i] === 0x03 && bodyUnmasked[i+1] === 0xeb) { reqHashOff = i + 2; break; }
95
+ }
96
+ if (reqHashOff >= 0) {
97
+ const reqHash = xxHash32(Buffer.from(url + body));
98
+ bodyUnmasked[reqHashOff] = (reqHash >>> 24) & 0xff;
99
+ bodyUnmasked[reqHashOff+1] = (reqHash >>> 16) & 0xff;
100
+ bodyUnmasked[reqHashOff+2] = (reqHash >>> 8) & 0xff;
101
+ bodyUnmasked[reqHashOff+3] = reqHash & 0xff;
102
+ }
103
+ // XOR-mask the body
104
+ for (let i = 0; i < bodyUnmasked.length; i++) p[16 + i] = bodyUnmasked[i] ^ mask;
105
+ return p;
106
+ }
107
+
108
+ /** Full rap generator.
109
+ * Optional inputs for byte-exact reproduction: mtime, padBytes, bodyEncryTime.
110
+ */
111
+ function buildRapPure({ api, data, fingerprint, mask, xorKey, ts, nonce, mtime, padBytes, bodyEncryTime, returnLayers }) {
112
+ api = api || '/api/sns/web/v1/homefeed';
113
+ data = typeof data === 'string' ? data : JSON.stringify(data || {});
114
+ ts = ts || Date.now();
115
+ // URL form used by the browser rap for RequestHash.
116
+ // 2026-07-25 实证(Creator post_note 模板 03eb 槽反解):
117
+ // 浏览器对发布接口用**完整 https URL** 求 xxHash32(url+body);
118
+ // 传完整 URL 时按原样使用,传路径时退回协议相对形式(兼容 PC 旧调用)。
119
+ let url;
120
+ if (/^https?:\/\//.test(api)) url = api;
121
+ else if (api.startsWith('//')) url = api;
122
+ else url = '//edith.xiaohongshu.com' + api;
123
+
124
+ // mask: a random byte mapped to alphabet36 ASCII (like '0'..'9','a'..'z')
125
+ if (mask == null) mask = ALPHABET36.charCodeAt(crypto.randomBytes(1)[0] % 36);
126
+
127
+ // xorKey: 16 ASCII chars from alphabet36
128
+ if (!xorKey) xorKey = Buffer.from(randomAscii(16));
129
+ else if (typeof xorKey === 'string') xorKey = Buffer.from(xorKey);
130
+
131
+ // Envelope nonce: 4-6 ASCII chars — magic byte determines length. Accept override.
132
+ let nonceLen;
133
+ if (nonce != null) {
134
+ if (typeof nonce === 'string') nonce = Buffer.from(nonce, 'ascii');
135
+ nonceLen = nonce.length;
136
+ } else {
137
+ nonceLen = 4 + Math.floor(crypto.randomBytes(1)[0] / 86); // 4/5/6 roughly balanced
138
+ nonce = Buffer.from(randomAscii(nonceLen), 'ascii');
139
+ }
140
+
141
+ // Build the plaintext payload (single canonical build; RequestHash uses url + data)
142
+ const p2 = buildPayload({ ts, url, body: data, fingerprint, mask });
143
+
144
+ // Encrypt via rap_crypto (DEFLATE + alphabet36 pad)
145
+ const encryptStart = Date.now();
146
+ const { encR, body: bodyEnc, gzipLen } = encryptPayload(Buffer.from(p2), Array.from(xorKey), { mtime: mtime || 0, padBytes });
147
+ const encryptTimeMs = bodyEncryTime != null ? bodyEncryTime : Math.max(1, Date.now() - encryptStart);
148
+
149
+ const B = bodyEnc.length;
150
+ const env = Buffer.alloc(60 + nonceLen + B);
151
+ env[0] = 0x07; env[1] = 0x24; env[2] = 0x01; env[3] = nonceLen;
152
+ env.writeUInt32BE(1, 4); // ver (protocolCryptVersion)
153
+ env.writeUInt32BE(20, 8); // keyLength (encR + encRlen)
154
+ env.writeUInt32BE(B + 4, 12); // payloadLen (bodyLen + 4)
155
+ // env[16:20] = chk16 (bodyhash) — computed below after body written
156
+ env.writeUInt32BE(0x0000283d, 20); // Version (0x283d = fingerprint version)
157
+ env.writeUInt32BE(encryptTimeMs, 24); // bodyEncryTime (elapsed ms)
158
+ // env[28:36] = 0 (already zero from Buffer.alloc) — note: header prototype writes 512 at [28:32] but final envelope has 0
159
+ nonce.copy(env, 36);
160
+ Buffer.from(encR).copy(env, 36 + nonceLen);
161
+ env.writeUInt32BE(16, 52 + nonceLen);
162
+ Buffer.from(bodyEnc).copy(env, 56 + nonceLen);
163
+ env.writeUInt32BE(gzipLen, 56 + nonceLen + B); // trailing = actual gzip length (NOT encrypted body length)
164
+ // Now compute chk16 = xxHash32(env[36:], seed=0) — hash of everything after the 36B header
165
+ const chk16 = xxHash32(env.slice(36), 0);
166
+ env.writeUInt32BE(chk16, 16);
167
+
168
+ const rap = env.toString('base64');
169
+ if (returnLayers) return { rap, plaintext: Buffer.from(p2), encR: Buffer.from(encR), body: Buffer.from(bodyEnc), envelope: env, mask, xorKey, nonce };
170
+ return rap;
171
+ }
172
+
173
+ module.exports = { buildRapPure, xxHash32, randomAscii, ALPHABET36 };
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+ // CLI: node rap_cli.js <api> [dataJsonString] [appId] [fingerprintHex]
3
+ // 输出 100% 纯算 x-rap-param(ByQ...)到 stdout。
4
+ // 零 VMP 执行、零浏览器补环境、零历史 bundle 加载。
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const { buildRapPure } = require('./rap.js');
8
+
9
+ const api = process.argv[2];
10
+ const data = process.argv[3] || '';
11
+ const appId = process.argv[4] || undefined;
12
+ const fingerprintHex = process.argv[5] || '';
13
+ if (!api) { process.stderr.write('usage: rap_cli.js <api> [data] [appId]\n'); process.exit(2); }
14
+
15
+ // Fingerprint template: 219-byte captured behavior/detection blob (session-safe reuse)
16
+ const TPL_PATH = path.join(__dirname, 'rap_fingerprint_template.json');
17
+ const tpl = JSON.parse(fs.readFileSync(TPL_PATH, 'utf8'));
18
+ const fingerprint = Buffer.from(fingerprintHex || tpl.bodyUnmaskedHex, 'hex');
19
+ if (!fingerprint.length) {
20
+ process.stderr.write('rap gen failed: fingerprint is empty\n');
21
+ process.exit(2);
22
+ }
23
+
24
+ try {
25
+ const rap = buildRapPure({ api, data, fingerprint });
26
+ process.stdout.write(rap);
27
+ } catch (e) {
28
+ process.stderr.write('rap gen failed: ' + (e && e.message) + '\n');
29
+ process.exit(1);
30
+ }