chainlesschain 0.162.20 → 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.
Files changed (156) hide show
  1. package/package.json +2 -2
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-Buh_mYlF.js → AIOps-DEhhAYYj.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-Dc7c0bZf.js → ActionButton-DEyfUz5F.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-CR9TWfM-.js → Analytics-D1rjS61W.js} +1 -1
  6. package/src/assets/web-panel/assets/{AppLayout-DVfanUep.js → AppLayout-CptFJPy6.js} +2 -2
  7. package/src/assets/web-panel/assets/{Audit-BxojBqRe.js → Audit-BG6KQUHY.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-CGkreaK9.js → Backup-D2ikQT54.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-B-Q2ZGfz.js → BaseInput-BG_d8yMt.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-Dd_VC5Av.js → Chat-CcHmGkLY.js} +1 -1
  11. package/src/assets/web-panel/assets/{ChatBubbleRenderer-B64kZfRH.js → ChatBubbleRenderer-DIXrOHbn.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-NYBD1rCW.js → Checkbox-Bx2h6vT3.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-B3Kqhgv1.js → Codegen-o14zzfuK.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-2A3Bg2GC.js → Col-HlZjxy7V.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-C84SkDEJ.js → Community-DXxMflr2.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-Crv3pyZJ.js → Compact-DbswbBjd.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-D4qasxoY.js → Compliance-C7Q4Da2p.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-Bfbvysw-.js → Cowork-BLImywOe.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cron-Dkjyiekd.js → Cron-DpXu1G4m.js} +1 -1
  20. package/src/assets/web-panel/assets/{Crosschain-DbJm9Kjd.js → Crosschain-C4qokeJu.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-C2HI6acG.js → DID-C0BjPiij.js} +1 -1
  22. package/src/assets/web-panel/assets/{Dashboard-BXMY7QVP.js → Dashboard-CsfDXwPl.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-DcOykpfh.js → Dropdown-CiwMUKrx.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-Doo52F2M.js → EmailListRenderer-DX7xfm5a.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-cHubZT6U.js → Federation-CA4Ibsg0.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-B5OVlspi.js → FormItemContext-DvM0lU-7.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-OgA46PJq.js → GenericCardRenderer-REjsnTHA.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-_OKH_JOq.js → Git-ZhnSDQGH.js} +1 -1
  29. package/src/assets/web-panel/assets/{Governance-CTByBwSV.js → Governance-BL8iUV_g.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-C0nqgySA.js → Inference-BONrgLk-.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-CWW4ZIqH.js → KnowledgeGraph-q9SAz7h7.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-DrwqUltr.js → Logs-eHfEFnEZ.js} +1 -1
  33. package/src/assets/web-panel/assets/{Marketplace-LJMRx-dm.js → Marketplace-DzwuVedm.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-F_EjC8j3.js → McpTools-BRoLIeq5.js} +1 -1
  35. package/src/assets/web-panel/assets/{Memory-VonDem9-.js → Memory-DjvAa1Ce.js} +1 -1
  36. package/src/assets/web-panel/assets/{MobileBridge-CUWxIrfb.js → MobileBridge-CGTanjsT.js} +1 -1
  37. package/src/assets/web-panel/assets/{MobileProjects-CGOo70lk.js → MobileProjects-cTccXHwN.js} +1 -1
  38. package/src/assets/web-panel/assets/{Mtc-CqUquFbQ.js → Mtc-CNUe29wS.js} +1 -1
  39. package/src/assets/web-panel/assets/{MtcAudit-CRVusaip.js → MtcAudit-COk24mdH.js} +1 -1
  40. package/src/assets/web-panel/assets/{Multisig-B0o8Mv8T.js → Multisig-CFq2j0BU.js} +1 -1
  41. package/src/assets/web-panel/assets/{NLProgramming-CxQINUhu.js → NLProgramming-dfRr-FGW.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-B6Q_apFW.js → Notes-iM4aoXiw.js} +1 -1
  43. package/src/assets/web-panel/assets/{NotificationSettings-DO-lgp6x.js → NotificationSettings-CeVJ-942.js} +1 -1
  44. package/src/assets/web-panel/assets/{OrderTableRenderer-DxoBoMtz.js → OrderTableRenderer-zQ5jvUZI.js} +1 -1
  45. package/src/assets/web-panel/assets/{Organization-DqJ_KawH.js → Organization-CTSWyMGT.js} +1 -1
  46. package/src/assets/web-panel/assets/{Overflow-Cg4iKODM.js → Overflow---n60sAp.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-n5GPMrwC.js → P2P-BCQurpAB.js} +1 -1
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-BdZI0EJs.js → PdhVaultBrowser-C5L1qAc0.js} +2 -2
  49. package/src/assets/web-panel/assets/{Permissions-BjtwAIGb.js → Permissions-I8sgLLE5.js} +1 -1
  50. package/src/assets/web-panel/assets/PersonalDataHub-CEUfFRCj.js +2 -0
  51. package/src/assets/web-panel/assets/{PersonalDataHub-D0ncF92t.css → PersonalDataHub-CO9-IYDY.css} +1 -1
  52. package/src/assets/web-panel/assets/{Pipeline-BZe2-Flj.js → Pipeline-BTBjJaRd.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-OJqMon4c.js → Privacy-aCs44rpW.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-Dw-5SMG1.js → ProjectInit-DZ2iIlbN.js} +1 -1
  55. package/src/assets/web-panel/assets/{ProjectSettings-DxqW44cV.js → ProjectSettings-oYUtNgHP.js} +1 -1
  56. package/src/assets/web-panel/assets/{Projects-CPOqs2ec.js → Projects-FonFg0ie.js} +1 -1
  57. package/src/assets/web-panel/assets/{Providers-hBfwscEl.js → Providers-CRmOuabY.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-DA3WFFAr.js → QuickAsk-BHd6UZ1A.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-DMpz3URg.js → Recommend-BkzGGuq-.js} +1 -1
  60. package/src/assets/web-panel/assets/{Reputation-6-z56FwC.js → Reputation-Do7cPr9u.js} +1 -1
  61. package/src/assets/web-panel/assets/{Row-CL1Y6SfW.js → Row-D_sR3MmD.js} +1 -1
  62. package/src/assets/web-panel/assets/{RssFeed-BIHU2bMf.js → RssFeed-pWSbZ06l.js} +1 -1
  63. package/src/assets/web-panel/assets/{Search-CRAfuEX-.js → Search-BTL71JSs.js} +1 -1
  64. package/src/assets/web-panel/assets/{Security-CVya3gvH.js → Security-8lbNoqY6.js} +1 -1
  65. package/src/assets/web-panel/assets/{Services-Cxv7_umF.js → Services-yr3QVduo.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skeleton-D_GvsCtv.js → Skeleton-DdnY1ZTz.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-DyiEpcvf.js → Skills-fk24WY3D.js} +1 -1
  68. package/src/assets/web-panel/assets/{Sla-wiglQzXV.js → Sla-bOXRT7wR.js} +1 -1
  69. package/src/assets/web-panel/assets/{SpeechSettings-CLtovaa5.js → SpeechSettings-DjonFUbP.js} +1 -1
  70. package/src/assets/web-panel/assets/{SyncSettings-BOgVef1O.js → SyncSettings-C-7HTl6d.js} +1 -1
  71. package/src/assets/web-panel/assets/{Tasks-Ik8AX7-J.js → Tasks-DDohfzFT.js} +1 -1
  72. package/src/assets/web-panel/assets/{Templates-CireiINW.js → Templates-C8PI_WTt.js} +1 -1
  73. package/src/assets/web-panel/assets/{Tenant-t7jF9NkQ.js → Tenant-CIvbeRkn.js} +1 -1
  74. package/src/assets/web-panel/assets/{Terminal-Btq_O951.js → Terminal-BuIPGugj.js} +1 -1
  75. package/src/assets/web-panel/assets/{TimelineRenderer-hC_Piiwy.js → TimelineRenderer-BefCbTxN.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tokens-BTIqA2J1.js → Tokens-gGAeez46.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trigger-Dzv2gJpK.js → Trigger-C_86E2v7.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trust-B13QpWOh.js → Trust-Beg23C1W.js} +1 -1
  79. package/src/assets/web-panel/assets/{UkeySign-CFj3UF4G.js → UkeySign--sGSaISg.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-CGMOGTQb.js → VideoEditing-DIsnY6D_.js} +1 -1
  81. package/src/assets/web-panel/assets/{Wallet-BkCJ573Y.js → Wallet-a7ECYY0i.js} +1 -1
  82. package/src/assets/web-panel/assets/{WebAuthn-C8dCrmGH.js → WebAuthn-CsVpkUE8.js} +1 -1
  83. package/src/assets/web-panel/assets/{WorkflowEditor-CclfhxwH.js → WorkflowEditor-BOJ4WTgl.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-CW_X_A9l.js → chat-ezCR5ZZX.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-6GtheJb2.js → colors-DSUEQ7GR.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-tz7dJapw.js → compact-item-D4w3DGj_.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-CbiGcRCw.js → createContext-B_hvu-NR.js} +1 -1
  88. package/src/assets/web-panel/assets/{hasIn-bmNFD3PH.js → hasIn-FY775Vpc.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-DxHmNJ8S.js → index-1ki6aTIz.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-UId2JAY6.js → index-B3lbDu2b.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-BM4slfDH.js → index-B9LXVosT.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-b7sSMzkC.js → index-BGdchdRx.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-KFdfLxVt.js → index-BGrm33US.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-B9sJpsz3.js → index-BHPD-_s2.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-B6GgMo1k.js → index-BMnGj-Ui.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-B2P1c5yD.js → index-BSp1hSN8.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-S9g45I5s.js → index-BaZmrJ6j.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Cb33JbDx.js → index-BnvLdkR8.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CQ7aJ-8s.js → index-BzME19Nh.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DeoJYTHX.js → index-C0wDzrUK.js} +3 -3
  101. package/src/assets/web-panel/assets/{index-CBNnbQxM.js → index-CAa3LSKI.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CLk3xgD2.js → index-CE3vDzJj.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-De0xvTEv.js → index-CLp4V3Tb.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CKP-AZD3.js → index-CNUQnJsg.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CzIQ0u4e.js → index-CSbCIyga.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-UjxsY00y.js → index-CUNYcWEF.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CgLF_zD1.js → index-CV-F9a_X.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-C5zlrlPd.js → index-CcvzscCg.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-B3UH6whC.js → index-CdzLJsac.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CU0MeCwH.js → index-Cug_yoee.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DxdLksCx.js → index-CxMArAIk.js} +1 -1
  112. package/src/assets/web-panel/assets/index-CzkEIn_T.js +1 -0
  113. package/src/assets/web-panel/assets/index-D2PcvXPP.js +1 -0
  114. package/src/assets/web-panel/assets/{index-DMGJqZ-o.js → index-D6yJ0RMr.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DaYfMM7r.js → index-DM77-xyl.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CV9aPPYM.js → index-DfU6zsU8.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-l2VM-W36.js → index-DqsQUPY7.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DK7ee5hC.js → index-DqxLCUYa.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DaK59dFw.js → index-DtztktoP.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Cs5KWviO.js → index-Dz8-7APi.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Da2RUdcb.js → index-Enin7rzK.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CYIwY9zQ.js → index-S6A76pR1.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DvWXdCxl.js → index-ZGJo2fz1.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Dj1N1idV.js → index-d0HXotWE.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CTnIkosg.js → index-gAtp_KkQ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-zP_o4yKV.js → index-mlTTTHla.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BxCPkuTG.js → index-wWVM6ENX.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-BTGwT0NA.js → initDefaultProps-BoLcNBz9.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-BheHkG8e.js → motion-ouRvP3eO.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-BkMMNkAw.js → move-BBITvaOV.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-B9zmSFf6.js → omit-D8Yxdvbb.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-CFNBcA5Z.js → pickAttrs-BIUy7mER.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-B4mvrUYw.js → placementArrow-CrNWpNSF.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-BJqsSjxJ.js → responsiveObserve-aHLPN464.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide-Cj78MXSi.js → slide-2qNtePjk.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-Bk6ChXAV.js → statusUtils-FAF3iR3R.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-B_YDSNYf.js → styleChecker-BNFTkrbl.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-q90prOQL.js → useFlexGapSupport-BgdPlW7E.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-BtpFJ9kI.js → useFs-q1QvAh3f.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-C3qhf1sQ.js → usePersonalDataHub-DEZ6cY8C.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-Bz2j0JiG.js → vnode-vp6H-NL_.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-CzaEtel2.js → zoom-UatfJMw4.js} +1 -1
  143. package/src/assets/web-panel/index.html +1 -1
  144. package/src/commands/__tests__/hub-bilibili-adb-sync.test.js +350 -0
  145. package/src/commands/__tests__/hub-douyin-adb-sync.test.js +199 -0
  146. package/src/commands/__tests__/hub-kuaishou-adb-sync.test.js +161 -0
  147. package/src/commands/__tests__/hub-toutiao-adb-sync.test.js +158 -0
  148. package/src/commands/__tests__/hub-weibo-adb-sync.test.js +163 -0
  149. package/src/commands/__tests__/hub-xhs-adb-sync.test.js +155 -0
  150. package/src/commands/hub.js +883 -0
  151. package/src/gateways/ws/personal-data-hub-protocol.js +84 -0
  152. package/src/lib/host-adb-bridge.js +77 -1
  153. package/src/lib/personal-data-hub-wiring.js +471 -12
  154. package/src/assets/web-panel/assets/PersonalDataHub-Bsf3Wh6n.js +0 -1
  155. package/src/assets/web-panel/assets/index-BFkZAdio.js +0 -1
  156. package/src/assets/web-panel/assets/index-BhgINPAH.js +0 -1
@@ -977,6 +977,733 @@ async function cmdWechatList(options) {
977
977
  }
978
978
  }
979
979
 
980
+ /**
981
+ * Phase 1c — `cc hub bilibili-adb-sync`
982
+ *
983
+ * One-shot Bilibili C 路径 sync: pulls cookies from the user's Android
984
+ * Bilibili App via ADB, fetches history/favourite/dynamic/follow, writes
985
+ * a snapshot, and ingests via the existing social-bilibili adapter.
986
+ *
987
+ * Requires:
988
+ * - `adb` on PATH (or ADB_PATH env var) — see host-adb-bridge.js
989
+ * - exactly one Android device attached + authorized (set ADB_SERIAL if
990
+ * multiple are present)
991
+ * - phone rooted (Bilibili release APK isn't debuggable; we use `su -c
992
+ * base64` to read /data/data/tv.danmaku.bili/app_webview/Default/Cookies)
993
+ * - user already logged into the Bilibili App on the phone
994
+ *
995
+ * Common failure reasons (UI maps these to actionable banners):
996
+ * - BRIDGE_UNAVAILABLE — adb missing on host
997
+ * - BILIBILI_NOT_INSTALLED_OR_NEVER_LOGGED_IN — Cookies path absent
998
+ * - BILIBILI_NO_ROOT — phone isn't rooted
999
+ * - BILIBILI_COOKIES_INCOMPLETE — user logged out, or Keystore-wrapped
1000
+ * cookies we can't decrypt yet
1001
+ * - SYNC_FAILED — anything else (HTTP / vault error)
1002
+ */
1003
+ async function cmdBilibiliAdbSync(options) {
1004
+ try {
1005
+ const hub = await (options._getHub || getHub)();
1006
+ const result = await hub.bilibiliAdbSync({
1007
+ stagingDir: options.stagingDir,
1008
+ displayName: options.displayName,
1009
+ limits:
1010
+ options.limitHistory ||
1011
+ options.limitFavourite ||
1012
+ options.limitDynamic ||
1013
+ options.limitFollow
1014
+ ? {
1015
+ history: parsePositiveInt(options.limitHistory),
1016
+ favourite: parsePositiveInt(options.limitFavourite),
1017
+ dynamic: parsePositiveInt(options.limitDynamic),
1018
+ follow: parsePositiveInt(options.limitFollow),
1019
+ }
1020
+ : undefined,
1021
+ });
1022
+ if (options.json) {
1023
+ printJson(result);
1024
+ return;
1025
+ }
1026
+ if (!result.ok) {
1027
+ logger.log(chalk.red(`✗ bilibili-adb-sync failed: ${result.reason}`));
1028
+ logger.log(chalk.gray(` ${result.message || ""}`));
1029
+ // Inline tips for the common reasons — saves a doc lookup.
1030
+ if (result.reason === "BRIDGE_UNAVAILABLE") {
1031
+ logger.log(
1032
+ chalk.gray(
1033
+ " Install Android Platform Tools or set ADB_PATH=/path/to/adb",
1034
+ ),
1035
+ );
1036
+ } else if (result.reason === "BILIBILI_NO_ROOT") {
1037
+ logger.log(
1038
+ chalk.gray(
1039
+ " Bilibili release APK isn't debuggable; root + Magisk required to read its Cookies DB",
1040
+ ),
1041
+ );
1042
+ } else if (
1043
+ result.reason === "BILIBILI_NOT_INSTALLED_OR_NEVER_LOGGED_IN"
1044
+ ) {
1045
+ logger.log(
1046
+ chalk.gray(
1047
+ " Install Bilibili App on the phone + log in once, then retry",
1048
+ ),
1049
+ );
1050
+ } else if (result.reason === "BILIBILI_COOKIES_INCOMPLETE") {
1051
+ logger.log(
1052
+ chalk.gray(
1053
+ " Cookie file is missing required fields — relog on the phone",
1054
+ ),
1055
+ );
1056
+ }
1057
+ process.exitCode = 1;
1058
+ return;
1059
+ }
1060
+ const report = result.report || {};
1061
+ const bili = report.bilibili || {};
1062
+ const counts = bili.eventCounts || {};
1063
+ logger.log(chalk.green(`✓ bilibili-adb-sync succeeded`));
1064
+ logger.log(` uid: ${chalk.cyan(bili.uid)}`);
1065
+ logger.log(` history: ${counts.history || 0}`);
1066
+ logger.log(` favourite: ${counts.favourite || 0}`);
1067
+ logger.log(` dynamic: ${counts.dynamic || 0}`);
1068
+ logger.log(` follow: ${counts.follow || 0}`);
1069
+ logger.log(` total: ${counts.total || 0}`);
1070
+ if (bili.lastErrorCode) {
1071
+ logger.log(
1072
+ chalk.yellow(
1073
+ ` ⚠ partial: lastErrorCode=${bili.lastErrorCode} (${bili.lastErrorMessage || "?"})`,
1074
+ ),
1075
+ );
1076
+ }
1077
+ logger.log(` status: ${report.status || "?"}`);
1078
+ logger.log(` rawCount: ${report.rawCount || 0}`);
1079
+ const ec = report.entityCounts || {};
1080
+ logger.log(` events: ${ec.events || 0}`);
1081
+ if (bili.cleanupFailed) {
1082
+ logger.log(
1083
+ chalk.gray(` (note: staging file cleanup failed — non-fatal)`),
1084
+ );
1085
+ }
1086
+ } catch (err) {
1087
+ fail(null, err, options.json);
1088
+ }
1089
+ }
1090
+
1091
+ /**
1092
+ * Phase 3c — `cc hub xhs-adb-sync`
1093
+ *
1094
+ * Pulls xiaohongshu.com cookies from the user's Android Xhs App, fetches
1095
+ * userId + 3 endpoints (notes/liked/follows, X-S signed best-effort).
1096
+ * Inline tip per typed reason codes + meFetchFailed warning.
1097
+ */
1098
+ async function cmdXhsAdbSync(options) {
1099
+ try {
1100
+ const hub = await (options._getHub || getHub)();
1101
+ const result = await hub.xhsAdbSync({
1102
+ stagingDir: options.stagingDir,
1103
+ displayName: options.displayName,
1104
+ limits:
1105
+ options.limitNote || options.limitLiked || options.limitFollow
1106
+ ? {
1107
+ note: parsePositiveInt(options.limitNote),
1108
+ liked: parsePositiveInt(options.limitLiked),
1109
+ follow: parsePositiveInt(options.limitFollow),
1110
+ }
1111
+ : undefined,
1112
+ });
1113
+ if (options.json) {
1114
+ printJson(result);
1115
+ return;
1116
+ }
1117
+ if (!result.ok) {
1118
+ logger.log(chalk.red(`✗ xhs-adb-sync failed: ${result.reason}`));
1119
+ logger.log(chalk.gray(` ${result.message || ""}`));
1120
+ if (result.reason === "BRIDGE_UNAVAILABLE") {
1121
+ logger.log(
1122
+ chalk.gray(
1123
+ " Install Android Platform Tools or set ADB_PATH=/path/to/adb",
1124
+ ),
1125
+ );
1126
+ } else if (result.reason === "XHS_NO_ROOT") {
1127
+ logger.log(
1128
+ chalk.gray(
1129
+ " Phone needs Magisk root — Xhs release APK isn't debuggable",
1130
+ ),
1131
+ );
1132
+ } else if (result.reason === "XHS_NOT_INSTALLED") {
1133
+ logger.log(
1134
+ chalk.gray(
1135
+ " Install Xiaohongshu App on the phone + log in once, then retry",
1136
+ ),
1137
+ );
1138
+ } else if (result.reason === "XHS_COOKIES_INCOMPLETE") {
1139
+ logger.log(
1140
+ chalk.gray(
1141
+ " a1 / web_session cookie missing — relog on the Xhs App",
1142
+ ),
1143
+ );
1144
+ } else if (
1145
+ result.reason === "XHS_COOKIES_TRUNCATED" ||
1146
+ result.reason === "XHS_NOT_SQLITE"
1147
+ ) {
1148
+ logger.log(
1149
+ chalk.gray(
1150
+ " ADB stream may be corrupted; unplug + replug USB and retry",
1151
+ ),
1152
+ );
1153
+ }
1154
+ process.exitCode = 1;
1155
+ return;
1156
+ }
1157
+ const report = result.report || {};
1158
+ const xhs = report.xhs || {};
1159
+ const counts = xhs.eventCounts || {};
1160
+ logger.log(chalk.green(`✓ xhs-adb-sync succeeded`));
1161
+ logger.log(
1162
+ ` userId: ${chalk.cyan(xhs.userId || "(me fetch failed)")}`,
1163
+ );
1164
+ if (xhs.nickname) {
1165
+ logger.log(` nickname: ${xhs.nickname}`);
1166
+ }
1167
+ logger.log(` notes: ${counts.note || 0}`);
1168
+ logger.log(` liked: ${counts.liked || 0}`);
1169
+ logger.log(` follows: ${counts.follow || 0}`);
1170
+ logger.log(` total: ${counts.total || 0}`);
1171
+ logger.log(` status: ${report.status || "?"}`);
1172
+ logger.log(` rawCount: ${report.rawCount || 0}`);
1173
+ if (xhs.meFetchFailed) {
1174
+ logger.log(
1175
+ chalk.yellow(
1176
+ ` ⚠ /user/me returned no user_id — cookie expired or web_session missing (lastErrorCode=${xhs.lastErrorCode})`,
1177
+ ),
1178
+ );
1179
+ } else if (xhs.lastErrorCode) {
1180
+ // X-S sign best-effort: ~60% GET hit, <30% POST hit; 461 = X-S rejected
1181
+ logger.log(
1182
+ chalk.yellow(
1183
+ ` ⚠ partial: lastErrorCode=${xhs.lastErrorCode} (${xhs.lastErrorMessage || "?"}) — X-S 签名 best-effort, 部分接口 461 可能正常`,
1184
+ ),
1185
+ );
1186
+ }
1187
+ if (xhs.cleanupFailed) {
1188
+ logger.log(chalk.gray(` (note: staging cleanup failed — non-fatal)`));
1189
+ }
1190
+ } catch (err) {
1191
+ fail(null, err, options.json);
1192
+ }
1193
+ }
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
+
1429
+ /**
1430
+ * Phase 3a — `cc hub weibo-adb-sync`
1431
+ *
1432
+ * Pulls m.weibo.cn cookies from the user's Android Weibo App, fetches
1433
+ * UID + 3 endpoints (posts/favourites/follows), ingests via social-weibo
1434
+ * adapter snapshot mode. Inline tip per typed reason codes.
1435
+ */
1436
+ async function cmdWeiboAdbSync(options) {
1437
+ try {
1438
+ const hub = await (options._getHub || getHub)();
1439
+ const result = await hub.weiboAdbSync({
1440
+ stagingDir: options.stagingDir,
1441
+ displayName: options.displayName,
1442
+ limits:
1443
+ options.limitPost || options.limitFavourite || options.limitFollow
1444
+ ? {
1445
+ post: parsePositiveInt(options.limitPost),
1446
+ favourite: parsePositiveInt(options.limitFavourite),
1447
+ follow: parsePositiveInt(options.limitFollow),
1448
+ }
1449
+ : undefined,
1450
+ });
1451
+ if (options.json) {
1452
+ printJson(result);
1453
+ return;
1454
+ }
1455
+ if (!result.ok) {
1456
+ logger.log(chalk.red(`✗ weibo-adb-sync failed: ${result.reason}`));
1457
+ logger.log(chalk.gray(` ${result.message || ""}`));
1458
+ if (result.reason === "BRIDGE_UNAVAILABLE") {
1459
+ logger.log(
1460
+ chalk.gray(
1461
+ " Install Android Platform Tools or set ADB_PATH=/path/to/adb",
1462
+ ),
1463
+ );
1464
+ } else if (result.reason === "WEIBO_NO_ROOT") {
1465
+ logger.log(
1466
+ chalk.gray(
1467
+ " Phone needs Magisk root — Weibo release APK isn't debuggable",
1468
+ ),
1469
+ );
1470
+ } else if (result.reason === "WEIBO_NOT_INSTALLED") {
1471
+ logger.log(
1472
+ chalk.gray(
1473
+ " Install Weibo App on the phone + log in once, then retry",
1474
+ ),
1475
+ );
1476
+ } else if (result.reason === "WEIBO_COOKIES_INCOMPLETE") {
1477
+ logger.log(
1478
+ chalk.gray(
1479
+ " SUB cookie missing — relog on the Weibo App (or app uses non-default WebView profile dir)",
1480
+ ),
1481
+ );
1482
+ } else if (
1483
+ result.reason === "WEIBO_COOKIES_TRUNCATED" ||
1484
+ result.reason === "WEIBO_NOT_SQLITE"
1485
+ ) {
1486
+ logger.log(
1487
+ chalk.gray(
1488
+ " ADB stream may be corrupted; unplug + replug USB and retry",
1489
+ ),
1490
+ );
1491
+ }
1492
+ process.exitCode = 1;
1493
+ return;
1494
+ }
1495
+ const report = result.report || {};
1496
+ const wb = report.weibo || {};
1497
+ const counts = wb.eventCounts || {};
1498
+ logger.log(chalk.green(`✓ weibo-adb-sync succeeded`));
1499
+ logger.log(` uid: ${chalk.cyan(wb.uid || "(uid fetch failed)")}`);
1500
+ logger.log(` posts: ${counts.post || 0}`);
1501
+ logger.log(` favourites: ${counts.favourite || 0}`);
1502
+ logger.log(` follows: ${counts.follow || 0}`);
1503
+ logger.log(` total: ${counts.total || 0}`);
1504
+ logger.log(` status: ${report.status || "?"}`);
1505
+ logger.log(` rawCount: ${report.rawCount || 0}`);
1506
+ if (wb.uidFetchFailed) {
1507
+ logger.log(
1508
+ chalk.yellow(
1509
+ ` ⚠ /api/config returned login=false — cookie expired or anti-bot redirect (lastErrorCode=${wb.lastErrorCode})`,
1510
+ ),
1511
+ );
1512
+ } else if (wb.lastErrorCode) {
1513
+ logger.log(
1514
+ chalk.yellow(
1515
+ ` ⚠ partial: lastErrorCode=${wb.lastErrorCode} (${wb.lastErrorMessage || "?"})`,
1516
+ ),
1517
+ );
1518
+ }
1519
+ if (wb.cleanupFailed) {
1520
+ logger.log(chalk.gray(` (note: staging cleanup failed — non-fatal)`));
1521
+ }
1522
+ } catch (err) {
1523
+ fail(null, err, options.json);
1524
+ }
1525
+ }
1526
+
1527
+ /**
1528
+ * Phase 2a — `cc hub douyin-adb-sync`
1529
+ *
1530
+ * Pulls <uid>_im.db from the user's Android Douyin App, parses msg +
1531
+ * SIMPLE_USER (abrignoni DFIR schema), ingests via social-douyin adapter
1532
+ * snapshot mode. Inline tip per 9 typed reason codes.
1533
+ */
1534
+ async function cmdDouyinAdbSync(options) {
1535
+ try {
1536
+ const hub = await (options._getHub || getHub)();
1537
+ const result = await hub.douyinAdbSync({
1538
+ uid: options.uid,
1539
+ stagingDir: options.stagingDir,
1540
+ displayName: options.displayName,
1541
+ limits:
1542
+ options.limitMessages || options.limitContacts
1543
+ ? {
1544
+ messages: parsePositiveInt(options.limitMessages),
1545
+ contacts: parsePositiveInt(options.limitContacts),
1546
+ }
1547
+ : undefined,
1548
+ });
1549
+ if (options.json) {
1550
+ printJson(result);
1551
+ return;
1552
+ }
1553
+ if (!result.ok) {
1554
+ logger.log(chalk.red(`✗ douyin-adb-sync failed: ${result.reason}`));
1555
+ logger.log(chalk.gray(` ${result.message || ""}`));
1556
+ if (result.reason === "BRIDGE_UNAVAILABLE") {
1557
+ logger.log(
1558
+ chalk.gray(
1559
+ " Install Android Platform Tools or set ADB_PATH=/path/to/adb",
1560
+ ),
1561
+ );
1562
+ } else if (result.reason === "DOUYIN_NO_ROOT") {
1563
+ logger.log(
1564
+ chalk.gray(
1565
+ " Phone needs Magisk root — Douyin release APK isn't debuggable",
1566
+ ),
1567
+ );
1568
+ } else if (result.reason === "DOUYIN_NOT_INSTALLED") {
1569
+ logger.log(chalk.gray(" Install Douyin App on the phone, then retry"));
1570
+ } else if (result.reason === "DOUYIN_NO_IM_DB") {
1571
+ logger.log(
1572
+ chalk.gray(
1573
+ " Log in to Douyin App + open any chat thread to materialize the IM database",
1574
+ ),
1575
+ );
1576
+ } else if (result.reason === "DOUYIN_MULTIPLE_USERS") {
1577
+ logger.log(
1578
+ chalk.gray(
1579
+ " Multiple accounts on this phone — pass --uid <19-digit> to pick one",
1580
+ ),
1581
+ );
1582
+ }
1583
+ process.exitCode = 1;
1584
+ return;
1585
+ }
1586
+ const report = result.report || {};
1587
+ const dy = report.douyin || {};
1588
+ const counts = dy.eventCounts || {};
1589
+ logger.log(chalk.green(`✓ douyin-adb-sync succeeded`));
1590
+ logger.log(` uid: ${chalk.cyan(dy.uid)}`);
1591
+ logger.log(` messages: ${counts.message || 0}`);
1592
+ logger.log(` contacts: ${counts.contact || 0}`);
1593
+ logger.log(` total: ${counts.total || 0}`);
1594
+ logger.log(` status: ${report.status || "?"}`);
1595
+ logger.log(` rawCount: ${report.rawCount || 0}`);
1596
+ const diag = dy.parserDiagnostic || {};
1597
+ if (!diag.hadMsgTable) {
1598
+ logger.log(
1599
+ chalk.yellow(
1600
+ ` ⚠ msg table not found — Douyin App version may use a different IM schema`,
1601
+ ),
1602
+ );
1603
+ }
1604
+ if (!diag.hadSimpleUserTable) {
1605
+ logger.log(
1606
+ chalk.yellow(
1607
+ ` ⚠ SIMPLE_USER table not found — contacts won't be ingested`,
1608
+ ),
1609
+ );
1610
+ }
1611
+ if (dy.cleanupFailed) {
1612
+ logger.log(chalk.gray(` (note: staging cleanup failed — non-fatal)`));
1613
+ }
1614
+ } catch (err) {
1615
+ fail(null, err, options.json);
1616
+ }
1617
+ }
1618
+
1619
+ /**
1620
+ * Phase 1e — `cc hub bilibili-adb-doctor`
1621
+ *
1622
+ * Dry-run env probe. Runs only the cookies-extraction half of the sync
1623
+ * pipeline (no api.bilibili.com calls, no vault writes) so the user can
1624
+ * confirm root + Bilibili-installed + cookie-complete BEFORE triggering
1625
+ * a real sync. Surfaces the same 9 typed reasons as bilibili-adb-sync
1626
+ * but with a green "✓ ready to sync" message on success.
1627
+ *
1628
+ * Use this as the first command in a real-device E2E session.
1629
+ */
1630
+ async function cmdBilibiliAdbDoctor(options) {
1631
+ try {
1632
+ const hub = await (options._getHub || getHub)();
1633
+ const result = await hub.bilibiliAdbDoctor();
1634
+ if (options.json) {
1635
+ printJson(result);
1636
+ return;
1637
+ }
1638
+ if (!result.ok) {
1639
+ logger.log(chalk.red(`✗ bilibili-adb-doctor: ${result.reason}`));
1640
+ logger.log(chalk.gray(` ${result.message || ""}`));
1641
+ if (result.reason === "BRIDGE_UNAVAILABLE") {
1642
+ logger.log(
1643
+ chalk.gray(
1644
+ " Fix: install Android Platform Tools, or set ADB_PATH=/path/to/adb",
1645
+ ),
1646
+ );
1647
+ } else if (result.reason === "BILIBILI_NO_ROOT") {
1648
+ logger.log(
1649
+ chalk.gray(
1650
+ " Fix: phone needs Magisk root — Bilibili release APK isn't debuggable",
1651
+ ),
1652
+ );
1653
+ } else if (
1654
+ result.reason === "BILIBILI_NOT_INSTALLED_OR_NEVER_LOGGED_IN"
1655
+ ) {
1656
+ logger.log(
1657
+ chalk.gray(" Fix: install Bilibili App + log in once on the phone"),
1658
+ );
1659
+ } else if (result.reason === "BILIBILI_COOKIES_INCOMPLETE") {
1660
+ logger.log(
1661
+ chalk.gray(
1662
+ " Fix: relog on the Bilibili App — required cookies are missing",
1663
+ ),
1664
+ );
1665
+ } else if (
1666
+ result.reason === "BILIBILI_COOKIES_TRUNCATED" ||
1667
+ result.reason === "BILIBILI_NOT_SQLITE"
1668
+ ) {
1669
+ logger.log(
1670
+ chalk.gray(
1671
+ " Fix: ADB stream may be corrupted; unplug + replug USB and retry",
1672
+ ),
1673
+ );
1674
+ }
1675
+ process.exitCode = 1;
1676
+ return;
1677
+ }
1678
+ const diag = result.cookieDiagnostic || {};
1679
+ logger.log(chalk.green(`✓ bilibili-adb-doctor: env ready to sync`));
1680
+ logger.log(` uid: ${chalk.cyan(result.uid)}`);
1681
+ logger.log(` cookies found: ${diag.cookieCount || "?"}`);
1682
+ if (diag.hadEncrypted) {
1683
+ logger.log(
1684
+ chalk.yellow(
1685
+ ` ⚠ encrypted rows: some cookies are Android-Keystore-wrapped (skipped)`,
1686
+ ),
1687
+ );
1688
+ logger.log(
1689
+ chalk.gray(
1690
+ ` This may indicate a newer Bilibili App on Android 14+ using Keystore wrap.`,
1691
+ ),
1692
+ );
1693
+ }
1694
+ logger.log(
1695
+ ` extracted at: ${new Date(result.extractedAt).toISOString()}`,
1696
+ );
1697
+ logger.log(
1698
+ chalk.gray(
1699
+ `\n Next: run \`cc hub bilibili-adb-sync\` to ingest 4 endpoints.`,
1700
+ ),
1701
+ );
1702
+ } catch (err) {
1703
+ fail(null, err, options.json);
1704
+ }
1705
+ }
1706
+
980
1707
  /**
981
1708
  * `cc hub wechat doctor` — env-probe + actionable interpretation +
982
1709
  * inline reference to the Phase 12.9 §5.1 Frida hook trap table.
@@ -1273,6 +2000,155 @@ export function registerHubCommand(program) {
1273
2000
  .option("--json", "Output JSON")
1274
2001
  .action(cmdSyncAll);
1275
2002
 
2003
+ // Phase 1c — Bilibili C 路径 one-shot
2004
+ hub
2005
+ .command("bilibili-adb-sync")
2006
+ .description(
2007
+ "Bilibili C 路径: pull cookies via ADB from the user's Android Bilibili App, fetch 4 endpoints, ingest as snapshot. Needs rooted Android + Bilibili App logged in + `adb` on PATH.",
2008
+ )
2009
+ .option("--limit-history <n>", "Cap history items (default 200)")
2010
+ .option(
2011
+ "--limit-favourite <n>",
2012
+ "Cap favourite items per folder (default 50)",
2013
+ )
2014
+ .option("--limit-dynamic <n>", "Cap dynamic items (default 50)")
2015
+ .option("--limit-follow <n>", "Cap follow items (default 200)")
2016
+ .option(
2017
+ "--display-name <s>",
2018
+ "Account displayName for the snapshot (default empty)",
2019
+ )
2020
+ .option(
2021
+ "--staging-dir <path>",
2022
+ "Custom dir for the temp snapshot JSON (default os.tmpdir())",
2023
+ )
2024
+ .option("--json", "Output JSON")
2025
+ .action(cmdBilibiliAdbSync);
2026
+
2027
+ // Phase 1e — dry-run env probe (cookies only, no API, no vault write)
2028
+ hub
2029
+ .command("bilibili-adb-doctor")
2030
+ .description(
2031
+ "Bilibili C 路径 dry-run: probe adb + root + Bilibili App + cookies completeness without triggering a sync. Use this BEFORE `cc hub bilibili-adb-sync` to debug env issues.",
2032
+ )
2033
+ .option("--json", "Output JSON")
2034
+ .action(cmdBilibiliAdbDoctor);
2035
+
2036
+ // Phase 3a — Weibo C 路径 one-shot (m.weibo.cn cookies + 4 endpoints, sign-less)
2037
+ hub
2038
+ .command("weibo-adb-sync")
2039
+ .description(
2040
+ "Weibo C 路径: pull m.weibo.cn cookies via ADB from the user's Android Weibo App, fetch UID + 3 endpoints (posts/favourites/follows), ingest as snapshot. Needs rooted Android + Weibo App logged in + `adb` on PATH. No X-Bogus / WBI signing required.",
2041
+ )
2042
+ .option("--limit-post <n>", "Cap user-timeline posts (default 100)")
2043
+ .option("--limit-favourite <n>", "Cap favourite items (default 100)")
2044
+ .option("--limit-follow <n>", "Cap follow items (default 200)")
2045
+ .option(
2046
+ "--display-name <s>",
2047
+ "Account displayName for the snapshot (default empty)",
2048
+ )
2049
+ .option(
2050
+ "--staging-dir <path>",
2051
+ "Custom dir for the temp snapshot JSON (default os.tmpdir())",
2052
+ )
2053
+ .option("--json", "Output JSON")
2054
+ .action(cmdWeiboAdbSync);
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
+
2103
+ // Phase 3c — Xhs C 路径 one-shot (xiaohongshu.com cookies + 4 endpoints with X-S signing)
2104
+ hub
2105
+ .command("xhs-adb-sync")
2106
+ .description(
2107
+ "Xhs C 路径: pull xiaohongshu.com cookies via ADB from the user's Android Xhs App, fetch userId + 3 endpoints (notes/liked/follows). Best-effort X-S signing (~60% GET hit rate). Needs rooted Android + Xhs App logged in + `adb` on PATH.",
2108
+ )
2109
+ .option("--limit-note <n>", "Cap user notes (default 30)")
2110
+ .option("--limit-liked <n>", "Cap liked notes (default 30)")
2111
+ .option("--limit-follow <n>", "Cap follow list (default 100)")
2112
+ .option(
2113
+ "--display-name <s>",
2114
+ "Account displayName for the snapshot (default = xhs nickname)",
2115
+ )
2116
+ .option(
2117
+ "--staging-dir <path>",
2118
+ "Custom dir for the temp snapshot JSON (default os.tmpdir())",
2119
+ )
2120
+ .option("--json", "Output JSON")
2121
+ .action(cmdXhsAdbSync);
2122
+
2123
+ // Phase 2a — Douyin C 路径 one-shot (pull <uid>_im.db → parse abrignoni DFIR schema)
2124
+ hub
2125
+ .command("douyin-adb-sync")
2126
+ .description(
2127
+ "Douyin C 路径: pull <uid>_im.db cohort via ADB from the user's Android Douyin App, parse msg + SIMPLE_USER (abrignoni DFIR), ingest as snapshot. Needs rooted Android + Douyin App logged in + `adb` on PATH.",
2128
+ )
2129
+ .option(
2130
+ "--uid <id>",
2131
+ "19-digit Douyin uid — required when multiple accounts logged in on the phone",
2132
+ )
2133
+ .option(
2134
+ "--limit-messages <n>",
2135
+ "Cap msg rows from the IM db (default 10000)",
2136
+ )
2137
+ .option(
2138
+ "--limit-contacts <n>",
2139
+ "Cap SIMPLE_USER rows from the IM db (default 5000)",
2140
+ )
2141
+ .option(
2142
+ "--display-name <s>",
2143
+ "Account displayName for the snapshot (default empty)",
2144
+ )
2145
+ .option(
2146
+ "--staging-dir <path>",
2147
+ "Custom dir for the temp snapshot JSON (default os.tmpdir())",
2148
+ )
2149
+ .option("--json", "Output JSON")
2150
+ .action(cmdDouyinAdbSync);
2151
+
1276
2152
  hub
1277
2153
  .command("rederive")
1278
2154
  .description(
@@ -1515,6 +2391,13 @@ export const _internal = {
1515
2391
  cmdWechatList,
1516
2392
  cmdWechatUnregister,
1517
2393
  cmdWechatDoctor,
2394
+ cmdBilibiliAdbSync,
2395
+ cmdBilibiliAdbDoctor,
2396
+ cmdDouyinAdbSync,
2397
+ cmdWeiboAdbSync,
2398
+ cmdXhsAdbSync,
2399
+ cmdToutiaoAdbSync,
2400
+ cmdKuaishouAdbSync,
1518
2401
  interpretWechatProbe,
1519
2402
  _defaultKnownVendors,
1520
2403
  };