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.
- package/LICENSE +21 -0
- package/README.en.md +177 -0
- package/README.ja.md +177 -0
- package/README.ko.md +177 -0
- package/README.md +174 -2
- package/README.zh-TW.md +177 -0
- package/assets/banner.txt +3 -0
- package/dist/cli/argv.js +98 -0
- package/dist/cli/commands/auth.js +11 -0
- package/dist/cli/commands/config.js +23 -0
- package/dist/cli/commands/doctor.js +134 -0
- package/dist/cli/commands/platforms.js +24 -0
- package/dist/cli/commands/version.js +4 -0
- package/dist/cli/confirm.js +22 -0
- package/dist/cli/dispatch.js +440 -0
- package/dist/cli/help.js +223 -0
- package/dist/cli/hints.js +60 -0
- package/dist/cli/main.js +18 -0
- package/dist/cli/output.js +66 -0
- package/dist/core/auth-handlers.js +41 -0
- package/dist/core/auth-store.js +119 -0
- package/dist/core/capabilities.js +163 -0
- package/dist/core/config.js +121 -0
- package/dist/core/cookies.js +164 -0
- package/dist/core/errors.js +37 -0
- package/dist/core/files.js +89 -0
- package/dist/core/fsutil.js +52 -0
- package/dist/core/hash.js +4 -0
- package/dist/core/http.js +331 -0
- package/dist/core/image.js +280 -0
- package/dist/core/log.js +40 -0
- package/dist/core/login.js +191 -0
- package/dist/core/mp4.js +130 -0
- package/dist/core/normalize.js +207 -0
- package/dist/core/options.js +110 -0
- package/dist/core/paths.js +38 -0
- package/dist/core/pb.js +59 -0
- package/dist/core/py.js +181 -0
- package/dist/core/rand.js +131 -0
- package/dist/core/registry.js +129 -0
- package/dist/core/schemas.js +41 -0
- package/dist/core/stream.js +104 -0
- package/dist/core/toolkit.js +35 -0
- package/dist/core/vm.js +73 -0
- package/dist/core/vocab.js +157 -0
- package/dist/platforms/_shared/impaas.js +410 -0
- package/dist/platforms/bilibili/index.js +216 -0
- package/dist/platforms/bilibili/web/api.js +806 -0
- package/dist/platforms/bilibili/web/client.js +311 -0
- package/dist/platforms/bilibili/web/commands.js +783 -0
- package/dist/platforms/bilibili/web/ddddocr.js +342 -0
- package/dist/platforms/bilibili/web/gaia.js +130 -0
- package/dist/platforms/bilibili/web/geetest-manual.js +154 -0
- package/dist/platforms/bilibili/web/geetest-vision.js +232 -0
- package/dist/platforms/bilibili/web/geetest.js +450 -0
- package/dist/platforms/bilibili/web/normalize.js +234 -0
- package/dist/platforms/bilibili/web/profile.js +135 -0
- package/dist/platforms/bilibili/web/resolve.js +112 -0
- package/dist/platforms/bilibili/web/sign.js +223 -0
- package/dist/platforms/bilibili/web/upos.js +102 -0
- package/dist/platforms/douyin/index.js +142 -0
- package/dist/platforms/douyin/web/api.js +863 -0
- package/dist/platforms/douyin/web/client.js +413 -0
- package/dist/platforms/douyin/web/commands.js +712 -0
- package/dist/platforms/douyin/web/creator.js +601 -0
- package/dist/platforms/douyin/web/crypto.js +247 -0
- package/dist/platforms/douyin/web/dtrait.js +190 -0
- package/dist/platforms/douyin/web/im.js +502 -0
- package/dist/platforms/douyin/web/image.js +5 -0
- package/dist/platforms/douyin/web/live.js +225 -0
- package/dist/platforms/douyin/web/mssdk.js +148 -0
- package/dist/platforms/douyin/web/normalize.js +326 -0
- package/dist/platforms/douyin/web/passport.js +1247 -0
- package/dist/platforms/douyin/web/profile.js +221 -0
- package/dist/platforms/douyin/web/proto.js +26 -0
- package/dist/platforms/douyin/web/resolve.js +88 -0
- package/dist/platforms/douyin/web/sign.js +436 -0
- package/dist/platforms/douyin/web/tos.js +78 -0
- package/dist/platforms/index.js +17 -0
- package/dist/platforms/jd/index.js +67 -0
- package/dist/platforms/jd/web/aks.js +115 -0
- package/dist/platforms/jd/web/api.js +549 -0
- package/dist/platforms/jd/web/chat.js +132 -0
- package/dist/platforms/jd/web/client.js +470 -0
- package/dist/platforms/jd/web/commands.js +557 -0
- package/dist/platforms/jd/web/jcap/helper.js +90 -0
- package/dist/platforms/jd/web/jcap/host.js +107 -0
- package/dist/platforms/jd/web/jcap/lsd.js +300 -0
- package/dist/platforms/jd/web/jcap/solver.js +995 -0
- package/dist/platforms/jd/web/jcap.js +150 -0
- package/dist/platforms/jd/web/js.js +420 -0
- package/dist/platforms/jd/web/login.js +775 -0
- package/dist/platforms/jd/web/normalize.js +297 -0
- package/dist/platforms/jd/web/profile.js +256 -0
- package/dist/platforms/jd/web/util.js +175 -0
- package/dist/platforms/jd/web/webm.js +133 -0
- package/dist/platforms/kuaishou/index.js +98 -0
- package/dist/platforms/kuaishou/web/api.js +694 -0
- package/dist/platforms/kuaishou/web/captcha-crypto.js +134 -0
- package/dist/platforms/kuaishou/web/captcha-fp.js +180 -0
- package/dist/platforms/kuaishou/web/captcha.js +273 -0
- package/dist/platforms/kuaishou/web/client.js +487 -0
- package/dist/platforms/kuaishou/web/commands.js +863 -0
- package/dist/platforms/kuaishou/web/gap.js +144 -0
- package/dist/platforms/kuaishou/web/gdfp.js +376 -0
- package/dist/platforms/kuaishou/web/gql.js +11 -0
- package/dist/platforms/kuaishou/web/live.js +125 -0
- package/dist/platforms/kuaishou/web/normalize.js +241 -0
- package/dist/platforms/kuaishou/web/oracle.js +329 -0
- package/dist/platforms/kuaishou/web/profile.js +259 -0
- package/dist/platforms/kuaishou/web/resolve.js +84 -0
- package/dist/platforms/kuaishou/web/session.js +592 -0
- package/dist/platforms/kuaishou/web/sign.js +568 -0
- package/dist/platforms/taobao/index.js +56 -0
- package/dist/platforms/taobao/web/api.js +96 -0
- package/dist/platforms/taobao/web/client.js +102 -0
- package/dist/platforms/taobao/web/commands.js +172 -0
- package/dist/platforms/taobao/web/im.js +68 -0
- package/dist/platforms/taobao/web/normalize.js +103 -0
- package/dist/platforms/taobao/web/profile.js +85 -0
- package/dist/platforms/taobao/web/resolve.js +51 -0
- package/dist/platforms/taobao/web/sign.js +35 -0
- package/dist/platforms/tiktok/index.js +120 -0
- package/dist/platforms/tiktok/web/api.js +1234 -0
- package/dist/platforms/tiktok/web/client.js +362 -0
- package/dist/platforms/tiktok/web/commands.js +821 -0
- package/dist/platforms/tiktok/web/im.js +133 -0
- package/dist/platforms/tiktok/web/jsrun.js +126 -0
- package/dist/platforms/tiktok/web/normalize.js +286 -0
- package/dist/platforms/tiktok/web/profile.js +169 -0
- package/dist/platforms/tiktok/web/resolve.js +156 -0
- package/dist/platforms/tiktok/web/sign.js +585 -0
- package/dist/platforms/tiktok/web/upload.js +805 -0
- package/dist/platforms/tiktok/web/wire.js +490 -0
- package/dist/platforms/weibo/index.js +91 -0
- package/dist/platforms/weibo/web/api.js +206 -0
- package/dist/platforms/weibo/web/client.js +155 -0
- package/dist/platforms/weibo/web/commands.js +202 -0
- package/dist/platforms/weibo/web/normalize.js +137 -0
- package/dist/platforms/weibo/web/profile.js +246 -0
- package/dist/platforms/weibo/web/resolve.js +60 -0
- package/dist/platforms/weibo/web/sign.js +85 -0
- package/dist/platforms/x/index.js +107 -0
- package/dist/platforms/x/web/api.js +443 -0
- package/dist/platforms/x/web/article.js +212 -0
- package/dist/platforms/x/web/client.js +202 -0
- package/dist/platforms/x/web/commands.js +363 -0
- package/dist/platforms/x/web/graphql.js +108 -0
- package/dist/platforms/x/web/normalize.js +235 -0
- package/dist/platforms/x/web/profile.js +99 -0
- package/dist/platforms/x/web/resolve.js +44 -0
- package/dist/platforms/x/web/transaction.js +171 -0
- package/dist/platforms/xhs/index.js +101 -0
- package/dist/platforms/xhs/web/api.js +278 -0
- package/dist/platforms/xhs/web/client.js +540 -0
- package/dist/platforms/xhs/web/commands.js +1046 -0
- package/dist/platforms/xhs/web/creator-api.js +609 -0
- package/dist/platforms/xhs/web/creator.js +203 -0
- package/dist/platforms/xhs/web/js.js +194 -0
- package/dist/platforms/xhs/web/login.js +230 -0
- package/dist/platforms/xhs/web/normalize.js +166 -0
- package/dist/platforms/xhs/web/profile.js +127 -0
- package/dist/platforms/xhs/web/push.js +361 -0
- package/dist/platforms/xhs/web/resolve.js +69 -0
- package/dist/platforms/xhs/web/state.js +368 -0
- package/dist/platforms/xianyu/index.js +59 -0
- package/dist/platforms/xianyu/web/api.js +312 -0
- package/dist/platforms/xianyu/web/client.js +134 -0
- package/dist/platforms/xianyu/web/commands.js +305 -0
- package/dist/platforms/xianyu/web/im.js +73 -0
- package/dist/platforms/xianyu/web/normalize.js +123 -0
- package/dist/platforms/xianyu/web/profile.js +146 -0
- package/dist/platforms/xianyu/web/resolve.js +48 -0
- package/dist/platforms/xianyu/web/sign.js +79 -0
- package/package.json +77 -4
- package/static/douyin/Live.proto +194 -0
- package/static/douyin/Request.proto +89 -0
- package/static/douyin/Response.proto +46 -0
- package/static/douyin/acrawler_runtime/ac_vm.js +2 -0
- package/static/douyin/acrawler_runtime/browser_window_shape.json +1 -0
- package/static/douyin/acrawler_runtime/canvas_actual_exact.json +1 -0
- package/static/douyin/acrawler_runtime/run_ac_node.js +1005 -0
- package/static/douyin/acrawler_runtime/runner_window_keys_baseline.txt +240 -0
- package/static/douyin/challenge_profile.json +801 -0
- package/static/douyin/challenge_template_runner.js +217 -0
- package/static/douyin/dtrait_profile.json +66 -0
- package/static/douyin/mstoken_common_profile.json +1 -0
- package/static/douyin/mstoken_profile.json +214 -0
- package/static/jd/h5st5_env.js +750 -0
- package/static/jd/h5st5_lib.js +5 -0
- package/static/jd/h5st5_server.js +49 -0
- package/static/jd/jcap/env/env_core.js +512 -0
- package/static/jd/jcap/env/run.js +1307 -0
- package/static/jd/jcap/run/jcap_ujb96b.js +39 -0
- package/static/jd/package.json +4 -0
- package/static/jd/pc_tk_lib.js +1 -0
- package/static/jd/pc_tk_server.js +63 -0
- package/static/jd/summer_cryptico_runner.js +50 -0
- package/static/jd/webm/env/run.js +453 -0
- package/static/jd/webm/run/jdwebm-riskhandle.js +49 -0
- package/static/kuaishou/bundles/weapon/kwf-0.0.2.2cee19b4b7dec496.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-0-0.0.1-obfuscated.4b7f3de1ab0ddcc2.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-1-0.0.1-obfuscated.346ebdb1c78df316.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-10-0.0.1-obfuscated.a33db41897b983af.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-11-0.0.1-obfuscated.5e0a90af726d8a7e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-12-0.0.1-obfuscated.20ea29345706ec8c.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-13-0.0.1-obfuscated.6b74e9640ff18648.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-14-0.0.1-obfuscated.551e4429d156bf66.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-15-0.0.1-obfuscated.8625b087aaba4a60.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-16-0.0.1-obfuscated.d5ffa4020f3be99a.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-17-0.0.1-obfuscated.6d13e19b22f50a80.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-18-0.0.1-obfuscated.6d31405044166289.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-19-0.0.1-obfuscated.a9a8d925942f19e4.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-2-0.0.1-obfuscated.b849a136ae4a6b3e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-3-0.0.1-obfuscated.f9e77ebf6186e2ac.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-4-0.0.1-obfuscated.2c3c155aa52ceb0e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-5-0.0.1-obfuscated.4e553d17691b3c5a.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-6-0.0.1-obfuscated.f73ddd3ff13e1217.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-7-0.0.1-obfuscated.1628b8729beaab7e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-8-0.0.1-obfuscated.6da6baef7aa81fff.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-9-0.0.1-obfuscated.8645d55e5323b743.js +1 -0
- package/static/kuaishou/fixtures/current_follow_script_813.network-response +2 -0
- package/static/kuaishou/fixtures/live_ws_maps.json +61 -0
- package/static/kuaishou/fixtures/live_ws_proto.json +1342 -0
- package/static/kuaishou/js/cp-kwf.js +1 -0
- package/static/kuaishou/tools/kwf_oracle.js +799 -0
- package/static/kuaishou/tools/kws_oracle.js +885 -0
- package/static/kuaishou/tools/like_token_oracle.js +70 -0
- package/static/taobao/taobao_js_20260407.js +570 -0
- package/static/tiktok/Tiktok_Request.json +597 -0
- package/static/tiktok/package.json +3 -0
- package/static/tiktok/shop_bsid/core.js +1 -0
- package/static/tiktok/shop_bsid/env_core.js +512 -0
- package/static/tiktok/shop_bsid/loader.js +1 -0
- package/static/tiktok/shop_bsid/run.js +753 -0
- package/static/tiktok/shop_bsid/sign.js +100 -0
- package/static/tiktok/shop_bsid/webmssdk.js +1 -0
- package/static/tiktok/shop_bsid/webmssdk_ex.js +1 -0
- package/static/tiktok/signing/env/env_core.js +512 -0
- package/static/tiktok/signing/env/sign.js +455 -0
- package/static/tiktok/signing/sdk_config.json +230 -0
- package/static/tiktok/signing/source/webmssdk.js +1 -0
- package/static/tiktok/signing/source/webmssdk_ex.js +1 -0
- package/static/tiktok/signing/source/webmssdk_legacy.js +1 -0
- package/static/x/graphql.json +7777 -0
- package/static/x/transaction_l1.json +12 -0
- package/static/xhs/package.json +3 -0
- package/static/xhs/xhs_core/js/b1.js +210 -0
- package/static/xhs/xhs_core/js/mns.js +340 -0
- package/static/xhs/xhs_core/js/mns_0101_keystream.json +5 -0
- package/static/xhs/xhs_core/js/mns_keystreams.json +1 -0
- package/static/xhs/xhs_core/js/sign.js +225 -0
- package/static/xhs/xhs_core/js/websectiga_cli.js +220 -0
- package/static/xhs/xhs_core/js/websectiga_env.js +512 -0
- package/static/xhs/xhs_core/js/xs_common.js +79 -0
- package/static/xhs/xhs_creator/js/profile.js +115 -0
- package/static/xhs/xhs_creator/js/rap_fingerprint_creator.json +6 -0
- package/static/xhs/xhs_creator/js/reference_profile.json +282 -0
- package/static/xhs/xhs_creator/js/xhs_creator_sign.js +29 -0
- package/static/xhs/xhs_creator/js/xhs_creator_signature.js +23 -0
- package/static/xhs/xhs_pc/js/aes_decrypt.js +22 -0
- package/static/xhs/xhs_pc/js/aes_encrypt.js +30 -0
- package/static/xhs/xhs_pc/js/b1.js +9 -0
- package/static/xhs/xhs_pc/js/deflate.js +320 -0
- package/static/xhs/xhs_pc/js/mns.js +4 -0
- package/static/xhs/xhs_pc/js/profile.js +141 -0
- package/static/xhs/xhs_pc/js/rap.js +173 -0
- package/static/xhs/xhs_pc/js/rap_cli.js +30 -0
- package/static/xhs/xhs_pc/js/rap_crypto.js +73 -0
- package/static/xhs/xhs_pc/js/rap_fingerprint_template.json +8 -0
- package/static/xhs/xhs_pc/js/reference_profile.json +262 -0
- package/static/xhs/xhs_pc/js/sign.js +6 -0
- package/static/xhs/xhs_pc/js/web_ssk.js +82 -0
- package/static/xhs/xhs_pc/js/xs_common.js +4 -0
- package/static/xianyu/et_f.js +2 -0
- package/static/xianyu/gen_tfstk.js +410 -0
- package/static/xianyu/goofish_js_version_2.js +568 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import * as n from '../../../core/normalize.js';
|
|
2
|
+
import { pyStr } from '../../../core/py.js';
|
|
3
|
+
import * as rand from '../../../core/rand.js';
|
|
4
|
+
import { DEFAULT_AREA } from './profile.js';
|
|
5
|
+
/** 纯算工具(上游 utils/jd_util.py、utils/jd_cookie.py、utils/trace_headers.py、utils/aks.py 的编码部分)。 */
|
|
6
|
+
export { sha256Hex } from '../../../core/hash.js';
|
|
7
|
+
/** 商品页 URL 里的 SKU:PC、移动(item.m.jd.com/product/)、全球购(item.jd.hk、npcitem.jd.hk)。 */
|
|
8
|
+
export const ITEM_URL_SKU = /(?:item(?:\.m)?\.jd\.(?:com|hk)|npcitem\.jd\.hk)\/(?:product\/)?(\d{4,})\.html/;
|
|
9
|
+
/** 去掉搜索结果里的高亮标签。 */
|
|
10
|
+
export function stripTags(text) {
|
|
11
|
+
if (!text)
|
|
12
|
+
return '';
|
|
13
|
+
return String(text).replace(/<[^>]+>/g, '');
|
|
14
|
+
}
|
|
15
|
+
/** 仿浏览器的 jQuery jsonp 回调名(jQuery2905208)。 */
|
|
16
|
+
export function randomJqueryCallback() {
|
|
17
|
+
return `jQuery${rand.randint(1000000, 9999999)}`;
|
|
18
|
+
}
|
|
19
|
+
/** 咚咚 WS 的 `_wid_`:8-4-4-4-12 的随机十六进制。 */
|
|
20
|
+
export function generateWid() {
|
|
21
|
+
const seg = () => rand.randint(0x10000, 0x1ffff).toString(16).slice(1);
|
|
22
|
+
return `${seg()}${seg()}-${seg()}-${seg()}-${seg()}-${seg()}${seg()}${seg()}`;
|
|
23
|
+
}
|
|
24
|
+
/** 咚咚会话 ID(getSessionId)。 */
|
|
25
|
+
export function sessionId(pin, app, venderId) {
|
|
26
|
+
return `${pin.toLowerCase()}:${app}:${venderId}`;
|
|
27
|
+
}
|
|
28
|
+
/** 从 `ipLoc-djd` 取收货地区;full 时保留 `.addressId` 后缀(area_of)。 */
|
|
29
|
+
export function areaOf(cookies, full = false) {
|
|
30
|
+
const raw = (cookies.get('ipLoc-djd') ?? '').trim();
|
|
31
|
+
let area = raw ? raw.replaceAll('-', '_') : DEFAULT_AREA;
|
|
32
|
+
if (!full)
|
|
33
|
+
area = area.split('.')[0];
|
|
34
|
+
return area;
|
|
35
|
+
}
|
|
36
|
+
/** 搜索页用 `__jda` 的第二段作 uuid,缺省为 -1(search_uuid_of)。 */
|
|
37
|
+
export function searchUuidOf(cookies) {
|
|
38
|
+
const fields = String(cookies.get('__jda') ?? '').split('.');
|
|
39
|
+
if (fields.length > 1 && fields[1] !== '' && fields[1] !== '-')
|
|
40
|
+
return fields[1];
|
|
41
|
+
return '-1';
|
|
42
|
+
}
|
|
43
|
+
/** JS `encodeURIComponent`(jQuery.param 用的编码,aks.encode_component)。 */
|
|
44
|
+
export function encodeComponent(value) {
|
|
45
|
+
const s = value == null ? '' : pyStr(value);
|
|
46
|
+
let out = '';
|
|
47
|
+
for (const byte of Buffer.from(s, 'utf8')) {
|
|
48
|
+
const ch = String.fromCharCode(byte);
|
|
49
|
+
out += byte < 0x80 && /[A-Za-z0-9\-_.!~*'()]/.test(ch) ? ch : '%' + byte.toString(16).toUpperCase().padStart(2, '0');
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
/** 按 jQuery 的方式序列化有序键值对(aks.encode_query_pairs)。 */
|
|
54
|
+
export function encodeQueryPairs(pairs) {
|
|
55
|
+
return pairs.map(([k, v]) => `${encodeComponent(k)}=${encodeComponent(v)}`).join('&');
|
|
56
|
+
}
|
|
57
|
+
// ---------------------------------------------------------------- 埋点 cookie 冷启动(utils/jd_cookie.py)
|
|
58
|
+
/** 各站点的埋点 siteId。 */
|
|
59
|
+
export const SITE_IDS = {
|
|
60
|
+
www: '122270672',
|
|
61
|
+
search: '143920055',
|
|
62
|
+
item: '122270672',
|
|
63
|
+
passport: '95931165',
|
|
64
|
+
chat: '23334881',
|
|
65
|
+
};
|
|
66
|
+
/** `__jdu` = 13 位毫秒时间戳 + 10 位随机数字。 */
|
|
67
|
+
export function generateJdu() {
|
|
68
|
+
return `${rand.now()}${rand.randint(10 ** 9, 10 ** 10 - 1)}`;
|
|
69
|
+
}
|
|
70
|
+
/** `__jdu` / `__jdc` / `__jda` / `__jdb` / `__jdv` 一组埋点 cookie。 */
|
|
71
|
+
export function trackingCookies(site = 'search', jdu, source = 'direct', visitCount = 1, pv = 1) {
|
|
72
|
+
const siteId = SITE_IDS[site] ?? SITE_IDS.www;
|
|
73
|
+
const u = jdu || generateJdu();
|
|
74
|
+
const ms = rand.now();
|
|
75
|
+
const sec = Math.floor(ms / 1000);
|
|
76
|
+
return [
|
|
77
|
+
['__jdu', u],
|
|
78
|
+
['__jdc', siteId],
|
|
79
|
+
['__jda', `${siteId}.${u}.${sec}.${sec}.${sec}.${visitCount}`],
|
|
80
|
+
['__jdb', `${siteId}.${pv}.${u}|${visitCount}.${sec}`],
|
|
81
|
+
['__jdv', `${siteId}|${source}|-|direct|-|${ms}`],
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
/** 给空会话灌冷启动 cookie(bootstrap):埋点 cookie + 收货地区。 */
|
|
85
|
+
export function bootstrapCookies(site = 'search', area) {
|
|
86
|
+
const a = area || DEFAULT_AREA.replaceAll('_', '-');
|
|
87
|
+
return [...trackingCookies(site), ['areaId', a.split('-')[0]], ['ipLoc-djd', a]];
|
|
88
|
+
}
|
|
89
|
+
// ---------------------------------------------------------------- 登录页 SGM / JDAS 链路头(utils/trace_headers.py)
|
|
90
|
+
/** 取 `secrets.randbits(k)`:与对拍框架的 getrandbits 一致,每 32 位一个随机数。 */
|
|
91
|
+
function randbits(k) {
|
|
92
|
+
let out = 0n;
|
|
93
|
+
let shift = 0n;
|
|
94
|
+
while (k > 0) {
|
|
95
|
+
const bits = Math.min(k, 32);
|
|
96
|
+
out |= BigInt(Math.floor(rand.random() * 2 ** bits)) << shift;
|
|
97
|
+
shift += BigInt(bits);
|
|
98
|
+
k -= bits;
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
/** RFC 9562 UUIDv7。 */
|
|
103
|
+
export function uuid7() {
|
|
104
|
+
const ts = BigInt(rand.now()) & ((1n << 48n) - 1n);
|
|
105
|
+
const randA = randbits(12);
|
|
106
|
+
const randB = randbits(62);
|
|
107
|
+
let v = (ts << 80n) | (0x7n << 76n) | (randA << 64n);
|
|
108
|
+
v |= (2n << 62n) | randB;
|
|
109
|
+
const h = v.toString(16).padStart(32, '0');
|
|
110
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
|
|
111
|
+
}
|
|
112
|
+
/** sgm-web 3.3.0 的数字 key。 */
|
|
113
|
+
function sgmKey() {
|
|
114
|
+
let out = '';
|
|
115
|
+
for (const ch of '1000-1000-4000-8000-1000000') {
|
|
116
|
+
if (!'018'.includes(ch)) {
|
|
117
|
+
out += ch;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const digit = Number(ch);
|
|
121
|
+
const shift = Math.trunc(digit / 4);
|
|
122
|
+
const part = Number(randbits(8)) & (15 >> shift);
|
|
123
|
+
out += String(digit ^ part);
|
|
124
|
+
}
|
|
125
|
+
const value = out.replaceAll('-', '').slice(0, 18);
|
|
126
|
+
return BigInt(value || '0').toString();
|
|
127
|
+
}
|
|
128
|
+
/** 登录页的 `#uuid` 取作 JDAS-Page-Id。 */
|
|
129
|
+
export function pageIdFromHtml(html) {
|
|
130
|
+
const inputs = html.match(/<input\b[^>]*>/gi) ?? [];
|
|
131
|
+
for (const tag of inputs) {
|
|
132
|
+
const attrs = parseAttrs(tag);
|
|
133
|
+
if (attrs.id === 'uuid' && attrs.value)
|
|
134
|
+
return attrs.value.trim();
|
|
135
|
+
}
|
|
136
|
+
throw new Error('登录页 HTML 缺少 #uuid,无法生成 JDAS-Page-Id');
|
|
137
|
+
}
|
|
138
|
+
/** 一个 input 标签的属性(小写名 → 值,实体已解码)。 */
|
|
139
|
+
export function parseAttrs(tag) {
|
|
140
|
+
const out = {};
|
|
141
|
+
const re = /([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
142
|
+
const body = tag.replace(/^<\s*[a-zA-Z0-9]+/, '').replace(/\/?>$/, '');
|
|
143
|
+
let m;
|
|
144
|
+
while ((m = re.exec(body))) {
|
|
145
|
+
const name = m[1].toLowerCase();
|
|
146
|
+
if (name in out)
|
|
147
|
+
continue;
|
|
148
|
+
out[name] = n.unescapeHtml(m[2] ?? m[3] ?? m[4] ?? '');
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
/** 同一登录页内的链路上下文:Page-Id 取自页面,Session-Id 页面内复用(LoginTraceContext)。 */
|
|
153
|
+
export class TraceContext {
|
|
154
|
+
pageId;
|
|
155
|
+
sessionId;
|
|
156
|
+
constructor(pageId, sessionId = (() => {
|
|
157
|
+
const g = uuid7();
|
|
158
|
+
return g.slice(0, -4) + '0001';
|
|
159
|
+
})()) {
|
|
160
|
+
this.pageId = pageId;
|
|
161
|
+
this.sessionId = sessionId;
|
|
162
|
+
}
|
|
163
|
+
static fromHtml(html) {
|
|
164
|
+
return new TraceContext(pageIdFromHtml(html));
|
|
165
|
+
}
|
|
166
|
+
nextHeaders() {
|
|
167
|
+
const key = sgmKey();
|
|
168
|
+
return {
|
|
169
|
+
'sgm-context': `${key};${key}`,
|
|
170
|
+
'jdas-trace-id': uuid7(),
|
|
171
|
+
'jdas-page-id': this.pageId,
|
|
172
|
+
'jdas-session-id': this.sessionId,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import * as realFs from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import vm from 'node:vm';
|
|
5
|
+
import { SpecificAIError } from '../../../core/errors.js';
|
|
6
|
+
import { staticFile } from '../../../core/paths.js';
|
|
7
|
+
import { DEFAULT_NOW, DEFAULT_SEED, isDeterministic, now } from '../../../core/rand.js';
|
|
8
|
+
import { createContext } from '../../../core/vm.js';
|
|
9
|
+
import { PROFILE } from './profile.js';
|
|
10
|
+
/**
|
|
11
|
+
* 京东 PC WebM 指纹(utils/webm.py + static/webm/env/run.js):原版 jdwebm.js 跑在 jsdom 里,产出 wsgw_getinfo 的正文。
|
|
12
|
+
* run.js 本是命令行脚本(读 stdin、写 stdout),这里放进 vm context,stdin / stdout 换成内存。
|
|
13
|
+
*/
|
|
14
|
+
const hostRequire = createRequire(import.meta.url);
|
|
15
|
+
const RESULT_PREFIX = '__WEBM_RESULT__';
|
|
16
|
+
/** jsdom window realm 的确定性(对拍时与 scripts/golden/jd/preload.cjs 相同)。 */
|
|
17
|
+
const REALM_JS = `(() => {
|
|
18
|
+
let a = __specific_ai_seed >>> 0
|
|
19
|
+
Math.random = () => { a = (a + 0x6d2b79f5) >>> 0; let t = Math.imul(a ^ (a >>> 15), a | 1); t = (t + Math.imul(t ^ (t >>> 7), t | 61)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296 }
|
|
20
|
+
const D = Date
|
|
21
|
+
globalThis.Date = class extends D { constructor(...x) { super(...(x.length ? x : [__specific_ai_now])) } static now() { return __specific_ai_now } }
|
|
22
|
+
try { const fill = (arr) => { const span = 2 ** (8 * arr.BYTES_PER_ELEMENT); for (let i = 0; i < arr.length; i++) arr[i] = Math.floor(Math.random() * span); return arr }; Object.defineProperty(crypto, 'getRandomValues', { value: fill, configurable: true, writable: true }) } catch (e) {}
|
|
23
|
+
try { const t0 = __specific_ai_now; Object.defineProperty(performance, 'now', { value: () => 0, configurable: true, writable: true }); Object.defineProperty(performance, 'timeOrigin', { value: t0, configurable: true }) } catch (e) {}
|
|
24
|
+
})()`;
|
|
25
|
+
/** 给脚本的 jsdom:确定性模式下,每个新 window 的 realm 固定随机数与时钟。 */
|
|
26
|
+
export function deterministicJsdom() {
|
|
27
|
+
const m = hostRequire('jsdom');
|
|
28
|
+
if (!isDeterministic())
|
|
29
|
+
return m;
|
|
30
|
+
const Base = m.JSDOM;
|
|
31
|
+
class JSDOM extends Base {
|
|
32
|
+
constructor(...args) {
|
|
33
|
+
super(...args);
|
|
34
|
+
try {
|
|
35
|
+
const ctx = this.getInternalVMContext();
|
|
36
|
+
ctx.__specific_ai_seed = DEFAULT_SEED;
|
|
37
|
+
ctx.__specific_ai_now = now() || DEFAULT_NOW;
|
|
38
|
+
vm.runInContext(REALM_JS, ctx);
|
|
39
|
+
}
|
|
40
|
+
catch { }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return Object.assign(Object.create(m), { JSDOM });
|
|
44
|
+
}
|
|
45
|
+
/** 在 vm 里跑一个命令行脚本:stdin 给定,stdout 收集,脚本调用 process.exit 或写出结果行后结束。 */
|
|
46
|
+
export async function runCliScript(file, input, options) {
|
|
47
|
+
const dir = join(file, '..');
|
|
48
|
+
let out = '';
|
|
49
|
+
let resolveDone;
|
|
50
|
+
const done = new Promise((r) => (resolveDone = r));
|
|
51
|
+
const onChunk = (chunk) => {
|
|
52
|
+
out += String(chunk);
|
|
53
|
+
let i;
|
|
54
|
+
while ((i = out.indexOf('\n')) >= 0) {
|
|
55
|
+
const line = out.slice(0, i);
|
|
56
|
+
out = out.slice(i + 1);
|
|
57
|
+
if (line.startsWith(options.resultPrefix))
|
|
58
|
+
resolveDone(line.slice(options.resultPrefix.length));
|
|
59
|
+
else
|
|
60
|
+
options.log?.(line);
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
};
|
|
64
|
+
const fakeStdout = { write: onChunk, isTTY: false };
|
|
65
|
+
const fs = {
|
|
66
|
+
...realFs,
|
|
67
|
+
readFileSync: (p, ...rest) => (p === 0 ? input : realFs.readFileSync(p, ...rest)),
|
|
68
|
+
};
|
|
69
|
+
const modules = { fs, ...options.modules };
|
|
70
|
+
const fakeProcess = new Proxy(process, {
|
|
71
|
+
get: (t, k) => (k === 'stdout' ? fakeStdout : k === 'exit' ? () => resolveDone('') : k === 'env' ? {} : Reflect.get(t, k)),
|
|
72
|
+
set: (_t, k) => k === 'exitCode',
|
|
73
|
+
});
|
|
74
|
+
const moduleShim = {
|
|
75
|
+
createRequire: () => (name) => (name in modules ? modules[name] : hostRequire(name)),
|
|
76
|
+
};
|
|
77
|
+
const req = (name) => (name === 'module' ? moduleShim : name in modules ? modules[name] : hostRequire(name));
|
|
78
|
+
const quiet = (...a) => options.log?.(a.map(String).join(' '));
|
|
79
|
+
const context = createContext({
|
|
80
|
+
require: req,
|
|
81
|
+
process: fakeProcess,
|
|
82
|
+
__dirname: dir,
|
|
83
|
+
__filename: file,
|
|
84
|
+
module: { exports: {} },
|
|
85
|
+
exports: {},
|
|
86
|
+
console: { log: quiet, info: quiet, warn: quiet, error: quiet, debug: quiet },
|
|
87
|
+
Buffer,
|
|
88
|
+
});
|
|
89
|
+
context.global = context;
|
|
90
|
+
vm.runInContext(realFs.readFileSync(file, 'utf8'), context, { filename: file });
|
|
91
|
+
let timer;
|
|
92
|
+
const timeout = new Promise((_, reject) => {
|
|
93
|
+
timer = setTimeout(() => reject(new SpecificAIError('ERROR', `${file} 运行超时`)), options.timeout);
|
|
94
|
+
});
|
|
95
|
+
try {
|
|
96
|
+
return await Promise.race([done, timeout]);
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
clearTimeout(timer);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** 跑原版 jdwebm.js,返回完整的 wsgw_getinfo 正文,同时把它写的 cookie 和 localStorage 交回会话(build_search_payload)。 */
|
|
103
|
+
export async function buildSearchPayload(jd, pageUrl, configData = '', localStorage) {
|
|
104
|
+
const input = {
|
|
105
|
+
pageUrl: String(pageUrl),
|
|
106
|
+
userAgent: PROFILE.ua,
|
|
107
|
+
cookies: Object.fromEntries([...jd.cookies].filter(([, v]) => v != null && v !== '')),
|
|
108
|
+
localStorage: localStorage ?? jd.localStorageFor(pageUrl),
|
|
109
|
+
configData: String(configData ?? ''),
|
|
110
|
+
};
|
|
111
|
+
const marker = await runCliScript(staticFile('jd', 'webm/env/run.js'), JSON.stringify(input), {
|
|
112
|
+
modules: { jsdom: deterministicJsdom() },
|
|
113
|
+
resultPrefix: RESULT_PREFIX,
|
|
114
|
+
timeout: 30_000,
|
|
115
|
+
log: (line) => jd.ctx.log.debug(`[webm] ${line.slice(0, 300)}`),
|
|
116
|
+
});
|
|
117
|
+
if (!marker)
|
|
118
|
+
throw new SpecificAIError('ERROR', '纯程序 WebM 指纹进程异常:无结果标记');
|
|
119
|
+
let result;
|
|
120
|
+
try {
|
|
121
|
+
result = JSON.parse(marker);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
throw new SpecificAIError('ERROR', '纯程序 WebM 指纹返回格式异常');
|
|
125
|
+
}
|
|
126
|
+
if (!result.ok)
|
|
127
|
+
throw new SpecificAIError('ERROR', `纯程序 WebM 指纹生成失败:${String(result.errorMessage || result.error || '').slice(0, 240)}`);
|
|
128
|
+
jd.absorbScript(pageUrl, result);
|
|
129
|
+
const payload = result.payload;
|
|
130
|
+
if (!payload || typeof payload !== 'object' || typeof payload.body !== 'object' || !payload.body)
|
|
131
|
+
throw new SpecificAIError('ERROR', '纯程序 WebM 指纹缺少上报正文');
|
|
132
|
+
return payload;
|
|
133
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { filter, PRODUCT } from '../../core/options.js';
|
|
2
|
+
import { definePlatform, handlers } from '../../core/registry.js';
|
|
3
|
+
/** 懒加载 web 端的 handler:只有执行到这条命令时才加载实现。 */
|
|
4
|
+
const { h, impl } = handlers(() => import('./web/commands.js'));
|
|
5
|
+
export default definePlatform({
|
|
6
|
+
id: 'kuaishou',
|
|
7
|
+
name: '快手',
|
|
8
|
+
aliases: ['ks'],
|
|
9
|
+
item: '作品',
|
|
10
|
+
endpoints: {
|
|
11
|
+
web: {
|
|
12
|
+
login: { methods: ['qrcode', 'sms', 'cookie'], default: 'qrcode' },
|
|
13
|
+
// 直播页的退出登录:passport logout + userLogout
|
|
14
|
+
logout: h('serverLogout'),
|
|
15
|
+
commands: {
|
|
16
|
+
'auth login': impl('full', 'authLogin'),
|
|
17
|
+
'auth status': impl('full', 'authStatus'),
|
|
18
|
+
'user get': impl('full', 'userGet'),
|
|
19
|
+
'user search': impl('full', 'userSearch'),
|
|
20
|
+
'user items': impl('full', 'userItems'),
|
|
21
|
+
'user likes': impl('full', 'userLikes', { note: '只支持 me' }),
|
|
22
|
+
'user collects': impl('full', 'userCollects'),
|
|
23
|
+
'user followers': impl('full', 'userFollowers', { note: '只支持 me' }),
|
|
24
|
+
'user following': impl('full', 'userFollowing', { note: '只支持 me' }),
|
|
25
|
+
'user follow': 'none',
|
|
26
|
+
'user unfollow': 'none',
|
|
27
|
+
'item get': impl('full', 'itemGet'),
|
|
28
|
+
'item search': impl('partial', 'itemSearch', { note: '没有筛选(--sort / --type / --time),只按关键词搜' }),
|
|
29
|
+
'item related': impl('full', 'itemRelated'),
|
|
30
|
+
'item list': impl('full', 'itemList'),
|
|
31
|
+
'item media': impl('full', 'itemMedia'),
|
|
32
|
+
'item download': impl('full', 'itemDownload'),
|
|
33
|
+
'item like': 'none',
|
|
34
|
+
'item unlike': 'none',
|
|
35
|
+
'item collect': 'none',
|
|
36
|
+
'item uncollect': 'none',
|
|
37
|
+
'item publish': impl('partial', 'itemPublish', {
|
|
38
|
+
note: '不支持 --cover、--mention、--poi、--category、--price;--image 1~31 张(每张不超过 15MB),与 --video 二选一',
|
|
39
|
+
supports: ['title', 'text', 'image', 'video', 'tag', 'topic', 'visibility', 'schedule'],
|
|
40
|
+
}),
|
|
41
|
+
'item delete': 'none',
|
|
42
|
+
'product get': 'none',
|
|
43
|
+
'comment list': impl('partial', 'commentList', { note: '--product 规划中', options: { product: PRODUCT } }),
|
|
44
|
+
'comment replies': impl('partial', 'commentReplies', { note: '上游的二级评论接口只核对过一组抓包参数,其余评论的结果没有经过上游核对' }),
|
|
45
|
+
'comment add': 'none',
|
|
46
|
+
'comment delete': 'none',
|
|
47
|
+
'comment like': 'none',
|
|
48
|
+
'comment unlike': 'none',
|
|
49
|
+
// recommend 是 new-reco 推荐流(上游 get_feed_hot);快手没有单独的热门流
|
|
50
|
+
'feed list': impl('partial', 'feedList', {
|
|
51
|
+
note: 'following 只有首屏',
|
|
52
|
+
options: { kind: filter.kind('recommend', 'following') },
|
|
53
|
+
}),
|
|
54
|
+
'live get': impl('full', 'liveGet'),
|
|
55
|
+
'live list': impl('full', 'liveList'),
|
|
56
|
+
'live search': 'none',
|
|
57
|
+
'live categories': impl('full', 'liveCategories'),
|
|
58
|
+
'live listen': impl('full', 'liveListen'),
|
|
59
|
+
'live history': 'none',
|
|
60
|
+
// 上游没有发直播弹幕、直播间点赞的方法(README:“直播间主动发送弹幕尚未接入”)
|
|
61
|
+
'live send': 'none',
|
|
62
|
+
'live like': 'none',
|
|
63
|
+
'live rank': 'none',
|
|
64
|
+
'live gifts': impl('full', 'liveGifts'),
|
|
65
|
+
'live products': 'none',
|
|
66
|
+
'live media': 'none',
|
|
67
|
+
'live replays': impl('full', 'liveReplays'),
|
|
68
|
+
'live start': 'none',
|
|
69
|
+
'live stop': 'none',
|
|
70
|
+
'keyword suggest': 'none',
|
|
71
|
+
'keyword hot': 'none',
|
|
72
|
+
'notice list': 'none',
|
|
73
|
+
'notice count': impl('full', 'noticeCount'),
|
|
74
|
+
'msg list': 'none',
|
|
75
|
+
'msg history': 'none',
|
|
76
|
+
'msg send': 'none',
|
|
77
|
+
'msg listen': 'none',
|
|
78
|
+
'msg read': 'none',
|
|
79
|
+
'msg revoke': 'none',
|
|
80
|
+
'msg delete': 'none',
|
|
81
|
+
'media upload': impl('full', 'mediaUpload'),
|
|
82
|
+
'folder list': 'none',
|
|
83
|
+
'folder items': 'none',
|
|
84
|
+
'folder create': 'none',
|
|
85
|
+
'folder update': 'none',
|
|
86
|
+
'folder delete': 'none',
|
|
87
|
+
'series list': 'none',
|
|
88
|
+
'series items': 'none',
|
|
89
|
+
// 上游 get_history_list 已下线(服务端 404,上游直接拒绝发包)
|
|
90
|
+
'history list': 'none',
|
|
91
|
+
'topic search': 'none',
|
|
92
|
+
'poi search': 'none',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
app: 'planned',
|
|
96
|
+
pc: 'planned',
|
|
97
|
+
},
|
|
98
|
+
});
|