chainlesschain 0.162.178 → 0.162.180

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 (164) 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-DRftcSue.js → AIOps-D_TIL54O.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-BFvUaMKR.js → ActionButton-eQwy9r-f.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-C0-Rru4P.js → Analytics-D2eNuDgT.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-bx-BCljj.js → AppLayout-CFhwOFsf.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-DAJ8B3Un.js → Artifacts-CJUMw6PB.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-hOk4L1lr.js → Audit-CDB9V58o.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-3pbDpM9z.js → BackgroundAgents-TBppYFjN.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-BtziGDn9.js → Backup-CYEOUpzJ.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-kmshphJ3.js → BaseInput-C88fFT2u.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-d7tGSosA.js → Chat-mkr1vC3N.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-p-_PBpX_.js → ChatBubbleRenderer-C7bofJuA.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-C4Eh4Tz5.js → Checkbox-CVU4xqti.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-r7gmhZyA.js → Codegen-rrNcByz3.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-U7mDaGlL.js → Col-Cqu-zCvh.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-Bpu8aH1T.js → Community-RGRlSgXV.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-CHb2XBJe.js → Compact-DBm6epOK.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-Cn_r4dey.js → Compliance-BgKw5iaW.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-CdMWfDcz.js → Cowork-BqQcNO3Z.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-BtnREHWy.js → Cron-DO5gTFNb.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-BKI9TSzr.js → Crosschain-B8hbJNuN.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-pkNubzzJ.js → DID-B08bFJ2C.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-1rrz1snN.js → Dashboard-xNdy_ifo.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-DLyFVxDc.js → Dropdown-D-KIZgWL.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-CtUWfYVq.js → EmailListRenderer-DKCvsaGo.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-B8eX2Qe4.js → FamilyGuardDashboard-D_cfqama.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-D8lsDGus.js → Federation-B-UqQD68.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-CRYt_aIw.js → FormItemContext-BWWvWuJW.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-ClKzY8_N.js → GenericCardRenderer-CPchx6N7.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-BXj5Dney.js → Git-Vx4co9Ff.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-Cm-I8RMz.js → Governance-D4x0pp_m.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CPvbhvgt.js → Inference-T3KvwPYR.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-sYNDZaoi.js → KnowledgeGraph-Ddv3KUv3.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-BZY_3CRZ.js → Logs-DfH-Dkae.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-up0BW6pv.js → MarkdownRenderer-DHW3PbCq.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-C5MEJ2Qe.js → Marketplace-tjgb0R3D.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-ihdrn43I.js → McpTools-DHAk5nYX.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-Br8bPMlh.js → Memory-A2Yx7thM.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-BNntgrYW.js → MobileBridge-BEmTVfN5.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-BdV915mw.js → MobileProjects-DuFHUgE4.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-A3wDmlqL.js → Mtc-BDSll0OZ.js} +2 -2
  43. package/src/assets/web-panel/assets/{MtcAudit-CF-mBLWM.js → MtcAudit-CuBvluaS.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-Iy2k_MSo.js → Multisig-BmXAA-Sb.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-kk0W7HUb.js → NLProgramming-ci2stZAe.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-B5u5t_4S.js → Notes-D-1RpqnS.js} +4 -4
  47. package/src/assets/web-panel/assets/{NotificationSettings-BN0POf6_.js → NotificationSettings-C0GyitIT.js} +1 -1
  48. package/src/assets/web-panel/assets/OrderTableRenderer-U2bN-hj5.js +1 -0
  49. package/src/assets/web-panel/assets/{Organization-DwWQESuN.js → Organization-BNZgpPci.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-DG6klrcC.js → Overflow-CDidKmUN.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BwJcKkgZ.js → P2P-BnreBaXq.js} +2 -2
  52. package/src/assets/web-panel/assets/PdhVaultBrowser-DwJUxO9j.js +7 -0
  53. package/src/assets/web-panel/assets/{Permissions-CGMXe4TX.js → Permissions-BQGgJPH0.js} +4 -4
  54. package/src/assets/web-panel/assets/PersonalDataHub-k_fSkCwL.js +9 -0
  55. package/src/assets/web-panel/assets/{Pipeline-BzJfeoGt.js → Pipeline-WGqKMqau.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-DLh3Bblx.js → Privacy-DtHj9Ymq.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-USVWW36c.js → ProjectInit-B3wH2SFC.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-CiiOn62h.js → ProjectSettings-Cd0cgcA1.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-BbgmyQnS.js +1 -0
  60. package/src/assets/web-panel/assets/{Providers-CBPWWzpu.js → Providers-DTpxEXur.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-QVwoRUq3.js → QrScannerModal-B2v6gR-C.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-CXnR-2Vn.js → QuickAsk-D3Nr9xXR.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-ClaNzjmF.js → Recommend-C9inoMky.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-C3rGw8vJ.js → RemoteSession-Bqk3EYSu.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-Ci-OWAoP.js → Reputation-41KFG646.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DRhAwXOS.js → Row-DqAaqqCv.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-BQLJQ2_y.js → RssFeed-CMDpCDKc.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-QQUpkfFd.js → Search-B6mtegNL.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-DdXKzuKT.js → Security-BaePFVZ8.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-x2Zh62Qd.js → Services-DeG3Qscl.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-GdY4KwRz.js → Skeleton-D_QP7F90.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-Bv4tY1Fd.js → Skills-DLGyUgIA.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-C3QnM-11.js → Sla-BN2pxW1Q.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-Bl3ytvmI.js → SpeechSettings-BWsZPd8B.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-Wp9IwTsP.js → SyncSettings-VOZRPODW.js} +2 -2
  76. package/src/assets/web-panel/assets/Tasks-3bDAUz8K.js +1 -0
  77. package/src/assets/web-panel/assets/{Templates-CGfvVVM9.js → Templates-eJSob-q0.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-CVXmwQ3U.js → Tenant-Bdwk8Rwy.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-bvwrt03E.js → Terminal-CsMLKIoV.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-D0g74KhH.js → TimelineRenderer-D5qkbPKT.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BH4IwzlT.js → Tokens-DKgwn8hI.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-DJOpM533.js → Trigger-HlEHniGM.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-B43EPRLr.js → Trust-CdPcsBpN.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-BmBFG-em.js → UkeySign-vAdXd-vG.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-n9oJwB8k.js → VideoEditing-9hHBYFnH.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-D5QeivHp.js → Wallet-Dxh5d1yR.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-DcNy7igO.js → WebAuthn-BsFmLHkp.js} +3 -3
  88. package/src/assets/web-panel/assets/{WorkflowEditor-BVjc7-5-.js → WorkflowEditor-Cn0OIwLn.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-Dy5Tfj3m.js → chat-CJ19O6Dh.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-C4c2CNZU.js → colors-Du9jzLN0.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-DPa3O5vN.js → compact-item-DhEscfzw.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-TLcfNA6_.js → createContext-CwzEfF3i.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-BBMFnsja.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-D41LVFkp.js → hasIn-DT_GSE6J.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-MKZDje0H.js → index-1LoutPJd.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DljFsVGI.js → index-53z1YUxX.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BsQ7Ev6i.js → index-B6QDWRLk.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BQiIxHBy.js → index-B8s5FxJY.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CQrEHoOG.js → index-BF-ZCVGE.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-0oevYjfT.js → index-BFD0cEna.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Cip7LWml.js → index-BIYTjbv_.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BPTGAjee.js → index-BQWENaZf.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CzZ49YZL.js → index-BgSR2WMG.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-388pLGJ1.js → index-BnHhFnwt.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Di4X8cfI.js → index-BrkJ7JPm.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BYPr9fkz.js → index-C4SN3by5.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BSM1cHVK.js → index-CAVW0cWM.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CX0VsoP4.js → index-CGvgnfl9.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DBUgRs4D.js → index-CQWxDOvM.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-OHAfRu_3.js → index-Cc9aPkv-.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-tQ3igERQ.js → index-Cnok8mhZ.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CVjgm992.js → index-CyEhUJcu.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Yjr6cqSK.js → index-D-QkW1fp.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-iQ6gtFih.js → index-D3llLkYM.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DNsBj2Ob.js → index-D4kZoXNr.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BPtrOBmW.js → index-D5IytTND.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CSxex23J.js → index-D8gt0j7r.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-WinCp6du.js → index-DN60VBn5.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CXl0YPNG.js → index-DWL3SYN3.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-C7xhTVyP.js → index-DfRDyY1q.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Cq_hlWNt.js → index-DhXay17G.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B8nLBZbv.js → index-DnVVYKCE.js} +3 -3
  123. package/src/assets/web-panel/assets/{index-CNk_ZooP.js → index-Dny_ZnAO.js} +1 -1
  124. package/src/assets/web-panel/assets/index-Do8JMV4N.js +1 -0
  125. package/src/assets/web-panel/assets/{index-BvmziZSG.js → index-DtRwLhee.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BvopLC_T.js → index-DzjzrOli.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CWK4zOPe.js → index-Mobw3PHs.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Btk9qLL2.js → index-NA2oBLSB.js} +1 -1
  129. package/src/assets/web-panel/assets/index-UYAJ6iNM.js +1 -0
  130. package/src/assets/web-panel/assets/{index-WEJr8PL5.js → index-VeHs6qH0.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-Korc3QIe.js → index-gFjYdstW.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D2Nkivlm.js → index-tND8-TDU.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-pt3s7yvV.js → index-v5HLYCVa.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-BFZu87WZ.js → initDefaultProps-Cvt59c10.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-DJIo7qvt.js → motion-D15pPEzl.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-Cfr5qknL.js → move-N55KeXJ7.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-Bgc-OadH.js → mtc-parser-CyFO1bYP.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-X3WI-TNp.js → omit-B5IXfxU2.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-pJhU9IxK.js → pickAttrs-CpVqsulV.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-BNg_Vj3T.js → placementArrow-ME36sMF3.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-TeSEGQ5-.js → responsiveObserve-BXAIR1r-.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-D_t_P0bv.js → slide-BkOX3-Dp.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-DrrIMwAT.js → statusUtils-Cy4Aklpt.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-BjZCuFRm.js → styleChecker-gUd2T206.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-rTFdlqdQ.js → useFlexGapSupport-CaVD4o99.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-BidCpfLX.js → useFs--pFGl0KD.js} +1 -1
  147. package/src/assets/web-panel/assets/usePersonalDataHub-D45iP86i.js +1 -0
  148. package/src/assets/web-panel/assets/{vnode-BrbcRiHZ.js → vnode-BayoChH_.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-PISX7AkR.js → zoom-CpP38poi.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/hub.js +350 -39
  152. package/src/data/changelog.json +27 -1
  153. package/src/gateways/ws/personal-data-hub-protocol.js +86 -23
  154. package/src/lib/host-adb-bridge.js +92 -14
  155. package/src/lib/personal-data-hub-wiring.js +173 -13
  156. package/src/assets/web-panel/assets/OrderTableRenderer-CHkSUrXB.js +0 -1
  157. package/src/assets/web-panel/assets/PdhVaultBrowser-BwOHG0in.js +0 -7
  158. package/src/assets/web-panel/assets/PersonalDataHub-ziYqg7-A.js +0 -8
  159. package/src/assets/web-panel/assets/Projects-6V-PNmcA.js +0 -1
  160. package/src/assets/web-panel/assets/Tasks-CF3rIrDe.js +0 -1
  161. package/src/assets/web-panel/assets/devWarning-BAwdDA_5.js +0 -1
  162. package/src/assets/web-panel/assets/index-DEjVkdzq.js +0 -1
  163. package/src/assets/web-panel/assets/index-LMOc5xrS.js +0 -1
  164. package/src/assets/web-panel/assets/usePersonalDataHub-C5mHttA9.js +0 -1
@@ -60,6 +60,8 @@ function parsePositiveInt(raw) {
60
60
 
61
61
  const SYNC_ENTITY_KEYS = ["events", "persons", "places", "items", "topics"];
62
62
  const MAX_SYNC_COOKIE_BYTES = 64 * 1024;
63
+ const MAX_SYNC_ACCESS_TOKEN_BYTES = 16 * 1024;
64
+ const MAX_SYNC_APP_KEY_BYTES = 16 * 1024;
63
65
 
64
66
  function resolveSyncCookie(options = {}, env = process.env) {
65
67
  if (options.cookie && options.cookieFile) {
@@ -80,6 +82,113 @@ function resolveSyncCookie(options = {}, env = process.env) {
80
82
  return normalized || null;
81
83
  }
82
84
 
85
+ function resolveSyncAccessToken(options = {}, env = process.env) {
86
+ if (options.accessToken && options.accessTokenFile) {
87
+ throw new Error(
88
+ "Use either --access-token or --access-token-file, not both",
89
+ );
90
+ }
91
+ let raw = options.accessToken || env.CC_PDH_ACCESS_TOKEN || null;
92
+ if (options.accessTokenFile) {
93
+ raw = readFileSync(String(options.accessTokenFile), "utf8");
94
+ if (Buffer.byteLength(raw, "utf8") > MAX_SYNC_ACCESS_TOKEN_BYTES) {
95
+ throw new Error(
96
+ `Access token file exceeds ${MAX_SYNC_ACCESS_TOKEN_BYTES} bytes`,
97
+ );
98
+ }
99
+ }
100
+ if (raw == null) return null;
101
+ const normalized = String(raw).trim();
102
+ if (Buffer.byteLength(normalized, "utf8") > MAX_SYNC_ACCESS_TOKEN_BYTES) {
103
+ throw new Error(
104
+ `Access token exceeds ${MAX_SYNC_ACCESS_TOKEN_BYTES} bytes`,
105
+ );
106
+ }
107
+ return normalized || null;
108
+ }
109
+
110
+ function resolveSyncAppKey(options = {}, env = process.env) {
111
+ if (options.appKey && options.appKeyFile) {
112
+ throw new Error("Use either --app-key or --app-key-file, not both");
113
+ }
114
+ let raw = options.appKey || env.CC_PDH_APP_KEY || null;
115
+ if (options.appKeyFile) {
116
+ raw = readFileSync(String(options.appKeyFile), "utf8");
117
+ if (Buffer.byteLength(raw, "utf8") > MAX_SYNC_APP_KEY_BYTES) {
118
+ throw new Error(`App key file exceeds ${MAX_SYNC_APP_KEY_BYTES} bytes`);
119
+ }
120
+ }
121
+ if (raw == null) return null;
122
+ const normalized = String(raw).trim();
123
+ if (Buffer.byteLength(normalized, "utf8") > MAX_SYNC_APP_KEY_BYTES) {
124
+ throw new Error(`App key exceeds ${MAX_SYNC_APP_KEY_BYTES} bytes`);
125
+ }
126
+ return normalized || null;
127
+ }
128
+
129
+ function collectSyncRoot(value, previous = []) {
130
+ const roots = Array.isArray(previous) ? previous : [];
131
+ return [...roots, String(value)];
132
+ }
133
+
134
+ /**
135
+ * Resolve local-files scan roots without interpreting path characters.
136
+ *
137
+ * `--root` is repeatable and preserves each argument byte-for-byte after the
138
+ * shell/Commander handoff. The deprecated `--roots` spelling remains safe for
139
+ * one exact path, but comma-containing values are inherently ambiguous (a
140
+ * legal filename versus the former comma-separated syntax), so they fail
141
+ * closed instead of scanning unintended directories.
142
+ */
143
+ function resolveSyncRoots(options = {}) {
144
+ const roots = Array.isArray(options.root)
145
+ ? options.root.map((value) => String(value))
146
+ : options.root == null
147
+ ? []
148
+ : [String(options.root)];
149
+
150
+ if (options.roots != null) {
151
+ const legacyRoot = String(options.roots);
152
+ if (legacyRoot.includes(",")) {
153
+ throw new Error(
154
+ "--roots no longer accepts ambiguous comma-separated values; repeat --root <path> for each exact directory",
155
+ );
156
+ }
157
+ roots.push(legacyRoot);
158
+ }
159
+
160
+ if (roots.some((root) => root.length === 0)) {
161
+ throw new Error("--root requires a non-empty exact directory path");
162
+ }
163
+ return roots;
164
+ }
165
+
166
+ function applySyncSinceOverride(registry, name, options = {}) {
167
+ if (!Object.prototype.hasOwnProperty.call(options, "since")) return options;
168
+ const adapter =
169
+ registry && typeof registry.get === "function" ? registry.get(name) : null;
170
+ if (adapter?.watermarkStrategy !== "partitioned") {
171
+ options.sinceWatermark = options.since;
172
+ return options;
173
+ }
174
+
175
+ const targetKeys =
176
+ typeof adapter.targetWatermarkKeys === "function"
177
+ ? adapter.targetWatermarkKeys(options)
178
+ : adapter.watermarkStreams;
179
+ const keys = Array.isArray(targetKeys)
180
+ ? targetKeys.filter(
181
+ (key) => typeof key === "string" && key.trim().length > 0,
182
+ )
183
+ : [];
184
+ if (keys.length > 0) {
185
+ options.sinceWatermarks = Object.fromEntries(
186
+ keys.map((key) => [key, options.since]),
187
+ );
188
+ }
189
+ return options;
190
+ }
191
+
83
192
  function syncCount(value) {
84
193
  return Number.isFinite(value) && value > 0 ? value : 0;
85
194
  }
@@ -756,7 +865,10 @@ async function cmdSyncAdapter(name, options) {
756
865
  try {
757
866
  const hub = await (options._getHub || getHub)();
758
867
  const opts = {};
759
- if (options.since) opts.since = Number(options.since);
868
+ if (options.since) {
869
+ const sinceWatermark = Number(options.since);
870
+ opts.since = sinceWatermark;
871
+ }
760
872
  if (options.until) opts.until = Number(options.until);
761
873
  if (options.limit) opts.limit = Number(options.limit);
762
874
  // Plan A v0.1 — system-data-android needs a snapshot file path. Generic
@@ -779,23 +891,77 @@ async function cmdSyncAdapter(name, options) {
779
891
  // history and process listings. The value remains transient.
780
892
  const runtimeCookie = resolveSyncCookie(options);
781
893
  if (runtimeCookie) opts.cookie = runtimeCookie;
782
- // Constructor-optional cookie collectors use a transient account identity
783
- // to isolate watermarks without persisting the cookie or account id.
894
+ // Official OAuth collectors receive their token only for this invocation.
895
+ // File/env input is preferred so the secret stays out of shell history.
896
+ const runtimeAccessToken = resolveSyncAccessToken(options);
897
+ if (runtimeAccessToken) opts.accessToken = runtimeAccessToken;
898
+ const runtimeAppKey = resolveSyncAppKey(options);
899
+ if (runtimeAppKey) opts.appKey = runtimeAppKey;
900
+ const runtimeAppId = options.appId || process.env.CC_PDH_APP_ID;
901
+ if (runtimeAppId) opts.appId = String(runtimeAppId);
902
+ // Constructor-optional credential collectors use a transient account
903
+ // identity to isolate watermarks without persisting credentials or ids.
784
904
  const runtimeAccountId = options.accountId || process.env.CC_PDH_ACCOUNT_ID;
785
905
  if (runtimeAccountId) opts.accountId = String(runtimeAccountId);
906
+ const runtimeSourceUrl = options.sourceUrl || process.env.CC_PDH_SOURCE_URL;
907
+ if (runtimeSourceUrl) opts.sourceUrl = String(runtimeSourceUrl);
908
+ const runtimeDriveId = options.driveId || process.env.CC_PDH_DRIVE_ID;
909
+ if (runtimeDriveId) opts.driveId = String(runtimeDriveId);
910
+ if (options.parentId) opts.parentId = String(options.parentId);
911
+ if (options.shallow) opts.recursive = false;
912
+ if (options.dir) opts.dir = String(options.dir);
913
+ if (options.exportDir) opts.exportDir = String(options.exportDir);
914
+ if (options.profilePath) opts.profilePath = String(options.profilePath);
915
+ if (options.cursorHome) opts.cursorHome = String(options.cursorHome);
916
+ if (options.claudeHome) opts.claudeHome = String(options.claudeHome);
917
+ if (options.hbuilderxHome) {
918
+ opts.hbuilderxHome = String(options.hbuilderxHome);
919
+ }
920
+ if (options.sourceTimezone) {
921
+ opts.sourceTimezone = String(options.sourceTimezone);
922
+ }
923
+ if (options.sourceTimezoneOffsetMinutes != null) {
924
+ opts.sourceTimezoneOffsetMinutes = Number(
925
+ options.sourceTimezoneOffsetMinutes,
926
+ );
927
+ }
928
+ const include = {};
929
+ if (options.history === false) include.history = false;
930
+ if (options.bookmarks === false) include.bookmarks = false;
931
+ if (options.downloads === false) include.downloads = false;
932
+ if (options.localHistory === false) include.localHistory = false;
933
+ if (options.agentTranscripts === false) include.agentTranscripts = false;
934
+ if (options.aiTracking === false) include.aiTracking = false;
935
+ if (options.claudeSubagents === false) {
936
+ include.subagentTranscripts = false;
937
+ }
938
+ if (options.claudeStats === false) include.stats = false;
939
+ if (options.participants === false) include.participants = false;
940
+ if (options.artifacts === false) include.artifacts = false;
941
+ if (options.contacts === false) include.contacts = false;
942
+ if (options.apps === false) include.apps = false;
943
+ if (options.sms === false) include.sms = false;
944
+ if (options.calls === false) include.calls = false;
945
+ if (options.media === false) include.media = false;
946
+ if (Object.keys(include).length > 0) opts.include = include;
947
+ // Keep the legacy field while every shell converges on dataDisclosure's
948
+ // standard include map.
949
+ if (options.participants === false) opts.includeParticipants = false;
950
+ if (options.maxParticipants) {
951
+ opts.maxParticipants = Number(options.maxParticipants);
952
+ }
953
+ if (options.pageSize) opts.pageSize = Number(options.pageSize);
954
+ if (options.maxPages) opts.maxPages = Number(options.maxPages);
955
+ if (options.maxFiles) opts.maxFiles = Number(options.maxFiles);
786
956
  // Alipay bill ZIP exports may be password protected. Environment fallback
787
957
  // keeps the password out of shell history for automated collection.
788
958
  const zipPassword = options.zipPassword || process.env.CC_PDH_ZIP_PASSWORD;
789
959
  if (zipPassword) opts.zipPassword = String(zipPassword);
790
- // L1 local files (module 101): the local-files adapter walks directories
791
- // given via opts.roots (comma-separated dirs → array). Without this the
792
- // adapter falls back to its default user-data dirs.
793
- if (options.roots) {
794
- opts.roots = String(options.roots)
795
- .split(",")
796
- .map((s) => s.trim())
797
- .filter(Boolean);
798
- }
960
+ // L1 local files (module 101): repeated --root values are exact paths.
961
+ // The deprecated --roots alias accepts one unambiguous exact path only.
962
+ const roots = resolveSyncRoots(options);
963
+ if (roots.length > 0) opts.roots = roots;
964
+ applySyncSinceOverride(hub.registry, name, opts);
799
965
  const report = await hub.registry.syncAdapter(name, opts);
800
966
  const analysis = analyzeSyncReport(report);
801
967
  // 2026-05-24 in-APK Android exit + flush-race fix. Real-device repro on
@@ -1460,6 +1626,25 @@ async function cmdSalvage(dumpfile, options) {
1460
1626
  // the messages, and ingests them. The Android CollectQqNativeTool su-reads the
1461
1627
  // DB + uid candidates, then invokes this. Same logic also runs on PC (USB).
1462
1628
  // Plaintext is written to a temp file only for parsing, then deleted.
1629
+ function ingestQqParsedBatch(vault, parsed, conflictResolver) {
1630
+ if (!vault || typeof vault.putBatchResolved !== "function") {
1631
+ throw new Error(
1632
+ "collect-qq requires a vault with transactional putBatchResolved support",
1633
+ );
1634
+ }
1635
+ if (typeof conflictResolver !== "function") {
1636
+ throw new Error("collect-qq requires the QQ quality conflict resolver");
1637
+ }
1638
+ const batch = {
1639
+ events: Array.isArray(parsed) ? parsed : parsed?.events || [],
1640
+ persons: Array.isArray(parsed) ? [] : parsed?.persons || [],
1641
+ places: [],
1642
+ items: [],
1643
+ topics: Array.isArray(parsed) ? [] : parsed?.topics || [],
1644
+ };
1645
+ return vault.putBatchResolved(batch, { conflictResolver });
1646
+ }
1647
+
1463
1648
  async function cmdCollectQq(options) {
1464
1649
  const spinner = options.json
1465
1650
  ? null
@@ -1540,30 +1725,14 @@ async function cmdCollectQq(options) {
1540
1725
  const events = Array.isArray(parsed) ? parsed : parsed.events || [];
1541
1726
  const persons = Array.isArray(parsed) ? [] : parsed.persons || [];
1542
1727
  const topics = Array.isArray(parsed) ? [] : parsed.topics || [];
1543
-
1544
- for (const p of persons) {
1545
- try {
1546
- vault.putPerson(p);
1547
- } catch {
1548
- /* dup / optional */
1549
- }
1550
- }
1551
- for (const t of topics) {
1552
- try {
1553
- if (typeof vault.putTopic === "function") vault.putTopic(t);
1554
- } catch {
1555
- /* dup / optional */
1556
- }
1557
- }
1558
- let ingested = 0;
1559
- for (const ev of events) {
1560
- try {
1561
- vault.putEvent(ev);
1562
- ingested++;
1563
- } catch {
1564
- /* skip dup/invalid */
1565
- }
1566
- }
1728
+ const qualityModule =
1729
+ options._qqQuality ||
1730
+ (await importPdh("@chainlesschain/personal-data-hub"));
1731
+ const conflictResolver =
1732
+ qualityModule.mergeQqEntityConflict ||
1733
+ qualityModule.default?.mergeQqEntityConflict;
1734
+ const ingestResult = ingestQqParsedBatch(vault, parsed, conflictResolver);
1735
+ const ingested = ingestResult.counts.events;
1567
1736
  const report = {
1568
1737
  ok: true,
1569
1738
  matchedUid: result.uid,
@@ -1572,6 +1741,8 @@ async function cmdCollectQq(options) {
1572
1741
  persons: persons.length,
1573
1742
  topics: topics.length,
1574
1743
  ingested,
1744
+ entityCounts: ingestResult.counts,
1745
+ resolvedConflicts: ingestResult.conflicts.length,
1575
1746
  };
1576
1747
  if (spinner) spinner.succeed(`collect-qq: ${ingested} QQ messages → vault`);
1577
1748
  if (options.json) {
@@ -3399,8 +3570,13 @@ export function registerHubCommand(program) {
3399
3570
  "Path to a snapshot file or local DB (system-data-android snapshot, douyin <uid>_im.db, wechat-pc MSG*.db, etc.)",
3400
3571
  )
3401
3572
  .option(
3402
- "--roots <dirs>",
3403
- "Comma-separated directories to scan (local-files adapter, e.g. /sdcard/Documents,/sdcard/Download)",
3573
+ "--root <path>",
3574
+ "Exact directory to scan with local-files; repeat for multiple roots (commas and surrounding spaces are preserved)",
3575
+ collectSyncRoot,
3576
+ )
3577
+ .option(
3578
+ "--roots <path>",
3579
+ "Deprecated single exact directory alias; comma-containing values are rejected (use repeated --root)",
3404
3580
  )
3405
3581
  .option(
3406
3582
  "--db-path <path>",
@@ -3434,9 +3610,138 @@ export function registerHubCommand(program) {
3434
3610
  "--cookie-file <path>",
3435
3611
  "Read the login cookie from a local file (preferred over putting it in shell history)",
3436
3612
  )
3613
+ .option(
3614
+ "--access-token <token>",
3615
+ "OAuth access token for supported official API collectors",
3616
+ )
3617
+ .option(
3618
+ "--access-token-file <path>",
3619
+ "Read an OAuth access token from a local file (preferred; or set CC_PDH_ACCESS_TOKEN)",
3620
+ )
3621
+ .option(
3622
+ "--app-id <id>",
3623
+ "OAuth application ID for collectors that require request signing (or set CC_PDH_APP_ID)",
3624
+ )
3625
+ .option("--app-key <key>", "OAuth application key for request signing")
3626
+ .option(
3627
+ "--app-key-file <path>",
3628
+ "Read an OAuth application key from a local file (preferred; or set CC_PDH_APP_KEY)",
3629
+ )
3437
3630
  .option(
3438
3631
  "--account-id <id>",
3439
- "Platform account identity (for example a Zhihu url_token; stored only as a hashed scope)",
3632
+ "Stable local platform account identity; stored only as a hashed scope",
3633
+ )
3634
+ .option(
3635
+ "--source-url <https-url>",
3636
+ "One-shot HTTPS source endpoint for collectors that require an explicitly captured API URL (or set CC_PDH_SOURCE_URL)",
3637
+ )
3638
+ .option(
3639
+ "--dir <path>",
3640
+ "Baidu Netdisk application directory (required; /apps/{appname} or a subdirectory)",
3641
+ )
3642
+ .option(
3643
+ "--export-dir <path>",
3644
+ "Local directory containing files exported from Tencent Docs",
3645
+ )
3646
+ .option(
3647
+ "--profile-path <path>",
3648
+ "Local browser/app profile or scan directory (also accepts Tencent Meeting and local-files roots)",
3649
+ )
3650
+ .option(
3651
+ "--cursor-home <path>",
3652
+ "Cursor local data home containing projects/agent-transcripts and ai-tracking",
3653
+ )
3654
+ .option(
3655
+ "--claude-home <path>",
3656
+ "Claude Code local data home containing projects and stats-cache.json",
3657
+ )
3658
+ .option(
3659
+ "--hbuilderx-home <path>",
3660
+ "HBuilderX local data directory containing file-activity INI metadata",
3661
+ )
3662
+ .option(
3663
+ "--source-timezone <iana-or-offset>",
3664
+ "Timezone for local timestamps without an embedded offset (for example Asia/Shanghai or UTC+08:00)",
3665
+ )
3666
+ .option(
3667
+ "--source-timezone-offset-minutes <minutes>",
3668
+ "Fixed source timezone offset in minutes (-840 to 840)",
3669
+ )
3670
+ .option("--no-history", "Exclude browser visit history")
3671
+ .option("--no-bookmarks", "Exclude browser bookmarks")
3672
+ .option("--no-downloads", "Exclude browser download history")
3673
+ .option("--no-local-history", "Exclude VS Code Local History save metadata")
3674
+ .option(
3675
+ "--no-agent-transcripts",
3676
+ "Exclude Cursor local Agent prompt/response transcripts",
3677
+ )
3678
+ .option(
3679
+ "--no-ai-tracking",
3680
+ "Exclude Cursor AI conversation summaries and code-activity metadata",
3681
+ )
3682
+ .option(
3683
+ "--no-claude-subagents",
3684
+ "Exclude Claude Code local subagent transcripts",
3685
+ )
3686
+ .option(
3687
+ "--no-claude-stats",
3688
+ "Exclude Claude Code aggregate activity and model-token statistics",
3689
+ )
3690
+ .option(
3691
+ "--no-participants",
3692
+ "Exclude participant names from Tencent Meeting history collection",
3693
+ )
3694
+ .option(
3695
+ "--no-artifacts",
3696
+ "Exclude document, recording, AI-summary, and chat metadata from Tencent Meeting history collection",
3697
+ )
3698
+ .option(
3699
+ "--no-contacts",
3700
+ "Exclude Android contacts from system-data-android collection",
3701
+ )
3702
+ .option(
3703
+ "--no-apps",
3704
+ "Exclude installed Android apps from system-data-android collection",
3705
+ )
3706
+ .option(
3707
+ "--no-sms",
3708
+ "Exclude Android SMS messages from system-data-android collection",
3709
+ )
3710
+ .option(
3711
+ "--no-calls",
3712
+ "Exclude Android call history from system-data-android collection",
3713
+ )
3714
+ .option(
3715
+ "--no-media",
3716
+ "Exclude Android media file metadata from system-data-android collection",
3717
+ )
3718
+ .option(
3719
+ "--max-participants <n>",
3720
+ "Maximum participant records retained per Tencent Meeting (default 1000, max 10000)",
3721
+ )
3722
+ .option(
3723
+ "--drive-id <id>",
3724
+ "Cloud drive ID for collectors that require it (WPS OpenAPI)",
3725
+ )
3726
+ .option(
3727
+ "--parent-id <id>",
3728
+ "Parent folder ID for collectors that support it (WPS defaults to 0)",
3729
+ )
3730
+ .option(
3731
+ "--shallow",
3732
+ "Collect only direct children instead of recursively traversing folders (WPS, Baidu Netdisk, or Tencent Docs exports)",
3733
+ )
3734
+ .option(
3735
+ "--page-size <n>",
3736
+ "Source page size when supported (Baidu Netdisk: 1-1000; WPS: 1-500)",
3737
+ )
3738
+ .option(
3739
+ "--max-pages <n>",
3740
+ "Maximum source pages for this run; an incomplete scan keeps the old watermark",
3741
+ )
3742
+ .option(
3743
+ "--max-files <n>",
3744
+ "Maximum local export files to inspect; an incomplete scan keeps the old watermark",
3440
3745
  )
3441
3746
  .option(
3442
3747
  "--zip-password <password>",
@@ -4005,6 +4310,11 @@ export const _internal = {
4005
4310
  summarizeSyncReports,
4006
4311
  formatSyncReportLine,
4007
4312
  resolveSyncCookie,
4313
+ resolveSyncAccessToken,
4314
+ resolveSyncAppKey,
4315
+ collectSyncRoot,
4316
+ resolveSyncRoots,
4317
+ applySyncSinceOverride,
4008
4318
  cmdSyncAdapter,
4009
4319
  cmdSyncAll,
4010
4320
  cmdAIChatList,
@@ -4033,4 +4343,5 @@ export const _internal = {
4033
4343
  importEventsInto,
4034
4344
  cmdExportEvents,
4035
4345
  cmdImportEvents,
4346
+ ingestQqParsedBatch,
4036
4347
  };
@@ -1,6 +1,32 @@
1
1
  {
2
2
  "source": "CHANGELOG.md",
3
3
  "releases": [
4
+ {
5
+ "productVersion": null,
6
+ "cliVersion": "0.162.180",
7
+ "cliVersions": ["0.162.180"],
8
+ "date": "2026-07-26",
9
+ "title": "",
10
+ "sections": [
11
+ {
12
+ "heading": "Added — cc CLI 0.162.180 + PDH 0.4.56: transactional multi-source ingest",
13
+ "body": "> `chainlesschain` **0.162.180** bundles\n> `@chainlesschain/personal-data-hub` **0.4.56** (2026-07-26).\n\n- **Transactional identity resolution**: adapters can persist source aliases,\n raw observations, and field-level conflict decisions in one transaction;\n entity resolution, KG, and RAG sinks now consume the exact persisted IDs and\n rewritten references.\n- **QQ NT evidence convergence**: direct database reads and Python sidecar\n decoding share canonical message identities while retaining both raw\n observations. Rich decoded text and mutable read state merge without losing\n older evidence, and pre-upgrade rows are rekeyed instead of duplicated.\n- **Reliable bounded sync**: harden cancellation, checkpoint, lookback, and\n resume boundaries for long-running sidecars plus Android and Windows\n incremental collectors.\n- **Release validation**: 253 PDH test files / 4215 tests, the focused\n multi-source ingest suite, and 110 Python bridge tests passed before release."
14
+ }
15
+ ]
16
+ },
17
+ {
18
+ "productVersion": null,
19
+ "cliVersion": "0.162.179",
20
+ "cliVersions": ["0.162.179"],
21
+ "date": "2026-07-25",
22
+ "title": "",
23
+ "sections": [
24
+ {
25
+ "heading": "Added — cc CLI 0.162.179 + PDH 0.4.55:跨端实时采集与输入安全收口",
26
+ "body": "> `chainlesschain` **0.162.179** 搭载\n> `@chainlesschain/personal-data-hub` **0.4.55**(2026-07-25)。\n\n- **跨端实时采集**:CLI、桌面端与 Web Panel 统一暴露实时采集控制和流式结果,\n 支持有界 cookie 运行时采集、临时 OAuth 输入及 Android ADB 数据桥接。\n- **可达性与失败闭合**:readiness 只报告当前可达的输入路径;缺失或无效的\n cookie 来源页、采集输入和 WhatsApp 备份不再被误报为成功。\n- **采集可靠性**:支持带密钥的 WhatsApp ADB 同步,WeRead 源错误时保留游标,\n 并恢复 Bilibili Android 采集客户端。\n- **发布链安全**:npm 发布工作流启用 Trusted Publishing/OIDC 与 provenance,\n 同时保留 `NPM_TOKEN` 作为当前发布的备用通道。"
27
+ }
28
+ ]
29
+ },
4
30
  {
5
31
  "productVersion": null,
6
32
  "cliVersion": "0.162.178",
@@ -10,7 +36,7 @@
10
36
  "sections": [
11
37
  {
12
38
  "heading": "Fixed — cc CLI 0.162.178 + IDE provider 403 diagnosis hotfix",
13
- "body": "- Preserve structured provider error codes/messages for failed LLM requests and\n `cc llm test`, with credential-shaped text redacted before display.\n- Do not classify every HTTP 403 as an invalid API key: overdue accounts,\n billing failures, and model-access denials now retain their real corrective\n action in CLI, VS Code, and JetBrains.\n- Release VS Code 0.37.33 and JetBrains 0.4.70 with corrected setup guidance."
39
+ "body": "- Preserve structured provider error codes/messages for failed LLM requests and\n `cc llm test`, with credential-shaped text redacted before display.\n- Do not classify every HTTP 403 as an invalid API key: overdue accounts,\n billing failures, and model-access denials now retain their real corrective\n action in CLI, VS Code, and JetBrains.\n- Release VS Code 0.37.33 and JetBrains 0.4.71 with corrected setup guidance;\n JetBrains 0.4.70 was deleted during Marketplace review and superseded after\n clearing the IntelliJ 2026.2 internal-API warning.\n- Prepare JetBrains 0.4.72 so `/status` and the other live-session slash\n commands use the same routing, output, aliases, arguments, and help text as\n the VS Code panel, including `/expand` reasoning-block toggling."
14
40
  }
15
41
  ]
16
42
  },
@@ -49,6 +49,55 @@ async function ingestSystemDataAndroidSnapshot(hub, snapshot) {
49
49
  return _ingestSnapshotFn(hub, snapshot);
50
50
  }
51
51
 
52
+ /**
53
+ * Adapters that must never consume an Android staging snapshot implicitly.
54
+ * They either have a richer live bridge or read desktop-local state directly.
55
+ * Exported as an immutable array so the transport contract can be guarded
56
+ * without exercising ADB in unit tests.
57
+ */
58
+ export const ADB_AUTO_PULL_BYPASS_ADAPTERS = Object.freeze([
59
+ "system-data-android",
60
+ "browser-history-chrome",
61
+ "browser-history-edge",
62
+ "browser-history-brave",
63
+ "browser-history-opera",
64
+ "browser-history-vivaldi",
65
+ "browser-history-safari",
66
+ "browser-history-firefox",
67
+ "vscode",
68
+ "vscodium",
69
+ "cursor",
70
+ "claude-code",
71
+ "jetbrains-ide",
72
+ "hbuilderx",
73
+ "win-recent",
74
+ "git-activity",
75
+ "shell-history",
76
+ "local-files",
77
+ "meeting-tencent",
78
+ ]);
79
+
80
+ const ADB_AUTO_PULL_BYPASS_SET = new Set(ADB_AUTO_PULL_BYPASS_ADAPTERS);
81
+ const EXPLICIT_LIVE_SOURCE_OPTION_KEYS = Object.freeze([
82
+ "cookie",
83
+ "cookies",
84
+ "accessToken",
85
+ "appKey",
86
+ "apiKey",
87
+ "token",
88
+ "sourceUrl",
89
+ ]);
90
+
91
+ export function hasExplicitLiveSourceOptions(options) {
92
+ if (!options || typeof options !== "object") return false;
93
+ return EXPLICIT_LIVE_SOURCE_OPTION_KEYS.some((key) => {
94
+ const value = options[key];
95
+ if (typeof value === "string") return value.trim().length > 0;
96
+ if (Array.isArray(value)) return value.length > 0;
97
+ return value != null && typeof value === "object";
98
+ });
99
+ }
100
+
52
101
  /**
53
102
  * If the caller didn't pass `inputPath`, try to pull a snapshot for
54
103
  * this adapter off the attached Android phone via `adb shell run-as
@@ -70,7 +119,7 @@ async function ingestSystemDataAndroidSnapshot(hub, snapshot) {
70
119
  * return the original options unchanged so the adapter's normal error
71
120
  * path fires and the UI banner shows a meaningful message.
72
121
  */
73
- async function _tryAdbAutoPullInputPath(hub, name, options) {
122
+ export async function _tryAdbAutoPullInputPath(hub, name, options) {
74
123
  if (
75
124
  options &&
76
125
  typeof options.inputPath === "string" &&
@@ -78,6 +127,13 @@ async function _tryAdbAutoPullInputPath(hub, name, options) {
78
127
  ) {
79
128
  return options; // caller already supplied a path
80
129
  }
130
+ // An explicit live credential or endpoint is a stronger user intent than
131
+ // an implicit, potentially stale Android staging snapshot. Every dual-mode
132
+ // adapter checks inputPath first, so adding an auto-pulled path here would
133
+ // silently turn a requested live sync into a snapshot import.
134
+ if (hasExplicitLiveSourceOptions(options)) {
135
+ return options;
136
+ }
81
137
  // Skip auto-pull for adapters that have a live bridge mode — pulling
82
138
  // a stale snapshot file would short-circuit the bridge path which
83
139
  // yields fresher / richer data (e.g. system-data-android's bridge
@@ -85,20 +141,11 @@ async function _tryAdbAutoPullInputPath(hub, name, options) {
85
141
  // an Android-collected snapshot only contains contacts + apps).
86
142
  // Keep this list in sync with adapters whose _syncViaBridge is
87
143
  // strictly richer than their snapshot output.
88
- // browser-history-* and vscode read desktop-local files (browser History
89
- // SQLite + Bookmarks JSON, or VS Code workspaceStorage + state.vscdb);
90
- // ADB snapshot is meaningless for them.
91
- const BRIDGE_PREFERRED = new Set([
92
- "system-data-android",
93
- "browser-history-chrome",
94
- "browser-history-edge",
95
- "vscode",
96
- "win-recent",
97
- "git-activity",
98
- "shell-history",
99
- "local-files",
100
- ]);
101
- if (BRIDGE_PREFERRED.has(name)) {
144
+ // browser-history-*, vscode/vscodium/cursor, claude-code, and
145
+ // jetbrains-ide read desktop-local files (browser profiles, editor/Agent
146
+ // state, Claude Code transcripts, or JetBrains recentProjects.xml);
147
+ // an ADB snapshot is meaningless for them.
148
+ if (ADB_AUTO_PULL_BYPASS_SET.has(name)) {
102
149
  return options || {};
103
150
  }
104
151
  try {
@@ -326,7 +373,9 @@ export const PERSONAL_DATA_HUB_HANDLERS = {
326
373
  unlinkSync(p);
327
374
  removed.push(p);
328
375
  }
329
- } catch (_e) {}
376
+ } catch {
377
+ // Account files are removed on a best-effort basis.
378
+ }
330
379
  }
331
380
  }
332
381
  if (alsoWipeMasterKey) {
@@ -337,14 +386,20 @@ export const PERSONAL_DATA_HUB_HANDLERS = {
337
386
  try {
338
387
  unlinkSync(join(keyDir, f));
339
388
  removed.push(join(keyDir, f));
340
- } catch (_e) {}
389
+ } catch {
390
+ // Key fragments are removed on a best-effort basis.
391
+ }
341
392
  }
342
393
  }
343
- } catch (_e) {}
394
+ } catch {
395
+ // Key-directory cleanup is best-effort.
396
+ }
344
397
  }
345
398
  try {
346
399
  closeHub();
347
- } catch (_e) {}
400
+ } catch {
401
+ // The hub may already be closed after a partial wipe.
402
+ }
348
403
  return { result: { ok: true, removed } };
349
404
  } catch (err) {
350
405
  return { error: err && err.message ? err.message : String(err) };
@@ -640,11 +695,15 @@ export const PERSONAL_DATA_HUB_STREAMING_HANDLERS = {
640
695
  type: "personal-data-hub.sync-adapter-stream.event",
641
696
  event: evt,
642
697
  });
643
- } catch (_e) {}
698
+ } catch {
699
+ // Streaming clients may disconnect while collection continues.
700
+ }
644
701
  if (typeof original === "function") {
645
702
  try {
646
703
  original(evt);
647
- } catch (_e) {}
704
+ } catch {
705
+ // A prior observer must not break collection telemetry.
706
+ }
648
707
  }
649
708
  };
650
709
  const options = await _tryAdbAutoPullInputPath(hub, msg.name, msg.options);
@@ -669,11 +728,15 @@ export const PERSONAL_DATA_HUB_STREAMING_HANDLERS = {
669
728
  hub.registry.onSyncEvent = (evt) => {
670
729
  try {
671
730
  sender({ type: "personal-data-hub.sync-all-stream.event", event: evt });
672
- } catch (_e) {}
731
+ } catch {
732
+ // Streaming clients may disconnect while collection continues.
733
+ }
673
734
  if (typeof original === "function") {
674
735
  try {
675
736
  original(evt);
676
- } catch (_e) {}
737
+ } catch {
738
+ // A prior observer must not break collection telemetry.
739
+ }
677
740
  }
678
741
  };
679
742
  try {