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.
- package/package.json +2 -2
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-VbMLpnCu.js → AIOps-CAdsXHkz.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BR6BrZ2w.js → ActionButton-JsLRBlBP.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DWN2iHDA.js → Analytics-DeM96q71.js} +1 -1
- package/src/assets/web-panel/assets/{AppLayout-CyBXOO_H.js → AppLayout-eb_6mT6V.js} +2 -2
- package/src/assets/web-panel/assets/{Audit-Cu5a4b5S.js → Audit-BEOY-CQE.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-D0vrJYkS.js → Backup-irFD_DBa.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DnFk3wDi.js → BaseInput-Dq7wtv_k.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DFMa9lEz.js → Chat-CstRKWdu.js} +1 -1
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CpVazmEP.js → ChatBubbleRenderer-3BBqk317.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-J23wKgNR.js → Checkbox-A8D25IT5.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-Ca6Z4r1p.js → Codegen-1EfMWeRA.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BZ3XsxN2.js → Col-Bc08LzOn.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DhK5H5dD.js → Community-BNnNv7xp.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DjJMyddV.js → Compact-nFjLneHM.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bup3Rk9z.js → Compliance-CZpaaHND.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BqsPMIpo.js → Cowork-DzHugH3j.js} +1 -1
- package/src/assets/web-panel/assets/{Cron-Ca69LFf-.js → Cron-C7YRyiN-.js} +1 -1
- package/src/assets/web-panel/assets/{Crosschain-mTB21YwK.js → Crosschain-er2h0L0q.js} +1 -1
- package/src/assets/web-panel/assets/{DID-C1_Eree4.js → DID-DaMdE0A7.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-L2OWv5io.js → Dashboard-1z2K_E1B.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CpqNwurh.js → Dropdown-B9NIqAXf.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Mo7D9ADF.js → EmailListRenderer-CBHLFokz.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-COTeKl2R.js → Federation-D2fEJEki.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-0oTTQ9MI.js → FormItemContext-tmsd70yG.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DrMs3upM.js → GenericCardRenderer-DW4Lnrsr.js} +1 -1
- package/src/assets/web-panel/assets/{Git-C6eFUBhz.js → Git-D8aBagAp.js} +1 -1
- package/src/assets/web-panel/assets/{Governance-5ivL10kw.js → Governance-CKDAHamy.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DcERJUS_.js → Inference-BLLp118j.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Dm1eQ_Hv.js → KnowledgeGraph-Cawx-yfu.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-Cjt-JTuL.js → Logs-sWSzp5PY.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-fNDRN8YJ.js → Marketplace-BQMX3dSy.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-kKgzl9Bb.js → McpTools-DjlFST3o.js} +1 -1
- package/src/assets/web-panel/assets/{Memory-BdJujXCH.js → Memory-LZE1wG7m.js} +1 -1
- package/src/assets/web-panel/assets/{MobileBridge-lmvJfnpK.js → MobileBridge-BWG47jQ6.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-ZM-DVPhi.js → MobileProjects-DHVv0tYo.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CeoaWgzU.js → Mtc-Di1hrhM3.js} +1 -1
- package/src/assets/web-panel/assets/{MtcAudit-CRvKTLqr.js → MtcAudit-BowOCi-O.js} +1 -1
- package/src/assets/web-panel/assets/{Multisig-DnzFw69O.js → Multisig-CgpR0s8E.js} +1 -1
- package/src/assets/web-panel/assets/{NLProgramming-0xP2rtGN.js → NLProgramming-O4U3Plxd.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-tJpp3z8e.js → Notes-Ce_5C6XT.js} +1 -1
- package/src/assets/web-panel/assets/{NotificationSettings-CRdpEoHX.js → NotificationSettings-BVVopkxj.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BEH9lAIb.js → OrderTableRenderer-XW2G50wt.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-Dhwy6dXT.js → Organization-DaW54kdP.js} +1 -1
- package/src/assets/web-panel/assets/{Overflow-COwwTMb-.js → Overflow-CVTGMUVt.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-79YWb-cn.js → P2P-CpEwrhBx.js} +1 -1
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Dhn5S3Vs.js → PdhVaultBrowser-CDqun-Z9.js} +2 -2
- package/src/assets/web-panel/assets/{Permissions-ELRvDOlY.js → Permissions-BsSU3MaL.js} +1 -1
- package/src/assets/web-panel/assets/PersonalDataHub-BafcAH5d.js +1 -0
- package/src/assets/web-panel/assets/{PersonalDataHub-D0ncF92t.css → PersonalDataHub-DmUc89_0.css} +1 -1
- package/src/assets/web-panel/assets/{Pipeline-Dzi1zmAr.js → Pipeline-CPCDpDyd.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BYzWkaRt.js → Privacy-oZEQvVWA.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-D_5DqLSS.js → ProjectInit-CRu5bDNW.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectSettings-DrAAYJgR.js → ProjectSettings-ComIZ7gQ.js} +1 -1
- package/src/assets/web-panel/assets/{Projects-BOrF4_X8.js → Projects-Djr10qFp.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DYLWZVe4.js → Providers-CltNzV7e.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-k76cZTaF.js → QuickAsk-C21t1JEf.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CXM4p6RB.js → Recommend-BjJForJo.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-DLnLpz8L.js → Reputation-Cp8Ym-Hk.js} +1 -1
- package/src/assets/web-panel/assets/{Row-BavvTUrL.js → Row-n8ruyEoT.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CtCarC8_.js → RssFeed-CosaiHvS.js} +1 -1
- package/src/assets/web-panel/assets/{Search-CJcDGc1x.js → Search-DUNSNFJZ.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DB3GmnY9.js → Security-Z3WyX_MB.js} +1 -1
- package/src/assets/web-panel/assets/{Services-BWEvVHhz.js → Services-U0m9Oj8V.js} +1 -1
- package/src/assets/web-panel/assets/{Skeleton-BHbixeGV.js → Skeleton-B_M5sv2W.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DWwIjvjl.js → Skills-NKU9c6LT.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-C1Qu9o0j.js → Sla-DscfpqF8.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BiEysZg1.js → SpeechSettings-DP0O84ej.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-XlOLFUv2.js → SyncSettings-P_WEyoH3.js} +1 -1
- package/src/assets/web-panel/assets/{Tasks-BKjOcYxW.js → Tasks-C8YAncnj.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BVKjc9j-.js → Templates-F12-SRc3.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-C33WmSiJ.js → Tenant-CULHbrwu.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-Dl8RJZHg.js → Terminal-DdYrQ0n3.js} +1 -1
- package/src/assets/web-panel/assets/{TimelineRenderer-CFN36N3H.js → TimelineRenderer-C12_BYAe.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Dy2kxiCA.js → Tokens-8MVLlc0s.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-C7bt0leJ.js → Trigger-Bl6QmvKw.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CVKbWEMH.js → Trust-BH_aEsWy.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B91QGwbs.js → UkeySign-DsX2CeE4.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C8xBqRiE.js → VideoEditing-DaRdn6e9.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-RW2IBbpH.js → Wallet-DO89dzlU.js} +1 -1
- package/src/assets/web-panel/assets/{WebAuthn-CZPv5ayn.js → WebAuthn-KUKNjImY.js} +1 -1
- package/src/assets/web-panel/assets/{WorkflowEditor-CFCwic4a.js → WorkflowEditor-CnSoP-Z9.js} +1 -1
- package/src/assets/web-panel/assets/{chat-PVfHUaHj.js → chat-Jp0M9E52.js} +1 -1
- package/src/assets/web-panel/assets/{colors-C4WA0-Iq.js → colors-Bkq4q91x.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DQfLwjbC.js → compact-item-CCt1byem.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-31PSlu0j.js → createContext-DwRLi0Uq.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-Csltfg47.js → hasIn-CTVm-2lw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfbOwhkA.js → index-6OmglXey.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdrYhK__.js → index-B2pkXVOU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuZKKJDi.js → index-B3irAeY8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DW-7sqm0.js → index-B9q3wKEr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ba6cQs-y.js → index-BD2U0Ciz.js} +3 -3
- package/src/assets/web-panel/assets/{index-CArRkfYM.js → index-BJmiHI2z.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiFRGYGH.js → index-B_U_xDvh.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-aRUTIJ.js → index-BhSbNePw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BqOmboiA.js → index-BjGcDwb2.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVHR_To_.js → index-BtzUDs1M.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpEj71nk.js → index-BziGa10S.js} +1 -1
- package/src/assets/web-panel/assets/{index-nAhW-NN4.js → index-C2ovimhG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSO9eU_G.js → index-C3ZzMEg0.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cgt2_bmM.js → index-CDbgmmSF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQWE2WKS.js → index-CFh73FRz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmzuBE2G.js → index-CIBi-rGt.js} +1 -1
- package/src/assets/web-panel/assets/{index-CIeFJLHG.js → index-CJWYdgmz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfWk9Yzf.js → index-CLjugrcu.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCxhSOsr.js → index-CPnSymMN.js} +1 -1
- package/src/assets/web-panel/assets/index-CR2wmKfw.js +1 -0
- package/src/assets/web-panel/assets/{index-CEtPU50W.js → index-CdlVwys1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLwrQAlK.js → index-CjJWQqPG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BD-K1g5z.js → index-Cvrka2TD.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUFwsCsD.js → index-D-UAtktg.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4gzD6Ra.js → index-DC-8E2CG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCyTEqF7.js → index-DFcr_PT2.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpXm8DBk.js → index-DJegJP2N.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdQUx-5Z.js → index-DRkGL2HJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtUcq8Nn.js → index-DRwUdk7I.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dymm64KM.js → index-DWQ9NRWg.js} +1 -1
- package/src/assets/web-panel/assets/{index-41v9Hflm.js → index-DXDSJwDk.js} +1 -1
- package/src/assets/web-panel/assets/{index-8IVdEMKr.js → index-Db3R-khd.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_k_btzK.js → index-MdArO-BF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CE8i90GF.js → index-TL3iHPdE.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbpUjcEf.js → index-TqiVFiUf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEbbRpw2.js → index-eyVkd-kF.js} +1 -1
- package/src/assets/web-panel/assets/{index-45Iyof95.js → index-hpRtUCjU.js} +1 -1
- package/src/assets/web-panel/assets/index-iXk8Oeci.js +1 -0
- package/src/assets/web-panel/assets/{index-P9rJg2C1.js → index-zwGGcR7I.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DPk1oFCk.js → initDefaultProps-9iSe4Eid.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CzQ1k8MU.js → motion-CcUz2pq7.js} +1 -1
- package/src/assets/web-panel/assets/{move-BCExaQ0x.js → move-3Po9fM71.js} +1 -1
- package/src/assets/web-panel/assets/{omit-D71nF5o_.js → omit-BBcRnciw.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-pvCq8tx4.js → pickAttrs-UCqcWwOy.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-D8lSLg_0.js → placementArrow-boVtHxA4.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-ZN6pVHvM.js → responsiveObserve-V0qPHZOl.js} +1 -1
- package/src/assets/web-panel/assets/{slide-VHxyZhFr.js → slide-CZyysA_q.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-B0jFTTFP.js → statusUtils-BLyCU5L3.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Bc6FRYW9.js → styleChecker-BfXrPW7h.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-D4YDiqxr.js → useFlexGapSupport-Be2wILDi.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CQgQ7iah.js → useFs-CzJYoHOI.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-LDh_Sq7B.js → usePersonalDataHub-__HEfTF9.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BwPvBDfI.js → vnode-C0XE0aCE.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-r6u1_Sq8.js → zoom-CphwCBhl.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/__tests__/hub-bilibili-adb-sync.test.js +350 -0
- package/src/commands/__tests__/hub-douyin-adb-sync.test.js +199 -0
- package/src/commands/__tests__/hub-weibo-adb-sync.test.js +163 -0
- package/src/commands/__tests__/hub-xhs-adb-sync.test.js +155 -0
- package/src/commands/hub.js +600 -0
- package/src/gateways/ws/personal-data-hub-protocol.js +50 -0
- package/src/lib/host-adb-bridge.js +77 -1
- package/src/lib/personal-data-hub-wiring.js +349 -12
- package/src/assets/web-panel/assets/PersonalDataHub-DIWbfS5k.js +0 -1
- package/src/assets/web-panel/assets/index-3NsH3wNq.js +0 -1
- 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
|
|
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
|
-
|
|
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
|
-
// **
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
//
|
|
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
|
-
|
|
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)
|
|
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
|
|