chainlesschain 0.162.25 → 0.162.26
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/package.json +2 -2
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-CAdsXHkz.js → AIOps-DEhhAYYj.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-JsLRBlBP.js → ActionButton-DEyfUz5F.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DeM96q71.js → Analytics-D1rjS61W.js} +1 -1
- package/src/assets/web-panel/assets/{AppLayout-eb_6mT6V.js → AppLayout-CptFJPy6.js} +2 -2
- package/src/assets/web-panel/assets/{Audit-BEOY-CQE.js → Audit-BG6KQUHY.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-irFD_DBa.js → Backup-D2ikQT54.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Dq7wtv_k.js → BaseInput-BG_d8yMt.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CstRKWdu.js → Chat-CcHmGkLY.js} +1 -1
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-3BBqk317.js → ChatBubbleRenderer-DIXrOHbn.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-A8D25IT5.js → Checkbox-Bx2h6vT3.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-1EfMWeRA.js → Codegen-o14zzfuK.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Bc08LzOn.js → Col-HlZjxy7V.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BNnNv7xp.js → Community-DXxMflr2.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-nFjLneHM.js → Compact-DbswbBjd.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CZpaaHND.js → Compliance-C7Q4Da2p.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DzHugH3j.js → Cowork-BLImywOe.js} +1 -1
- package/src/assets/web-panel/assets/{Cron-C7YRyiN-.js → Cron-DpXu1G4m.js} +1 -1
- package/src/assets/web-panel/assets/{Crosschain-er2h0L0q.js → Crosschain-C4qokeJu.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DaMdE0A7.js → DID-C0BjPiij.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-1z2K_E1B.js → Dashboard-CsfDXwPl.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-B9NIqAXf.js → Dropdown-CiwMUKrx.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CBHLFokz.js → EmailListRenderer-DX7xfm5a.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D2fEJEki.js → Federation-CA4Ibsg0.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-tmsd70yG.js → FormItemContext-DvM0lU-7.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DW4Lnrsr.js → GenericCardRenderer-REjsnTHA.js} +1 -1
- package/src/assets/web-panel/assets/{Git-D8aBagAp.js → Git-ZhnSDQGH.js} +1 -1
- package/src/assets/web-panel/assets/{Governance-CKDAHamy.js → Governance-BL8iUV_g.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BLLp118j.js → Inference-BONrgLk-.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cawx-yfu.js → KnowledgeGraph-q9SAz7h7.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-sWSzp5PY.js → Logs-eHfEFnEZ.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BQMX3dSy.js → Marketplace-DzwuVedm.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DjlFST3o.js → McpTools-BRoLIeq5.js} +1 -1
- package/src/assets/web-panel/assets/{Memory-LZE1wG7m.js → Memory-DjvAa1Ce.js} +1 -1
- package/src/assets/web-panel/assets/{MobileBridge-BWG47jQ6.js → MobileBridge-CGTanjsT.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DHVv0tYo.js → MobileProjects-cTccXHwN.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Di1hrhM3.js → Mtc-CNUe29wS.js} +1 -1
- package/src/assets/web-panel/assets/{MtcAudit-BowOCi-O.js → MtcAudit-COk24mdH.js} +1 -1
- package/src/assets/web-panel/assets/{Multisig-CgpR0s8E.js → Multisig-CFq2j0BU.js} +1 -1
- package/src/assets/web-panel/assets/{NLProgramming-O4U3Plxd.js → NLProgramming-dfRr-FGW.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Ce_5C6XT.js → Notes-iM4aoXiw.js} +1 -1
- package/src/assets/web-panel/assets/{NotificationSettings-BVVopkxj.js → NotificationSettings-CeVJ-942.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-XW2G50wt.js → OrderTableRenderer-zQ5jvUZI.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DaW54kdP.js → Organization-CTSWyMGT.js} +1 -1
- package/src/assets/web-panel/assets/{Overflow-CVTGMUVt.js → Overflow---n60sAp.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CpEwrhBx.js → P2P-BCQurpAB.js} +1 -1
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CDqun-Z9.js → PdhVaultBrowser-C5L1qAc0.js} +2 -2
- package/src/assets/web-panel/assets/{Permissions-BsSU3MaL.js → Permissions-I8sgLLE5.js} +1 -1
- package/src/assets/web-panel/assets/PersonalDataHub-CEUfFRCj.js +2 -0
- package/src/assets/web-panel/assets/{PersonalDataHub-DmUc89_0.css → PersonalDataHub-CO9-IYDY.css} +1 -1
- package/src/assets/web-panel/assets/{Pipeline-CPCDpDyd.js → Pipeline-BTBjJaRd.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-oZEQvVWA.js → Privacy-aCs44rpW.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CRu5bDNW.js → ProjectInit-DZ2iIlbN.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectSettings-ComIZ7gQ.js → ProjectSettings-oYUtNgHP.js} +1 -1
- package/src/assets/web-panel/assets/{Projects-Djr10qFp.js → Projects-FonFg0ie.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CltNzV7e.js → Providers-CRmOuabY.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-C21t1JEf.js → QuickAsk-BHd6UZ1A.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BjJForJo.js → Recommend-BkzGGuq-.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-Cp8Ym-Hk.js → Reputation-Do7cPr9u.js} +1 -1
- package/src/assets/web-panel/assets/{Row-n8ruyEoT.js → Row-D_sR3MmD.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CosaiHvS.js → RssFeed-pWSbZ06l.js} +1 -1
- package/src/assets/web-panel/assets/{Search-DUNSNFJZ.js → Search-BTL71JSs.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Z3WyX_MB.js → Security-8lbNoqY6.js} +1 -1
- package/src/assets/web-panel/assets/{Services-U0m9Oj8V.js → Services-yr3QVduo.js} +1 -1
- package/src/assets/web-panel/assets/{Skeleton-B_M5sv2W.js → Skeleton-DdnY1ZTz.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-NKU9c6LT.js → Skills-fk24WY3D.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DscfpqF8.js → Sla-bOXRT7wR.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DP0O84ej.js → SpeechSettings-DjonFUbP.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-P_WEyoH3.js → SyncSettings-C-7HTl6d.js} +1 -1
- package/src/assets/web-panel/assets/{Tasks-C8YAncnj.js → Tasks-DDohfzFT.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-F12-SRc3.js → Templates-C8PI_WTt.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CULHbrwu.js → Tenant-CIvbeRkn.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DdYrQ0n3.js → Terminal-BuIPGugj.js} +1 -1
- package/src/assets/web-panel/assets/{TimelineRenderer-C12_BYAe.js → TimelineRenderer-BefCbTxN.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-8MVLlc0s.js → Tokens-gGAeez46.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Bl6QmvKw.js → Trigger-C_86E2v7.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BH_aEsWy.js → Trust-Beg23C1W.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DsX2CeE4.js → UkeySign--sGSaISg.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DaRdn6e9.js → VideoEditing-DIsnY6D_.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DO89dzlU.js → Wallet-a7ECYY0i.js} +1 -1
- package/src/assets/web-panel/assets/{WebAuthn-KUKNjImY.js → WebAuthn-CsVpkUE8.js} +1 -1
- package/src/assets/web-panel/assets/{WorkflowEditor-CnSoP-Z9.js → WorkflowEditor-BOJ4WTgl.js} +1 -1
- package/src/assets/web-panel/assets/{chat-Jp0M9E52.js → chat-ezCR5ZZX.js} +1 -1
- package/src/assets/web-panel/assets/{colors-Bkq4q91x.js → colors-DSUEQ7GR.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CCt1byem.js → compact-item-D4w3DGj_.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DwRLi0Uq.js → createContext-B_hvu-NR.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-CTVm-2lw.js → hasIn-FY775Vpc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLjugrcu.js → index-1ki6aTIz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdlVwys1.js → index-B3lbDu2b.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9q3wKEr.js → index-B9LXVosT.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFcr_PT2.js → index-BGdchdRx.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-UAtktg.js → index-BGrm33US.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFh73FRz.js → index-BHPD-_s2.js} +1 -1
- package/src/assets/web-panel/assets/{index-CIBi-rGt.js → index-BMnGj-Ui.js} +1 -1
- package/src/assets/web-panel/assets/{index-CjJWQqPG.js → index-BSp1hSN8.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2pkXVOU.js → index-BaZmrJ6j.js} +1 -1
- package/src/assets/web-panel/assets/{index-6OmglXey.js → index-BnvLdkR8.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJmiHI2z.js → index-BzME19Nh.js} +1 -1
- package/src/assets/web-panel/assets/{index-BD2U0Ciz.js → index-C0wDzrUK.js} +3 -3
- package/src/assets/web-panel/assets/{index-CPnSymMN.js → index-CAa3LSKI.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRkGL2HJ.js → index-CE3vDzJj.js} +1 -1
- package/src/assets/web-panel/assets/{index-eyVkd-kF.js → index-CLp4V3Tb.js} +1 -1
- package/src/assets/web-panel/assets/{index-TL3iHPdE.js → index-CNUQnJsg.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cvrka2TD.js → index-CSbCIyga.js} +1 -1
- package/src/assets/web-panel/assets/{index-MdArO-BF.js → index-CUNYcWEF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWQ9NRWg.js → index-CV-F9a_X.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJegJP2N.js → index-CcvzscCg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BjGcDwb2.js → index-CdzLJsac.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRwUdk7I.js → index-Cug_yoee.js} +1 -1
- package/src/assets/web-panel/assets/{index-TqiVFiUf.js → index-CxMArAIk.js} +1 -1
- package/src/assets/web-panel/assets/index-CzkEIn_T.js +1 -0
- package/src/assets/web-panel/assets/index-D2PcvXPP.js +1 -0
- package/src/assets/web-panel/assets/{index-CJWYdgmz.js → index-D6yJ0RMr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtzUDs1M.js → index-DM77-xyl.js} +1 -1
- package/src/assets/web-panel/assets/{index-hpRtUCjU.js → index-DfU6zsU8.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2ovimhG.js → index-DqsQUPY7.js} +1 -1
- package/src/assets/web-panel/assets/{index-zwGGcR7I.js → index-DqxLCUYa.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_U_xDvh.js → index-DtztktoP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDbgmmSF.js → index-Dz8-7APi.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3irAeY8.js → index-Enin7rzK.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3ZzMEg0.js → index-S6A76pR1.js} +1 -1
- package/src/assets/web-panel/assets/{index-Db3R-khd.js → index-ZGJo2fz1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BziGa10S.js → index-d0HXotWE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DXDSJwDk.js → index-gAtp_KkQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhSbNePw.js → index-mlTTTHla.js} +1 -1
- package/src/assets/web-panel/assets/{index-DC-8E2CG.js → index-wWVM6ENX.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-9iSe4Eid.js → initDefaultProps-BoLcNBz9.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CcUz2pq7.js → motion-ouRvP3eO.js} +1 -1
- package/src/assets/web-panel/assets/{move-3Po9fM71.js → move-BBITvaOV.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BBcRnciw.js → omit-D8Yxdvbb.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-UCqcWwOy.js → pickAttrs-BIUy7mER.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-boVtHxA4.js → placementArrow-CrNWpNSF.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-V0qPHZOl.js → responsiveObserve-aHLPN464.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CZyysA_q.js → slide-2qNtePjk.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BLyCU5L3.js → statusUtils-FAF3iR3R.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BfXrPW7h.js → styleChecker-BNFTkrbl.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Be2wILDi.js → useFlexGapSupport-BgdPlW7E.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CzJYoHOI.js → useFs-q1QvAh3f.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-__HEfTF9.js → usePersonalDataHub-DEZ6cY8C.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-C0XE0aCE.js → vnode-vp6H-NL_.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CphwCBhl.js → zoom-UatfJMw4.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/__tests__/hub-kuaishou-adb-sync.test.js +161 -0
- package/src/commands/__tests__/hub-toutiao-adb-sync.test.js +158 -0
- package/src/commands/hub.js +283 -0
- package/src/gateways/ws/personal-data-hub-protocol.js +34 -0
- package/src/lib/personal-data-hub-wiring.js +122 -0
- package/src/assets/web-panel/assets/PersonalDataHub-BafcAH5d.js +0 -1
- package/src/assets/web-panel/assets/index-CR2wmKfw.js +0 -1
- package/src/assets/web-panel/assets/index-iXk8Oeci.js +0 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 6d — cc hub kuaishou-adb-sync CLI command unit tests.
|
|
3
|
+
*
|
|
4
|
+
* Mirror of hub-toutiao-adb-sync. Covers profileFetchFailed + CLI no-bridge
|
|
5
|
+
* short-circuit banner + lastErrorCode propagation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
9
|
+
|
|
10
|
+
import { _internal } from "../hub.js";
|
|
11
|
+
|
|
12
|
+
let logSpy, errSpy;
|
|
13
|
+
let prevExitCode;
|
|
14
|
+
|
|
15
|
+
function fakeHub(syncResult) {
|
|
16
|
+
return { kuaishouAdbSync: vi.fn(async (_opts) => syncResult) };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const HAPPY_RESULT = {
|
|
20
|
+
ok: true,
|
|
21
|
+
report: {
|
|
22
|
+
adapter: "social-kuaishou",
|
|
23
|
+
status: "ok",
|
|
24
|
+
rawCount: 26,
|
|
25
|
+
entityCounts: { events: 26, persons: 1, places: 0, items: 0, topics: 0 },
|
|
26
|
+
kuaishou: {
|
|
27
|
+
uid: "12345",
|
|
28
|
+
nickname: "Alice",
|
|
29
|
+
eventCounts: {
|
|
30
|
+
profile: 1,
|
|
31
|
+
watch: 15,
|
|
32
|
+
collect: 5,
|
|
33
|
+
search: 5,
|
|
34
|
+
total: 26,
|
|
35
|
+
},
|
|
36
|
+
lastErrorCode: 0,
|
|
37
|
+
lastErrorMessage: null,
|
|
38
|
+
cookieDiagnostic: { cookieCount: 8, hadEncrypted: false },
|
|
39
|
+
profileFetchFailed: false,
|
|
40
|
+
signProviderUsed: "KuaishouSignBridge",
|
|
41
|
+
signProviderHits: 3,
|
|
42
|
+
signProviderFallbacks: 0,
|
|
43
|
+
cleanupFailed: false,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
50
|
+
errSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
51
|
+
prevExitCode = process.exitCode;
|
|
52
|
+
process.exitCode = 0;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
afterEach(() => {
|
|
56
|
+
logSpy.mockRestore();
|
|
57
|
+
errSpy.mockRestore();
|
|
58
|
+
process.exitCode = prevExitCode;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("cc hub kuaishou-adb-sync — happy path", () => {
|
|
62
|
+
it("prints human summary with nickname", async () => {
|
|
63
|
+
const hub = fakeHub(HAPPY_RESULT);
|
|
64
|
+
await _internal.cmdKuaishouAdbSync({ _getHub: async () => hub });
|
|
65
|
+
expect(hub.kuaishouAdbSync).toHaveBeenCalledOnce();
|
|
66
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
67
|
+
expect(out).toMatch(/kuaishou-adb-sync succeeded/);
|
|
68
|
+
expect(out).toMatch(/uid:.*12345/);
|
|
69
|
+
expect(out).toMatch(/nickname:\s+Alice/);
|
|
70
|
+
expect(out).toMatch(/profile:\s+1/);
|
|
71
|
+
expect(out).toMatch(/watch:\s+15/);
|
|
72
|
+
expect(out).toMatch(/collect:\s+5/);
|
|
73
|
+
expect(out).toMatch(/search:\s+5/);
|
|
74
|
+
expect(out).toMatch(/total:\s+26/);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("--json outputs raw result", async () => {
|
|
78
|
+
const hub = fakeHub(HAPPY_RESULT);
|
|
79
|
+
await _internal.cmdKuaishouAdbSync({
|
|
80
|
+
_getHub: async () => hub,
|
|
81
|
+
json: true,
|
|
82
|
+
});
|
|
83
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
84
|
+
const parsed = JSON.parse(out);
|
|
85
|
+
expect(parsed.ok).toBe(true);
|
|
86
|
+
expect(parsed.report.kuaishou.uid).toBe("12345");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("renders profileFetchFailed warning when set", async () => {
|
|
90
|
+
const result = JSON.parse(JSON.stringify(HAPPY_RESULT));
|
|
91
|
+
result.report.kuaishou.profileFetchFailed = true;
|
|
92
|
+
result.report.kuaishou.lastErrorCode = -8;
|
|
93
|
+
result.report.kuaishou.uid = null;
|
|
94
|
+
const hub = fakeHub(result);
|
|
95
|
+
await _internal.cmdKuaishouAdbSync({ _getHub: async () => hub });
|
|
96
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
97
|
+
expect(out).toMatch(/cookie 缺 kuaishou\.web\.cp\.api_ph/);
|
|
98
|
+
expect(out).toMatch(/profile fetch failed/);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("renders 'no sign bridge' short-circuit banner in CLI context", async () => {
|
|
102
|
+
const result = JSON.parse(JSON.stringify(HAPPY_RESULT));
|
|
103
|
+
result.report.kuaishou.signProviderUsed = "none";
|
|
104
|
+
result.report.kuaishou.signProviderHits = 0;
|
|
105
|
+
result.report.kuaishou.signProviderFallbacks = 3;
|
|
106
|
+
result.report.kuaishou.eventCounts.watch = 0;
|
|
107
|
+
result.report.kuaishou.eventCounts.collect = 0;
|
|
108
|
+
result.report.kuaishou.eventCounts.search = 0;
|
|
109
|
+
result.report.kuaishou.eventCounts.total = 1;
|
|
110
|
+
result.report.kuaishou.lastErrorCode = -99;
|
|
111
|
+
const hub = fakeHub(result);
|
|
112
|
+
await _internal.cmdKuaishouAdbSync({ _getHub: async () => hub });
|
|
113
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
114
|
+
expect(out).toMatch(/3 signed endpoints short-circuited/);
|
|
115
|
+
expect(out).toMatch(/no sign bridge in CLI context/);
|
|
116
|
+
expect(out).toMatch(/__NS_sig3 via Electron WebContentsView/);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("renders lastErrorCode partial-result warning (non-(-99))", async () => {
|
|
120
|
+
const result = JSON.parse(JSON.stringify(HAPPY_RESULT));
|
|
121
|
+
result.report.kuaishou.lastErrorCode = 412;
|
|
122
|
+
result.report.kuaishou.lastErrorMessage = "HTTP 412";
|
|
123
|
+
const hub = fakeHub(result);
|
|
124
|
+
await _internal.cmdKuaishouAdbSync({ _getHub: async () => hub });
|
|
125
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
126
|
+
expect(out).toMatch(/partial: lastErrorCode=412/);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("cc hub kuaishou-adb-sync — failure paths", () => {
|
|
131
|
+
function failCase(reason, expectedHint) {
|
|
132
|
+
return async () => {
|
|
133
|
+
const hub = fakeHub({ ok: false, reason, message: "synthetic" });
|
|
134
|
+
await _internal.cmdKuaishouAdbSync({ _getHub: async () => hub });
|
|
135
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
136
|
+
expect(out).toMatch(new RegExp(`kuaishou-adb-sync failed: ${reason}`));
|
|
137
|
+
if (expectedHint) {
|
|
138
|
+
expect(out).toMatch(expectedHint);
|
|
139
|
+
}
|
|
140
|
+
expect(process.exitCode).toBe(1);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
it("BRIDGE_UNAVAILABLE banner", failCase("BRIDGE_UNAVAILABLE", /ADB_PATH/));
|
|
145
|
+
it("KUAISHOU_NO_ROOT banner", failCase("KUAISHOU_NO_ROOT", /Magisk root/));
|
|
146
|
+
it(
|
|
147
|
+
"KUAISHOU_NOT_INSTALLED banner",
|
|
148
|
+
failCase("KUAISHOU_NOT_INSTALLED", /com\.smile\.gifmaker/),
|
|
149
|
+
);
|
|
150
|
+
it(
|
|
151
|
+
"KUAISHOU_COOKIES_INCOMPLETE banner",
|
|
152
|
+
failCase(
|
|
153
|
+
"KUAISHOU_COOKIES_INCOMPLETE",
|
|
154
|
+
/userId \/ kuaishou\.web\.cp\.api_ph/,
|
|
155
|
+
),
|
|
156
|
+
);
|
|
157
|
+
it(
|
|
158
|
+
"KUAISHOU_COOKIES_TRUNCATED banner",
|
|
159
|
+
failCase("KUAISHOU_COOKIES_TRUNCATED", /unplug \+ replug USB/),
|
|
160
|
+
);
|
|
161
|
+
});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 6c — cc hub toutiao-adb-sync CLI command unit tests.
|
|
3
|
+
*
|
|
4
|
+
* Mirror of hub-xhs-adb-sync. Covers profileFetchFailed + CLI no-bridge
|
|
5
|
+
* short-circuit banner + lastErrorCode propagation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
9
|
+
|
|
10
|
+
import { _internal } from "../hub.js";
|
|
11
|
+
|
|
12
|
+
let logSpy, errSpy;
|
|
13
|
+
let prevExitCode;
|
|
14
|
+
|
|
15
|
+
function fakeHub(syncResult) {
|
|
16
|
+
return { toutiaoAdbSync: vi.fn(async (_opts) => syncResult) };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const HAPPY_RESULT = {
|
|
20
|
+
ok: true,
|
|
21
|
+
report: {
|
|
22
|
+
adapter: "social-toutiao",
|
|
23
|
+
status: "ok",
|
|
24
|
+
rawCount: 26,
|
|
25
|
+
entityCounts: { events: 26, persons: 1, places: 0, items: 0, topics: 0 },
|
|
26
|
+
toutiao: {
|
|
27
|
+
uid: "12345",
|
|
28
|
+
nickname: "Alice",
|
|
29
|
+
eventCounts: {
|
|
30
|
+
profile: 1,
|
|
31
|
+
feed: 15,
|
|
32
|
+
collection: 5,
|
|
33
|
+
search: 5,
|
|
34
|
+
total: 26,
|
|
35
|
+
},
|
|
36
|
+
lastErrorCode: 0,
|
|
37
|
+
lastErrorMessage: null,
|
|
38
|
+
cookieDiagnostic: { cookieCount: 8, hadEncrypted: false },
|
|
39
|
+
profileFetchFailed: false,
|
|
40
|
+
signProviderUsed: "ToutiaoSignBridge",
|
|
41
|
+
signProviderHits: 3,
|
|
42
|
+
signProviderFallbacks: 0,
|
|
43
|
+
cleanupFailed: false,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
50
|
+
errSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
51
|
+
prevExitCode = process.exitCode;
|
|
52
|
+
process.exitCode = 0;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
afterEach(() => {
|
|
56
|
+
logSpy.mockRestore();
|
|
57
|
+
errSpy.mockRestore();
|
|
58
|
+
process.exitCode = prevExitCode;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("cc hub toutiao-adb-sync — happy path", () => {
|
|
62
|
+
it("prints human summary with nickname", async () => {
|
|
63
|
+
const hub = fakeHub(HAPPY_RESULT);
|
|
64
|
+
await _internal.cmdToutiaoAdbSync({ _getHub: async () => hub });
|
|
65
|
+
expect(hub.toutiaoAdbSync).toHaveBeenCalledOnce();
|
|
66
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
67
|
+
expect(out).toMatch(/toutiao-adb-sync succeeded/);
|
|
68
|
+
expect(out).toMatch(/uid:.*12345/);
|
|
69
|
+
expect(out).toMatch(/nickname:\s+Alice/);
|
|
70
|
+
expect(out).toMatch(/profile:\s+1/);
|
|
71
|
+
expect(out).toMatch(/feed:\s+15/);
|
|
72
|
+
expect(out).toMatch(/collection:\s+5/);
|
|
73
|
+
expect(out).toMatch(/search:\s+5/);
|
|
74
|
+
expect(out).toMatch(/total:\s+26/);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("--json outputs raw result", async () => {
|
|
78
|
+
const hub = fakeHub(HAPPY_RESULT);
|
|
79
|
+
await _internal.cmdToutiaoAdbSync({
|
|
80
|
+
_getHub: async () => hub,
|
|
81
|
+
json: true,
|
|
82
|
+
});
|
|
83
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
84
|
+
const parsed = JSON.parse(out);
|
|
85
|
+
expect(parsed.ok).toBe(true);
|
|
86
|
+
expect(parsed.report.toutiao.uid).toBe("12345");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("renders profileFetchFailed warning when set", async () => {
|
|
90
|
+
const result = JSON.parse(JSON.stringify(HAPPY_RESULT));
|
|
91
|
+
result.report.toutiao.profileFetchFailed = true;
|
|
92
|
+
result.report.toutiao.lastErrorCode = 1;
|
|
93
|
+
result.report.toutiao.lastErrorMessage = "token expired";
|
|
94
|
+
result.report.toutiao.uid = null;
|
|
95
|
+
const hub = fakeHub(result);
|
|
96
|
+
await _internal.cmdToutiaoAdbSync({ _getHub: async () => hub });
|
|
97
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
98
|
+
expect(out).toMatch(/passport\/info\/v2 returned no user_id/);
|
|
99
|
+
expect(out).toMatch(/profile fetch failed/);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("renders 'no sign bridge' short-circuit banner in CLI context", async () => {
|
|
103
|
+
const result = JSON.parse(JSON.stringify(HAPPY_RESULT));
|
|
104
|
+
result.report.toutiao.signProviderUsed = "none";
|
|
105
|
+
result.report.toutiao.signProviderHits = 0;
|
|
106
|
+
result.report.toutiao.signProviderFallbacks = 3;
|
|
107
|
+
result.report.toutiao.eventCounts.feed = 0;
|
|
108
|
+
result.report.toutiao.eventCounts.collection = 0;
|
|
109
|
+
result.report.toutiao.eventCounts.search = 0;
|
|
110
|
+
result.report.toutiao.eventCounts.total = 1; // just profile
|
|
111
|
+
result.report.toutiao.lastErrorCode = -99;
|
|
112
|
+
const hub = fakeHub(result);
|
|
113
|
+
await _internal.cmdToutiaoAdbSync({ _getHub: async () => hub });
|
|
114
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
115
|
+
expect(out).toMatch(/3 signed endpoints short-circuited/);
|
|
116
|
+
expect(out).toMatch(/no sign bridge in CLI context/);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("renders lastErrorCode partial-result warning (non-(-99))", async () => {
|
|
120
|
+
const result = JSON.parse(JSON.stringify(HAPPY_RESULT));
|
|
121
|
+
result.report.toutiao.lastErrorCode = 412;
|
|
122
|
+
result.report.toutiao.lastErrorMessage = "HTTP 412";
|
|
123
|
+
const hub = fakeHub(result);
|
|
124
|
+
await _internal.cmdToutiaoAdbSync({ _getHub: async () => hub });
|
|
125
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
126
|
+
expect(out).toMatch(/partial: lastErrorCode=412/);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("cc hub toutiao-adb-sync — failure paths", () => {
|
|
131
|
+
function failCase(reason, expectedHint) {
|
|
132
|
+
return async () => {
|
|
133
|
+
const hub = fakeHub({ ok: false, reason, message: "synthetic" });
|
|
134
|
+
await _internal.cmdToutiaoAdbSync({ _getHub: async () => hub });
|
|
135
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
136
|
+
expect(out).toMatch(new RegExp(`toutiao-adb-sync failed: ${reason}`));
|
|
137
|
+
if (expectedHint) {
|
|
138
|
+
expect(out).toMatch(expectedHint);
|
|
139
|
+
}
|
|
140
|
+
expect(process.exitCode).toBe(1);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
it("BRIDGE_UNAVAILABLE banner", failCase("BRIDGE_UNAVAILABLE", /ADB_PATH/));
|
|
145
|
+
it("TOUTIAO_NO_ROOT banner", failCase("TOUTIAO_NO_ROOT", /Magisk root/));
|
|
146
|
+
it(
|
|
147
|
+
"TOUTIAO_NOT_INSTALLED banner",
|
|
148
|
+
failCase("TOUTIAO_NOT_INSTALLED", /com\.ss\.android\.article\.news/),
|
|
149
|
+
);
|
|
150
|
+
it(
|
|
151
|
+
"TOUTIAO_COOKIES_INCOMPLETE banner",
|
|
152
|
+
failCase("TOUTIAO_COOKIES_INCOMPLETE", /sessionid \/ sessionid_ss/),
|
|
153
|
+
);
|
|
154
|
+
it(
|
|
155
|
+
"TOUTIAO_COOKIES_TRUNCATED banner",
|
|
156
|
+
failCase("TOUTIAO_COOKIES_TRUNCATED", /unplug \+ replug USB/),
|
|
157
|
+
);
|
|
158
|
+
});
|
package/src/commands/hub.js
CHANGED
|
@@ -1192,6 +1192,240 @@ async function cmdXhsAdbSync(options) {
|
|
|
1192
1192
|
}
|
|
1193
1193
|
}
|
|
1194
1194
|
|
|
1195
|
+
/**
|
|
1196
|
+
* Phase 6d — `cc hub kuaishou-adb-sync`
|
|
1197
|
+
*
|
|
1198
|
+
* Pulls www.kuaishou.com cookies from the user's Android Kuaishou App,
|
|
1199
|
+
* fetches uid + profile via cookie's api_ph payload (PURE COOKIE PARSE,
|
|
1200
|
+
* no HTTP) + 3 signed GraphQL endpoints (watch / collect / search).
|
|
1201
|
+
* CLI context has NO sign bridge — 3 signed endpoints short-circuit
|
|
1202
|
+
* with lastErrorCode=-99. Desktop wiring upgrades via KuaishouSignBridge.
|
|
1203
|
+
*/
|
|
1204
|
+
async function cmdKuaishouAdbSync(options) {
|
|
1205
|
+
try {
|
|
1206
|
+
const hub = await (options._getHub || getHub)();
|
|
1207
|
+
const result = await hub.kuaishouAdbSync({
|
|
1208
|
+
stagingDir: options.stagingDir,
|
|
1209
|
+
displayName: options.displayName,
|
|
1210
|
+
limits:
|
|
1211
|
+
options.limitWatch || options.limitCollect || options.limitSearch
|
|
1212
|
+
? {
|
|
1213
|
+
watch: parsePositiveInt(options.limitWatch),
|
|
1214
|
+
collect: parsePositiveInt(options.limitCollect),
|
|
1215
|
+
search: parsePositiveInt(options.limitSearch),
|
|
1216
|
+
}
|
|
1217
|
+
: undefined,
|
|
1218
|
+
});
|
|
1219
|
+
if (options.json) {
|
|
1220
|
+
printJson(result);
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
if (!result.ok) {
|
|
1224
|
+
logger.log(chalk.red(`✗ kuaishou-adb-sync failed: ${result.reason}`));
|
|
1225
|
+
logger.log(chalk.gray(` ${result.message || ""}`));
|
|
1226
|
+
if (result.reason === "BRIDGE_UNAVAILABLE") {
|
|
1227
|
+
logger.log(
|
|
1228
|
+
chalk.gray(
|
|
1229
|
+
" Install Android Platform Tools or set ADB_PATH=/path/to/adb",
|
|
1230
|
+
),
|
|
1231
|
+
);
|
|
1232
|
+
} else if (result.reason === "KUAISHOU_NO_ROOT") {
|
|
1233
|
+
logger.log(
|
|
1234
|
+
chalk.gray(
|
|
1235
|
+
" Phone needs Magisk root — Kuaishou release APK isn't debuggable",
|
|
1236
|
+
),
|
|
1237
|
+
);
|
|
1238
|
+
} else if (result.reason === "KUAISHOU_NOT_INSTALLED") {
|
|
1239
|
+
logger.log(
|
|
1240
|
+
chalk.gray(
|
|
1241
|
+
" Install 快手 (com.smile.gifmaker, NOT 极速版 com.kuaishou.nebula) + log in once + open any video (WebView populates cookies), then retry",
|
|
1242
|
+
),
|
|
1243
|
+
);
|
|
1244
|
+
} else if (result.reason === "KUAISHOU_COOKIES_INCOMPLETE") {
|
|
1245
|
+
logger.log(
|
|
1246
|
+
chalk.gray(
|
|
1247
|
+
" userId / kuaishou.web.cp.api_ph missing — relog on the Kuaishou App",
|
|
1248
|
+
),
|
|
1249
|
+
);
|
|
1250
|
+
} else if (
|
|
1251
|
+
result.reason === "KUAISHOU_COOKIES_TRUNCATED" ||
|
|
1252
|
+
result.reason === "KUAISHOU_NOT_SQLITE"
|
|
1253
|
+
) {
|
|
1254
|
+
logger.log(
|
|
1255
|
+
chalk.gray(
|
|
1256
|
+
" ADB stream may be corrupted; unplug + replug USB and retry",
|
|
1257
|
+
),
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
process.exitCode = 1;
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
const report = result.report || {};
|
|
1264
|
+
const kuaishou = report.kuaishou || {};
|
|
1265
|
+
const counts = kuaishou.eventCounts || {};
|
|
1266
|
+
logger.log(chalk.green(`✓ kuaishou-adb-sync succeeded`));
|
|
1267
|
+
logger.log(
|
|
1268
|
+
` uid: ${chalk.cyan(kuaishou.uid || "(profile fetch failed)")}`,
|
|
1269
|
+
);
|
|
1270
|
+
if (kuaishou.nickname) {
|
|
1271
|
+
logger.log(` nickname: ${kuaishou.nickname}`);
|
|
1272
|
+
}
|
|
1273
|
+
logger.log(` profile: ${counts.profile || 0}`);
|
|
1274
|
+
logger.log(` watch: ${counts.watch || 0}`);
|
|
1275
|
+
logger.log(` collect: ${counts.collect || 0}`);
|
|
1276
|
+
logger.log(` search: ${counts.search || 0}`);
|
|
1277
|
+
logger.log(` total: ${counts.total || 0}`);
|
|
1278
|
+
logger.log(` status: ${report.status || "?"}`);
|
|
1279
|
+
logger.log(` rawCount: ${report.rawCount || 0}`);
|
|
1280
|
+
if (kuaishou.profileFetchFailed) {
|
|
1281
|
+
logger.log(
|
|
1282
|
+
chalk.yellow(
|
|
1283
|
+
` ⚠ cookie 缺 kuaishou.web.cp.api_ph — relog on Kuaishou (lastErrorCode=${kuaishou.lastErrorCode})`,
|
|
1284
|
+
),
|
|
1285
|
+
);
|
|
1286
|
+
} else if (
|
|
1287
|
+
kuaishou.signProviderUsed === "none" &&
|
|
1288
|
+
counts.watch === 0 &&
|
|
1289
|
+
counts.collect === 0 &&
|
|
1290
|
+
counts.search === 0
|
|
1291
|
+
) {
|
|
1292
|
+
logger.log(
|
|
1293
|
+
chalk.yellow(
|
|
1294
|
+
` ⚠ 3 signed endpoints short-circuited (no sign bridge in CLI context) — run from desktop app to enable __NS_sig3 via Electron WebContentsView`,
|
|
1295
|
+
),
|
|
1296
|
+
);
|
|
1297
|
+
} else if (kuaishou.lastErrorCode) {
|
|
1298
|
+
logger.log(
|
|
1299
|
+
chalk.yellow(
|
|
1300
|
+
` ⚠ partial: lastErrorCode=${kuaishou.lastErrorCode} (${kuaishou.lastErrorMessage || "?"})`,
|
|
1301
|
+
),
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
if (kuaishou.cleanupFailed) {
|
|
1305
|
+
logger.log(chalk.gray(` (note: staging cleanup failed — non-fatal)`));
|
|
1306
|
+
}
|
|
1307
|
+
} catch (err) {
|
|
1308
|
+
fail(null, err, options.json);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
/**
|
|
1313
|
+
* Phase 6c — `cc hub toutiao-adb-sync`
|
|
1314
|
+
*
|
|
1315
|
+
* Pulls www.toutiao.com cookies from the user's Android Toutiao App,
|
|
1316
|
+
* fetches uid + profile (no _sig) + 3 signed endpoints (feed / collection
|
|
1317
|
+
* / search). CLI context has NO sign bridge — 3 signed endpoints short-
|
|
1318
|
+
* circuit with lastErrorCode=-99 (no HTTP traffic). Desktop wiring
|
|
1319
|
+
* upgrades via ToutiaoSignBridge (Electron WebContentsView).
|
|
1320
|
+
*/
|
|
1321
|
+
async function cmdToutiaoAdbSync(options) {
|
|
1322
|
+
try {
|
|
1323
|
+
const hub = await (options._getHub || getHub)();
|
|
1324
|
+
const result = await hub.toutiaoAdbSync({
|
|
1325
|
+
stagingDir: options.stagingDir,
|
|
1326
|
+
displayName: options.displayName,
|
|
1327
|
+
limits:
|
|
1328
|
+
options.limitFeed || options.limitCollection || options.limitSearch
|
|
1329
|
+
? {
|
|
1330
|
+
feed: parsePositiveInt(options.limitFeed),
|
|
1331
|
+
collection: parsePositiveInt(options.limitCollection),
|
|
1332
|
+
search: parsePositiveInt(options.limitSearch),
|
|
1333
|
+
}
|
|
1334
|
+
: undefined,
|
|
1335
|
+
});
|
|
1336
|
+
if (options.json) {
|
|
1337
|
+
printJson(result);
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
if (!result.ok) {
|
|
1341
|
+
logger.log(chalk.red(`✗ toutiao-adb-sync failed: ${result.reason}`));
|
|
1342
|
+
logger.log(chalk.gray(` ${result.message || ""}`));
|
|
1343
|
+
if (result.reason === "BRIDGE_UNAVAILABLE") {
|
|
1344
|
+
logger.log(
|
|
1345
|
+
chalk.gray(
|
|
1346
|
+
" Install Android Platform Tools or set ADB_PATH=/path/to/adb",
|
|
1347
|
+
),
|
|
1348
|
+
);
|
|
1349
|
+
} else if (result.reason === "TOUTIAO_NO_ROOT") {
|
|
1350
|
+
logger.log(
|
|
1351
|
+
chalk.gray(
|
|
1352
|
+
" Phone needs Magisk root — Toutiao release APK isn't debuggable",
|
|
1353
|
+
),
|
|
1354
|
+
);
|
|
1355
|
+
} else if (result.reason === "TOUTIAO_NOT_INSTALLED") {
|
|
1356
|
+
logger.log(
|
|
1357
|
+
chalk.gray(
|
|
1358
|
+
" Install 今日头条 (com.ss.android.article.news, NOT 极速版/.lite) + log in once + open any article (WebView populates cookies), then retry",
|
|
1359
|
+
),
|
|
1360
|
+
);
|
|
1361
|
+
} else if (result.reason === "TOUTIAO_COOKIES_INCOMPLETE") {
|
|
1362
|
+
logger.log(
|
|
1363
|
+
chalk.gray(
|
|
1364
|
+
" sessionid / sessionid_ss missing — relog on the Toutiao App",
|
|
1365
|
+
),
|
|
1366
|
+
);
|
|
1367
|
+
} else if (
|
|
1368
|
+
result.reason === "TOUTIAO_COOKIES_TRUNCATED" ||
|
|
1369
|
+
result.reason === "TOUTIAO_NOT_SQLITE"
|
|
1370
|
+
) {
|
|
1371
|
+
logger.log(
|
|
1372
|
+
chalk.gray(
|
|
1373
|
+
" ADB stream may be corrupted; unplug + replug USB and retry",
|
|
1374
|
+
),
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
process.exitCode = 1;
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
const report = result.report || {};
|
|
1381
|
+
const toutiao = report.toutiao || {};
|
|
1382
|
+
const counts = toutiao.eventCounts || {};
|
|
1383
|
+
logger.log(chalk.green(`✓ toutiao-adb-sync succeeded`));
|
|
1384
|
+
logger.log(
|
|
1385
|
+
` uid: ${chalk.cyan(toutiao.uid || "(profile fetch failed)")}`,
|
|
1386
|
+
);
|
|
1387
|
+
if (toutiao.nickname) {
|
|
1388
|
+
logger.log(` nickname: ${toutiao.nickname}`);
|
|
1389
|
+
}
|
|
1390
|
+
logger.log(` profile: ${counts.profile || 0}`);
|
|
1391
|
+
logger.log(` feed: ${counts.feed || 0}`);
|
|
1392
|
+
logger.log(` collection: ${counts.collection || 0}`);
|
|
1393
|
+
logger.log(` search: ${counts.search || 0}`);
|
|
1394
|
+
logger.log(` total: ${counts.total || 0}`);
|
|
1395
|
+
logger.log(` status: ${report.status || "?"}`);
|
|
1396
|
+
logger.log(` rawCount: ${report.rawCount || 0}`);
|
|
1397
|
+
if (toutiao.profileFetchFailed) {
|
|
1398
|
+
logger.log(
|
|
1399
|
+
chalk.yellow(
|
|
1400
|
+
` ⚠ passport/info/v2 returned no user_id — cookie expired or sessionid missing (lastErrorCode=${toutiao.lastErrorCode})`,
|
|
1401
|
+
),
|
|
1402
|
+
);
|
|
1403
|
+
} else if (
|
|
1404
|
+
toutiao.signProviderUsed === "none" &&
|
|
1405
|
+
counts.feed === 0 &&
|
|
1406
|
+
counts.collection === 0 &&
|
|
1407
|
+
counts.search === 0
|
|
1408
|
+
) {
|
|
1409
|
+
logger.log(
|
|
1410
|
+
chalk.yellow(
|
|
1411
|
+
` ⚠ 3 signed endpoints short-circuited (no sign bridge in CLI context) — run from desktop app to enable _signature via Electron WebContentsView`,
|
|
1412
|
+
),
|
|
1413
|
+
);
|
|
1414
|
+
} else if (toutiao.lastErrorCode) {
|
|
1415
|
+
logger.log(
|
|
1416
|
+
chalk.yellow(
|
|
1417
|
+
` ⚠ partial: lastErrorCode=${toutiao.lastErrorCode} (${toutiao.lastErrorMessage || "?"})`,
|
|
1418
|
+
),
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
if (toutiao.cleanupFailed) {
|
|
1422
|
+
logger.log(chalk.gray(` (note: staging cleanup failed — non-fatal)`));
|
|
1423
|
+
}
|
|
1424
|
+
} catch (err) {
|
|
1425
|
+
fail(null, err, options.json);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1195
1429
|
/**
|
|
1196
1430
|
* Phase 3a — `cc hub weibo-adb-sync`
|
|
1197
1431
|
*
|
|
@@ -1819,6 +2053,53 @@ export function registerHubCommand(program) {
|
|
|
1819
2053
|
.option("--json", "Output JSON")
|
|
1820
2054
|
.action(cmdWeiboAdbSync);
|
|
1821
2055
|
|
|
2056
|
+
// Phase 6d — Kuaishou C 路径 one-shot (www.kuaishou.com cookies + profile
|
|
2057
|
+
// from api_ph + 3 GraphQL endpoints with __NS_sig3 + kpf/kpn).
|
|
2058
|
+
// CLI: signed endpoints short-circuit. Desktop: KuaishouSignBridge.
|
|
2059
|
+
hub
|
|
2060
|
+
.command("kuaishou-adb-sync")
|
|
2061
|
+
.description(
|
|
2062
|
+
"Kuaishou C 路径: pull www.kuaishou.com cookies via ADB from the user's Android Kuaishou App (com.smile.gifmaker, NOT 极速版 com.kuaishou.nebula), parse profile from kuaishou.web.cp.api_ph cookie payload (no HTTP) + 3 GraphQL endpoints (watch/collect/search, __NS_sig3 + kpf/kpn — CLI short-circuits, desktop uses KuaishouSignBridge). Needs rooted Android + Kuaishou App logged in once + opened a video once + `adb` on PATH.",
|
|
2063
|
+
)
|
|
2064
|
+
.option(
|
|
2065
|
+
"--limit-watch <n>",
|
|
2066
|
+
"Cap recommended feed watch history (default 50)",
|
|
2067
|
+
)
|
|
2068
|
+
.option("--limit-collect <n>", "Cap own posted photos (default 100)")
|
|
2069
|
+
.option("--limit-search <n>", "Cap search history (default 50)")
|
|
2070
|
+
.option(
|
|
2071
|
+
"--display-name <s>",
|
|
2072
|
+
"Account displayName for the snapshot (default = api_ph user_name)",
|
|
2073
|
+
)
|
|
2074
|
+
.option(
|
|
2075
|
+
"--staging-dir <path>",
|
|
2076
|
+
"Custom dir for the temp snapshot JSON (default os.tmpdir())",
|
|
2077
|
+
)
|
|
2078
|
+
.option("--json", "Output JSON")
|
|
2079
|
+
.action(cmdKuaishouAdbSync);
|
|
2080
|
+
|
|
2081
|
+
// Phase 6c — Toutiao C 路径 one-shot (www.toutiao.com cookies + profile +
|
|
2082
|
+
// 3 endpoints with _signature). CLI context: signed endpoints short-circuit
|
|
2083
|
+
// (-99) with no HTTP traffic. Desktop: ToutiaoSignBridge → ~100% hit.
|
|
2084
|
+
hub
|
|
2085
|
+
.command("toutiao-adb-sync")
|
|
2086
|
+
.description(
|
|
2087
|
+
"Toutiao C 路径: pull www.toutiao.com cookies via ADB from the user's Android Toutiao App (com.ss.android.article.news, NOT 极速版), fetch profile (no _sig) + 3 endpoints (feed/collection/search, _signature required — CLI short-circuits, desktop uses ToutiaoSignBridge). Needs rooted Android + Toutiao App logged in once + `adb` on PATH.",
|
|
2088
|
+
)
|
|
2089
|
+
.option("--limit-feed <n>", "Cap recommended feed (default 50)")
|
|
2090
|
+
.option("--limit-collection <n>", "Cap saved articles (default 200)")
|
|
2091
|
+
.option("--limit-search <n>", "Cap search history (default 100)")
|
|
2092
|
+
.option(
|
|
2093
|
+
"--display-name <s>",
|
|
2094
|
+
"Account displayName for the snapshot (default = passport screen_name)",
|
|
2095
|
+
)
|
|
2096
|
+
.option(
|
|
2097
|
+
"--staging-dir <path>",
|
|
2098
|
+
"Custom dir for the temp snapshot JSON (default os.tmpdir())",
|
|
2099
|
+
)
|
|
2100
|
+
.option("--json", "Output JSON")
|
|
2101
|
+
.action(cmdToutiaoAdbSync);
|
|
2102
|
+
|
|
1822
2103
|
// Phase 3c — Xhs C 路径 one-shot (xiaohongshu.com cookies + 4 endpoints with X-S signing)
|
|
1823
2104
|
hub
|
|
1824
2105
|
.command("xhs-adb-sync")
|
|
@@ -2115,6 +2396,8 @@ export const _internal = {
|
|
|
2115
2396
|
cmdDouyinAdbSync,
|
|
2116
2397
|
cmdWeiboAdbSync,
|
|
2117
2398
|
cmdXhsAdbSync,
|
|
2399
|
+
cmdToutiaoAdbSync,
|
|
2400
|
+
cmdKuaishouAdbSync,
|
|
2118
2401
|
interpretWechatProbe,
|
|
2119
2402
|
_defaultKnownVendors,
|
|
2120
2403
|
};
|
|
@@ -442,6 +442,40 @@ export const PERSONAL_DATA_HUB_HANDLERS = {
|
|
|
442
442
|
}),
|
|
443
443
|
),
|
|
444
444
|
|
|
445
|
+
// Phase 6c — Toutiao C 路径 one-shot sync (www.toutiao.com cookies +
|
|
446
|
+
// passport profile + 3 _signature endpoints). CLI/web context with no
|
|
447
|
+
// desktop bridge: 3 signed endpoints short-circuit (no HTTP). Desktop
|
|
448
|
+
// wiring uses ToutiaoSignBridge → ~100% hit.
|
|
449
|
+
"personal-data-hub.toutiao-adb-sync": async (msg) =>
|
|
450
|
+
withHub((hub) =>
|
|
451
|
+
hub.toutiaoAdbSync({
|
|
452
|
+
limits: msg && msg.limits,
|
|
453
|
+
stagingDir: msg && msg.stagingDir,
|
|
454
|
+
displayName: msg && msg.displayName,
|
|
455
|
+
}),
|
|
456
|
+
),
|
|
457
|
+
|
|
458
|
+
// Phase 6d — Kuaishou C 路径 one-shot sync (www.kuaishou.com cookies +
|
|
459
|
+
// profile from api_ph payload + 3 GraphQL endpoints with __NS_sig3 +
|
|
460
|
+
// kpf/kpn). CLI/web context: signed endpoints short-circuit (no HTTP).
|
|
461
|
+
// Desktop wiring uses KuaishouSignBridge → ~100% hit.
|
|
462
|
+
"personal-data-hub.kuaishou-adb-sync": async (msg) =>
|
|
463
|
+
withHub((hub) =>
|
|
464
|
+
hub.kuaishouAdbSync({
|
|
465
|
+
limits: msg && msg.limits,
|
|
466
|
+
stagingDir: msg && msg.stagingDir,
|
|
467
|
+
displayName: msg && msg.displayName,
|
|
468
|
+
}),
|
|
469
|
+
),
|
|
470
|
+
|
|
471
|
+
// Phase 6e — Bridge dry-run doctor. Spins up Xhs / Toutiao / Kuaishou
|
|
472
|
+
// sign bridges with empty cookie, probes for candidate signing globals,
|
|
473
|
+
// times each phase. No phone needed. Detects SDK rotation BEFORE the
|
|
474
|
+
// user starts a real sync. Desktop-only — CLI/web-shell-no-desktop
|
|
475
|
+
// returns MODULE_LOAD_FAILED.
|
|
476
|
+
"personal-data-hub.bridge-doctor": async () =>
|
|
477
|
+
withHub((hub) => hub.bridgeDoctor()),
|
|
478
|
+
|
|
445
479
|
// ─── Phase 8 — EntityResolver review / merge / unmerge ───────────────
|
|
446
480
|
|
|
447
481
|
"personal-data-hub.review-queue-list": async (msg) =>
|