chainlesschain 0.162.13 → 0.162.15

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 (147) hide show
  1. package/README.md +1 -1
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-Bq_zxhCr.js → AIOps-Dr5poTWh.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CaevDm9t.js → ActionButton-DLOGPHQ-.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-B0gOmwPw.js → Analytics-DBlO3FPX.js} +1 -1
  6. package/src/assets/web-panel/assets/{AppLayout-DWhZiV0Q.js → AppLayout-BrMarmWx.js} +2 -2
  7. package/src/assets/web-panel/assets/{Audit-ZuZJBCxo.js → Audit-DX6omFqA.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-CX7jhH5l.js → Backup-0QSLlQkB.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-CVLx7HVq.js → BaseInput-Dn4GFfR8.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-C6yL5tRD.js → Chat-CMTeA0Zo.js} +1 -1
  11. package/src/assets/web-panel/assets/{Checkbox-BePhbqVq.js → Checkbox-BQQxf6iA.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-B5E4x1Lm.js → Codegen-D2vrazFC.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-CWhNU6A7.js → Col-P1hVDVKm.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-mSEAuJhp.js → Community-CPcDkABv.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-DSudHzX3.js → Compact-DvBQwE-b.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-CxJLYjyn.js → Compliance-GX2qsHkq.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-C-trppQj.js → Cowork-CEEISrBb.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cron-_Ij4v5fY.js → Cron-Y3D6wB4C.js} +1 -1
  19. package/src/assets/web-panel/assets/{Crosschain-eLHXzp5T.js → Crosschain-CnCjDKKD.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-BP04AUUB.js → DID-CJ2EFB5b.js} +1 -1
  21. package/src/assets/web-panel/assets/{Dashboard-CzEeQv62.js → Dashboard-CWujrs70.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-C_SGOB22.js → Dropdown-BzKSQA5P.js} +1 -1
  23. package/src/assets/web-panel/assets/{Federation-BgaP4BOv.js → Federation-DgaAWIwd.js} +1 -1
  24. package/src/assets/web-panel/assets/{FormItemContext-BxGLLt9r.js → FormItemContext-BitgB4Gb.js} +1 -1
  25. package/src/assets/web-panel/assets/{Git-Bt_uM_Gw.js → Git-Bzj4vLLf.js} +1 -1
  26. package/src/assets/web-panel/assets/{Governance-N2-0RG_o.js → Governance-D7DEdSBD.js} +1 -1
  27. package/src/assets/web-panel/assets/{Inference-eS3g-CzP.js → Inference-CVsGLfcX.js} +1 -1
  28. package/src/assets/web-panel/assets/{KnowledgeGraph-CUuvNVah.js → KnowledgeGraph-CF6C6ZNo.js} +1 -1
  29. package/src/assets/web-panel/assets/{Logs-wPbwEt2r.js → Logs-C0Er-tt8.js} +1 -1
  30. package/src/assets/web-panel/assets/{Marketplace-DH91kTwo.js → Marketplace-_E7uwUUU.js} +1 -1
  31. package/src/assets/web-panel/assets/{McpTools-D-GyyBrI.js → McpTools-dHbktLMV.js} +1 -1
  32. package/src/assets/web-panel/assets/{Memory-BOtUy-tw.js → Memory-CaQ4UY7b.js} +1 -1
  33. package/src/assets/web-panel/assets/{MobileBridge-DIP__XQd.js → MobileBridge-BbOh0uJt.js} +1 -1
  34. package/src/assets/web-panel/assets/{MobileProjects-1nqr1UsU.js → MobileProjects-CfdNEimZ.js} +1 -1
  35. package/src/assets/web-panel/assets/{Mtc-CCE0x7h2.js → Mtc-HLvyepRP.js} +1 -1
  36. package/src/assets/web-panel/assets/{MtcAudit-BBkz0XUO.js → MtcAudit-DMaHGoIr.js} +1 -1
  37. package/src/assets/web-panel/assets/{Multisig-CIKSJvTY.js → Multisig-DFNZf0AB.js} +1 -1
  38. package/src/assets/web-panel/assets/{NLProgramming-BDkgeFcq.js → NLProgramming-cZwXhWy2.js} +1 -1
  39. package/src/assets/web-panel/assets/{Notes-ONiUxfN1.js → Notes-U_3n8Zid.js} +1 -1
  40. package/src/assets/web-panel/assets/{NotificationSettings-CGcyKEIe.js → NotificationSettings-B4VvhkKf.js} +1 -1
  41. package/src/assets/web-panel/assets/{Organization-BZtMYBJt.js → Organization-BIwBFrdX.js} +1 -1
  42. package/src/assets/web-panel/assets/{Overflow-C4LfFZAI.js → Overflow-_zxR8gF2.js} +1 -1
  43. package/src/assets/web-panel/assets/{P2P-D71Cpk-m.js → P2P-DJXmWhLC.js} +1 -1
  44. package/src/assets/web-panel/assets/{Permissions-DRGYF29v.js → Permissions-DwJ35K8Z.js} +1 -1
  45. package/src/assets/web-panel/assets/{PersonalDataHub-CfRYoIua.js → PersonalDataHub-CzZXLXGN.js} +1 -1
  46. package/src/assets/web-panel/assets/{Pipeline-BOyp0_Qo.js → Pipeline-DjtRGM8X.js} +1 -1
  47. package/src/assets/web-panel/assets/{Privacy-a_AcphvF.js → Privacy-BJhsIhGf.js} +1 -1
  48. package/src/assets/web-panel/assets/{ProjectInit-CFu1grYt.js → ProjectInit-DEx5SDLh.js} +1 -1
  49. package/src/assets/web-panel/assets/{ProjectSettings-p54Eivhh.js → ProjectSettings-B6qDg7nn.js} +1 -1
  50. package/src/assets/web-panel/assets/{Projects-DkB88XAu.js → Projects-CKkTgrBf.js} +1 -1
  51. package/src/assets/web-panel/assets/{Providers-EK7f8DEd.js → Providers-3GEshcW_.js} +1 -1
  52. package/src/assets/web-panel/assets/{QuickAsk-CpO0w3iP.js → QuickAsk-BfUXbKxa.js} +1 -1
  53. package/src/assets/web-panel/assets/{Recommend-BUJVQdv9.js → Recommend-DgxYasSJ.js} +1 -1
  54. package/src/assets/web-panel/assets/{Reputation-kU2fOuZt.js → Reputation-CWvVg_V1.js} +1 -1
  55. package/src/assets/web-panel/assets/{Row-oGWRbk6g.js → Row-42M5ot1v.js} +1 -1
  56. package/src/assets/web-panel/assets/{RssFeed-saC_46Yo.js → RssFeed-Do3isL1x.js} +1 -1
  57. package/src/assets/web-panel/assets/{Search-CjtRqFUu.js → Search-_7wgwjaR.js} +1 -1
  58. package/src/assets/web-panel/assets/{Security-BX0NBVfQ.js → Security-DKB6pe8t.js} +1 -1
  59. package/src/assets/web-panel/assets/{Services-Bgxsnei_.js → Services-BHrBJAcB.js} +1 -1
  60. package/src/assets/web-panel/assets/{Skeleton-CwBb3k2a.js → Skeleton-C8L-EfGp.js} +1 -1
  61. package/src/assets/web-panel/assets/{Skills-CZCMYH6Z.js → Skills-CU66huTh.js} +1 -1
  62. package/src/assets/web-panel/assets/{Sla-Djy1uHnZ.js → Sla-DA8AKNmI.js} +1 -1
  63. package/src/assets/web-panel/assets/{SpeechSettings-CGUI_Uyh.js → SpeechSettings-CufjYTzV.js} +1 -1
  64. package/src/assets/web-panel/assets/{SyncSettings-DK2CKHRD.js → SyncSettings-D_VAYHIg.js} +1 -1
  65. package/src/assets/web-panel/assets/{Tasks-BKiOzeIO.js → Tasks-BE6nYh9k.js} +1 -1
  66. package/src/assets/web-panel/assets/{Templates-CnQpleXj.js → Templates-BfRbttd6.js} +1 -1
  67. package/src/assets/web-panel/assets/{Tenant-DwKz0cjm.js → Tenant-YO71CL80.js} +1 -1
  68. package/src/assets/web-panel/assets/{Terminal-A7t_wsR8.js → Terminal-D1mQaE_Z.js} +1 -1
  69. package/src/assets/web-panel/assets/{Tokens-BqYY9l44.js → Tokens-CvnNQtX4.js} +1 -1
  70. package/src/assets/web-panel/assets/{Trigger-BI4bXFmi.js → Trigger-BnN19FJt.js} +1 -1
  71. package/src/assets/web-panel/assets/{Trust-yMynKTRG.js → Trust-C6oZLHqp.js} +1 -1
  72. package/src/assets/web-panel/assets/{UkeySign-Br4IScM6.js → UkeySign-BOhFYuWu.js} +1 -1
  73. package/src/assets/web-panel/assets/{VideoEditing-CWcThGsP.js → VideoEditing-NMc6-qc-.js} +1 -1
  74. package/src/assets/web-panel/assets/{Wallet-CZcAtjxj.js → Wallet-BALmcKtd.js} +1 -1
  75. package/src/assets/web-panel/assets/{WebAuthn-BnTZFMA0.js → WebAuthn-6IVe-W6O.js} +1 -1
  76. package/src/assets/web-panel/assets/{WorkflowEditor-N7gGz3_n.js → WorkflowEditor-BIv20DXb.js} +1 -1
  77. package/src/assets/web-panel/assets/{chat-D175ZIO0.js → chat-CiceyA69.js} +1 -1
  78. package/src/assets/web-panel/assets/{colors-LKhZyttv.js → colors-CxwvpRW0.js} +1 -1
  79. package/src/assets/web-panel/assets/{compact-item-CsJSebxT.js → compact-item-N6ASDseQ.js} +1 -1
  80. package/src/assets/web-panel/assets/{createContext-BJ_CPYFC.js → createContext-CQ8PEhyP.js} +1 -1
  81. package/src/assets/web-panel/assets/{hasIn-CzD3IqH8.js → hasIn-DDiIuryC.js} +1 -1
  82. package/src/assets/web-panel/assets/{index-BhiZDGg7.js → index-1jBrqw2R.js} +1 -1
  83. package/src/assets/web-panel/assets/index-1losWCP0.js +1 -0
  84. package/src/assets/web-panel/assets/{index-CRGNuUIM.js → index-92sMXPmR.js} +1 -1
  85. package/src/assets/web-panel/assets/{index-Dm-3kvtD.js → index-BCgZTQf8.js} +1 -1
  86. package/src/assets/web-panel/assets/{index-BZluCuTH.js → index-BKLatStI.js} +1 -1
  87. package/src/assets/web-panel/assets/{index-CTIkCKav.js → index-BKmY57ry.js} +1 -1
  88. package/src/assets/web-panel/assets/{index-E7t1hAnk.js → index-BZ4O1Vp7.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-D7ZcBI5s.js → index-BdmcwwMp.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-BNVLVzN5.js → index-Bj-mAQFK.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-R1cFADfk.js → index-BjvirvV4.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-BvF2tC6C.js → index-Bw6UqIkJ.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-6qPbrYF7.js → index-BzdEj9_B.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BMvdoiFr.js → index-C43WIe_p.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BjOrt4vw.js → index-CHsNn-Qv.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BsirlkJ0.js → index-COIxnEwP.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-D401L3yx.js → index-C_dbCu-F.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BDSZDDb2.js → index-Cb0QVAZL.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-BV-__mlC.js → index-CcUK2M7W.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DXp1jVsK.js → index-ClASVywF.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-D8kltMTW.js → index-CnArbjXg.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Kn-Of5ew.js → index-Cur_KKpV.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-RIO4JKMP.js → index-Cwk0olXV.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CKjBAdm0.js → index-D0yG93O4.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BSNibAqz.js → index-D58a5SL3.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-uTEVWPYA.js → index-DEjAgx2R.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DJVkBmSc.js → index-DUKjS4kF.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BmJdof_c.js → index-DWKigrAM.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-C2HBKw07.js → index-DcMESTJs.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DnPt5OdD.js → index-DgwpVvQq.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DOO73rHE.js → index-Dj2wgF3A.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-D5IZCkZG.js → index-DobYLmfZ.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-JTX9A7w0.js → index-Dp3r80qO.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CyHdYUeZ.js → index-DtpWqJ-2.js} +1 -1
  115. package/src/assets/web-panel/assets/index-EoP_WtDt.js +1 -0
  116. package/src/assets/web-panel/assets/{index-BEDFHKO3.js → index-_BHtKVC_.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BXH9ujMW.js → index-bYorCa3r.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Dd7dICwB.js → index-hn9LVkIY.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-B7UYymse.js → index-mmpauJ3E.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DM3uBEWD.js → index-xQlQRo2j.js} +1 -1
  121. package/src/assets/web-panel/assets/{initDefaultProps-CBW0okek.js → initDefaultProps-1MKZtqXZ.js} +1 -1
  122. package/src/assets/web-panel/assets/{motion-DGAffQ0Z.js → motion-DFBQpRS8.js} +1 -1
  123. package/src/assets/web-panel/assets/{move-DFJ0-5IW.js → move-CiI8Ada0.js} +1 -1
  124. package/src/assets/web-panel/assets/{omit-AvrDghg1.js → omit-CjeFoPcC.js} +1 -1
  125. package/src/assets/web-panel/assets/{pickAttrs-D7csw9i1.js → pickAttrs-Be3kefJq.js} +1 -1
  126. package/src/assets/web-panel/assets/{placementArrow-hZ6Lg6kG.js → placementArrow-Be5Ra1_B.js} +1 -1
  127. package/src/assets/web-panel/assets/{responsiveObserve-DLLx5VvS.js → responsiveObserve-NCu3YHiX.js} +1 -1
  128. package/src/assets/web-panel/assets/{slide-BaRIT3ev.js → slide-f23lSB4X.js} +1 -1
  129. package/src/assets/web-panel/assets/{statusUtils-Cdjyuhrz.js → statusUtils-Dq99US_U.js} +1 -1
  130. package/src/assets/web-panel/assets/{styleChecker-CbrNybTt.js → styleChecker-B-UUq5Ww.js} +1 -1
  131. package/src/assets/web-panel/assets/{useFlexGapSupport-B8gAhiRC.js → useFlexGapSupport-D6aUzeVO.js} +1 -1
  132. package/src/assets/web-panel/assets/{useFs-BZPy4ICP.js → useFs-DU-R5a4I.js} +1 -1
  133. package/src/assets/web-panel/assets/{vnode-6Y0NDMVv.js → vnode-N7r8LSGe.js} +1 -1
  134. package/src/assets/web-panel/assets/{zoom-DTbMGsSH.js → zoom-07xoxB1t.js} +1 -1
  135. package/src/assets/web-panel/index.html +1 -1
  136. package/src/commands/__tests__/android.test.js +260 -0
  137. package/src/commands/__tests__/hub-wechat.test.js +186 -15
  138. package/src/commands/android.js +284 -0
  139. package/src/commands/hub.js +263 -19
  140. package/src/gateways/ws/personal-data-hub-protocol.js +28 -8
  141. package/src/index.js +2 -0
  142. package/src/lib/__tests__/cc-android-bridge.test.js +245 -0
  143. package/src/lib/cc-android-bridge.js +206 -0
  144. package/src/lib/personal-data-hub-wiring.js +98 -20
  145. package/src/lib/web-ui-server.js +2 -1
  146. package/src/assets/web-panel/assets/index-CY1mQA2I.js +0 -1
  147. package/src/assets/web-panel/assets/index-D9nXHfUB.js +0 -1
@@ -0,0 +1,284 @@
1
+ /**
2
+ * `cc android` — Android-side data + control commands.
3
+ *
4
+ * Plan A Sub-Phase A7 scaffold. Exposes the bridge surface from
5
+ * `lib/cc-android-bridge.js` as commander subcommands. Method shapes mirror
6
+ * `docs/design/Personal_Data_Hub_Android_Standalone_Cc.md` §6.1.
7
+ *
8
+ * **v0.1 status**: native JNI bridge (`cc-android-bridge.node`) not yet
9
+ * bundled (A6 in progress). On non-Android hosts every command exits non-zero
10
+ * with `ANDROID_BRIDGE_NOT_AVAILABLE`. On Android with a missing binding,
11
+ * same error with a different reason string. This lets the surface land and
12
+ * be tested ahead of the JNI shipping work.
13
+ *
14
+ * All commands support `--json` for scripting and exit non-zero on error.
15
+ */
16
+
17
+ import chalk from "chalk";
18
+ import { logger } from "../lib/logger.js";
19
+ import * as bridgeModule from "../lib/cc-android-bridge.js";
20
+
21
+ function printJson(obj) {
22
+ console.log(JSON.stringify(obj, null, 2));
23
+ }
24
+
25
+ function fail(err, asJson) {
26
+ const msg = err && err.message ? err.message : String(err);
27
+ const code = err && err.code ? err.code : null;
28
+ if (asJson) {
29
+ printJson({ error: msg, code });
30
+ } else {
31
+ logger.error(chalk.red(`✗ ${msg}`));
32
+ }
33
+ process.exit(1);
34
+ }
35
+
36
+ async function run(method, params, options) {
37
+ try {
38
+ const result = await _deps.bridge.invoke(method, params);
39
+ if (options.json) {
40
+ printJson(result);
41
+ } else {
42
+ console.log(JSON.stringify(result, null, 2));
43
+ }
44
+ } catch (err) {
45
+ fail(err, options.json);
46
+ }
47
+ }
48
+
49
+ // ─── caps ────────────────────────────────────────────────────────────
50
+
51
+ async function cmdCaps(options) {
52
+ const capsResult = _deps.bridge.caps();
53
+ if (options.json) {
54
+ printJson(capsResult);
55
+ return;
56
+ }
57
+ if (capsResult.available) {
58
+ logger.log(chalk.green("✓ android bridge available"));
59
+ } else {
60
+ logger.log(
61
+ chalk.yellow(`⚠ android bridge unavailable: ${capsResult.reason}`),
62
+ );
63
+ }
64
+ }
65
+
66
+ // ─── contacts / sms / calls — P1 ContentResolver ─────────────────────
67
+
68
+ function cmdContactsPull(options) {
69
+ return run(
70
+ "contacts.query",
71
+ { since: options.since ? Number(options.since) : undefined },
72
+ options,
73
+ );
74
+ }
75
+ function cmdSmsPull(options) {
76
+ return run(
77
+ "sms.query",
78
+ { since: options.since ? Number(options.since) : undefined },
79
+ options,
80
+ );
81
+ }
82
+ function cmdCallsPull(options) {
83
+ return run(
84
+ "calls.query",
85
+ { since: options.since ? Number(options.since) : undefined },
86
+ options,
87
+ );
88
+ }
89
+
90
+ // ─── app — PackageManager ────────────────────────────────────────────
91
+
92
+ function cmdAppList(options) {
93
+ return run("app.list", { includeSystem: !!options.system }, options);
94
+ }
95
+ function cmdAppLaunch(pkg, options) {
96
+ return run("app.launch", { pkg }, options);
97
+ }
98
+ function cmdAppIntent(pkg, action, options) {
99
+ const extras = {};
100
+ if (Array.isArray(options.extra)) {
101
+ for (const kv of options.extra) {
102
+ const idx = kv.indexOf("=");
103
+ if (idx > 0) extras[kv.slice(0, idx)] = kv.slice(idx + 1);
104
+ }
105
+ }
106
+ return run("app.intent", { pkg, action, extras }, options);
107
+ }
108
+
109
+ // ─── fs — SAF / sandbox ──────────────────────────────────────────────
110
+
111
+ function cmdFsRead(target, options) {
112
+ return run("fs.read", { target }, options);
113
+ }
114
+ function cmdFsList(target, options) {
115
+ return run("fs.list", { target }, options);
116
+ }
117
+
118
+ // ─── a11y — Accessibility Service ────────────────────────────────────
119
+
120
+ function cmdA11yQuery(options) {
121
+ return run("a11y.query", { filter: options.filter }, options);
122
+ }
123
+ function cmdA11yClick(nodeId, options) {
124
+ return run("a11y.click", { nodeId }, options);
125
+ }
126
+ function cmdA11yType(text, options) {
127
+ return run("a11y.type", { text }, options);
128
+ }
129
+
130
+ // ─── shizuku / root ──────────────────────────────────────────────────
131
+
132
+ function cmdShizukuExec(cmd, options) {
133
+ return run("shizuku.exec", { cmd }, options);
134
+ }
135
+ function cmdRootExec(cmd, options) {
136
+ return run("root.exec", { cmd }, options);
137
+ }
138
+
139
+ // ─── perms ────────────────────────────────────────────────────────────
140
+
141
+ function cmdPerms(name, options) {
142
+ return run("perms.check", { name }, options);
143
+ }
144
+
145
+ // ─── Commander wire-up ───────────────────────────────────────────────
146
+
147
+ export function registerAndroidCommand(program) {
148
+ const android = program
149
+ .command("android")
150
+ .description(
151
+ "Android-native bridge: ContentResolver / SAF / Accessibility / Shizuku / root (Plan A A7)",
152
+ );
153
+
154
+ android
155
+ .command("caps")
156
+ .description("Probe what bridge capabilities are available on this device")
157
+ .option("--json", "Output JSON")
158
+ .action(cmdCaps);
159
+
160
+ const contacts = android
161
+ .command("contacts")
162
+ .description("ContentResolver: contacts");
163
+ contacts
164
+ .command("pull")
165
+ .description("Pull contacts (READ_CONTACTS runtime permission required)")
166
+ .option("--since <ms>", "Watermark — only newer entries")
167
+ .option("--json", "Output JSON")
168
+ .action(cmdContactsPull);
169
+
170
+ const sms = android.command("sms").description("ContentResolver: SMS");
171
+ sms
172
+ .command("pull")
173
+ .description("Pull SMS (READ_SMS runtime permission required)")
174
+ .option("--since <ms>", "Watermark")
175
+ .option("--json", "Output JSON")
176
+ .action(cmdSmsPull);
177
+
178
+ const calls = android
179
+ .command("calls")
180
+ .description("ContentResolver: call log");
181
+ calls
182
+ .command("pull")
183
+ .description("Pull call log (READ_CALL_LOG runtime permission required)")
184
+ .option("--since <ms>", "Watermark")
185
+ .option("--json", "Output JSON")
186
+ .action(cmdCallsPull);
187
+
188
+ const app = android
189
+ .command("app")
190
+ .description("PackageManager: app inventory + Intent");
191
+ app
192
+ .command("list")
193
+ .description("List installed packages")
194
+ .option("--system", "Include system packages")
195
+ .option("--json", "Output JSON")
196
+ .action(cmdAppList);
197
+ app
198
+ .command("launch <pkg>")
199
+ .description("Launch app by package id (default activity)")
200
+ .option("--json", "Output JSON")
201
+ .action(cmdAppLaunch);
202
+ app
203
+ .command("intent <pkg> <action>")
204
+ .description("Fire an Intent at <pkg> with <action>")
205
+ .option(
206
+ "--extra <kv...>",
207
+ "Repeatable extras as KEY=VAL (e.g. --extra foo=bar --extra n=1)",
208
+ )
209
+ .option("--json", "Output JSON")
210
+ .action(cmdAppIntent);
211
+
212
+ const fs = android.command("fs").description("SAF / sandbox filesystem");
213
+ fs.command("read <target>")
214
+ .description("Read file (sandbox path OR SAF tree URI)")
215
+ .option("--json", "Output JSON")
216
+ .action(cmdFsRead);
217
+ fs.command("list <target>")
218
+ .description("List directory")
219
+ .option("--json", "Output JSON")
220
+ .action(cmdFsList);
221
+
222
+ const a11y = android.command("a11y").description("Accessibility Service");
223
+ a11y
224
+ .command("query")
225
+ .description("Dump current screen's node tree")
226
+ .option("--filter <css>", "css-like filter")
227
+ .option("--json", "Output JSON")
228
+ .action(cmdA11yQuery);
229
+ a11y
230
+ .command("click <nodeId>")
231
+ .description("Click a node")
232
+ .option("--json", "Output JSON")
233
+ .action(cmdA11yClick);
234
+ a11y
235
+ .command("type <text>")
236
+ .description("Type text into focused field")
237
+ .option("--json", "Output JSON")
238
+ .action(cmdA11yType);
239
+
240
+ const shizuku = android
241
+ .command("shizuku")
242
+ .description("Shizuku ADB-like privileges");
243
+ shizuku
244
+ .command("exec <cmd>")
245
+ .description("Run shell via Shizuku")
246
+ .option("--json", "Output JSON")
247
+ .action(cmdShizukuExec);
248
+
249
+ const root = android.command("root").description("Magisk root su");
250
+ root
251
+ .command("exec <cmd>")
252
+ .description("Run shell as root")
253
+ .option("--json", "Output JSON")
254
+ .action(cmdRootExec);
255
+
256
+ android
257
+ .command("perms <name>")
258
+ .description("Check (and request) a runtime permission")
259
+ .option("--json", "Output JSON")
260
+ .action(cmdPerms);
261
+ }
262
+
263
+ // _deps injection seam — tests reach in and replace `bridge` with a mock so
264
+ // the cmd* functions exercise the routing code without a real bridge.
265
+ export const _deps = { bridge: bridgeModule };
266
+
267
+ // Test-only exports.
268
+ export const _cmds = {
269
+ cmdCaps,
270
+ cmdContactsPull,
271
+ cmdSmsPull,
272
+ cmdCallsPull,
273
+ cmdAppList,
274
+ cmdAppLaunch,
275
+ cmdAppIntent,
276
+ cmdFsRead,
277
+ cmdFsList,
278
+ cmdA11yQuery,
279
+ cmdA11yClick,
280
+ cmdA11yType,
281
+ cmdShizukuExec,
282
+ cmdRootExec,
283
+ cmdPerms,
284
+ };
@@ -191,6 +191,9 @@ async function cmdSyncAdapter(name, options) {
191
191
  if (options.since) opts.since = Number(options.since);
192
192
  if (options.until) opts.until = Number(options.until);
193
193
  if (options.limit) opts.limit = Number(options.limit);
194
+ // Plan A v0.1 — system-data-android needs a snapshot file path. Generic
195
+ // pass-through so other input-driven adapters reuse the same flag.
196
+ if (options.input) opts.inputPath = String(options.input);
194
197
  const report = await hub.registry.syncAdapter(name, opts);
195
198
  if (spinner) spinner.succeed(`synced ${name}`);
196
199
  if (options.json) {
@@ -496,8 +499,13 @@ async function cmdAIChatHealth(options) {
496
499
  const factoryDeps = options._factoryDeps || {};
497
500
  const hubDir =
498
501
  factoryDeps.hubDir || (await (options._getHub || getHub)()).hubDir;
499
- const { createAIChatHealthChecker } =
500
- await import("@chainlesschain/personal-data-hub/adapters/ai-chat-history/health-checker");
502
+ // Bypass vite import-analysis (which can't resolve subpath exports
503
+ // for dynamic imports in vitest SSR mode) by composing the specifier
504
+ // at runtime — the static analyzer skips non-literal arguments.
505
+ const hcSpecifier =
506
+ "@chainlesschain/personal-data-hub" +
507
+ "/adapters/ai-chat-history/health-checker";
508
+ const { createAIChatHealthChecker } = await import(hcSpecifier);
501
509
  const { createAccountsStore, createVendorAdapterBridge } =
502
510
  await import("../lib/personal-data-hub-aichat-wizard.js");
503
511
  const accountsStore =
@@ -566,13 +574,25 @@ async function cmdWechatEnvProbe(options) {
566
574
  return;
567
575
  }
568
576
  logger.log(chalk.bold("WeChat env-probe:"));
569
- logger.log(` ${probe.ok ? chalk.green("✓") : chalk.red("✗")} suggested: ${chalk.cyan(probe.suggestedKeyProvider)}`);
570
- logger.log(` device: ${probe.device.reachable ? chalk.green("reachable") : chalk.red("unreachable")}${probe.device.serial ? " (" + probe.device.serial + ")" : ""} abi=${probe.device.abi || "?"}`);
571
- logger.log(` root: ${probe.root.detected ? chalk.green("yes") : chalk.gray("no")} magisk=${probe.root.magiskInstalled ? "yes" : "no"}`);
572
- logger.log(` frida-server: ${probe.frida.serverRunning ? chalk.green("running") : chalk.gray("not running")}${probe.frida.port ? " :" + probe.frida.port : ""}`);
573
- logger.log(` wechat: ${probe.wechat.installed ? chalk.green(probe.wechat.versionName) : chalk.gray("not installed")}`);
574
- for (const reason of probe.reasons || []) logger.log(` · ${chalk.gray(reason)}`);
575
- for (const w of probe.warnings || []) logger.log(` ${chalk.yellow("!")} ${chalk.yellow(w)}`);
577
+ logger.log(
578
+ ` ${probe.ok ? chalk.green("") : chalk.red("")} suggested: ${chalk.cyan(probe.suggestedKeyProvider)}`,
579
+ );
580
+ logger.log(
581
+ ` device: ${probe.device.reachable ? chalk.green("reachable") : chalk.red("unreachable")}${probe.device.serial ? " (" + probe.device.serial + ")" : ""} abi=${probe.device.abi || "?"}`,
582
+ );
583
+ logger.log(
584
+ ` root: ${probe.root.detected ? chalk.green("yes") : chalk.gray("no")} magisk=${probe.root.magiskInstalled ? "yes" : "no"}`,
585
+ );
586
+ logger.log(
587
+ ` frida-server: ${probe.frida.serverRunning ? chalk.green("running") : chalk.gray("not running")}${probe.frida.port ? " :" + probe.frida.port : ""}`,
588
+ );
589
+ logger.log(
590
+ ` wechat: ${probe.wechat.installed ? chalk.green(probe.wechat.versionName) : chalk.gray("not installed")}`,
591
+ );
592
+ for (const reason of probe.reasons || [])
593
+ logger.log(` · ${chalk.gray(reason)}`);
594
+ for (const w of probe.warnings || [])
595
+ logger.log(` ${chalk.yellow("!")} ${chalk.yellow(w)}`);
576
596
  } catch (err) {
577
597
  fail(null, err, options.json);
578
598
  }
@@ -589,16 +609,21 @@ async function cmdWechatRegister(options) {
589
609
  dbPath: options.db || null,
590
610
  wechatDataPath: options.wechatDataPath || null,
591
611
  keyProviderOverride: options.forceProvider || null,
592
- fridaOpts: options.fridaDeviceId ? { deviceId: options.fridaDeviceId } : null,
612
+ fridaOpts: options.fridaDeviceId
613
+ ? { deviceId: options.fridaDeviceId }
614
+ : null,
593
615
  });
594
616
  if (options.json) {
595
617
  printJson(r);
596
618
  return;
597
619
  }
598
620
  if (!r.ok) {
599
- logger.error(chalk.red(`✗ ${r.reason || "register failed"}: ${r.message || ""}`));
621
+ logger.error(
622
+ chalk.red(`✗ ${r.reason || "register failed"}: ${r.message || ""}`),
623
+ );
600
624
  if (r.probe) {
601
- for (const reason of r.probe.reasons || []) logger.error(chalk.gray(" · " + reason));
625
+ for (const reason of r.probe.reasons || [])
626
+ logger.error(chalk.gray(" · " + reason));
602
627
  }
603
628
  process.exit(1);
604
629
  }
@@ -624,13 +649,202 @@ async function cmdWechatList(options) {
624
649
  }
625
650
  logger.log(chalk.bold("Registered WeChat accounts:"));
626
651
  for (const row of rows) {
627
- logger.log(` • uin=${chalk.cyan(row.uin)} provider=${row.chosenKeyProvider || "?"} db=${row.dbPath || "(none)"} regAt=${row.registeredAt ? new Date(row.registeredAt).toISOString() : "?"}`);
652
+ logger.log(
653
+ ` • uin=${chalk.cyan(row.uin)} provider=${row.chosenKeyProvider || "?"} db=${row.dbPath || "(none)"} regAt=${row.registeredAt ? new Date(row.registeredAt).toISOString() : "?"}`,
654
+ );
655
+ }
656
+ } catch (err) {
657
+ fail(null, err, options.json);
658
+ }
659
+ }
660
+
661
+ /**
662
+ * `cc hub wechat doctor` — env-probe + actionable interpretation +
663
+ * inline reference to the Phase 12.9 §5.1 Frida hook trap table.
664
+ *
665
+ * Designed to be the single command user runs on a rooted Android during
666
+ * Phase 12.9 real-device E2E to figure out "what should I do next?" —
667
+ * combines env-probe output + readiness checklist (per md5 vs frida
668
+ * path) + post-register telemetry fields to capture if hook fails.
669
+ *
670
+ * Returns the same JSON as env-probe + a `doctor` block under --json so
671
+ * scripts can branch on `doctor.readiness === 'ready' | 'blocked' | 'partial'`.
672
+ */
673
+ async function cmdWechatDoctor(options) {
674
+ try {
675
+ const hub = await (options._getHub || getHub)();
676
+ const probe = await hub.probeWechatEnv();
677
+
678
+ // Determine readiness + concrete next-action based on probe shape.
679
+ const advice = interpretWechatProbe(probe);
680
+
681
+ if (options.json) {
682
+ printJson({ probe, doctor: advice });
683
+ return;
684
+ }
685
+
686
+ // Human-readable: re-use env-probe formatting then append advice.
687
+ logger.log(chalk.bold("WeChat env-probe:"));
688
+ logger.log(
689
+ ` ${probe.ok ? chalk.green("✓") : chalk.red("✗")} suggested: ${chalk.cyan(probe.suggestedKeyProvider)}`,
690
+ );
691
+ logger.log(
692
+ ` device: ${probe.device.reachable ? chalk.green("reachable") : chalk.red("unreachable")}${probe.device.serial ? " (" + probe.device.serial + ")" : ""} abi=${probe.device.abi || "?"}`,
693
+ );
694
+ logger.log(
695
+ ` root: ${probe.root.detected ? chalk.green("yes") : chalk.gray("no")} magisk=${probe.root.magiskInstalled ? "yes" : "no"}`,
696
+ );
697
+ logger.log(
698
+ ` frida-server: ${probe.frida.serverRunning ? chalk.green("running") : chalk.gray("not running")}${probe.frida.port ? " :" + probe.frida.port : ""}`,
699
+ );
700
+ logger.log(
701
+ ` wechat: ${probe.wechat.installed ? chalk.green(probe.wechat.versionName) : chalk.gray("not installed")}`,
702
+ );
703
+
704
+ logger.log("");
705
+ const statusColor =
706
+ advice.readiness === "ready"
707
+ ? chalk.green
708
+ : advice.readiness === "partial"
709
+ ? chalk.yellow
710
+ : chalk.red;
711
+ logger.log(
712
+ chalk.bold(`Doctor: ${statusColor(advice.readiness.toUpperCase())}`),
713
+ );
714
+ for (const blocker of advice.blockers) {
715
+ logger.log(` ${chalk.red("✗")} ${blocker}`);
716
+ }
717
+ for (const w of advice.warnings) {
718
+ logger.log(` ${chalk.yellow("!")} ${w}`);
719
+ }
720
+ for (const step of advice.nextSteps) {
721
+ logger.log(` ${chalk.cyan("→")} ${step}`);
722
+ }
723
+
724
+ if (advice.readiness !== "blocked") {
725
+ logger.log("");
726
+ logger.log(
727
+ chalk.bold("After `cc hub wechat register`, capture telemetry:"),
728
+ );
729
+ logger.log(
730
+ chalk.gray(
731
+ " cc hub wechat register --uin <UIN> --db ... --json | jq '.fridaTelemetry'",
732
+ ),
733
+ );
734
+ logger.log(
735
+ chalk.gray(
736
+ " Expected fields: hooked / keySource / keySig / keyFormat / keyLength / keyAlt / errors / durationMs",
737
+ ),
738
+ );
739
+ logger.log("");
740
+ logger.log(
741
+ chalk.bold("If hook fails, match telemetry against trap table:"),
742
+ );
743
+ logger.log(
744
+ chalk.gray(
745
+ " A — hooked:[] empty → libWCDB.so module name (try OEM custom names)",
746
+ ),
747
+ );
748
+ logger.log(
749
+ chalk.gray(
750
+ " B — keySig:v2 but DB won't open → sqlite3_key_v2 args index wrong",
751
+ ),
752
+ );
753
+ logger.log(
754
+ chalk.gray(
755
+ " C — keyFormat:raw-bytes but len=64 → ascii-hex path missed",
756
+ ),
757
+ );
758
+ logger.log(
759
+ chalk.gray(
760
+ " Full table: docs/design/Personal_Data_Hub_Phase_12_9_*Runbook.md §5.1",
761
+ ),
762
+ );
628
763
  }
629
764
  } catch (err) {
630
765
  fail(null, err, options.json);
631
766
  }
632
767
  }
633
768
 
769
+ /**
770
+ * Interpret env-probe result into { readiness, blockers, warnings, nextSteps }.
771
+ * Pure function — testable without device.
772
+ *
773
+ * @param {object} probe output of hub.probeWechatEnv()
774
+ * @returns {{readiness: 'ready'|'partial'|'blocked', blockers: string[],
775
+ * warnings: string[], nextSteps: string[]}}
776
+ */
777
+ function interpretWechatProbe(probe) {
778
+ const blockers = [];
779
+ const warnings = [];
780
+ const nextSteps = [];
781
+
782
+ if (!probe || !probe.device || !probe.device.reachable) {
783
+ blockers.push("adb 设备未连接 (USB 调试 / drivers / authorize?)");
784
+ nextSteps.push("`adb devices` 应列出至少一台 device 状态");
785
+ return { readiness: "blocked", blockers, warnings, nextSteps };
786
+ }
787
+
788
+ if (!probe.wechat || !probe.wechat.installed) {
789
+ blockers.push("WeChat (com.tencent.mm) 未安装");
790
+ nextSteps.push("先把 WeChat 装上 + 登一次产生 EnMicroMsg.db");
791
+ return { readiness: "blocked", blockers, warnings, nextSteps };
792
+ }
793
+
794
+ const ver = probe.wechat.majorVersion || 0;
795
+ const suggested = probe.suggestedKeyProvider;
796
+
797
+ if (ver < 8 && suggested === "md5") {
798
+ nextSteps.push(
799
+ "MD5 path: `adb pull /data/data/com.tencent.mm/ /tmp/wechat-data/`",
800
+ );
801
+ nextSteps.push(
802
+ "拉到本地后跑: `cc hub wechat register --uin <UIN> --db /tmp/wechat-data/MicroMsg/<md5-uin>/EnMicroMsg.db --wechat-data-path /tmp/wechat-data/`",
803
+ );
804
+ if (!probe.root.detected) {
805
+ warnings.push(
806
+ "非 root 设备只能 adb pull WeChat backup 子集 — 可能拿不到全部 db / 文件",
807
+ );
808
+ }
809
+ return { readiness: "ready", blockers, warnings, nextSteps };
810
+ }
811
+
812
+ if (ver >= 8 && suggested === "frida") {
813
+ if (!probe.root.detected) {
814
+ blockers.push("WeChat ≥ 8.0 必须 root + Frida hook,当前设备未 root");
815
+ nextSteps.push("Magisk 刷入 → 重启 → 重跑 doctor");
816
+ return { readiness: "blocked", blockers, warnings, nextSteps };
817
+ }
818
+ if (!probe.frida.serverRunning) {
819
+ blockers.push("Frida server 未运行");
820
+ nextSteps.push(
821
+ "见 docs/design/Adapter_WeChat_SQLCipher_Frida_Setup.md §2 启 frida-server",
822
+ );
823
+ return { readiness: "partial", blockers, warnings, nextSteps };
824
+ }
825
+ nextSteps.push(
826
+ "Frida path 就绪。register: `cc hub wechat register --uin <wxid>`(无需 --db / --wechat-data-path)",
827
+ );
828
+ nextSteps.push("WeChat 必须前台运行(已登录),register 期间不要切走");
829
+ return { readiness: "ready", blockers, warnings, nextSteps };
830
+ }
831
+
832
+ if (suggested === "unsupported") {
833
+ blockers.push("env-probe 判定为 unsupported");
834
+ for (const reason of probe.reasons || []) blockers.push(reason);
835
+ nextSteps.push(
836
+ "见 docs/design/Adapter_WeChat_SQLCipher.md §13 — 检查 WeChat 版本兼容矩阵",
837
+ );
838
+ return { readiness: "blocked", blockers, warnings, nextSteps };
839
+ }
840
+
841
+ // Fallback — probe shape we don't recognize
842
+ warnings.push(
843
+ `未识别的 suggested='${suggested}' (version=${probe.wechat.versionName || "?"})`,
844
+ );
845
+ return { readiness: "partial", blockers, warnings, nextSteps };
846
+ }
847
+
634
848
  async function cmdWechatUnregister(uin, options) {
635
849
  try {
636
850
  if (!uin) throw new Error("uin argument required");
@@ -644,8 +858,12 @@ async function cmdWechatUnregister(uin, options) {
644
858
  logger.error(chalk.red(`✗ ${r.reason || "unregister failed"}`));
645
859
  process.exit(1);
646
860
  }
647
- if (r.removed) logger.log(chalk.green(`✓ removed wechat account (uin=${uin})`));
648
- else logger.log(chalk.gray(`(uin=${uin} was not registered — nothing removed)`));
861
+ if (r.removed)
862
+ logger.log(chalk.green(`✓ removed wechat account (uin=${uin})`));
863
+ else
864
+ logger.log(
865
+ chalk.gray(`(uin=${uin} was not registered — nothing removed)`),
866
+ );
649
867
  } catch (err) {
650
868
  fail(null, err, options.json);
651
869
  }
@@ -712,6 +930,10 @@ export function registerHubCommand(program) {
712
930
  .option("--since <ms>", "Override watermark — sync from this unix-ms")
713
931
  .option("--until <ms>", "Stop at this unix-ms")
714
932
  .option("--limit <n>", "Cap ingested events")
933
+ .option(
934
+ "--input <path>",
935
+ "Path to a snapshot file produced by a UI layer (system-data-android, etc.)",
936
+ )
715
937
  .option("--json", "Output JSON")
716
938
  .action(cmdSyncAdapter);
717
939
 
@@ -833,7 +1055,9 @@ export function registerHubCommand(program) {
833
1055
 
834
1056
  wechat
835
1057
  .command("env-probe")
836
- .description("Probe attached Android device for adb / root / frida-server / WeChat version")
1058
+ .description(
1059
+ "Probe attached Android device for adb / root / frida-server / WeChat version",
1060
+ )
837
1061
  .option("--json", "Output JSON")
838
1062
  .action(cmdWechatEnvProbe);
839
1063
 
@@ -844,9 +1068,15 @@ export function registerHubCommand(program) {
844
1068
  )
845
1069
  .requiredOption("--uin <id>", "WeChat numeric UIN (≤ 8.0) or wxid (8.0+)")
846
1070
  .option("--db <path>", "Local path to the already-pulled EnMicroMsg.db")
847
- .option("--wechat-data-path <dir>", "Local pulled /data/data/com.tencent.mm/ tree (required for md5 path)")
1071
+ .option(
1072
+ "--wechat-data-path <dir>",
1073
+ "Local pulled /data/data/com.tencent.mm/ tree (required for md5 path)",
1074
+ )
848
1075
  .option("--force-provider <md5|frida>", "Override env-probe suggestion")
849
- .option("--frida-device-id <id>", "Frida device id (defaults to first USB device)")
1076
+ .option(
1077
+ "--frida-device-id <id>",
1078
+ "Frida device id (defaults to first USB device)",
1079
+ )
850
1080
  .option("--json", "Output JSON")
851
1081
  .action(cmdWechatRegister);
852
1082
 
@@ -858,9 +1088,21 @@ export function registerHubCommand(program) {
858
1088
 
859
1089
  wechat
860
1090
  .command("unregister <uin>")
861
- .description("Remove a registered WeChat account (does not touch vault data)")
1091
+ .description(
1092
+ "Remove a registered WeChat account (does not touch vault data)",
1093
+ )
862
1094
  .option("--json", "Output JSON")
863
1095
  .action(cmdWechatUnregister);
1096
+
1097
+ // Phase 12.9 — diagnostic helper for real-device E2E. Combines env-probe
1098
+ // with actionable readiness checklist + inline §5.1 Frida trap reference.
1099
+ wechat
1100
+ .command("doctor")
1101
+ .description(
1102
+ "Diagnose WeChat setup — env-probe + readiness checklist + Phase 12.9 trap reference",
1103
+ )
1104
+ .option("--json", "Output JSON")
1105
+ .action(cmdWechatDoctor);
864
1106
  }
865
1107
 
866
1108
  // exported for tests — handler functions can be invoked directly with
@@ -877,5 +1119,7 @@ export const _internal = {
877
1119
  cmdWechatRegister,
878
1120
  cmdWechatList,
879
1121
  cmdWechatUnregister,
1122
+ cmdWechatDoctor,
1123
+ interpretWechatProbe,
880
1124
  _defaultKnownVendors,
881
1125
  };