chainlesschain 0.162.25 → 0.162.27

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 (157) 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-CAdsXHkz.js → AIOps-D67bnWOm.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-JsLRBlBP.js → ActionButton-z7o1N942.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-DeM96q71.js → Analytics-ygwetD7a.js} +1 -1
  6. package/src/assets/web-panel/assets/{AppLayout-eb_6mT6V.js → AppLayout-DKIKC0vr.js} +2 -2
  7. package/src/assets/web-panel/assets/{Audit-BEOY-CQE.js → Audit-RfvwANd0.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-irFD_DBa.js → Backup-CLC0Npju.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-Dq7wtv_k.js → BaseInput-Cy955ldQ.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-CstRKWdu.js → Chat-Di1G3WpZ.js} +1 -1
  11. package/src/assets/web-panel/assets/{ChatBubbleRenderer-3BBqk317.js → ChatBubbleRenderer-D2CwVV3N.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-A8D25IT5.js → Checkbox-mM6T49x7.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-1EfMWeRA.js → Codegen-CFRknQ28.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-Bc08LzOn.js → Col-Md7VPq32.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-BNnNv7xp.js → Community-B75271cm.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-nFjLneHM.js → Compact-BOrAa1JM.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-CZpaaHND.js → Compliance-CqgTFpLC.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-DzHugH3j.js → Cowork-Bvdzx2lF.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cron-C7YRyiN-.js → Cron-BP3ZLMIx.js} +1 -1
  20. package/src/assets/web-panel/assets/{Crosschain-er2h0L0q.js → Crosschain-Dy8IWecD.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-DaMdE0A7.js → DID-DxnpBszy.js} +1 -1
  22. package/src/assets/web-panel/assets/{Dashboard-1z2K_E1B.js → Dashboard-h8TY-9dT.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-B9NIqAXf.js → Dropdown-CfIh_9pn.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-CBHLFokz.js → EmailListRenderer-egkHFDB9.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-D2fEJEki.js → Federation-iXsC9ljf.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-tmsd70yG.js → FormItemContext-D16LYrK1.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-DW4Lnrsr.js → GenericCardRenderer-B2l7qvIp.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-D8aBagAp.js → Git-DshuXpeI.js} +1 -1
  29. package/src/assets/web-panel/assets/{Governance-CKDAHamy.js → Governance-BzgoQvxX.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-BLLp118j.js → Inference-CMikYc6i.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-Cawx-yfu.js → KnowledgeGraph-DAP_uKkx.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-sWSzp5PY.js → Logs-Zax7aDPt.js} +1 -1
  33. package/src/assets/web-panel/assets/{Marketplace-BQMX3dSy.js → Marketplace-DC8_c3hX.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-DjlFST3o.js → McpTools-CvpCNH6E.js} +1 -1
  35. package/src/assets/web-panel/assets/{Memory-LZE1wG7m.js → Memory-BVgvCyNB.js} +1 -1
  36. package/src/assets/web-panel/assets/{MobileBridge-BWG47jQ6.js → MobileBridge-BvffJp-I.js} +1 -1
  37. package/src/assets/web-panel/assets/{MobileProjects-DHVv0tYo.js → MobileProjects-BBEmZ56X.js} +1 -1
  38. package/src/assets/web-panel/assets/{Mtc-Di1hrhM3.js → Mtc-D4Lg-E10.js} +1 -1
  39. package/src/assets/web-panel/assets/{MtcAudit-BowOCi-O.js → MtcAudit-BpuX4w67.js} +1 -1
  40. package/src/assets/web-panel/assets/{Multisig-CgpR0s8E.js → Multisig-CclH6FTt.js} +1 -1
  41. package/src/assets/web-panel/assets/{NLProgramming-O4U3Plxd.js → NLProgramming-DPBSg0ot.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-Ce_5C6XT.js → Notes-CapzFL8Y.js} +1 -1
  43. package/src/assets/web-panel/assets/{NotificationSettings-BVVopkxj.js → NotificationSettings-Fxts3vW8.js} +1 -1
  44. package/src/assets/web-panel/assets/{OrderTableRenderer-XW2G50wt.js → OrderTableRenderer-DexmnSf8.js} +1 -1
  45. package/src/assets/web-panel/assets/{Organization-DaW54kdP.js → Organization-Cx9wnh2C.js} +1 -1
  46. package/src/assets/web-panel/assets/{Overflow-CVTGMUVt.js → Overflow-uNU3Jy_O.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-CpEwrhBx.js → P2P-DZqGX3zP.js} +1 -1
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-CDqun-Z9.js → PdhVaultBrowser-BbXlJUmU.js} +2 -2
  49. package/src/assets/web-panel/assets/{Permissions-BsSU3MaL.js → Permissions-tSlqIXRk.js} +1 -1
  50. package/src/assets/web-panel/assets/{PersonalDataHub-DmUc89_0.css → PersonalDataHub-CO9-IYDY.css} +1 -1
  51. package/src/assets/web-panel/assets/PersonalDataHub-DdPngiX0.js +2 -0
  52. package/src/assets/web-panel/assets/{Pipeline-CPCDpDyd.js → Pipeline-G-YXvU25.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-oZEQvVWA.js → Privacy-BIDsJhkC.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-CRu5bDNW.js → ProjectInit-DTGzvgQ9.js} +1 -1
  55. package/src/assets/web-panel/assets/{ProjectSettings-ComIZ7gQ.js → ProjectSettings-8QjihLTL.js} +1 -1
  56. package/src/assets/web-panel/assets/{Projects-Djr10qFp.js → Projects-D7S7x7R1.js} +1 -1
  57. package/src/assets/web-panel/assets/Providers-BNI-WkmA.css +1 -0
  58. package/src/assets/web-panel/assets/Providers-DpTXneeZ.js +1 -0
  59. package/src/assets/web-panel/assets/{QuickAsk-C21t1JEf.js → QuickAsk-C83JwW8d.js} +1 -1
  60. package/src/assets/web-panel/assets/{Recommend-BjJForJo.js → Recommend-PO0TXid1.js} +1 -1
  61. package/src/assets/web-panel/assets/{Reputation-Cp8Ym-Hk.js → Reputation-pwXhN9Lv.js} +1 -1
  62. package/src/assets/web-panel/assets/{Row-n8ruyEoT.js → Row-829tJQ3D.js} +1 -1
  63. package/src/assets/web-panel/assets/{RssFeed-CosaiHvS.js → RssFeed-Btnw67Mk.js} +1 -1
  64. package/src/assets/web-panel/assets/{Search-DUNSNFJZ.js → Search-olQ94qFA.js} +1 -1
  65. package/src/assets/web-panel/assets/{Security-Z3WyX_MB.js → Security-C_Vfuu0K.js} +1 -1
  66. package/src/assets/web-panel/assets/{Services-U0m9Oj8V.js → Services-ByfhJMDp.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skeleton-B_M5sv2W.js → Skeleton-CWxck8Jk.js} +1 -1
  68. package/src/assets/web-panel/assets/{Skills-NKU9c6LT.js → Skills-yady6VUJ.js} +1 -1
  69. package/src/assets/web-panel/assets/{Sla-DscfpqF8.js → Sla-Dc_HEsj7.js} +1 -1
  70. package/src/assets/web-panel/assets/{SpeechSettings-DP0O84ej.js → SpeechSettings--vhH3ORV.js} +1 -1
  71. package/src/assets/web-panel/assets/{SyncSettings-P_WEyoH3.js → SyncSettings-GUPyaG2O.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tasks-C8YAncnj.js → Tasks-Sj1OYuDS.js} +1 -1
  73. package/src/assets/web-panel/assets/{Templates-F12-SRc3.js → Templates-rdiV_eFo.js} +1 -1
  74. package/src/assets/web-panel/assets/{Tenant-CULHbrwu.js → Tenant-N76bdElE.js} +1 -1
  75. package/src/assets/web-panel/assets/{Terminal-DdYrQ0n3.js → Terminal-KZ1H0ObH.js} +1 -1
  76. package/src/assets/web-panel/assets/{TimelineRenderer-C12_BYAe.js → TimelineRenderer-D7nI-o49.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tokens-8MVLlc0s.js → Tokens-c2LL4v7q.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trigger-Bl6QmvKw.js → Trigger-B57LuN-B.js} +1 -1
  79. package/src/assets/web-panel/assets/{Trust-BH_aEsWy.js → Trust-BMmdOSoP.js} +1 -1
  80. package/src/assets/web-panel/assets/{UkeySign-DsX2CeE4.js → UkeySign-CtGUfuxi.js} +1 -1
  81. package/src/assets/web-panel/assets/{VideoEditing-DaRdn6e9.js → VideoEditing-CzrCzzLF.js} +1 -1
  82. package/src/assets/web-panel/assets/{Wallet-DO89dzlU.js → Wallet-Ciiwlppn.js} +1 -1
  83. package/src/assets/web-panel/assets/{WebAuthn-KUKNjImY.js → WebAuthn-BPFoLjnF.js} +1 -1
  84. package/src/assets/web-panel/assets/{WorkflowEditor-CnSoP-Z9.js → WorkflowEditor-Bf8UZPRx.js} +1 -1
  85. package/src/assets/web-panel/assets/{chat-Jp0M9E52.js → chat-aoVkknKT.js} +1 -1
  86. package/src/assets/web-panel/assets/{colors-Bkq4q91x.js → colors-CKv2MZMK.js} +1 -1
  87. package/src/assets/web-panel/assets/{compact-item-CCt1byem.js → compact-item-1FPS5mTP.js} +1 -1
  88. package/src/assets/web-panel/assets/{createContext-DwRLi0Uq.js → createContext-o2GXZ1sO.js} +1 -1
  89. package/src/assets/web-panel/assets/{hasIn-CTVm-2lw.js → hasIn-CMm7HgH-.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-TqiVFiUf.js → index-866hrngI.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-B9q3wKEr.js → index-8AzFTShP.js} +1 -1
  92. package/src/assets/web-panel/assets/index-BHtt98Qo.js +1 -0
  93. package/src/assets/web-panel/assets/{index-Cvrka2TD.js → index-BQcTgRAZ.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DJegJP2N.js → index-BSqFVnSI.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CIBi-rGt.js → index-BUWd4FFK.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DRwUdk7I.js → index-Bb7YRgoc.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-hpRtUCjU.js → index-BbJDxap_.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DC-8E2CG.js → index-BwA5hOgO.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CPnSymMN.js → index-CFgWbJuM.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CDbgmmSF.js → index-CXDN0kh7.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BhSbNePw.js → index-CXHs9New.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DXDSJwDk.js → index-CalorQK0.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BziGa10S.js → index-CcfXX0ID.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B3irAeY8.js → index-CdDX6sEY.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DWQ9NRWg.js → index-Ch-sPaT4.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-MdArO-BF.js → index-Chh-oVTw.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-zwGGcR7I.js → index-Cr1ZwF59.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BtzUDs1M.js → index-Cr2EOK21.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-B2pkXVOU.js → index-Crp4UcP-.js} +1 -1
  110. package/src/assets/web-panel/assets/index-Cul_OH-A.js +1 -0
  111. package/src/assets/web-panel/assets/{index-TL3iHPdE.js → index-D1QEfzq_.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BjGcDwb2.js → index-D3aMAQEQ.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B_U_xDvh.js → index-D3bKIme7.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CjJWQqPG.js → index-DCxs8O-B.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Db3R-khd.js → index-DDP25Jlo.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DFcr_PT2.js → index-DT76REiB.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-D-UAtktg.js → index-DZ747YY7.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-C2ovimhG.js → index-De4hAcPZ.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CdlVwys1.js → index-DoVMBKvC.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BD2U0Ciz.js → index-JP5LGd1K.js} +3 -3
  121. package/src/assets/web-panel/assets/{index-BJmiHI2z.js → index-OiF8vib7.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-C3ZzMEg0.js → index-Y_UOzu30.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CFh73FRz.js → index-bJ5UBvQQ.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-eyVkd-kF.js → index-dXE5uCaT.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CLjugrcu.js → index-fyC4Vx7E.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DRkGL2HJ.js → index-g4LWpdR7.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-6OmglXey.js → index-u3FjYN52.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CJWYdgmz.js → index-yhq2Pttf.js} +1 -1
  129. package/src/assets/web-panel/assets/{initDefaultProps-9iSe4Eid.js → initDefaultProps-Co0jgVso.js} +1 -1
  130. package/src/assets/web-panel/assets/{motion-CcUz2pq7.js → motion-Zw-nUuCZ.js} +1 -1
  131. package/src/assets/web-panel/assets/{move-3Po9fM71.js → move-O_Pya0SD.js} +1 -1
  132. package/src/assets/web-panel/assets/{omit-BBcRnciw.js → omit-bqQYnuYR.js} +1 -1
  133. package/src/assets/web-panel/assets/parsers-UEvh_ShA.js +4 -0
  134. package/src/assets/web-panel/assets/{pickAttrs-UCqcWwOy.js → pickAttrs-PeesUh3N.js} +1 -1
  135. package/src/assets/web-panel/assets/{placementArrow-boVtHxA4.js → placementArrow-DoUJMHry.js} +1 -1
  136. package/src/assets/web-panel/assets/{responsiveObserve-V0qPHZOl.js → responsiveObserve-QHPYy6hK.js} +1 -1
  137. package/src/assets/web-panel/assets/{slide-CZyysA_q.js → slide-H_RDRwbZ.js} +1 -1
  138. package/src/assets/web-panel/assets/{statusUtils-BLyCU5L3.js → statusUtils-RuUT_noY.js} +1 -1
  139. package/src/assets/web-panel/assets/{styleChecker-BfXrPW7h.js → styleChecker-DuWlOmft.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFlexGapSupport-Be2wILDi.js → useFlexGapSupport-CJmU3crw.js} +1 -1
  141. package/src/assets/web-panel/assets/{useFs-CzJYoHOI.js → useFs-CBYaEAuH.js} +1 -1
  142. package/src/assets/web-panel/assets/{usePersonalDataHub-__HEfTF9.js → usePersonalDataHub-B0x06BwX.js} +1 -1
  143. package/src/assets/web-panel/assets/{vnode-C0XE0aCE.js → vnode-B2BMGDRL.js} +1 -1
  144. package/src/assets/web-panel/assets/{zoom-CphwCBhl.js → zoom-D_OEfpnp.js} +1 -1
  145. package/src/assets/web-panel/index.html +1 -1
  146. package/src/commands/__tests__/hub-ask.test.js +104 -1
  147. package/src/commands/__tests__/hub-kuaishou-adb-sync.test.js +161 -0
  148. package/src/commands/__tests__/hub-toutiao-adb-sync.test.js +158 -0
  149. package/src/commands/hub.js +353 -1
  150. package/src/gateways/ws/personal-data-hub-protocol.js +34 -0
  151. package/src/lib/personal-data-hub-wiring.js +218 -0
  152. package/src/assets/web-panel/assets/PersonalDataHub-BafcAH5d.js +0 -1
  153. package/src/assets/web-panel/assets/Providers-BEakqcO5.css +0 -1
  154. package/src/assets/web-panel/assets/Providers-CltNzV7e.js +0 -2
  155. package/src/assets/web-panel/assets/index-CR2wmKfw.js +0 -1
  156. package/src/assets/web-panel/assets/index-iXk8Oeci.js +0 -1
  157. package/src/assets/web-panel/assets/parsers-DftYMnlk.js +0 -3
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Phase 6c — cc hub toutiao-adb-sync CLI command unit tests.
3
+ *
4
+ * Mirror of hub-xhs-adb-sync. Covers profileFetchFailed + CLI no-bridge
5
+ * short-circuit banner + lastErrorCode propagation.
6
+ */
7
+
8
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
9
+
10
+ import { _internal } from "../hub.js";
11
+
12
+ let logSpy, errSpy;
13
+ let prevExitCode;
14
+
15
+ function fakeHub(syncResult) {
16
+ return { toutiaoAdbSync: vi.fn(async (_opts) => syncResult) };
17
+ }
18
+
19
+ const HAPPY_RESULT = {
20
+ ok: true,
21
+ report: {
22
+ adapter: "social-toutiao",
23
+ status: "ok",
24
+ rawCount: 26,
25
+ entityCounts: { events: 26, persons: 1, places: 0, items: 0, topics: 0 },
26
+ toutiao: {
27
+ uid: "12345",
28
+ nickname: "Alice",
29
+ eventCounts: {
30
+ profile: 1,
31
+ feed: 15,
32
+ collection: 5,
33
+ search: 5,
34
+ total: 26,
35
+ },
36
+ lastErrorCode: 0,
37
+ lastErrorMessage: null,
38
+ cookieDiagnostic: { cookieCount: 8, hadEncrypted: false },
39
+ profileFetchFailed: false,
40
+ signProviderUsed: "ToutiaoSignBridge",
41
+ signProviderHits: 3,
42
+ signProviderFallbacks: 0,
43
+ cleanupFailed: false,
44
+ },
45
+ },
46
+ };
47
+
48
+ beforeEach(() => {
49
+ logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
50
+ errSpy = vi.spyOn(console, "error").mockImplementation(() => {});
51
+ prevExitCode = process.exitCode;
52
+ process.exitCode = 0;
53
+ });
54
+
55
+ afterEach(() => {
56
+ logSpy.mockRestore();
57
+ errSpy.mockRestore();
58
+ process.exitCode = prevExitCode;
59
+ });
60
+
61
+ describe("cc hub toutiao-adb-sync — happy path", () => {
62
+ it("prints human summary with nickname", async () => {
63
+ const hub = fakeHub(HAPPY_RESULT);
64
+ await _internal.cmdToutiaoAdbSync({ _getHub: async () => hub });
65
+ expect(hub.toutiaoAdbSync).toHaveBeenCalledOnce();
66
+ const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
67
+ expect(out).toMatch(/toutiao-adb-sync succeeded/);
68
+ expect(out).toMatch(/uid:.*12345/);
69
+ expect(out).toMatch(/nickname:\s+Alice/);
70
+ expect(out).toMatch(/profile:\s+1/);
71
+ expect(out).toMatch(/feed:\s+15/);
72
+ expect(out).toMatch(/collection:\s+5/);
73
+ expect(out).toMatch(/search:\s+5/);
74
+ expect(out).toMatch(/total:\s+26/);
75
+ });
76
+
77
+ it("--json outputs raw result", async () => {
78
+ const hub = fakeHub(HAPPY_RESULT);
79
+ await _internal.cmdToutiaoAdbSync({
80
+ _getHub: async () => hub,
81
+ json: true,
82
+ });
83
+ const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
84
+ const parsed = JSON.parse(out);
85
+ expect(parsed.ok).toBe(true);
86
+ expect(parsed.report.toutiao.uid).toBe("12345");
87
+ });
88
+
89
+ it("renders profileFetchFailed warning when set", async () => {
90
+ const result = JSON.parse(JSON.stringify(HAPPY_RESULT));
91
+ result.report.toutiao.profileFetchFailed = true;
92
+ result.report.toutiao.lastErrorCode = 1;
93
+ result.report.toutiao.lastErrorMessage = "token expired";
94
+ result.report.toutiao.uid = null;
95
+ const hub = fakeHub(result);
96
+ await _internal.cmdToutiaoAdbSync({ _getHub: async () => hub });
97
+ const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
98
+ expect(out).toMatch(/passport\/info\/v2 returned no user_id/);
99
+ expect(out).toMatch(/profile fetch failed/);
100
+ });
101
+
102
+ it("renders 'no sign bridge' short-circuit banner in CLI context", async () => {
103
+ const result = JSON.parse(JSON.stringify(HAPPY_RESULT));
104
+ result.report.toutiao.signProviderUsed = "none";
105
+ result.report.toutiao.signProviderHits = 0;
106
+ result.report.toutiao.signProviderFallbacks = 3;
107
+ result.report.toutiao.eventCounts.feed = 0;
108
+ result.report.toutiao.eventCounts.collection = 0;
109
+ result.report.toutiao.eventCounts.search = 0;
110
+ result.report.toutiao.eventCounts.total = 1; // just profile
111
+ result.report.toutiao.lastErrorCode = -99;
112
+ const hub = fakeHub(result);
113
+ await _internal.cmdToutiaoAdbSync({ _getHub: async () => hub });
114
+ const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
115
+ expect(out).toMatch(/3 signed endpoints short-circuited/);
116
+ expect(out).toMatch(/no sign bridge in CLI context/);
117
+ });
118
+
119
+ it("renders lastErrorCode partial-result warning (non-(-99))", async () => {
120
+ const result = JSON.parse(JSON.stringify(HAPPY_RESULT));
121
+ result.report.toutiao.lastErrorCode = 412;
122
+ result.report.toutiao.lastErrorMessage = "HTTP 412";
123
+ const hub = fakeHub(result);
124
+ await _internal.cmdToutiaoAdbSync({ _getHub: async () => hub });
125
+ const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
126
+ expect(out).toMatch(/partial: lastErrorCode=412/);
127
+ });
128
+ });
129
+
130
+ describe("cc hub toutiao-adb-sync — failure paths", () => {
131
+ function failCase(reason, expectedHint) {
132
+ return async () => {
133
+ const hub = fakeHub({ ok: false, reason, message: "synthetic" });
134
+ await _internal.cmdToutiaoAdbSync({ _getHub: async () => hub });
135
+ const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
136
+ expect(out).toMatch(new RegExp(`toutiao-adb-sync failed: ${reason}`));
137
+ if (expectedHint) {
138
+ expect(out).toMatch(expectedHint);
139
+ }
140
+ expect(process.exitCode).toBe(1);
141
+ };
142
+ }
143
+
144
+ it("BRIDGE_UNAVAILABLE banner", failCase("BRIDGE_UNAVAILABLE", /ADB_PATH/));
145
+ it("TOUTIAO_NO_ROOT banner", failCase("TOUTIAO_NO_ROOT", /Magisk root/));
146
+ it(
147
+ "TOUTIAO_NOT_INSTALLED banner",
148
+ failCase("TOUTIAO_NOT_INSTALLED", /com\.ss\.android\.article\.news/),
149
+ );
150
+ it(
151
+ "TOUTIAO_COOKIES_INCOMPLETE banner",
152
+ failCase("TOUTIAO_COOKIES_INCOMPLETE", /sessionid \/ sessionid_ss/),
153
+ );
154
+ it(
155
+ "TOUTIAO_COOKIES_TRUNCATED banner",
156
+ failCase("TOUTIAO_COOKIES_TRUNCATED", /unplug \+ replug USB/),
157
+ );
158
+ });
@@ -18,7 +18,7 @@
18
18
  import chalk from "chalk";
19
19
  import ora from "ora";
20
20
  import { logger } from "../lib/logger.js";
21
- import { getHub } from "../lib/personal-data-hub-wiring.js";
21
+ import { getHub, getHubMinimal } from "../lib/personal-data-hub-wiring.js";
22
22
  import { getAIChatWizard } from "../lib/personal-data-hub-aichat-wizard.js";
23
23
 
24
24
  function printJson(obj) {
@@ -121,6 +121,55 @@ async function cmdAsk(question, options) {
121
121
  }
122
122
  }
123
123
 
124
+ // ─── retrieve-context (LLM-free RAG preflight) ─────────────────────────
125
+ //
126
+ // 2026-05-27 — Bridges Android CLOUD_ANDROID route to RAG. AnalysisEngine
127
+ // already exposes `retrieveContext()` which runs parseQuery → _gatherFacts
128
+ // → buildPrompt and returns the LLM-ready messages WITHOUT calling any
129
+ // LLM. This subcommand surfaces that JSON to stdout so a non-Ollama
130
+ // caller (Android cloud LLM provider — Doubao / DeepSeek / Kimi) can:
131
+ // 1. Spawn `cc hub retrieve-context "<q>" --max-facts 20 --json`
132
+ // 2. Parse JSON → get { messages, factIds, factCount, parsed, ... }
133
+ // 3. POST messages to the cloud LLM provider directly
134
+ // 4. Validate citations from the answer against factIds locally
135
+ //
136
+ // Privacy invariant: retrieveContext does NOT invoke any LLM, so it never
137
+ // touches `acceptNonLocal` — the caller's own gate is the gate. The
138
+ // returned messages contain raw user data (events / persons / items)
139
+ // formatted as the prompt's FACTS block; the caller is responsible for
140
+ // where they POST it.
141
+
142
+ async function cmdRetrieveContext(question, options) {
143
+ try {
144
+ // §S4.1 cold-start optimization — when --minimal (or default to true
145
+ // since this command never needs adapters), use getHubMinimal which
146
+ // skips the 50+ adapter registry + KG/RAG sinks + email account auto-
147
+ // load + ADB extension imports. retrieveContext only touches vault
148
+ // queries + AnalysisEngine, so the full hub init is wasted work.
149
+ // In-APK Android cold-start drops from ~90s to <5s. Tests can still
150
+ // pass _getHub override for stubbing.
151
+ const useMinimal = options.minimal !== false; // default true
152
+ const hub = options._getHub
153
+ ? await options._getHub()
154
+ : useMinimal
155
+ ? await getHubMinimal()
156
+ : await getHub();
157
+ if (!hub.engine) throw new Error("Analysis engine unavailable");
158
+ const maxFacts = parsePositiveInt(options.maxFacts);
159
+ const maxQueryLimit = parsePositiveInt(options.maxQueryLimit);
160
+ const retrieveOptions = { skipAudit: false };
161
+ if (maxFacts !== null) retrieveOptions.maxFacts = maxFacts;
162
+ if (maxQueryLimit !== null) retrieveOptions.maxQueryLimit = maxQueryLimit;
163
+ const result = await hub.engine.retrieveContext(question, retrieveOptions);
164
+ // Always JSON output — this command is machine-only by design. The
165
+ // shape mirrors AnalysisEngine.retrieveContext return + the system
166
+ // prompt baked in (so the caller doesn't need to re-fetch it).
167
+ printJson(result);
168
+ } catch (err) {
169
+ fail(null, err, true /* json */);
170
+ }
171
+ }
172
+
124
173
  // ─── stats ────────────────────────────────────────────────────────────
125
174
 
126
175
  async function cmdStats(options) {
@@ -1192,6 +1241,240 @@ async function cmdXhsAdbSync(options) {
1192
1241
  }
1193
1242
  }
1194
1243
 
1244
+ /**
1245
+ * Phase 6d — `cc hub kuaishou-adb-sync`
1246
+ *
1247
+ * Pulls www.kuaishou.com cookies from the user's Android Kuaishou App,
1248
+ * fetches uid + profile via cookie's api_ph payload (PURE COOKIE PARSE,
1249
+ * no HTTP) + 3 signed GraphQL endpoints (watch / collect / search).
1250
+ * CLI context has NO sign bridge — 3 signed endpoints short-circuit
1251
+ * with lastErrorCode=-99. Desktop wiring upgrades via KuaishouSignBridge.
1252
+ */
1253
+ async function cmdKuaishouAdbSync(options) {
1254
+ try {
1255
+ const hub = await (options._getHub || getHub)();
1256
+ const result = await hub.kuaishouAdbSync({
1257
+ stagingDir: options.stagingDir,
1258
+ displayName: options.displayName,
1259
+ limits:
1260
+ options.limitWatch || options.limitCollect || options.limitSearch
1261
+ ? {
1262
+ watch: parsePositiveInt(options.limitWatch),
1263
+ collect: parsePositiveInt(options.limitCollect),
1264
+ search: parsePositiveInt(options.limitSearch),
1265
+ }
1266
+ : undefined,
1267
+ });
1268
+ if (options.json) {
1269
+ printJson(result);
1270
+ return;
1271
+ }
1272
+ if (!result.ok) {
1273
+ logger.log(chalk.red(`✗ kuaishou-adb-sync failed: ${result.reason}`));
1274
+ logger.log(chalk.gray(` ${result.message || ""}`));
1275
+ if (result.reason === "BRIDGE_UNAVAILABLE") {
1276
+ logger.log(
1277
+ chalk.gray(
1278
+ " Install Android Platform Tools or set ADB_PATH=/path/to/adb",
1279
+ ),
1280
+ );
1281
+ } else if (result.reason === "KUAISHOU_NO_ROOT") {
1282
+ logger.log(
1283
+ chalk.gray(
1284
+ " Phone needs Magisk root — Kuaishou release APK isn't debuggable",
1285
+ ),
1286
+ );
1287
+ } else if (result.reason === "KUAISHOU_NOT_INSTALLED") {
1288
+ logger.log(
1289
+ chalk.gray(
1290
+ " Install 快手 (com.smile.gifmaker, NOT 极速版 com.kuaishou.nebula) + log in once + open any video (WebView populates cookies), then retry",
1291
+ ),
1292
+ );
1293
+ } else if (result.reason === "KUAISHOU_COOKIES_INCOMPLETE") {
1294
+ logger.log(
1295
+ chalk.gray(
1296
+ " userId / kuaishou.web.cp.api_ph missing — relog on the Kuaishou App",
1297
+ ),
1298
+ );
1299
+ } else if (
1300
+ result.reason === "KUAISHOU_COOKIES_TRUNCATED" ||
1301
+ result.reason === "KUAISHOU_NOT_SQLITE"
1302
+ ) {
1303
+ logger.log(
1304
+ chalk.gray(
1305
+ " ADB stream may be corrupted; unplug + replug USB and retry",
1306
+ ),
1307
+ );
1308
+ }
1309
+ process.exitCode = 1;
1310
+ return;
1311
+ }
1312
+ const report = result.report || {};
1313
+ const kuaishou = report.kuaishou || {};
1314
+ const counts = kuaishou.eventCounts || {};
1315
+ logger.log(chalk.green(`✓ kuaishou-adb-sync succeeded`));
1316
+ logger.log(
1317
+ ` uid: ${chalk.cyan(kuaishou.uid || "(profile fetch failed)")}`,
1318
+ );
1319
+ if (kuaishou.nickname) {
1320
+ logger.log(` nickname: ${kuaishou.nickname}`);
1321
+ }
1322
+ logger.log(` profile: ${counts.profile || 0}`);
1323
+ logger.log(` watch: ${counts.watch || 0}`);
1324
+ logger.log(` collect: ${counts.collect || 0}`);
1325
+ logger.log(` search: ${counts.search || 0}`);
1326
+ logger.log(` total: ${counts.total || 0}`);
1327
+ logger.log(` status: ${report.status || "?"}`);
1328
+ logger.log(` rawCount: ${report.rawCount || 0}`);
1329
+ if (kuaishou.profileFetchFailed) {
1330
+ logger.log(
1331
+ chalk.yellow(
1332
+ ` ⚠ cookie 缺 kuaishou.web.cp.api_ph — relog on Kuaishou (lastErrorCode=${kuaishou.lastErrorCode})`,
1333
+ ),
1334
+ );
1335
+ } else if (
1336
+ kuaishou.signProviderUsed === "none" &&
1337
+ counts.watch === 0 &&
1338
+ counts.collect === 0 &&
1339
+ counts.search === 0
1340
+ ) {
1341
+ logger.log(
1342
+ chalk.yellow(
1343
+ ` ⚠ 3 signed endpoints short-circuited (no sign bridge in CLI context) — run from desktop app to enable __NS_sig3 via Electron WebContentsView`,
1344
+ ),
1345
+ );
1346
+ } else if (kuaishou.lastErrorCode) {
1347
+ logger.log(
1348
+ chalk.yellow(
1349
+ ` ⚠ partial: lastErrorCode=${kuaishou.lastErrorCode} (${kuaishou.lastErrorMessage || "?"})`,
1350
+ ),
1351
+ );
1352
+ }
1353
+ if (kuaishou.cleanupFailed) {
1354
+ logger.log(chalk.gray(` (note: staging cleanup failed — non-fatal)`));
1355
+ }
1356
+ } catch (err) {
1357
+ fail(null, err, options.json);
1358
+ }
1359
+ }
1360
+
1361
+ /**
1362
+ * Phase 6c — `cc hub toutiao-adb-sync`
1363
+ *
1364
+ * Pulls www.toutiao.com cookies from the user's Android Toutiao App,
1365
+ * fetches uid + profile (no _sig) + 3 signed endpoints (feed / collection
1366
+ * / search). CLI context has NO sign bridge — 3 signed endpoints short-
1367
+ * circuit with lastErrorCode=-99 (no HTTP traffic). Desktop wiring
1368
+ * upgrades via ToutiaoSignBridge (Electron WebContentsView).
1369
+ */
1370
+ async function cmdToutiaoAdbSync(options) {
1371
+ try {
1372
+ const hub = await (options._getHub || getHub)();
1373
+ const result = await hub.toutiaoAdbSync({
1374
+ stagingDir: options.stagingDir,
1375
+ displayName: options.displayName,
1376
+ limits:
1377
+ options.limitFeed || options.limitCollection || options.limitSearch
1378
+ ? {
1379
+ feed: parsePositiveInt(options.limitFeed),
1380
+ collection: parsePositiveInt(options.limitCollection),
1381
+ search: parsePositiveInt(options.limitSearch),
1382
+ }
1383
+ : undefined,
1384
+ });
1385
+ if (options.json) {
1386
+ printJson(result);
1387
+ return;
1388
+ }
1389
+ if (!result.ok) {
1390
+ logger.log(chalk.red(`✗ toutiao-adb-sync failed: ${result.reason}`));
1391
+ logger.log(chalk.gray(` ${result.message || ""}`));
1392
+ if (result.reason === "BRIDGE_UNAVAILABLE") {
1393
+ logger.log(
1394
+ chalk.gray(
1395
+ " Install Android Platform Tools or set ADB_PATH=/path/to/adb",
1396
+ ),
1397
+ );
1398
+ } else if (result.reason === "TOUTIAO_NO_ROOT") {
1399
+ logger.log(
1400
+ chalk.gray(
1401
+ " Phone needs Magisk root — Toutiao release APK isn't debuggable",
1402
+ ),
1403
+ );
1404
+ } else if (result.reason === "TOUTIAO_NOT_INSTALLED") {
1405
+ logger.log(
1406
+ chalk.gray(
1407
+ " Install 今日头条 (com.ss.android.article.news, NOT 极速版/.lite) + log in once + open any article (WebView populates cookies), then retry",
1408
+ ),
1409
+ );
1410
+ } else if (result.reason === "TOUTIAO_COOKIES_INCOMPLETE") {
1411
+ logger.log(
1412
+ chalk.gray(
1413
+ " sessionid / sessionid_ss missing — relog on the Toutiao App",
1414
+ ),
1415
+ );
1416
+ } else if (
1417
+ result.reason === "TOUTIAO_COOKIES_TRUNCATED" ||
1418
+ result.reason === "TOUTIAO_NOT_SQLITE"
1419
+ ) {
1420
+ logger.log(
1421
+ chalk.gray(
1422
+ " ADB stream may be corrupted; unplug + replug USB and retry",
1423
+ ),
1424
+ );
1425
+ }
1426
+ process.exitCode = 1;
1427
+ return;
1428
+ }
1429
+ const report = result.report || {};
1430
+ const toutiao = report.toutiao || {};
1431
+ const counts = toutiao.eventCounts || {};
1432
+ logger.log(chalk.green(`✓ toutiao-adb-sync succeeded`));
1433
+ logger.log(
1434
+ ` uid: ${chalk.cyan(toutiao.uid || "(profile fetch failed)")}`,
1435
+ );
1436
+ if (toutiao.nickname) {
1437
+ logger.log(` nickname: ${toutiao.nickname}`);
1438
+ }
1439
+ logger.log(` profile: ${counts.profile || 0}`);
1440
+ logger.log(` feed: ${counts.feed || 0}`);
1441
+ logger.log(` collection: ${counts.collection || 0}`);
1442
+ logger.log(` search: ${counts.search || 0}`);
1443
+ logger.log(` total: ${counts.total || 0}`);
1444
+ logger.log(` status: ${report.status || "?"}`);
1445
+ logger.log(` rawCount: ${report.rawCount || 0}`);
1446
+ if (toutiao.profileFetchFailed) {
1447
+ logger.log(
1448
+ chalk.yellow(
1449
+ ` ⚠ passport/info/v2 returned no user_id — cookie expired or sessionid missing (lastErrorCode=${toutiao.lastErrorCode})`,
1450
+ ),
1451
+ );
1452
+ } else if (
1453
+ toutiao.signProviderUsed === "none" &&
1454
+ counts.feed === 0 &&
1455
+ counts.collection === 0 &&
1456
+ counts.search === 0
1457
+ ) {
1458
+ logger.log(
1459
+ chalk.yellow(
1460
+ ` ⚠ 3 signed endpoints short-circuited (no sign bridge in CLI context) — run from desktop app to enable _signature via Electron WebContentsView`,
1461
+ ),
1462
+ );
1463
+ } else if (toutiao.lastErrorCode) {
1464
+ logger.log(
1465
+ chalk.yellow(
1466
+ ` ⚠ partial: lastErrorCode=${toutiao.lastErrorCode} (${toutiao.lastErrorMessage || "?"})`,
1467
+ ),
1468
+ );
1469
+ }
1470
+ if (toutiao.cleanupFailed) {
1471
+ logger.log(chalk.gray(` (note: staging cleanup failed — non-fatal)`));
1472
+ }
1473
+ } catch (err) {
1474
+ fail(null, err, options.json);
1475
+ }
1476
+ }
1477
+
1195
1478
  /**
1196
1479
  * Phase 3a — `cc hub weibo-adb-sync`
1197
1480
  *
@@ -1726,6 +2009,25 @@ export function registerHubCommand(program) {
1726
2009
  .option("--json", "Output JSON")
1727
2010
  .action(cmdAsk);
1728
2011
 
2012
+ hub
2013
+ .command("retrieve-context <question>")
2014
+ .description(
2015
+ "RAG preflight — gather facts + build LLM prompt WITHOUT calling LLM (for cloud-LLM callers that need RAG context)",
2016
+ )
2017
+ .option(
2018
+ "--max-facts <n>",
2019
+ "Cap facts in prompt (default 80; on-device small models e.g. Qwen2.5-1.5B should pass 20)",
2020
+ )
2021
+ .option(
2022
+ "--max-query-limit <n>",
2023
+ "Cap vault queryEvents limit (default 200; small-model callers should pass 50)",
2024
+ )
2025
+ .option(
2026
+ "--no-minimal",
2027
+ "Use full hub init (with adapters / KG / RAG sinks). Default uses minimal init (vault + engine only) which is ~10-20x faster on cold-start.",
2028
+ )
2029
+ .action(cmdRetrieveContext);
2030
+
1729
2031
  hub
1730
2032
  .command("stats")
1731
2033
  .description("Vault row counts + registered adapter list + hub paths")
@@ -1819,6 +2121,53 @@ export function registerHubCommand(program) {
1819
2121
  .option("--json", "Output JSON")
1820
2122
  .action(cmdWeiboAdbSync);
1821
2123
 
2124
+ // Phase 6d — Kuaishou C 路径 one-shot (www.kuaishou.com cookies + profile
2125
+ // from api_ph + 3 GraphQL endpoints with __NS_sig3 + kpf/kpn).
2126
+ // CLI: signed endpoints short-circuit. Desktop: KuaishouSignBridge.
2127
+ hub
2128
+ .command("kuaishou-adb-sync")
2129
+ .description(
2130
+ "Kuaishou C 路径: pull www.kuaishou.com cookies via ADB from the user's Android Kuaishou App (com.smile.gifmaker, NOT 极速版 com.kuaishou.nebula), parse profile from kuaishou.web.cp.api_ph cookie payload (no HTTP) + 3 GraphQL endpoints (watch/collect/search, __NS_sig3 + kpf/kpn — CLI short-circuits, desktop uses KuaishouSignBridge). Needs rooted Android + Kuaishou App logged in once + opened a video once + `adb` on PATH.",
2131
+ )
2132
+ .option(
2133
+ "--limit-watch <n>",
2134
+ "Cap recommended feed watch history (default 50)",
2135
+ )
2136
+ .option("--limit-collect <n>", "Cap own posted photos (default 100)")
2137
+ .option("--limit-search <n>", "Cap search history (default 50)")
2138
+ .option(
2139
+ "--display-name <s>",
2140
+ "Account displayName for the snapshot (default = api_ph user_name)",
2141
+ )
2142
+ .option(
2143
+ "--staging-dir <path>",
2144
+ "Custom dir for the temp snapshot JSON (default os.tmpdir())",
2145
+ )
2146
+ .option("--json", "Output JSON")
2147
+ .action(cmdKuaishouAdbSync);
2148
+
2149
+ // Phase 6c — Toutiao C 路径 one-shot (www.toutiao.com cookies + profile +
2150
+ // 3 endpoints with _signature). CLI context: signed endpoints short-circuit
2151
+ // (-99) with no HTTP traffic. Desktop: ToutiaoSignBridge → ~100% hit.
2152
+ hub
2153
+ .command("toutiao-adb-sync")
2154
+ .description(
2155
+ "Toutiao C 路径: pull www.toutiao.com cookies via ADB from the user's Android Toutiao App (com.ss.android.article.news, NOT 极速版), fetch profile (no _sig) + 3 endpoints (feed/collection/search, _signature required — CLI short-circuits, desktop uses ToutiaoSignBridge). Needs rooted Android + Toutiao App logged in once + `adb` on PATH.",
2156
+ )
2157
+ .option("--limit-feed <n>", "Cap recommended feed (default 50)")
2158
+ .option("--limit-collection <n>", "Cap saved articles (default 200)")
2159
+ .option("--limit-search <n>", "Cap search history (default 100)")
2160
+ .option(
2161
+ "--display-name <s>",
2162
+ "Account displayName for the snapshot (default = passport screen_name)",
2163
+ )
2164
+ .option(
2165
+ "--staging-dir <path>",
2166
+ "Custom dir for the temp snapshot JSON (default os.tmpdir())",
2167
+ )
2168
+ .option("--json", "Output JSON")
2169
+ .action(cmdToutiaoAdbSync);
2170
+
1822
2171
  // Phase 3c — Xhs C 路径 one-shot (xiaohongshu.com cookies + 4 endpoints with X-S signing)
1823
2172
  hub
1824
2173
  .command("xhs-adb-sync")
@@ -2098,6 +2447,7 @@ export function registerHubCommand(program) {
2098
2447
  // real `getHub()` call. The commander wiring above is the runtime path.
2099
2448
  export const _internal = {
2100
2449
  cmdAsk,
2450
+ cmdRetrieveContext,
2101
2451
  parsePositiveInt,
2102
2452
  cmdAIChatList,
2103
2453
  cmdAIChatLogin,
@@ -2115,6 +2465,8 @@ export const _internal = {
2115
2465
  cmdDouyinAdbSync,
2116
2466
  cmdWeiboAdbSync,
2117
2467
  cmdXhsAdbSync,
2468
+ cmdToutiaoAdbSync,
2469
+ cmdKuaishouAdbSync,
2118
2470
  interpretWechatProbe,
2119
2471
  _defaultKnownVendors,
2120
2472
  };
@@ -442,6 +442,40 @@ export const PERSONAL_DATA_HUB_HANDLERS = {
442
442
  }),
443
443
  ),
444
444
 
445
+ // Phase 6c — Toutiao C 路径 one-shot sync (www.toutiao.com cookies +
446
+ // passport profile + 3 _signature endpoints). CLI/web context with no
447
+ // desktop bridge: 3 signed endpoints short-circuit (no HTTP). Desktop
448
+ // wiring uses ToutiaoSignBridge → ~100% hit.
449
+ "personal-data-hub.toutiao-adb-sync": async (msg) =>
450
+ withHub((hub) =>
451
+ hub.toutiaoAdbSync({
452
+ limits: msg && msg.limits,
453
+ stagingDir: msg && msg.stagingDir,
454
+ displayName: msg && msg.displayName,
455
+ }),
456
+ ),
457
+
458
+ // Phase 6d — Kuaishou C 路径 one-shot sync (www.kuaishou.com cookies +
459
+ // profile from api_ph payload + 3 GraphQL endpoints with __NS_sig3 +
460
+ // kpf/kpn). CLI/web context: signed endpoints short-circuit (no HTTP).
461
+ // Desktop wiring uses KuaishouSignBridge → ~100% hit.
462
+ "personal-data-hub.kuaishou-adb-sync": async (msg) =>
463
+ withHub((hub) =>
464
+ hub.kuaishouAdbSync({
465
+ limits: msg && msg.limits,
466
+ stagingDir: msg && msg.stagingDir,
467
+ displayName: msg && msg.displayName,
468
+ }),
469
+ ),
470
+
471
+ // Phase 6e — Bridge dry-run doctor. Spins up Xhs / Toutiao / Kuaishou
472
+ // sign bridges with empty cookie, probes for candidate signing globals,
473
+ // times each phase. No phone needed. Detects SDK rotation BEFORE the
474
+ // user starts a real sync. Desktop-only — CLI/web-shell-no-desktop
475
+ // returns MODULE_LOAD_FAILED.
476
+ "personal-data-hub.bridge-doctor": async () =>
477
+ withHub((hub) => hub.bridgeDoctor()),
478
+
445
479
  // ─── Phase 8 — EntityResolver review / merge / unmerge ───────────────
446
480
 
447
481
  "personal-data-hub.review-queue-list": async (msg) =>