chainlesschain 0.162.19 → 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-VbMLpnCu.js → AIOps-CAdsXHkz.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-BR6BrZ2w.js → ActionButton-JsLRBlBP.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-DWN2iHDA.js → Analytics-DeM96q71.js} +1 -1
  6. package/src/assets/web-panel/assets/{AppLayout-CyBXOO_H.js → AppLayout-eb_6mT6V.js} +2 -2
  7. package/src/assets/web-panel/assets/{Audit-Cu5a4b5S.js → Audit-BEOY-CQE.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-D0vrJYkS.js → Backup-irFD_DBa.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-DnFk3wDi.js → BaseInput-Dq7wtv_k.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-DFMa9lEz.js → Chat-CstRKWdu.js} +1 -1
  11. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CpVazmEP.js → ChatBubbleRenderer-3BBqk317.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-J23wKgNR.js → Checkbox-A8D25IT5.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-Ca6Z4r1p.js → Codegen-1EfMWeRA.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-BZ3XsxN2.js → Col-Bc08LzOn.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-DhK5H5dD.js → Community-BNnNv7xp.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-DjJMyddV.js → Compact-nFjLneHM.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-Bup3Rk9z.js → Compliance-CZpaaHND.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-BqsPMIpo.js → Cowork-DzHugH3j.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cron-Ca69LFf-.js → Cron-C7YRyiN-.js} +1 -1
  20. package/src/assets/web-panel/assets/{Crosschain-mTB21YwK.js → Crosschain-er2h0L0q.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-C1_Eree4.js → DID-DaMdE0A7.js} +1 -1
  22. package/src/assets/web-panel/assets/{Dashboard-L2OWv5io.js → Dashboard-1z2K_E1B.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-CpqNwurh.js → Dropdown-B9NIqAXf.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-Mo7D9ADF.js → EmailListRenderer-CBHLFokz.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-COTeKl2R.js → Federation-D2fEJEki.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-0oTTQ9MI.js → FormItemContext-tmsd70yG.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-DrMs3upM.js → GenericCardRenderer-DW4Lnrsr.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-C6eFUBhz.js → Git-D8aBagAp.js} +1 -1
  29. package/src/assets/web-panel/assets/{Governance-5ivL10kw.js → Governance-CKDAHamy.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-DcERJUS_.js → Inference-BLLp118j.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-Dm1eQ_Hv.js → KnowledgeGraph-Cawx-yfu.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-Cjt-JTuL.js → Logs-sWSzp5PY.js} +1 -1
  33. package/src/assets/web-panel/assets/{Marketplace-fNDRN8YJ.js → Marketplace-BQMX3dSy.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-kKgzl9Bb.js → McpTools-DjlFST3o.js} +1 -1
  35. package/src/assets/web-panel/assets/{Memory-BdJujXCH.js → Memory-LZE1wG7m.js} +1 -1
  36. package/src/assets/web-panel/assets/{MobileBridge-lmvJfnpK.js → MobileBridge-BWG47jQ6.js} +1 -1
  37. package/src/assets/web-panel/assets/{MobileProjects-ZM-DVPhi.js → MobileProjects-DHVv0tYo.js} +1 -1
  38. package/src/assets/web-panel/assets/{Mtc-CeoaWgzU.js → Mtc-Di1hrhM3.js} +1 -1
  39. package/src/assets/web-panel/assets/{MtcAudit-CRvKTLqr.js → MtcAudit-BowOCi-O.js} +1 -1
  40. package/src/assets/web-panel/assets/{Multisig-DnzFw69O.js → Multisig-CgpR0s8E.js} +1 -1
  41. package/src/assets/web-panel/assets/{NLProgramming-0xP2rtGN.js → NLProgramming-O4U3Plxd.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-tJpp3z8e.js → Notes-Ce_5C6XT.js} +1 -1
  43. package/src/assets/web-panel/assets/{NotificationSettings-CRdpEoHX.js → NotificationSettings-BVVopkxj.js} +1 -1
  44. package/src/assets/web-panel/assets/{OrderTableRenderer-BEH9lAIb.js → OrderTableRenderer-XW2G50wt.js} +1 -1
  45. package/src/assets/web-panel/assets/{Organization-Dhwy6dXT.js → Organization-DaW54kdP.js} +1 -1
  46. package/src/assets/web-panel/assets/{Overflow-COwwTMb-.js → Overflow-CVTGMUVt.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-79YWb-cn.js → P2P-CpEwrhBx.js} +1 -1
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-Dhn5S3Vs.js → PdhVaultBrowser-CDqun-Z9.js} +2 -2
  49. package/src/assets/web-panel/assets/{Permissions-ELRvDOlY.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-Dzi1zmAr.js → Pipeline-CPCDpDyd.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-BYzWkaRt.js → Privacy-oZEQvVWA.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-D_5DqLSS.js → ProjectInit-CRu5bDNW.js} +1 -1
  55. package/src/assets/web-panel/assets/{ProjectSettings-DrAAYJgR.js → ProjectSettings-ComIZ7gQ.js} +1 -1
  56. package/src/assets/web-panel/assets/{Projects-BOrF4_X8.js → Projects-Djr10qFp.js} +1 -1
  57. package/src/assets/web-panel/assets/{Providers-DYLWZVe4.js → Providers-CltNzV7e.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-k76cZTaF.js → QuickAsk-C21t1JEf.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-CXM4p6RB.js → Recommend-BjJForJo.js} +1 -1
  60. package/src/assets/web-panel/assets/{Reputation-DLnLpz8L.js → Reputation-Cp8Ym-Hk.js} +1 -1
  61. package/src/assets/web-panel/assets/{Row-BavvTUrL.js → Row-n8ruyEoT.js} +1 -1
  62. package/src/assets/web-panel/assets/{RssFeed-CtCarC8_.js → RssFeed-CosaiHvS.js} +1 -1
  63. package/src/assets/web-panel/assets/{Search-CJcDGc1x.js → Search-DUNSNFJZ.js} +1 -1
  64. package/src/assets/web-panel/assets/{Security-DB3GmnY9.js → Security-Z3WyX_MB.js} +1 -1
  65. package/src/assets/web-panel/assets/{Services-BWEvVHhz.js → Services-U0m9Oj8V.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skeleton-BHbixeGV.js → Skeleton-B_M5sv2W.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-DWwIjvjl.js → Skills-NKU9c6LT.js} +1 -1
  68. package/src/assets/web-panel/assets/{Sla-C1Qu9o0j.js → Sla-DscfpqF8.js} +1 -1
  69. package/src/assets/web-panel/assets/{SpeechSettings-BiEysZg1.js → SpeechSettings-DP0O84ej.js} +1 -1
  70. package/src/assets/web-panel/assets/{SyncSettings-XlOLFUv2.js → SyncSettings-P_WEyoH3.js} +1 -1
  71. package/src/assets/web-panel/assets/{Tasks-BKjOcYxW.js → Tasks-C8YAncnj.js} +1 -1
  72. package/src/assets/web-panel/assets/{Templates-BVKjc9j-.js → Templates-F12-SRc3.js} +1 -1
  73. package/src/assets/web-panel/assets/{Tenant-C33WmSiJ.js → Tenant-CULHbrwu.js} +1 -1
  74. package/src/assets/web-panel/assets/{Terminal-Dl8RJZHg.js → Terminal-DdYrQ0n3.js} +1 -1
  75. package/src/assets/web-panel/assets/{TimelineRenderer-CFN36N3H.js → TimelineRenderer-C12_BYAe.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tokens-Dy2kxiCA.js → Tokens-8MVLlc0s.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trigger-C7bt0leJ.js → Trigger-Bl6QmvKw.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trust-CVKbWEMH.js → Trust-BH_aEsWy.js} +1 -1
  79. package/src/assets/web-panel/assets/{UkeySign-B91QGwbs.js → UkeySign-DsX2CeE4.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-C8xBqRiE.js → VideoEditing-DaRdn6e9.js} +1 -1
  81. package/src/assets/web-panel/assets/{Wallet-RW2IBbpH.js → Wallet-DO89dzlU.js} +1 -1
  82. package/src/assets/web-panel/assets/{WebAuthn-CZPv5ayn.js → WebAuthn-KUKNjImY.js} +1 -1
  83. package/src/assets/web-panel/assets/{WorkflowEditor-CFCwic4a.js → WorkflowEditor-CnSoP-Z9.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-PVfHUaHj.js → chat-Jp0M9E52.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-C4WA0-Iq.js → colors-Bkq4q91x.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-DQfLwjbC.js → compact-item-CCt1byem.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-31PSlu0j.js → createContext-DwRLi0Uq.js} +1 -1
  88. package/src/assets/web-panel/assets/{hasIn-Csltfg47.js → hasIn-CTVm-2lw.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-DfbOwhkA.js → index-6OmglXey.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-DdrYhK__.js → index-B2pkXVOU.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-DuZKKJDi.js → index-B3irAeY8.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-DW-7sqm0.js → index-B9q3wKEr.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-Ba6cQs-y.js → index-BD2U0Ciz.js} +3 -3
  94. package/src/assets/web-panel/assets/{index-CArRkfYM.js → index-BJmiHI2z.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BiFRGYGH.js → index-B_U_xDvh.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-B-aRUTIJ.js → index-BhSbNePw.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BqOmboiA.js → index-BjGcDwb2.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DVHR_To_.js → index-BtzUDs1M.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DpEj71nk.js → index-BziGa10S.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-nAhW-NN4.js → index-C2ovimhG.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DSO9eU_G.js → index-C3ZzMEg0.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cgt2_bmM.js → index-CDbgmmSF.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CQWE2WKS.js → index-CFh73FRz.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CmzuBE2G.js → index-CIBi-rGt.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CIeFJLHG.js → index-CJWYdgmz.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CfWk9Yzf.js → index-CLjugrcu.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BCxhSOsr.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-CEtPU50W.js → index-CdlVwys1.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BLwrQAlK.js → index-CjJWQqPG.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BD-K1g5z.js → index-Cvrka2TD.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DUFwsCsD.js → index-D-UAtktg.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-C4gzD6Ra.js → index-DC-8E2CG.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BCyTEqF7.js → index-DFcr_PT2.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CpXm8DBk.js → index-DJegJP2N.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CdQUx-5Z.js → index-DRkGL2HJ.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DtUcq8Nn.js → index-DRwUdk7I.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Dymm64KM.js → index-DWQ9NRWg.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-41v9Hflm.js → index-DXDSJwDk.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-8IVdEMKr.js → index-Db3R-khd.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-C_k_btzK.js → index-MdArO-BF.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CE8i90GF.js → index-TL3iHPdE.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CbpUjcEf.js → index-TqiVFiUf.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CEbbRpw2.js → index-eyVkd-kF.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-45Iyof95.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-P9rJg2C1.js → index-zwGGcR7I.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-DPk1oFCk.js → initDefaultProps-9iSe4Eid.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-CzQ1k8MU.js → motion-CcUz2pq7.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-BCExaQ0x.js → move-3Po9fM71.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-D71nF5o_.js → omit-BBcRnciw.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-pvCq8tx4.js → pickAttrs-UCqcWwOy.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-D8lSLg_0.js → placementArrow-boVtHxA4.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-ZN6pVHvM.js → responsiveObserve-V0qPHZOl.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide-VHxyZhFr.js → slide-CZyysA_q.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-B0jFTTFP.js → statusUtils-BLyCU5L3.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-Bc6FRYW9.js → styleChecker-BfXrPW7h.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-D4YDiqxr.js → useFlexGapSupport-Be2wILDi.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-CQgQ7iah.js → useFs-CzJYoHOI.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-LDh_Sq7B.js → usePersonalDataHub-__HEfTF9.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-BwPvBDfI.js → vnode-C0XE0aCE.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-r6u1_Sq8.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-DIWbfS5k.js +0 -1
  153. package/src/assets/web-panel/assets/index-3NsH3wNq.js +0 -1
  154. package/src/assets/web-panel/assets/index-DWtc_qRj.js +0 -1
@@ -381,7 +381,71 @@ async function readSnapshot(params, opts) {
381
381
  return stdout;
382
382
  }
383
383
 
384
+ /**
385
+ * Phase B0 — plugin point for platform-specific ADB methods.
386
+ *
387
+ * `opts.extensions` is an optional `{ [methodName]: handler }` map. Each
388
+ * handler is called as `handler(params, ctx)` where `ctx` exposes the
389
+ * shared ADB primitives so platform extensions don't re-implement
390
+ * device-picking or row-parsing:
391
+ *
392
+ * ctx = {
393
+ * adb, // (args, opts) => Promise<stdout>
394
+ * pickDevice, // (opts) => Promise<serial>
395
+ * parseContentQueryRows, // (stdout) => Array<{key:value}>
396
+ * }
397
+ *
398
+ * Resolution order in `invoke(method, params)`:
399
+ * 1. Built-in method (the switch below)
400
+ * 2. Extension method from opts.extensions
401
+ * 3. Throw HostAdbBridgeUnavailableError
402
+ *
403
+ * Built-ins always win — extensions cannot shadow them. This guarantees
404
+ * the multipath plan's Phase 1+ per-platform extensions can't break the
405
+ * already-shipping system-data-android consumer.
406
+ *
407
+ * Example (Phase 1 will land for real):
408
+ * const bridge = createHostAdbBridge({
409
+ * extensions: {
410
+ * "douyin.snapshot": async (params, { adb, pickDevice }) => {
411
+ * const serial = await pickDevice();
412
+ * const uid = params.uid;
413
+ * const out = await adb(
414
+ * ["shell", "su", "-c", `base64 /data/data/com.ss.android.ugc.aweme/databases/${uid}_im.db`],
415
+ * { serial, timeoutMs: 60_000 },
416
+ * );
417
+ * return { snapshotBase64: out };
418
+ * },
419
+ * },
420
+ * });
421
+ * await bridge.invoke("douyin.snapshot", { uid: "1234567890" });
422
+ */
423
+ const BUILTIN_METHODS = new Set([
424
+ "contacts.query",
425
+ "app.list",
426
+ "sms.query",
427
+ "call.query",
428
+ "media.list",
429
+ "snapshot.list",
430
+ "snapshot.read",
431
+ ]);
432
+
384
433
  export function createHostAdbBridge(opts = {}) {
434
+ const extensions = opts.extensions || {};
435
+ // Defense: warn (but allow) when an extension tries to shadow a
436
+ // built-in. Built-ins always win in dispatch — Phase B0 keeps this
437
+ // a soft warning so it surfaces in tests; we can upgrade to a hard
438
+ // throw in Phase 1 if any extension author actually attempts it.
439
+ for (const k of Object.keys(extensions)) {
440
+ if (BUILTIN_METHODS.has(k)) {
441
+ // eslint-disable-next-line no-console
442
+ console.warn(
443
+ `host-adb-bridge: extension "${k}" shadows a built-in method and will be ignored at dispatch`,
444
+ );
445
+ }
446
+ }
447
+ const ctx = { adb, pickDevice, parseContentQueryRows };
448
+
385
449
  return {
386
450
  /**
387
451
  * SystemDataAndroidAdapter._bridgeAvailable() reads this; must be
@@ -393,6 +457,13 @@ export function createHostAdbBridge(opts = {}) {
393
457
  caps() {
394
458
  return { available: true };
395
459
  },
460
+ /**
461
+ * Diagnostic only — reflects extension method names registered for
462
+ * this bridge instance. Useful for `cc doctor`-style introspection.
463
+ */
464
+ extensionMethods() {
465
+ return Object.keys(extensions).filter((k) => !BUILTIN_METHODS.has(k));
466
+ },
396
467
  async invoke(method, params = {}) {
397
468
  switch (method) {
398
469
  case "contacts.query":
@@ -409,10 +480,15 @@ export function createHostAdbBridge(opts = {}) {
409
480
  return await listSnapshots(params, opts);
410
481
  case "snapshot.read":
411
482
  return await readSnapshot(params, opts);
412
- default:
483
+ default: {
484
+ const ext = extensions[method];
485
+ if (typeof ext === "function") {
486
+ return await ext(params, ctx);
487
+ }
413
488
  throw new HostAdbBridgeUnavailableError(
414
489
  `method "${method}" not implemented by host-adb-bridge`,
415
490
  );
491
+ }
416
492
  }
417
493
  },
418
494
  };
@@ -64,6 +64,12 @@ const {
64
64
  JdAdapter,
65
65
  MeituanAdapter,
66
66
  PinduoduoAdapter,
67
+ Train12306Adapter,
68
+ TaobaoAdapter,
69
+ CtripAdapter,
70
+ AmapAdapter,
71
+ TelegramAdapter,
72
+ WhatsAppAdapter,
67
73
  EntityResolver,
68
74
  EntityResolverEmbeddingStage,
69
75
  EntityResolverLLMStage,
@@ -147,6 +153,11 @@ export function resolveHubDir() {
147
153
  }
148
154
 
149
155
  async function initHub() {
156
+ // Phase 1c: hoisted so the hub-level `bilibiliAdbSync` method can reuse
157
+ // the same bridge instance the system-data-android adapter wires below.
158
+ // Single bridge per hub keeps `bilibili.cookies` extension state /
159
+ // adb path resolution / device picking consistent across callers.
160
+ let hostAdbBridge = null;
150
161
  const hubDir = resolveHubDir();
151
162
  mkdirSync(hubDir, { recursive: true });
152
163
  mkdirSync(join(hubDir, "keys"), { recursive: true });
@@ -204,9 +215,20 @@ async function initHub() {
204
215
  // and skip embedding+LLM stages entirely. Rule-stage still runs; the
205
216
  // resolve_queue picks up enqueued pairs later if a host with Ollama is
206
217
  // ever attached (e.g. desktop-side replay).
218
+ //
219
+ // 2026-05-25 — broadened the detection. The old startsWith("/data/data/
220
+ // com.chainlesschain.android") missed two cases:
221
+ // 1. /data/user/0/ — on Android 7+ (multi-user), context.filesDir
222
+ // resolves under /data/user/0/<pkg>/ instead of /data/data/<pkg>/,
223
+ // so the legacy startsWith never matched on real device.
224
+ // 2. Variant suffixes — com.chainlesschain.android.debug,
225
+ // .staging, etc. all have a separate package id but should share
226
+ // the in-APK skip behaviour.
227
+ // Match either path prefix + optional variant suffix in one regex; if
228
+ // the env override is set, honour it unconditionally.
207
229
  const isInAppAndroidCc =
208
230
  typeof process.env.PREFIX === "string" &&
209
- process.env.PREFIX.startsWith("/data/data/com.chainlesschain.android");
231
+ /\/com\.chainlesschain\.android(\.[a-z]+)?\//.test(process.env.PREFIX);
210
232
  const skipEmbeddings =
211
233
  isInAppAndroidCc || process.env.CC_HUB_DISABLE_EMBEDDINGS === "1";
212
234
  try {
@@ -290,7 +312,33 @@ async function initHub() {
290
312
  // + caveats (Windows CRLF parse trap, 0/multi device, ENOENT).
291
313
  try {
292
314
  const { createHostAdbBridge } = await import("./host-adb-bridge.js");
293
- const hostAdbBridge = createHostAdbBridge();
315
+ // Phase 1b: register `bilibili.cookies` extension so the Phase 1c
316
+ // collector can pull WebView cookies from the user's Android Bilibili
317
+ // App. The factory is a pure function — no side effects until the
318
+ // handler is invoked, so cost of always-registering is zero.
319
+ const { createBilibiliCookiesExtension } =
320
+ await import("@chainlesschain/personal-data-hub/adapters/social-bilibili-adb");
321
+ // Phase 2a: register `douyin.pull-im-db` extension so the
322
+ // douyinAdbSync hub method can pull <uid>_im.db cohort from the
323
+ // user's Android Douyin App.
324
+ const { createDouyinDbExtension } =
325
+ await import("@chainlesschain/personal-data-hub/adapters/social-douyin-adb");
326
+ // Phase 3a: register `weibo.cookies` extension for the Weibo
327
+ // C-path collector (m.weibo.cn cookies + 4 HTTP endpoints).
328
+ const { createWeiboCookiesExtension } =
329
+ await import("@chainlesschain/personal-data-hub/adapters/social-weibo-adb");
330
+ // Phase 3c: register `xhs.cookies` extension for the Xhs C-path
331
+ // collector (xiaohongshu.com cookies + 4 endpoints with X-S sign).
332
+ const { createXhsCookiesExtension } =
333
+ await import("@chainlesschain/personal-data-hub/adapters/social-xiaohongshu-adb");
334
+ hostAdbBridge = createHostAdbBridge({
335
+ extensions: {
336
+ "bilibili.cookies": createBilibiliCookiesExtension(),
337
+ "douyin.pull-im-db": createDouyinDbExtension(),
338
+ "weibo.cookies": createWeiboCookiesExtension(),
339
+ "xhs.cookies": createXhsCookiesExtension(),
340
+ },
341
+ });
294
342
  sda._deps.bridgeProvider = () => hostAdbBridge;
295
343
  } catch (_e) {
296
344
  // Bridge module missing or failed to load — leave snapshot-only.
@@ -387,14 +435,21 @@ async function initHub() {
387
435
  // reads it; no per-account credential needed at boot. Each wrapped in
388
436
  // its own try so one broken ctor doesn't cascade.
389
437
  //
390
- // **Deferred** (need per-account credential `<vendor>-accounts.json`
391
- // loader infra similar to email/alipay/wechat, not yet built):
392
- // Train12306Adapter (opts.account.username)
393
- // CtripAdapter (opts.account.email)
394
- // AmapAdapter (opts.account.deviceId)
395
- // TaobaoAdapter (opts.account.userId)
396
- // TelegramAdapter (opts.account.userId)
397
- // WhatsAppAdapter (opts.account.phone)
438
+ // **No more deferred adapters at boot** 2026-05-25 final pass made all
439
+ // sqlite/device-pull adapters (Amap/Telegram/WhatsApp) ctor-optional:
440
+ // account.<x> dropped from required, inputPath alias added. The sqlite
441
+ // sync still requires the user to pre-extract the SQLite db (Telegram
442
+ // cache4.db unencrypted; WhatsApp msgstore.db needs WhatsApp Crypt key;
443
+ // Amap amap.db needs root ADB pull) — but the registry slot is now
444
+ // claimed so syncAdapter("<name>", path) routes correctly instead of
445
+ // "no adapter X" silent swallow. v0.2 followup: Android in-APK extractor
446
+ // for these 3 sqlite adapters (depends on root ADB / device-specific
447
+ // Crypt key recovery; previously blocked the whole code path before).
448
+ // Train12306Adapter moved out of deferred (v0.2 added snapshot mode —
449
+ // account.username OPTIONAL, see adapters/travel-12306/index.js:53-56);
450
+ // Android Kyfw12306LocalCollector ships snapshot JSON via
451
+ // ccRunner.syncAdapter("travel-12306", path) — must be registered here
452
+ // or cc returns "unknown adapter" + UI shows misleading "v0.2 补齐" hint.
398
453
  // Earlier oversight: v5.0.3.84 wired Telegram + WhatsApp here, but their
399
454
  // ctors throw without account args, so they were silently swallowed by
400
455
  // try/catch and never actually registered. Removed to make the list
@@ -411,6 +466,12 @@ async function initHub() {
411
466
  JdAdapter,
412
467
  MeituanAdapter,
413
468
  PinduoduoAdapter,
469
+ Train12306Adapter,
470
+ TaobaoAdapter,
471
+ CtripAdapter,
472
+ AmapAdapter,
473
+ TelegramAdapter,
474
+ WhatsAppAdapter,
414
475
  ]) {
415
476
  try {
416
477
  const adapter = new Cls();
@@ -420,6 +481,21 @@ async function initHub() {
420
481
  }
421
482
  }
422
483
 
484
+ // Phase 5.8 — email-imap snapshot mode (2026-05-25): Android
485
+ // EmailLocalCollector does Jakarta Mail IMAP fetch on-device + writes
486
+ // staging JSON; desktop EmailAdapter consumes it via snapshot path
487
+ // (no IMAP-account credential needed at boot). The user-driven
488
+ // `registerEmailAdapter` per-account flow still wires explicit IMAP
489
+ // sessions for desktop-direct IMAP fetch (different code path, both
490
+ // resolve `name === "email-imap"` — registry de-dups by name; the
491
+ // per-account flow wins when the user has registered an account).
492
+ try {
493
+ const emailSnapshot = new EmailAdapter({ snapshotMode: true });
494
+ if (!registry.has(emailSnapshot.name)) registry.register(emailSnapshot);
495
+ } catch (_err) {
496
+ // Continue boot even if snapshot ctor throws (shouldn't happen — no required opts)
497
+ }
498
+
423
499
  // Phase 6: auto-register persisted Alipay accounts.
424
500
  const alipayAccountsPath = join(hubDir, "alipay-accounts.json");
425
501
  const alipayAccounts = loadAlipayAccounts(alipayAccountsPath);
@@ -529,8 +605,12 @@ async function initHub() {
529
605
  if (!account || typeof account !== "object")
530
606
  throw new Error("account required");
531
607
  const adapter = new EmailAdapter({ account, ...opts });
608
+ // Phase 5.8 — if the boot-time snapshot stub claimed the "email-imap"
609
+ // slot, swap it out for this per-account IMAP adapter (user's explicit
610
+ // IMAP credentials should take priority over the Android-snapshot
611
+ // fallback). Both share `name === "email-imap"`.
532
612
  if (registry.has(adapter.name)) {
533
- throw new Error(`adapter name "${adapter.name}" already registered`);
613
+ registry.unregister(adapter.name);
534
614
  }
535
615
  registry.register(adapter);
536
616
  const accounts = loadEmailAccounts(emailAccountsPath);
@@ -550,7 +630,16 @@ async function initHub() {
550
630
  const target = accounts.find((c) => c.account.email === emailAddress);
551
631
  const next = accounts.filter((c) => c.account.email !== emailAddress);
552
632
  saveEmailAccounts(emailAccountsPath, next);
553
- if (target) registry.unregister("email-imap");
633
+ if (target) {
634
+ registry.unregister("email-imap");
635
+ // Phase 5.8 — restore the snapshot stub so Android sync paths still
636
+ // work after the user unregisters their explicit IMAP account.
637
+ try {
638
+ registry.register(new EmailAdapter({ snapshotMode: true }));
639
+ } catch (_err) {
640
+ // Defensive — snapshot ctor has no required opts so this shouldn't fail
641
+ }
642
+ }
554
643
  return { ok: true, removed: !!target };
555
644
  },
556
645
 
@@ -735,6 +824,254 @@ async function initHub() {
735
824
  lastSyncAt: row.lastSyncAt || null,
736
825
  }));
737
826
  },
827
+
828
+ // ─── Phase 1e — Bilibili C 路径 dry-run env probe ────────────────────
829
+ //
830
+ // "Doctor" mode mirrors `cc hub wechat doctor`: runs only the cookies-
831
+ // extraction half of the sync pipeline (no API calls, no vault writes)
832
+ // so the user can confirm root / Bilibili-installed / cookie-complete
833
+ // status before triggering a real sync. Same 9 typed reasons as
834
+ // bilibiliAdbSync — UI maps reasons to the same banners — but with
835
+ // an extra `cookieDiagnostic` payload on success so the doctor can
836
+ // print "found 5 of 5 cookies, no encrypted_value rows skipped, uid=N".
837
+ //
838
+ // Returns one of:
839
+ // {ok: true, uid, extractedAt, cookieDiagnostic: {cookieCount, hadEncrypted}}
840
+ // {ok: false, reason, message} — same reason taxonomy as
841
+ // bilibiliAdbSync; UI re-uses bilibiliReasonMessage()
842
+ async bilibiliAdbDoctor() {
843
+ if (!hostAdbBridge) {
844
+ return {
845
+ ok: false,
846
+ reason: "BRIDGE_UNAVAILABLE",
847
+ message:
848
+ "host-adb-bridge failed to initialize at hub boot — check `adb` is on PATH or set ADB_PATH env var",
849
+ };
850
+ }
851
+ try {
852
+ const result = await hostAdbBridge.invoke("bilibili.cookies");
853
+ return {
854
+ ok: true,
855
+ uid: result.uid,
856
+ extractedAt: result.extractedAt,
857
+ cookieDiagnostic: result.diagnostic || null,
858
+ };
859
+ } catch (err) {
860
+ const msg = err && err.message ? err.message : String(err);
861
+ const m = msg.match(/^(BILIBILI_[A-Z_]+)/);
862
+ return {
863
+ ok: false,
864
+ reason: m ? m[1] : "PROBE_FAILED",
865
+ message: msg,
866
+ };
867
+ }
868
+ },
869
+
870
+ // ─── Phase 1c — Bilibili C 路径 one-shot sync ────────────────────────
871
+ //
872
+ // Pulls cookies via the bilibili.cookies extension (P1a) → fetches
873
+ // history/favourite/dynamic/follow via BilibiliApiClient (P1b) → writes
874
+ // a snapshot JSON → calls registry.syncAdapter("social-bilibili") to
875
+ // ingest into the vault. Always cleans up the staging file even on
876
+ // error. Returns `{ok: true, report}` on success or
877
+ // `{ok: false, reason, message}` on failure with a stable typed reason
878
+ // string the UI can pattern-match (BILIBILI_NO_ROOT /
879
+ // BILIBILI_NOT_INSTALLED_OR_NEVER_LOGGED_IN / BILIBILI_COOKIES_INCOMPLETE
880
+ // / SYNC_FAILED / BRIDGE_UNAVAILABLE).
881
+ async bilibiliAdbSync(opts = {}) {
882
+ if (!hostAdbBridge) {
883
+ return {
884
+ ok: false,
885
+ reason: "BRIDGE_UNAVAILABLE",
886
+ message:
887
+ "host-adb-bridge failed to initialize at hub boot — check `adb` is on PATH or set ADB_PATH env var",
888
+ };
889
+ }
890
+ let collector;
891
+ try {
892
+ const mod =
893
+ await import("@chainlesschain/personal-data-hub/adapters/social-bilibili-adb");
894
+ collector = mod.default ? mod.default : mod;
895
+ } catch (err) {
896
+ return {
897
+ ok: false,
898
+ reason: "MODULE_LOAD_FAILED",
899
+ message: err && err.message ? err.message : String(err),
900
+ };
901
+ }
902
+ try {
903
+ const report = await collector.collectAndSync(
904
+ hostAdbBridge,
905
+ registry,
906
+ opts,
907
+ );
908
+ return { ok: true, report };
909
+ } catch (err) {
910
+ const msg = err && err.message ? err.message : String(err);
911
+ // Extract a typed reason prefix from the BILIBILI_* error codes
912
+ // the cookies-extension throws, falling back to SYNC_FAILED for
913
+ // anything from the API client / registry path.
914
+ const m = msg.match(/^(BILIBILI_[A-Z_]+)/);
915
+ return {
916
+ ok: false,
917
+ reason: m ? m[1] : "SYNC_FAILED",
918
+ message: msg,
919
+ };
920
+ }
921
+ },
922
+
923
+ // ─── Phase 2a — Douyin C 路径 one-shot sync ─────────────────────────
924
+ //
925
+ // Pulls <uid>_im.db cohort via the douyin.pull-im-db extension (P2a) →
926
+ // parses msg + SIMPLE_USER (abrignoni DFIR) → writes snapshot →
927
+ // syncAdapter("social-douyin") snapshot mode.
928
+ //
929
+ // 9 typed reason codes: BRIDGE_UNAVAILABLE / MODULE_LOAD_FAILED /
930
+ // DOUYIN_NO_ROOT / DOUYIN_NOT_INSTALLED / DOUYIN_NO_IM_DB /
931
+ // DOUYIN_MULTIPLE_USERS / DOUYIN_PULL_FAILED / DOUYIN_NOT_SQLITE /
932
+ // SYNC_FAILED.
933
+ async douyinAdbSync(opts = {}) {
934
+ if (!hostAdbBridge) {
935
+ return {
936
+ ok: false,
937
+ reason: "BRIDGE_UNAVAILABLE",
938
+ message:
939
+ "host-adb-bridge failed to initialize at hub boot — check `adb` is on PATH or set ADB_PATH env var",
940
+ };
941
+ }
942
+ let collector;
943
+ try {
944
+ const mod =
945
+ await import("@chainlesschain/personal-data-hub/adapters/social-douyin-adb");
946
+ collector = mod.default ? mod.default : mod;
947
+ } catch (err) {
948
+ return {
949
+ ok: false,
950
+ reason: "MODULE_LOAD_FAILED",
951
+ message: err && err.message ? err.message : String(err),
952
+ };
953
+ }
954
+ try {
955
+ const report = await collector.collectAndSync(
956
+ hostAdbBridge,
957
+ registry,
958
+ opts,
959
+ );
960
+ return { ok: true, report };
961
+ } catch (err) {
962
+ const msg = err && err.message ? err.message : String(err);
963
+ const m = msg.match(/^(DOUYIN_[A-Z_]+)/);
964
+ return {
965
+ ok: false,
966
+ reason: m ? m[1] : "SYNC_FAILED",
967
+ message: msg,
968
+ };
969
+ }
970
+ },
971
+
972
+ // ─── Phase 3a — Weibo C 路径 one-shot sync ──────────────────────────
973
+ //
974
+ // Pulls m.weibo.cn cookies via the weibo.cookies extension → fetchUid
975
+ // (/api/config — cookie has no inline UID) → 3 endpoints (posts /
976
+ // favourites / follows) → snapshot → syncAdapter("social-weibo")
977
+ // snapshot mode. **No WBI signing** (m.weibo.cn mobile API is
978
+ // sign-less).
979
+ //
980
+ // Typed reason codes: BRIDGE_UNAVAILABLE / MODULE_LOAD_FAILED /
981
+ // WEIBO_NO_ROOT / WEIBO_NOT_INSTALLED / WEIBO_COOKIES_EMPTY /
982
+ // WEIBO_COOKIES_TRUNCATED / WEIBO_NOT_SQLITE / WEIBO_COOKIES_INCOMPLETE /
983
+ // WEIBO_BASE64_PARSE / SYNC_FAILED.
984
+ async weiboAdbSync(opts = {}) {
985
+ if (!hostAdbBridge) {
986
+ return {
987
+ ok: false,
988
+ reason: "BRIDGE_UNAVAILABLE",
989
+ message:
990
+ "host-adb-bridge failed to initialize at hub boot — check `adb` is on PATH or set ADB_PATH env var",
991
+ };
992
+ }
993
+ let collector;
994
+ try {
995
+ const mod =
996
+ await import("@chainlesschain/personal-data-hub/adapters/social-weibo-adb");
997
+ collector = mod.default ? mod.default : mod;
998
+ } catch (err) {
999
+ return {
1000
+ ok: false,
1001
+ reason: "MODULE_LOAD_FAILED",
1002
+ message: err && err.message ? err.message : String(err),
1003
+ };
1004
+ }
1005
+ try {
1006
+ const report = await collector.collectAndSync(
1007
+ hostAdbBridge,
1008
+ registry,
1009
+ opts,
1010
+ );
1011
+ return { ok: true, report };
1012
+ } catch (err) {
1013
+ const msg = err && err.message ? err.message : String(err);
1014
+ const m = msg.match(/^(WEIBO_[A-Z_]+)/);
1015
+ return {
1016
+ ok: false,
1017
+ reason: m ? m[1] : "SYNC_FAILED",
1018
+ message: msg,
1019
+ };
1020
+ }
1021
+ },
1022
+
1023
+ // ─── Phase 3c — Xhs C 路径 one-shot sync ────────────────────────────
1024
+ //
1025
+ // Pulls xiaohongshu.com cookies via xhs.cookies extension → fetchMe
1026
+ // (/user/me — no X-S) → 3 endpoints (notes/liked/follows, X-S signed)
1027
+ // → snapshot → syncAdapter("social-xiaohongshu") snapshot mode.
1028
+ //
1029
+ // **X-S signing is best-effort** (~60% GET hit, <30% POST). Endpoint
1030
+ // failures tolerated as partial results — lastErrorCode surfaces the
1031
+ // 461 X-S rejection so UI can recommend "稍后重试".
1032
+ //
1033
+ // Typed reason codes: BRIDGE_UNAVAILABLE / MODULE_LOAD_FAILED /
1034
+ // XHS_NO_ROOT / XHS_NOT_INSTALLED / XHS_COOKIES_EMPTY /
1035
+ // XHS_COOKIES_TRUNCATED / XHS_NOT_SQLITE / XHS_COOKIES_INCOMPLETE /
1036
+ // XHS_BASE64_PARSE / SYNC_FAILED.
1037
+ async xhsAdbSync(opts = {}) {
1038
+ if (!hostAdbBridge) {
1039
+ return {
1040
+ ok: false,
1041
+ reason: "BRIDGE_UNAVAILABLE",
1042
+ message:
1043
+ "host-adb-bridge failed to initialize at hub boot — check `adb` is on PATH or set ADB_PATH env var",
1044
+ };
1045
+ }
1046
+ let collector;
1047
+ try {
1048
+ const mod =
1049
+ await import("@chainlesschain/personal-data-hub/adapters/social-xiaohongshu-adb");
1050
+ collector = mod.default ? mod.default : mod;
1051
+ } catch (err) {
1052
+ return {
1053
+ ok: false,
1054
+ reason: "MODULE_LOAD_FAILED",
1055
+ message: err && err.message ? err.message : String(err),
1056
+ };
1057
+ }
1058
+ try {
1059
+ const report = await collector.collectAndSync(
1060
+ hostAdbBridge,
1061
+ registry,
1062
+ opts,
1063
+ );
1064
+ return { ok: true, report };
1065
+ } catch (err) {
1066
+ const msg = err && err.message ? err.message : String(err);
1067
+ const m = msg.match(/^(XHS_[A-Z_]+)/);
1068
+ return {
1069
+ ok: false,
1070
+ reason: m ? m[1] : "SYNC_FAILED",
1071
+ message: msg,
1072
+ };
1073
+ }
1074
+ },
738
1075
  };
739
1076
  }
740
1077