chainlesschain 0.162.20 → 0.162.25

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 (154) 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-CAdsXHkz.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-Dc7c0bZf.js → ActionButton-JsLRBlBP.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-CR9TWfM-.js → Analytics-DeM96q71.js} +1 -1
  6. package/src/assets/web-panel/assets/{AppLayout-DVfanUep.js → AppLayout-eb_6mT6V.js} +2 -2
  7. package/src/assets/web-panel/assets/{Audit-BxojBqRe.js → Audit-BEOY-CQE.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-CGkreaK9.js → Backup-irFD_DBa.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-B-Q2ZGfz.js → BaseInput-Dq7wtv_k.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-Dd_VC5Av.js → Chat-CstRKWdu.js} +1 -1
  11. package/src/assets/web-panel/assets/{ChatBubbleRenderer-B64kZfRH.js → ChatBubbleRenderer-3BBqk317.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-NYBD1rCW.js → Checkbox-A8D25IT5.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-B3Kqhgv1.js → Codegen-1EfMWeRA.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-2A3Bg2GC.js → Col-Bc08LzOn.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-C84SkDEJ.js → Community-BNnNv7xp.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-Crv3pyZJ.js → Compact-nFjLneHM.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-D4qasxoY.js → Compliance-CZpaaHND.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-Bfbvysw-.js → Cowork-DzHugH3j.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cron-Dkjyiekd.js → Cron-C7YRyiN-.js} +1 -1
  20. package/src/assets/web-panel/assets/{Crosschain-DbJm9Kjd.js → Crosschain-er2h0L0q.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-C2HI6acG.js → DID-DaMdE0A7.js} +1 -1
  22. package/src/assets/web-panel/assets/{Dashboard-BXMY7QVP.js → Dashboard-1z2K_E1B.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-DcOykpfh.js → Dropdown-B9NIqAXf.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-Doo52F2M.js → EmailListRenderer-CBHLFokz.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-cHubZT6U.js → Federation-D2fEJEki.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-B5OVlspi.js → FormItemContext-tmsd70yG.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-OgA46PJq.js → GenericCardRenderer-DW4Lnrsr.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-_OKH_JOq.js → Git-D8aBagAp.js} +1 -1
  29. package/src/assets/web-panel/assets/{Governance-CTByBwSV.js → Governance-CKDAHamy.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-C0nqgySA.js → Inference-BLLp118j.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-CWW4ZIqH.js → KnowledgeGraph-Cawx-yfu.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-DrwqUltr.js → Logs-sWSzp5PY.js} +1 -1
  33. package/src/assets/web-panel/assets/{Marketplace-LJMRx-dm.js → Marketplace-BQMX3dSy.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-F_EjC8j3.js → McpTools-DjlFST3o.js} +1 -1
  35. package/src/assets/web-panel/assets/{Memory-VonDem9-.js → Memory-LZE1wG7m.js} +1 -1
  36. package/src/assets/web-panel/assets/{MobileBridge-CUWxIrfb.js → MobileBridge-BWG47jQ6.js} +1 -1
  37. package/src/assets/web-panel/assets/{MobileProjects-CGOo70lk.js → MobileProjects-DHVv0tYo.js} +1 -1
  38. package/src/assets/web-panel/assets/{Mtc-CqUquFbQ.js → Mtc-Di1hrhM3.js} +1 -1
  39. package/src/assets/web-panel/assets/{MtcAudit-CRVusaip.js → MtcAudit-BowOCi-O.js} +1 -1
  40. package/src/assets/web-panel/assets/{Multisig-B0o8Mv8T.js → Multisig-CgpR0s8E.js} +1 -1
  41. package/src/assets/web-panel/assets/{NLProgramming-CxQINUhu.js → NLProgramming-O4U3Plxd.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-B6Q_apFW.js → Notes-Ce_5C6XT.js} +1 -1
  43. package/src/assets/web-panel/assets/{NotificationSettings-DO-lgp6x.js → NotificationSettings-BVVopkxj.js} +1 -1
  44. package/src/assets/web-panel/assets/{OrderTableRenderer-DxoBoMtz.js → OrderTableRenderer-XW2G50wt.js} +1 -1
  45. package/src/assets/web-panel/assets/{Organization-DqJ_KawH.js → Organization-DaW54kdP.js} +1 -1
  46. package/src/assets/web-panel/assets/{Overflow-Cg4iKODM.js → Overflow-CVTGMUVt.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-n5GPMrwC.js → P2P-CpEwrhBx.js} +1 -1
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-BdZI0EJs.js → PdhVaultBrowser-CDqun-Z9.js} +2 -2
  49. package/src/assets/web-panel/assets/{Permissions-BjtwAIGb.js → Permissions-BsSU3MaL.js} +1 -1
  50. package/src/assets/web-panel/assets/PersonalDataHub-BafcAH5d.js +1 -0
  51. package/src/assets/web-panel/assets/{PersonalDataHub-D0ncF92t.css → PersonalDataHub-DmUc89_0.css} +1 -1
  52. package/src/assets/web-panel/assets/{Pipeline-BZe2-Flj.js → Pipeline-CPCDpDyd.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-OJqMon4c.js → Privacy-oZEQvVWA.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-Dw-5SMG1.js → ProjectInit-CRu5bDNW.js} +1 -1
  55. package/src/assets/web-panel/assets/{ProjectSettings-DxqW44cV.js → ProjectSettings-ComIZ7gQ.js} +1 -1
  56. package/src/assets/web-panel/assets/{Projects-CPOqs2ec.js → Projects-Djr10qFp.js} +1 -1
  57. package/src/assets/web-panel/assets/{Providers-hBfwscEl.js → Providers-CltNzV7e.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-DA3WFFAr.js → QuickAsk-C21t1JEf.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-DMpz3URg.js → Recommend-BjJForJo.js} +1 -1
  60. package/src/assets/web-panel/assets/{Reputation-6-z56FwC.js → Reputation-Cp8Ym-Hk.js} +1 -1
  61. package/src/assets/web-panel/assets/{Row-CL1Y6SfW.js → Row-n8ruyEoT.js} +1 -1
  62. package/src/assets/web-panel/assets/{RssFeed-BIHU2bMf.js → RssFeed-CosaiHvS.js} +1 -1
  63. package/src/assets/web-panel/assets/{Search-CRAfuEX-.js → Search-DUNSNFJZ.js} +1 -1
  64. package/src/assets/web-panel/assets/{Security-CVya3gvH.js → Security-Z3WyX_MB.js} +1 -1
  65. package/src/assets/web-panel/assets/{Services-Cxv7_umF.js → Services-U0m9Oj8V.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skeleton-D_GvsCtv.js → Skeleton-B_M5sv2W.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-DyiEpcvf.js → Skills-NKU9c6LT.js} +1 -1
  68. package/src/assets/web-panel/assets/{Sla-wiglQzXV.js → Sla-DscfpqF8.js} +1 -1
  69. package/src/assets/web-panel/assets/{SpeechSettings-CLtovaa5.js → SpeechSettings-DP0O84ej.js} +1 -1
  70. package/src/assets/web-panel/assets/{SyncSettings-BOgVef1O.js → SyncSettings-P_WEyoH3.js} +1 -1
  71. package/src/assets/web-panel/assets/{Tasks-Ik8AX7-J.js → Tasks-C8YAncnj.js} +1 -1
  72. package/src/assets/web-panel/assets/{Templates-CireiINW.js → Templates-F12-SRc3.js} +1 -1
  73. package/src/assets/web-panel/assets/{Tenant-t7jF9NkQ.js → Tenant-CULHbrwu.js} +1 -1
  74. package/src/assets/web-panel/assets/{Terminal-Btq_O951.js → Terminal-DdYrQ0n3.js} +1 -1
  75. package/src/assets/web-panel/assets/{TimelineRenderer-hC_Piiwy.js → TimelineRenderer-C12_BYAe.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tokens-BTIqA2J1.js → Tokens-8MVLlc0s.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trigger-Dzv2gJpK.js → Trigger-Bl6QmvKw.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trust-B13QpWOh.js → Trust-BH_aEsWy.js} +1 -1
  79. package/src/assets/web-panel/assets/{UkeySign-CFj3UF4G.js → UkeySign-DsX2CeE4.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-CGMOGTQb.js → VideoEditing-DaRdn6e9.js} +1 -1
  81. package/src/assets/web-panel/assets/{Wallet-BkCJ573Y.js → Wallet-DO89dzlU.js} +1 -1
  82. package/src/assets/web-panel/assets/{WebAuthn-C8dCrmGH.js → WebAuthn-KUKNjImY.js} +1 -1
  83. package/src/assets/web-panel/assets/{WorkflowEditor-CclfhxwH.js → WorkflowEditor-CnSoP-Z9.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-CW_X_A9l.js → chat-Jp0M9E52.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-6GtheJb2.js → colors-Bkq4q91x.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-tz7dJapw.js → compact-item-CCt1byem.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-CbiGcRCw.js → createContext-DwRLi0Uq.js} +1 -1
  88. package/src/assets/web-panel/assets/{hasIn-bmNFD3PH.js → hasIn-CTVm-2lw.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-Cb33JbDx.js → index-6OmglXey.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-S9g45I5s.js → index-B2pkXVOU.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-Da2RUdcb.js → index-B3irAeY8.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-BM4slfDH.js → index-B9q3wKEr.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-DeoJYTHX.js → index-BD2U0Ciz.js} +3 -3
  94. package/src/assets/web-panel/assets/{index-CQ7aJ-8s.js → index-BJmiHI2z.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DaK59dFw.js → index-B_U_xDvh.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-zP_o4yKV.js → index-BhSbNePw.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-B3UH6whC.js → index-BjGcDwb2.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DaYfMM7r.js → index-BtzUDs1M.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-Dj1N1idV.js → index-BziGa10S.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-l2VM-W36.js → index-C2ovimhG.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CYIwY9zQ.js → index-C3ZzMEg0.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cs5KWviO.js → index-CDbgmmSF.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-B9sJpsz3.js → index-CFh73FRz.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B6GgMo1k.js → index-CIBi-rGt.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DMGJqZ-o.js → index-CJWYdgmz.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DxHmNJ8S.js → index-CLjugrcu.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CBNnbQxM.js → index-CPnSymMN.js} +1 -1
  108. package/src/assets/web-panel/assets/index-CR2wmKfw.js +1 -0
  109. package/src/assets/web-panel/assets/{index-UId2JAY6.js → index-CdlVwys1.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-B2P1c5yD.js → index-CjJWQqPG.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CzIQ0u4e.js → index-Cvrka2TD.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-KFdfLxVt.js → index-D-UAtktg.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BxCPkuTG.js → index-DC-8E2CG.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-b7sSMzkC.js → index-DFcr_PT2.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C5zlrlPd.js → index-DJegJP2N.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CLk3xgD2.js → index-DRkGL2HJ.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CU0MeCwH.js → index-DRwUdk7I.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CgLF_zD1.js → index-DWQ9NRWg.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CTnIkosg.js → index-DXDSJwDk.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DvWXdCxl.js → index-Db3R-khd.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-UjxsY00y.js → index-MdArO-BF.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CKP-AZD3.js → index-TL3iHPdE.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DxdLksCx.js → index-TqiVFiUf.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-De0xvTEv.js → index-eyVkd-kF.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CV9aPPYM.js → index-hpRtUCjU.js} +1 -1
  126. package/src/assets/web-panel/assets/index-iXk8Oeci.js +1 -0
  127. package/src/assets/web-panel/assets/{index-DK7ee5hC.js → index-zwGGcR7I.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-BTGwT0NA.js → initDefaultProps-9iSe4Eid.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-BheHkG8e.js → motion-CcUz2pq7.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-BkMMNkAw.js → move-3Po9fM71.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-B9zmSFf6.js → omit-BBcRnciw.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-CFNBcA5Z.js → pickAttrs-UCqcWwOy.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-B4mvrUYw.js → placementArrow-boVtHxA4.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-BJqsSjxJ.js → responsiveObserve-V0qPHZOl.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide-Cj78MXSi.js → slide-CZyysA_q.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-Bk6ChXAV.js → statusUtils-BLyCU5L3.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-B_YDSNYf.js → styleChecker-BfXrPW7h.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-q90prOQL.js → useFlexGapSupport-Be2wILDi.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-BtpFJ9kI.js → useFs-CzJYoHOI.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-C3qhf1sQ.js → usePersonalDataHub-__HEfTF9.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-Bz2j0JiG.js → vnode-C0XE0aCE.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-CzaEtel2.js → zoom-CphwCBhl.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-weibo-adb-sync.test.js +163 -0
  147. package/src/commands/__tests__/hub-xhs-adb-sync.test.js +155 -0
  148. package/src/commands/hub.js +600 -0
  149. package/src/gateways/ws/personal-data-hub-protocol.js +50 -0
  150. package/src/lib/host-adb-bridge.js +77 -1
  151. package/src/lib/personal-data-hub-wiring.js +349 -12
  152. package/src/assets/web-panel/assets/PersonalDataHub-Bsf3Wh6n.js +0 -1
  153. package/src/assets/web-panel/assets/index-BFkZAdio.js +0 -1
  154. package/src/assets/web-panel/assets/index-BhgINPAH.js +0 -1
@@ -977,6 +977,499 @@ 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 3a — `cc hub weibo-adb-sync`
1197
+ *
1198
+ * Pulls m.weibo.cn cookies from the user's Android Weibo App, fetches
1199
+ * UID + 3 endpoints (posts/favourites/follows), ingests via social-weibo
1200
+ * adapter snapshot mode. Inline tip per typed reason codes.
1201
+ */
1202
+ async function cmdWeiboAdbSync(options) {
1203
+ try {
1204
+ const hub = await (options._getHub || getHub)();
1205
+ const result = await hub.weiboAdbSync({
1206
+ stagingDir: options.stagingDir,
1207
+ displayName: options.displayName,
1208
+ limits:
1209
+ options.limitPost || options.limitFavourite || options.limitFollow
1210
+ ? {
1211
+ post: parsePositiveInt(options.limitPost),
1212
+ favourite: parsePositiveInt(options.limitFavourite),
1213
+ follow: parsePositiveInt(options.limitFollow),
1214
+ }
1215
+ : undefined,
1216
+ });
1217
+ if (options.json) {
1218
+ printJson(result);
1219
+ return;
1220
+ }
1221
+ if (!result.ok) {
1222
+ logger.log(chalk.red(`✗ weibo-adb-sync failed: ${result.reason}`));
1223
+ logger.log(chalk.gray(` ${result.message || ""}`));
1224
+ if (result.reason === "BRIDGE_UNAVAILABLE") {
1225
+ logger.log(
1226
+ chalk.gray(
1227
+ " Install Android Platform Tools or set ADB_PATH=/path/to/adb",
1228
+ ),
1229
+ );
1230
+ } else if (result.reason === "WEIBO_NO_ROOT") {
1231
+ logger.log(
1232
+ chalk.gray(
1233
+ " Phone needs Magisk root — Weibo release APK isn't debuggable",
1234
+ ),
1235
+ );
1236
+ } else if (result.reason === "WEIBO_NOT_INSTALLED") {
1237
+ logger.log(
1238
+ chalk.gray(
1239
+ " Install Weibo App on the phone + log in once, then retry",
1240
+ ),
1241
+ );
1242
+ } else if (result.reason === "WEIBO_COOKIES_INCOMPLETE") {
1243
+ logger.log(
1244
+ chalk.gray(
1245
+ " SUB cookie missing — relog on the Weibo App (or app uses non-default WebView profile dir)",
1246
+ ),
1247
+ );
1248
+ } else if (
1249
+ result.reason === "WEIBO_COOKIES_TRUNCATED" ||
1250
+ result.reason === "WEIBO_NOT_SQLITE"
1251
+ ) {
1252
+ logger.log(
1253
+ chalk.gray(
1254
+ " ADB stream may be corrupted; unplug + replug USB and retry",
1255
+ ),
1256
+ );
1257
+ }
1258
+ process.exitCode = 1;
1259
+ return;
1260
+ }
1261
+ const report = result.report || {};
1262
+ const wb = report.weibo || {};
1263
+ const counts = wb.eventCounts || {};
1264
+ logger.log(chalk.green(`✓ weibo-adb-sync succeeded`));
1265
+ logger.log(` uid: ${chalk.cyan(wb.uid || "(uid fetch failed)")}`);
1266
+ logger.log(` posts: ${counts.post || 0}`);
1267
+ logger.log(` favourites: ${counts.favourite || 0}`);
1268
+ logger.log(` follows: ${counts.follow || 0}`);
1269
+ logger.log(` total: ${counts.total || 0}`);
1270
+ logger.log(` status: ${report.status || "?"}`);
1271
+ logger.log(` rawCount: ${report.rawCount || 0}`);
1272
+ if (wb.uidFetchFailed) {
1273
+ logger.log(
1274
+ chalk.yellow(
1275
+ ` ⚠ /api/config returned login=false — cookie expired or anti-bot redirect (lastErrorCode=${wb.lastErrorCode})`,
1276
+ ),
1277
+ );
1278
+ } else if (wb.lastErrorCode) {
1279
+ logger.log(
1280
+ chalk.yellow(
1281
+ ` ⚠ partial: lastErrorCode=${wb.lastErrorCode} (${wb.lastErrorMessage || "?"})`,
1282
+ ),
1283
+ );
1284
+ }
1285
+ if (wb.cleanupFailed) {
1286
+ logger.log(chalk.gray(` (note: staging cleanup failed — non-fatal)`));
1287
+ }
1288
+ } catch (err) {
1289
+ fail(null, err, options.json);
1290
+ }
1291
+ }
1292
+
1293
+ /**
1294
+ * Phase 2a — `cc hub douyin-adb-sync`
1295
+ *
1296
+ * Pulls <uid>_im.db from the user's Android Douyin App, parses msg +
1297
+ * SIMPLE_USER (abrignoni DFIR schema), ingests via social-douyin adapter
1298
+ * snapshot mode. Inline tip per 9 typed reason codes.
1299
+ */
1300
+ async function cmdDouyinAdbSync(options) {
1301
+ try {
1302
+ const hub = await (options._getHub || getHub)();
1303
+ const result = await hub.douyinAdbSync({
1304
+ uid: options.uid,
1305
+ stagingDir: options.stagingDir,
1306
+ displayName: options.displayName,
1307
+ limits:
1308
+ options.limitMessages || options.limitContacts
1309
+ ? {
1310
+ messages: parsePositiveInt(options.limitMessages),
1311
+ contacts: parsePositiveInt(options.limitContacts),
1312
+ }
1313
+ : undefined,
1314
+ });
1315
+ if (options.json) {
1316
+ printJson(result);
1317
+ return;
1318
+ }
1319
+ if (!result.ok) {
1320
+ logger.log(chalk.red(`✗ douyin-adb-sync failed: ${result.reason}`));
1321
+ logger.log(chalk.gray(` ${result.message || ""}`));
1322
+ if (result.reason === "BRIDGE_UNAVAILABLE") {
1323
+ logger.log(
1324
+ chalk.gray(
1325
+ " Install Android Platform Tools or set ADB_PATH=/path/to/adb",
1326
+ ),
1327
+ );
1328
+ } else if (result.reason === "DOUYIN_NO_ROOT") {
1329
+ logger.log(
1330
+ chalk.gray(
1331
+ " Phone needs Magisk root — Douyin release APK isn't debuggable",
1332
+ ),
1333
+ );
1334
+ } else if (result.reason === "DOUYIN_NOT_INSTALLED") {
1335
+ logger.log(chalk.gray(" Install Douyin App on the phone, then retry"));
1336
+ } else if (result.reason === "DOUYIN_NO_IM_DB") {
1337
+ logger.log(
1338
+ chalk.gray(
1339
+ " Log in to Douyin App + open any chat thread to materialize the IM database",
1340
+ ),
1341
+ );
1342
+ } else if (result.reason === "DOUYIN_MULTIPLE_USERS") {
1343
+ logger.log(
1344
+ chalk.gray(
1345
+ " Multiple accounts on this phone — pass --uid <19-digit> to pick one",
1346
+ ),
1347
+ );
1348
+ }
1349
+ process.exitCode = 1;
1350
+ return;
1351
+ }
1352
+ const report = result.report || {};
1353
+ const dy = report.douyin || {};
1354
+ const counts = dy.eventCounts || {};
1355
+ logger.log(chalk.green(`✓ douyin-adb-sync succeeded`));
1356
+ logger.log(` uid: ${chalk.cyan(dy.uid)}`);
1357
+ logger.log(` messages: ${counts.message || 0}`);
1358
+ logger.log(` contacts: ${counts.contact || 0}`);
1359
+ logger.log(` total: ${counts.total || 0}`);
1360
+ logger.log(` status: ${report.status || "?"}`);
1361
+ logger.log(` rawCount: ${report.rawCount || 0}`);
1362
+ const diag = dy.parserDiagnostic || {};
1363
+ if (!diag.hadMsgTable) {
1364
+ logger.log(
1365
+ chalk.yellow(
1366
+ ` ⚠ msg table not found — Douyin App version may use a different IM schema`,
1367
+ ),
1368
+ );
1369
+ }
1370
+ if (!diag.hadSimpleUserTable) {
1371
+ logger.log(
1372
+ chalk.yellow(
1373
+ ` ⚠ SIMPLE_USER table not found — contacts won't be ingested`,
1374
+ ),
1375
+ );
1376
+ }
1377
+ if (dy.cleanupFailed) {
1378
+ logger.log(chalk.gray(` (note: staging cleanup failed — non-fatal)`));
1379
+ }
1380
+ } catch (err) {
1381
+ fail(null, err, options.json);
1382
+ }
1383
+ }
1384
+
1385
+ /**
1386
+ * Phase 1e — `cc hub bilibili-adb-doctor`
1387
+ *
1388
+ * Dry-run env probe. Runs only the cookies-extraction half of the sync
1389
+ * pipeline (no api.bilibili.com calls, no vault writes) so the user can
1390
+ * confirm root + Bilibili-installed + cookie-complete BEFORE triggering
1391
+ * a real sync. Surfaces the same 9 typed reasons as bilibili-adb-sync
1392
+ * but with a green "✓ ready to sync" message on success.
1393
+ *
1394
+ * Use this as the first command in a real-device E2E session.
1395
+ */
1396
+ async function cmdBilibiliAdbDoctor(options) {
1397
+ try {
1398
+ const hub = await (options._getHub || getHub)();
1399
+ const result = await hub.bilibiliAdbDoctor();
1400
+ if (options.json) {
1401
+ printJson(result);
1402
+ return;
1403
+ }
1404
+ if (!result.ok) {
1405
+ logger.log(chalk.red(`✗ bilibili-adb-doctor: ${result.reason}`));
1406
+ logger.log(chalk.gray(` ${result.message || ""}`));
1407
+ if (result.reason === "BRIDGE_UNAVAILABLE") {
1408
+ logger.log(
1409
+ chalk.gray(
1410
+ " Fix: install Android Platform Tools, or set ADB_PATH=/path/to/adb",
1411
+ ),
1412
+ );
1413
+ } else if (result.reason === "BILIBILI_NO_ROOT") {
1414
+ logger.log(
1415
+ chalk.gray(
1416
+ " Fix: phone needs Magisk root — Bilibili release APK isn't debuggable",
1417
+ ),
1418
+ );
1419
+ } else if (
1420
+ result.reason === "BILIBILI_NOT_INSTALLED_OR_NEVER_LOGGED_IN"
1421
+ ) {
1422
+ logger.log(
1423
+ chalk.gray(" Fix: install Bilibili App + log in once on the phone"),
1424
+ );
1425
+ } else if (result.reason === "BILIBILI_COOKIES_INCOMPLETE") {
1426
+ logger.log(
1427
+ chalk.gray(
1428
+ " Fix: relog on the Bilibili App — required cookies are missing",
1429
+ ),
1430
+ );
1431
+ } else if (
1432
+ result.reason === "BILIBILI_COOKIES_TRUNCATED" ||
1433
+ result.reason === "BILIBILI_NOT_SQLITE"
1434
+ ) {
1435
+ logger.log(
1436
+ chalk.gray(
1437
+ " Fix: ADB stream may be corrupted; unplug + replug USB and retry",
1438
+ ),
1439
+ );
1440
+ }
1441
+ process.exitCode = 1;
1442
+ return;
1443
+ }
1444
+ const diag = result.cookieDiagnostic || {};
1445
+ logger.log(chalk.green(`✓ bilibili-adb-doctor: env ready to sync`));
1446
+ logger.log(` uid: ${chalk.cyan(result.uid)}`);
1447
+ logger.log(` cookies found: ${diag.cookieCount || "?"}`);
1448
+ if (diag.hadEncrypted) {
1449
+ logger.log(
1450
+ chalk.yellow(
1451
+ ` ⚠ encrypted rows: some cookies are Android-Keystore-wrapped (skipped)`,
1452
+ ),
1453
+ );
1454
+ logger.log(
1455
+ chalk.gray(
1456
+ ` This may indicate a newer Bilibili App on Android 14+ using Keystore wrap.`,
1457
+ ),
1458
+ );
1459
+ }
1460
+ logger.log(
1461
+ ` extracted at: ${new Date(result.extractedAt).toISOString()}`,
1462
+ );
1463
+ logger.log(
1464
+ chalk.gray(
1465
+ `\n Next: run \`cc hub bilibili-adb-sync\` to ingest 4 endpoints.`,
1466
+ ),
1467
+ );
1468
+ } catch (err) {
1469
+ fail(null, err, options.json);
1470
+ }
1471
+ }
1472
+
980
1473
  /**
981
1474
  * `cc hub wechat doctor` — env-probe + actionable interpretation +
982
1475
  * inline reference to the Phase 12.9 §5.1 Frida hook trap table.
@@ -1273,6 +1766,108 @@ export function registerHubCommand(program) {
1273
1766
  .option("--json", "Output JSON")
1274
1767
  .action(cmdSyncAll);
1275
1768
 
1769
+ // Phase 1c — Bilibili C 路径 one-shot
1770
+ hub
1771
+ .command("bilibili-adb-sync")
1772
+ .description(
1773
+ "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.",
1774
+ )
1775
+ .option("--limit-history <n>", "Cap history items (default 200)")
1776
+ .option(
1777
+ "--limit-favourite <n>",
1778
+ "Cap favourite items per folder (default 50)",
1779
+ )
1780
+ .option("--limit-dynamic <n>", "Cap dynamic items (default 50)")
1781
+ .option("--limit-follow <n>", "Cap follow items (default 200)")
1782
+ .option(
1783
+ "--display-name <s>",
1784
+ "Account displayName for the snapshot (default empty)",
1785
+ )
1786
+ .option(
1787
+ "--staging-dir <path>",
1788
+ "Custom dir for the temp snapshot JSON (default os.tmpdir())",
1789
+ )
1790
+ .option("--json", "Output JSON")
1791
+ .action(cmdBilibiliAdbSync);
1792
+
1793
+ // Phase 1e — dry-run env probe (cookies only, no API, no vault write)
1794
+ hub
1795
+ .command("bilibili-adb-doctor")
1796
+ .description(
1797
+ "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.",
1798
+ )
1799
+ .option("--json", "Output JSON")
1800
+ .action(cmdBilibiliAdbDoctor);
1801
+
1802
+ // Phase 3a — Weibo C 路径 one-shot (m.weibo.cn cookies + 4 endpoints, sign-less)
1803
+ hub
1804
+ .command("weibo-adb-sync")
1805
+ .description(
1806
+ "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.",
1807
+ )
1808
+ .option("--limit-post <n>", "Cap user-timeline posts (default 100)")
1809
+ .option("--limit-favourite <n>", "Cap favourite items (default 100)")
1810
+ .option("--limit-follow <n>", "Cap follow items (default 200)")
1811
+ .option(
1812
+ "--display-name <s>",
1813
+ "Account displayName for the snapshot (default empty)",
1814
+ )
1815
+ .option(
1816
+ "--staging-dir <path>",
1817
+ "Custom dir for the temp snapshot JSON (default os.tmpdir())",
1818
+ )
1819
+ .option("--json", "Output JSON")
1820
+ .action(cmdWeiboAdbSync);
1821
+
1822
+ // Phase 3c — Xhs C 路径 one-shot (xiaohongshu.com cookies + 4 endpoints with X-S signing)
1823
+ hub
1824
+ .command("xhs-adb-sync")
1825
+ .description(
1826
+ "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.",
1827
+ )
1828
+ .option("--limit-note <n>", "Cap user notes (default 30)")
1829
+ .option("--limit-liked <n>", "Cap liked notes (default 30)")
1830
+ .option("--limit-follow <n>", "Cap follow list (default 100)")
1831
+ .option(
1832
+ "--display-name <s>",
1833
+ "Account displayName for the snapshot (default = xhs nickname)",
1834
+ )
1835
+ .option(
1836
+ "--staging-dir <path>",
1837
+ "Custom dir for the temp snapshot JSON (default os.tmpdir())",
1838
+ )
1839
+ .option("--json", "Output JSON")
1840
+ .action(cmdXhsAdbSync);
1841
+
1842
+ // Phase 2a — Douyin C 路径 one-shot (pull <uid>_im.db → parse abrignoni DFIR schema)
1843
+ hub
1844
+ .command("douyin-adb-sync")
1845
+ .description(
1846
+ "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.",
1847
+ )
1848
+ .option(
1849
+ "--uid <id>",
1850
+ "19-digit Douyin uid — required when multiple accounts logged in on the phone",
1851
+ )
1852
+ .option(
1853
+ "--limit-messages <n>",
1854
+ "Cap msg rows from the IM db (default 10000)",
1855
+ )
1856
+ .option(
1857
+ "--limit-contacts <n>",
1858
+ "Cap SIMPLE_USER rows from the IM db (default 5000)",
1859
+ )
1860
+ .option(
1861
+ "--display-name <s>",
1862
+ "Account displayName for the snapshot (default empty)",
1863
+ )
1864
+ .option(
1865
+ "--staging-dir <path>",
1866
+ "Custom dir for the temp snapshot JSON (default os.tmpdir())",
1867
+ )
1868
+ .option("--json", "Output JSON")
1869
+ .action(cmdDouyinAdbSync);
1870
+
1276
1871
  hub
1277
1872
  .command("rederive")
1278
1873
  .description(
@@ -1515,6 +2110,11 @@ export const _internal = {
1515
2110
  cmdWechatList,
1516
2111
  cmdWechatUnregister,
1517
2112
  cmdWechatDoctor,
2113
+ cmdBilibiliAdbSync,
2114
+ cmdBilibiliAdbDoctor,
2115
+ cmdDouyinAdbSync,
2116
+ cmdWeiboAdbSync,
2117
+ cmdXhsAdbSync,
1518
2118
  interpretWechatProbe,
1519
2119
  _defaultKnownVendors,
1520
2120
  };
@@ -392,6 +392,56 @@ export const PERSONAL_DATA_HUB_HANDLERS = {
392
392
  "personal-data-hub.list-wechat-accounts": async () =>
393
393
  withHub((hub) => hub.listWechatAccounts()),
394
394
 
395
+ // ─── Phase 1c — Bilibili C 路径 one-shot sync ─────────────────────────
396
+ //
397
+ // Pulls cookies from the user's Android Bilibili App via ADB, fetches 4
398
+ // endpoints, ingests as a snapshot. Returns the standard
399
+ // `{ok, report?, reason?, message?}` shape — see hub.bilibiliAdbSync for
400
+ // the full reason taxonomy. UI maps reasons to banner strings.
401
+ "personal-data-hub.bilibili-adb-sync": async (msg) =>
402
+ withHub((hub) =>
403
+ hub.bilibiliAdbSync({
404
+ limits: msg && msg.limits,
405
+ stagingDir: msg && msg.stagingDir,
406
+ displayName: msg && msg.displayName,
407
+ }),
408
+ ),
409
+
410
+ // Phase 1e — dry-run env probe (cookies path only, no API calls / vault writes)
411
+ "personal-data-hub.bilibili-adb-doctor": async () =>
412
+ withHub((hub) => hub.bilibiliAdbDoctor()),
413
+
414
+ // Phase 2a — Douyin C 路径 one-shot sync (db extraction)
415
+ "personal-data-hub.douyin-adb-sync": async (msg) =>
416
+ withHub((hub) =>
417
+ hub.douyinAdbSync({
418
+ uid: msg && msg.uid,
419
+ limits: msg && msg.limits,
420
+ stagingDir: msg && msg.stagingDir,
421
+ displayName: msg && msg.displayName,
422
+ }),
423
+ ),
424
+
425
+ // Phase 3a — Weibo C 路径 one-shot sync (m.weibo.cn cookies + 4 endpoints)
426
+ "personal-data-hub.weibo-adb-sync": async (msg) =>
427
+ withHub((hub) =>
428
+ hub.weiboAdbSync({
429
+ limits: msg && msg.limits,
430
+ stagingDir: msg && msg.stagingDir,
431
+ displayName: msg && msg.displayName,
432
+ }),
433
+ ),
434
+
435
+ // Phase 3c — Xhs C 路径 one-shot sync (xiaohongshu.com cookies + 4 endpoints X-S signed)
436
+ "personal-data-hub.xhs-adb-sync": async (msg) =>
437
+ withHub((hub) =>
438
+ hub.xhsAdbSync({
439
+ limits: msg && msg.limits,
440
+ stagingDir: msg && msg.stagingDir,
441
+ displayName: msg && msg.displayName,
442
+ }),
443
+ ),
444
+
395
445
  // ─── Phase 8 — EntityResolver review / merge / unmerge ───────────────
396
446
 
397
447
  "personal-data-hub.review-queue-list": async (msg) =>