chainlesschain 0.162.11 → 0.162.13

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 (166) hide show
  1. package/README.md +30 -25
  2. package/package.json +4 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-ebtJGjAG.js → AIOps-Bq_zxhCr.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-CypkRN-G.js → ActionButton-CaevDm9t.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-B2JMlIng.js → Analytics-B0gOmwPw.js} +1 -1
  7. package/src/assets/web-panel/assets/{AppLayout-B8QQ4pk7.js → AppLayout-DWhZiV0Q.js} +2 -2
  8. package/src/assets/web-panel/assets/{Audit-BoYaAyFa.js → Audit-ZuZJBCxo.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BfackGZ5.js → Backup-CX7jhH5l.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-C06FUpDz.js → BaseInput-CVLx7HVq.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-BWxMkBYZ.js → Chat-C6yL5tRD.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-XJMvS3PV.js → Checkbox-BePhbqVq.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-CzR462RK.js → Codegen-B5E4x1Lm.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-BQHpLNCA.js → Col-CWhNU6A7.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-BWRRbJYd.js → Community-mSEAuJhp.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-BunoKIy9.js → Compact-DSudHzX3.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-CtJfZctm.js → Compliance-CxJLYjyn.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-ER5-_bod.js → Cowork-C-trppQj.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cron-C80jYBw1.js → Cron-_Ij4v5fY.js} +1 -1
  20. package/src/assets/web-panel/assets/{Crosschain-fEMlCNsL.js → Crosschain-eLHXzp5T.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-BZpctKmU.js → DID-BP04AUUB.js} +1 -1
  22. package/src/assets/web-panel/assets/{Dashboard-RQhZmLi4.js → Dashboard-CzEeQv62.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-CrpwS84l.js → Dropdown-C_SGOB22.js} +1 -1
  24. package/src/assets/web-panel/assets/{Federation-BEQyZtdR.js → Federation-BgaP4BOv.js} +1 -1
  25. package/src/assets/web-panel/assets/{FormItemContext-DCwvl6Vh.js → FormItemContext-BxGLLt9r.js} +1 -1
  26. package/src/assets/web-panel/assets/{Git-6FihOxMK.js → Git-Bt_uM_Gw.js} +2 -2
  27. package/src/assets/web-panel/assets/{Governance-DlBLHSlJ.js → Governance-N2-0RG_o.js} +1 -1
  28. package/src/assets/web-panel/assets/{Inference-DdSokzV0.js → Inference-eS3g-CzP.js} +1 -1
  29. package/src/assets/web-panel/assets/{KnowledgeGraph-bg8GBHMr.js → KnowledgeGraph-CUuvNVah.js} +1 -1
  30. package/src/assets/web-panel/assets/{Logs-DdFYdLQ-.js → Logs-wPbwEt2r.js} +1 -1
  31. package/src/assets/web-panel/assets/{Marketplace-DjnlAeYF.js → Marketplace-DH91kTwo.js} +1 -1
  32. package/src/assets/web-panel/assets/{McpTools-Czs41YUh.js → McpTools-D-GyyBrI.js} +1 -1
  33. package/src/assets/web-panel/assets/{Memory-CX0b3c8D.js → Memory-BOtUy-tw.js} +1 -1
  34. package/src/assets/web-panel/assets/{MobileBridge-BoFGb9Mm.js → MobileBridge-DIP__XQd.js} +1 -1
  35. package/src/assets/web-panel/assets/{MobileProjects-B8qQ9H-0.js → MobileProjects-1nqr1UsU.js} +1 -1
  36. package/src/assets/web-panel/assets/{Mtc-CRF1NLae.js → Mtc-CCE0x7h2.js} +1 -1
  37. package/src/assets/web-panel/assets/{MtcAudit-CdCm70cJ.js → MtcAudit-BBkz0XUO.js} +1 -1
  38. package/src/assets/web-panel/assets/{Multisig-yoZlpq2Y.js → Multisig-CIKSJvTY.js} +2 -2
  39. package/src/assets/web-panel/assets/{NLProgramming-hFCgqDxJ.js → NLProgramming-BDkgeFcq.js} +1 -1
  40. package/src/assets/web-panel/assets/{Notes-DC8pnxs-.js → Notes-ONiUxfN1.js} +1 -1
  41. package/src/assets/web-panel/assets/{NotificationSettings-DDVg5Nc8.js → NotificationSettings-CGcyKEIe.js} +1 -1
  42. package/src/assets/web-panel/assets/{Organization-0YCtAFMS.js → Organization-BZtMYBJt.js} +4 -4
  43. package/src/assets/web-panel/assets/{Overflow-DhPLoAdz.js → Overflow-C4LfFZAI.js} +1 -1
  44. package/src/assets/web-panel/assets/{OverrideContext-x9ZzjLwk.js → OverrideContext-C_4H9tGA.js} +1 -1
  45. package/src/assets/web-panel/assets/{P2P-BWpuJhkD.js → P2P-D71Cpk-m.js} +1 -1
  46. package/src/assets/web-panel/assets/{Permissions-B4IrizO9.js → Permissions-DRGYF29v.js} +1 -1
  47. package/src/assets/web-panel/assets/PersonalDataHub-CfRYoIua.js +1 -0
  48. package/src/assets/web-panel/assets/PersonalDataHub-Dvaa8niQ.css +1 -0
  49. package/src/assets/web-panel/assets/{Pipeline-M65jR6sq.js → Pipeline-BOyp0_Qo.js} +1 -1
  50. package/src/assets/web-panel/assets/{Privacy-BeO8zLup.js → Privacy-a_AcphvF.js} +1 -1
  51. package/src/assets/web-panel/assets/{ProjectInit-Ck_ZjrVZ.js → ProjectInit-CFu1grYt.js} +1 -1
  52. package/src/assets/web-panel/assets/{ProjectSettings-ijn-97s0.js → ProjectSettings-p54Eivhh.js} +1 -1
  53. package/src/assets/web-panel/assets/{Projects-BsNBemeh.js → Projects-DkB88XAu.js} +1 -1
  54. package/src/assets/web-panel/assets/{Providers-CI7UxKVO.js → Providers-EK7f8DEd.js} +1 -1
  55. package/src/assets/web-panel/assets/{QuickAsk-dE2M1KOB.js → QuickAsk-CpO0w3iP.js} +1 -1
  56. package/src/assets/web-panel/assets/{Recommend-B30EgbKS.js → Recommend-BUJVQdv9.js} +1 -1
  57. package/src/assets/web-panel/assets/{Reputation-CV6n7wMx.js → Reputation-kU2fOuZt.js} +1 -1
  58. package/src/assets/web-panel/assets/{Row-DSaoTjlN.js → Row-oGWRbk6g.js} +1 -1
  59. package/src/assets/web-panel/assets/{RssFeed-_NgBmHaC.js → RssFeed-saC_46Yo.js} +1 -1
  60. package/src/assets/web-panel/assets/{Search-B341ooTV.js → Search-CjtRqFUu.js} +1 -1
  61. package/src/assets/web-panel/assets/{Security-CqCQD8hf.js → Security-BX0NBVfQ.js} +1 -1
  62. package/src/assets/web-panel/assets/{Services-Cju_95rB.js → Services-Bgxsnei_.js} +1 -1
  63. package/src/assets/web-panel/assets/{Skeleton-kh_uW22l.js → Skeleton-CwBb3k2a.js} +1 -1
  64. package/src/assets/web-panel/assets/{Skills-BVRPgciI.js → Skills-CZCMYH6Z.js} +1 -1
  65. package/src/assets/web-panel/assets/{Sla-y-vKFYkI.js → Sla-Djy1uHnZ.js} +1 -1
  66. package/src/assets/web-panel/assets/{SpeechSettings-BXJm9zyo.js → SpeechSettings-CGUI_Uyh.js} +1 -1
  67. package/src/assets/web-panel/assets/{SyncSettings-BmHZR-Kv.js → SyncSettings-DK2CKHRD.js} +1 -1
  68. package/src/assets/web-panel/assets/{Tasks-C7zmYW9f.js → Tasks-BKiOzeIO.js} +1 -1
  69. package/src/assets/web-panel/assets/{Templates-DVEG7FdA.js → Templates-CnQpleXj.js} +1 -1
  70. package/src/assets/web-panel/assets/{Tenant-CpvjzPCo.js → Tenant-DwKz0cjm.js} +1 -1
  71. package/src/assets/web-panel/assets/{Terminal-D_Wpp2iE.js → Terminal-A7t_wsR8.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tokens-QBrjdNqi.js → Tokens-BqYY9l44.js} +1 -1
  73. package/src/assets/web-panel/assets/{Trigger-BhR_VEvQ.js → Trigger-BI4bXFmi.js} +1 -1
  74. package/src/assets/web-panel/assets/{Trust-C0xhM2lC.js → Trust-yMynKTRG.js} +1 -1
  75. package/src/assets/web-panel/assets/{UkeySign-BnyP-W3-.js → UkeySign-Br4IScM6.js} +1 -1
  76. package/src/assets/web-panel/assets/{VideoEditing-C5Y8MyEK.js → VideoEditing-CWcThGsP.js} +1 -1
  77. package/src/assets/web-panel/assets/{Wallet-DzCPCQNF.js → Wallet-CZcAtjxj.js} +1 -1
  78. package/src/assets/web-panel/assets/{WebAuthn-6X5bLtHU.js → WebAuthn-BnTZFMA0.js} +3 -3
  79. package/src/assets/web-panel/assets/{WorkflowEditor-ekS27G9f.js → WorkflowEditor-N7gGz3_n.js} +1 -1
  80. package/src/assets/web-panel/assets/{chat-BikodUwh.js → chat-D175ZIO0.js} +1 -1
  81. package/src/assets/web-panel/assets/{collapseMotion-CjFH_Jop.js → collapseMotion-DfnRZex1.js} +1 -1
  82. package/src/assets/web-panel/assets/{colors-8yIg5K7E.js → colors-LKhZyttv.js} +1 -1
  83. package/src/assets/web-panel/assets/{compact-item-MLWo5-GY.js → compact-item-CsJSebxT.js} +1 -1
  84. package/src/assets/web-panel/assets/{createContext-nir7ccDv.js → createContext-BJ_CPYFC.js} +1 -1
  85. package/src/assets/web-panel/assets/{echarts-Bq-n0MtJ.js → echarts-Dj_pBaVI.js} +1 -1
  86. package/src/assets/web-panel/assets/{hasIn-DxUIHW2P.js → hasIn-CzD3IqH8.js} +1 -1
  87. package/src/assets/web-panel/assets/{icons-CLQTHa5-.js → icons-BOPtEWK4.js} +4 -4
  88. package/src/assets/web-panel/assets/{index-vVrIg9Jk.js → index-6qPbrYF7.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-CCGf6IJj.js → index-B7UYymse.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-DSjWvxVr.js → index-BDSZDDb2.js} +4 -4
  91. package/src/assets/web-panel/assets/{index-BURKtxBq.js → index-BEDFHKO3.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-q3Lr2UzW.js → index-BMvdoiFr.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-BgQtoOHc.js → index-BNVLVzN5.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-Bn5VWKW1.js → index-BSNibAqz.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-4SFekeAy.js → index-BV-__mlC.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CfeuuE7v.js → index-BXH9ujMW.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-h05fIj9Q.js → index-BZluCuTH.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-9Y0IyfeM.js → index-BhiZDGg7.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BkMtxzcM.js → index-BjOrt4vw.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DOIryna2.js → index-BmJdof_c.js} +2 -2
  101. package/src/assets/web-panel/assets/{index-TB5vrA0Z.js → index-BsirlkJ0.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BgHPrMXP.js → index-BvF2tC6C.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BSIaRmzU.js → index-C2HBKw07.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CkSN2Ki_.js → index-CKjBAdm0.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DLiexKJ2.js → index-CRGNuUIM.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CeX-HLIi.js → index-CTIkCKav.js} +1 -1
  107. package/src/assets/web-panel/assets/index-CY1mQA2I.js +1 -0
  108. package/src/assets/web-panel/assets/{index-78olN7S9.js → index-CyHdYUeZ.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BcyG-9vV.js → index-D401L3yx.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-C_W1kVtY.js → index-D5IZCkZG.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Bpa9senE.js → index-D7ZcBI5s.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-dgZZAsxo.js → index-D8kltMTW.js} +1 -1
  113. package/src/assets/web-panel/assets/index-D9nXHfUB.js +1 -0
  114. package/src/assets/web-panel/assets/{index-DkIon-Gv.js → index-DJVkBmSc.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C-UB9bYd.js → index-DM3uBEWD.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Dx5xZmzt.js → index-DOO73rHE.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DVLJ1iGu.js → index-DXp1jVsK.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-S8mYImvf.js → index-Dd7dICwB.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-SUYLhwZI.js → index-Dm-3kvtD.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-C_Xi08tu.js → index-DnPt5OdD.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-hu-wjfWv.js → index-E7t1hAnk.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Cp-YnzHN.js → index-JTX9A7w0.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-8qrwsaKy.js → index-Kn-Of5ew.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Dl-O2OkQ.js → index-R1cFADfk.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-yfNusVbo.js → index-RIO4JKMP.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DDmc4cig.js → index-uTEVWPYA.js} +1 -1
  127. package/src/assets/web-panel/assets/{initDefaultProps-xUjF_bq0.js → initDefaultProps-CBW0okek.js} +1 -1
  128. package/src/assets/web-panel/assets/{motion-B019-Q6h.js → motion-DGAffQ0Z.js} +1 -1
  129. package/src/assets/web-panel/assets/{move-D2XYj_gA.js → move-DFJ0-5IW.js} +1 -1
  130. package/src/assets/web-panel/assets/{omit-AIzzlguv.js → omit-AvrDghg1.js} +1 -1
  131. package/src/assets/web-panel/assets/{pickAttrs-CRkEQaLs.js → pickAttrs-D7csw9i1.js} +1 -1
  132. package/src/assets/web-panel/assets/{placementArrow-s4kAStH6.js → placementArrow-hZ6Lg6kG.js} +1 -1
  133. package/src/assets/web-panel/assets/{responsiveObserve-BCsWrTkb.js → responsiveObserve-DLLx5VvS.js} +1 -1
  134. package/src/assets/web-panel/assets/{slide-B_Hggtvv.js → slide-BaRIT3ev.js} +1 -1
  135. package/src/assets/web-panel/assets/{statusUtils-DnNf15VW.js → statusUtils-Cdjyuhrz.js} +1 -1
  136. package/src/assets/web-panel/assets/{styleChecker-CSQdy9SQ.js → styleChecker-CbrNybTt.js} +1 -1
  137. package/src/assets/web-panel/assets/useFlexGapSupport-B8gAhiRC.js +1 -0
  138. package/src/assets/web-panel/assets/{useFs-D78PlgeG.js → useFs-BZPy4ICP.js} +1 -1
  139. package/src/assets/web-panel/assets/{useMergedState-O7QXt4P5.js → useMergedState-WwedrFR0.js} +1 -1
  140. package/src/assets/web-panel/assets/{useRefs-0J6m8UWN.js → useRefs-Cdq8EWeF.js} +1 -1
  141. package/src/assets/web-panel/assets/{useState-CSzR8F8O.js → useState-DGS1NOyn.js} +1 -1
  142. package/src/assets/web-panel/assets/{vendor-M5lGV-wr.js → vendor-DhFY8mDK.js} +1 -1
  143. package/src/assets/web-panel/assets/{vnode-BTMmpsWu.js → vnode-6Y0NDMVv.js} +1 -1
  144. package/src/assets/web-panel/assets/{zoom-CwOTbvKc.js → zoom-DTbMGsSH.js} +1 -1
  145. package/src/assets/web-panel/index.html +3 -3
  146. package/src/commands/__tests__/hub-aichat.test.js +277 -0
  147. package/src/commands/__tests__/hub-wechat.test.js +243 -0
  148. package/src/commands/hub.js +881 -0
  149. package/src/commands/sync-providers.js +436 -0
  150. package/src/gateways/ws/personal-data-hub-protocol.js +68 -0
  151. package/src/index.js +6 -0
  152. package/src/lib/__tests__/personal-data-hub-aichat-wizard.test.js +209 -0
  153. package/src/lib/__tests__/sync-credentials.test.js +265 -0
  154. package/src/lib/__tests__/sync-engine-cli.test.js +293 -0
  155. package/src/lib/personal-data-hub-aichat-wizard.js +242 -0
  156. package/src/lib/personal-data-hub-wiring.js +189 -0
  157. package/src/lib/sync-cli-db.js +194 -0
  158. package/src/lib/sync-credentials.js +225 -0
  159. package/src/lib/sync-engine-cli.js +406 -0
  160. package/src/lib/sync-oss-client.js +273 -0
  161. package/src/lib/sync-webdav-client.js +194 -0
  162. package/src/assets/web-panel/assets/PersonalDataHub-BK7I0Rsb.css +0 -1
  163. package/src/assets/web-panel/assets/PersonalDataHub-ZbziiUr6.js +0 -1
  164. package/src/assets/web-panel/assets/index-BeA3spHc.js +0 -1
  165. package/src/assets/web-panel/assets/index-DoLRjAoc.js +0 -1
  166. package/src/assets/web-panel/assets/useFlexGapSupport-Bt-T27Pf.js +0 -1
@@ -0,0 +1,881 @@
1
+ /**
2
+ * Personal Data Hub — CLI subcommand surface.
3
+ *
4
+ * Exposes the same operations the WS gateway (gateways/ws/personal-data-
5
+ * hub-protocol.js) handles, so `cc hub <verb>` works identically across:
6
+ * - Desktop in-app terminal (Phase 2.5 cc bundle)
7
+ * - `cc ui` web-shell (WS topic personal-data-hub.* — peer to this)
8
+ * - Direct CLI invocation (this file)
9
+ *
10
+ * Plan A v0.1 Sub-Phase A3.1. Real-device verified on Xiaomi 24115RA8EC
11
+ * 2026-05-20 via cc-smoke.js (T1/T2/T3 PASS) — bs3mc + SQLCipher vault
12
+ * proven working in-app. This command surface is what makes those
13
+ * capabilities usable without writing JS.
14
+ *
15
+ * All output supports --json for scripting. Default is human-readable.
16
+ */
17
+
18
+ import chalk from "chalk";
19
+ import ora from "ora";
20
+ import { logger } from "../lib/logger.js";
21
+ import { getHub } from "../lib/personal-data-hub-wiring.js";
22
+ import { getAIChatWizard } from "../lib/personal-data-hub-aichat-wizard.js";
23
+
24
+ function printJson(obj) {
25
+ console.log(JSON.stringify(obj, null, 2));
26
+ }
27
+
28
+ function fail(spinner, err, asJson) {
29
+ if (spinner) spinner.stop();
30
+ const msg = err && err.message ? err.message : String(err);
31
+ if (asJson) {
32
+ printJson({ error: msg });
33
+ } else {
34
+ logger.error(chalk.red(`✗ ${msg}`));
35
+ }
36
+ process.exit(1);
37
+ }
38
+
39
+ // ─── ask ──────────────────────────────────────────────────────────────
40
+
41
+ async function cmdAsk(question, options) {
42
+ const spinner = options.json ? null : ora("Asking hub...").start();
43
+ try {
44
+ const hub = await getHub();
45
+ if (!hub.engine) throw new Error("Analysis engine unavailable");
46
+ const result = await hub.engine.ask(question, {
47
+ useRag: options.useRag !== false,
48
+ acceptNonLocal: !!options.acceptNonLocal,
49
+ });
50
+ if (spinner) spinner.stop();
51
+ if (options.json) {
52
+ printJson(result);
53
+ } else {
54
+ if (result.error) {
55
+ logger.error(chalk.red(`✗ ${result.error}`));
56
+ process.exit(1);
57
+ }
58
+ logger.log(result.answer);
59
+ if (result.citations && result.citations.length) {
60
+ logger.log(
61
+ chalk.gray(
62
+ `\n依据: ${result.citations.map((c) => c.eventId).join(", ")}`,
63
+ ),
64
+ );
65
+ }
66
+ if (result.llmName) {
67
+ const localTag = result.isLocal
68
+ ? chalk.green("[local]")
69
+ : chalk.yellow("[remote]");
70
+ logger.log(chalk.gray(`-- ${result.llmName} ${localTag}`));
71
+ }
72
+ }
73
+ } catch (err) {
74
+ fail(spinner, err, options.json);
75
+ }
76
+ }
77
+
78
+ // ─── stats ────────────────────────────────────────────────────────────
79
+
80
+ async function cmdStats(options) {
81
+ try {
82
+ const hub = await getHub();
83
+ const out = {
84
+ vault: hub.vault.stats(),
85
+ adapters: hub.registry.list(),
86
+ hubDir: hub.hubDir,
87
+ llm: hub.llm ? { name: hub.llm.name, isLocal: hub.llm.isLocal } : null,
88
+ };
89
+ if (options.json) {
90
+ printJson(out);
91
+ } else {
92
+ const v = out.vault;
93
+ logger.log(chalk.bold("vault:"));
94
+ logger.log(` events: ${v.events}`);
95
+ logger.log(` persons: ${v.persons}`);
96
+ logger.log(` places: ${v.places}`);
97
+ logger.log(` items: ${v.items}`);
98
+ logger.log(` topics: ${v.topics}`);
99
+ logger.log(chalk.bold(`\nadapters (${out.adapters.length}):`));
100
+ for (const a of out.adapters) {
101
+ logger.log(` ${chalk.cyan(a.name)} v${a.version}`);
102
+ }
103
+ logger.log(chalk.gray(`\nhubDir: ${out.hubDir}`));
104
+ if (out.llm) {
105
+ const tag = out.llm.isLocal
106
+ ? chalk.green("[local]")
107
+ : chalk.yellow("[remote]");
108
+ logger.log(chalk.gray(`llm: ${out.llm.name} ${tag}`));
109
+ } else {
110
+ logger.log(chalk.yellow("llm: (none)"));
111
+ }
112
+ }
113
+ } catch (err) {
114
+ fail(null, err, options.json);
115
+ }
116
+ }
117
+
118
+ // ─── health ───────────────────────────────────────────────────────────
119
+
120
+ async function cmdHealth(options) {
121
+ try {
122
+ const hub = await getHub();
123
+ const out = {
124
+ vault: {
125
+ ok: !!hub.vault.db,
126
+ schemaVersion: hub.vault.schemaVersion(),
127
+ },
128
+ llm: hub.llm
129
+ ? { ok: true, isLocal: hub.llm.isLocal, name: hub.llm.name }
130
+ : { ok: false, reason: "LLM unavailable" },
131
+ kgSink: { ok: !!hub.kgSink },
132
+ ragSink: { ok: !!hub.ragSink },
133
+ };
134
+ if (options.json) {
135
+ printJson(out);
136
+ } else {
137
+ const mark = (ok) => (ok ? chalk.green("✓") : chalk.red("✗"));
138
+ logger.log(
139
+ `${mark(out.vault.ok)} vault schema=${out.vault.schemaVersion}`,
140
+ );
141
+ logger.log(
142
+ `${mark(out.llm.ok)} llm ${out.llm.name || out.llm.reason}${
143
+ out.llm.ok
144
+ ? out.llm.isLocal
145
+ ? chalk.green(" [local]")
146
+ : chalk.yellow(" [remote]")
147
+ : ""
148
+ }`,
149
+ );
150
+ logger.log(`${mark(out.kgSink.ok)} kgSink`);
151
+ logger.log(`${mark(out.ragSink.ok)} ragSink`);
152
+ }
153
+ } catch (err) {
154
+ fail(null, err, options.json);
155
+ }
156
+ }
157
+
158
+ // ─── list-adapters ────────────────────────────────────────────────────
159
+
160
+ async function cmdListAdapters(options) {
161
+ try {
162
+ const hub = await getHub();
163
+ const adapters = hub.registry.list();
164
+ if (options.json) {
165
+ printJson(adapters);
166
+ } else {
167
+ if (!adapters.length) {
168
+ logger.log(chalk.yellow("(no adapters registered)"));
169
+ return;
170
+ }
171
+ for (const a of adapters) {
172
+ logger.log(
173
+ `${chalk.cyan(a.name.padEnd(22))} v${a.version.padEnd(8)} ${
174
+ a.sensitivity ? chalk.gray(`(${a.sensitivity})`) : ""
175
+ }`,
176
+ );
177
+ }
178
+ }
179
+ } catch (err) {
180
+ fail(null, err, options.json);
181
+ }
182
+ }
183
+
184
+ // ─── sync-adapter / sync-all ──────────────────────────────────────────
185
+
186
+ async function cmdSyncAdapter(name, options) {
187
+ const spinner = options.json ? null : ora(`syncing ${name}...`).start();
188
+ try {
189
+ const hub = await getHub();
190
+ const opts = {};
191
+ if (options.since) opts.since = Number(options.since);
192
+ if (options.until) opts.until = Number(options.until);
193
+ if (options.limit) opts.limit = Number(options.limit);
194
+ const report = await hub.registry.syncAdapter(name, opts);
195
+ if (spinner) spinner.succeed(`synced ${name}`);
196
+ if (options.json) {
197
+ printJson(report);
198
+ } else {
199
+ logger.log(
200
+ `ingested=${report.ingested} kgTriples=${report.kgTriples} ragDocs=${report.ragDocs} durationMs=${report.durationMs}`,
201
+ );
202
+ }
203
+ } catch (err) {
204
+ fail(spinner, err, options.json);
205
+ }
206
+ }
207
+
208
+ async function cmdSyncAll(options) {
209
+ const spinner = options.json ? null : ora("syncing all...").start();
210
+ try {
211
+ const hub = await getHub();
212
+ const opts = {};
213
+ if (options.since) opts.since = Number(options.since);
214
+ if (options.until) opts.until = Number(options.until);
215
+ if (options.limit) opts.limit = Number(options.limit);
216
+ const reports = await hub.registry.syncAll(opts);
217
+ if (spinner) spinner.succeed(`synced ${reports.length} adapters`);
218
+ if (options.json) {
219
+ printJson(reports);
220
+ } else {
221
+ for (const r of reports) {
222
+ logger.log(
223
+ `${chalk.cyan(r.adapter)} ingested=${r.ingested} dur=${r.durationMs}ms`,
224
+ );
225
+ }
226
+ }
227
+ } catch (err) {
228
+ fail(spinner, err, options.json);
229
+ }
230
+ }
231
+
232
+ // ─── query-events / recent-audit ─────────────────────────────────────
233
+
234
+ async function cmdQueryEvents(options) {
235
+ try {
236
+ const hub = await getHub();
237
+ const q = {};
238
+ if (options.subtype) q.subtype = options.subtype;
239
+ if (options.since) q.since = Number(options.since);
240
+ if (options.until) q.until = Number(options.until);
241
+ if (options.actor) q.actor = options.actor;
242
+ if (options.adapter) q.adapter = options.adapter;
243
+ if (options.limit) q.limit = Number(options.limit);
244
+ const events = hub.vault.queryEvents(q);
245
+ if (options.json) {
246
+ printJson(events);
247
+ } else {
248
+ logger.log(`${events.length} events:`);
249
+ for (const ev of events) {
250
+ const at = new Date(ev.at).toISOString();
251
+ logger.log(
252
+ ` ${chalk.gray(at)} ${chalk.cyan(ev.subtype)} ${ev.summary || ev.id}`,
253
+ );
254
+ }
255
+ }
256
+ } catch (err) {
257
+ fail(null, err, options.json);
258
+ }
259
+ }
260
+
261
+ async function cmdRecentAudit(options) {
262
+ try {
263
+ const hub = await getHub();
264
+ const q = {};
265
+ if (options.since) q.since = Number(options.since);
266
+ if (options.action) q.action = options.action;
267
+ if (options.limit) q.limit = Number(options.limit);
268
+ const rows = hub.vault.queryAudit(q);
269
+ if (options.json) {
270
+ printJson(rows);
271
+ } else {
272
+ logger.log(`${rows.length} audit rows:`);
273
+ for (const r of rows) {
274
+ const at = new Date(r.at).toISOString();
275
+ logger.log(
276
+ ` ${chalk.gray(at)} ${chalk.cyan(r.action)} ${r.adapter || ""} ${r.eventId || ""}`,
277
+ );
278
+ }
279
+ }
280
+ } catch (err) {
281
+ fail(null, err, options.json);
282
+ }
283
+ }
284
+
285
+ // ─── register-mock ───────────────────────────────────────────────────
286
+
287
+ async function cmdRegisterMock(options) {
288
+ try {
289
+ const hub = await getHub();
290
+ const a = hub.registerMockAdapter({
291
+ name: options.name || "mock",
292
+ count: options.count ? Number(options.count) : 20,
293
+ seed: options.seed ? Number(options.seed) : 1,
294
+ });
295
+ const out = { name: a.name, version: a.version };
296
+ if (options.json) {
297
+ printJson(out);
298
+ } else {
299
+ logger.log(chalk.green(`✓ registered ${out.name} v${out.version}`));
300
+ }
301
+ } catch (err) {
302
+ fail(null, err, options.json);
303
+ }
304
+ }
305
+
306
+ // ─── destroy ─────────────────────────────────────────────────────────
307
+
308
+ async function cmdDestroy(options) {
309
+ if (!options.confirm) {
310
+ const msg =
311
+ "Destructive: pass --confirm to wipe vault. This deletes vault.db + WAL.";
312
+ if (options.json) {
313
+ printJson({ error: msg });
314
+ } else {
315
+ logger.error(chalk.red(`✗ ${msg}`));
316
+ }
317
+ process.exit(1);
318
+ }
319
+ try {
320
+ const hub = await getHub();
321
+ hub.vault.destroy();
322
+ if (options.json) {
323
+ printJson({ ok: true });
324
+ } else {
325
+ logger.log(chalk.green("✓ vault destroyed"));
326
+ }
327
+ } catch (err) {
328
+ fail(null, err, options.json);
329
+ }
330
+ }
331
+
332
+ // ─── runSkill ────────────────────────────────────────────────────────
333
+
334
+ async function cmdRunSkill(name, options) {
335
+ const spinner = options.json
336
+ ? null
337
+ : ora(`running analysis skill ${name}...`).start();
338
+ try {
339
+ const hub = await getHub();
340
+ if (!hub.analysisSkillNames.includes(name)) {
341
+ throw new Error(
342
+ `Unknown skill: ${name}. Available: ${hub.analysisSkillNames.join(", ")}`,
343
+ );
344
+ }
345
+ const skillOpts = {};
346
+ if (options.since) skillOpts.since = Number(options.since);
347
+ if (options.until) skillOpts.until = Number(options.until);
348
+ const result = await hub.runSkill(name, skillOpts);
349
+ if (spinner) spinner.stop();
350
+ if (options.json) {
351
+ printJson(result);
352
+ } else {
353
+ logger.log(JSON.stringify(result, null, 2));
354
+ }
355
+ } catch (err) {
356
+ fail(spinner, err, options.json);
357
+ }
358
+ }
359
+
360
+ // ─── Phase 10.3 — cc hub aichat <verb> wizard subcommand surface ─────
361
+ //
362
+ // Mirrors the WS topics (personal-data-hub.aichat-*) so scripts and the
363
+ // Android in-app terminal can drive the AIChat WebView wizard without a
364
+ // UI. cc ui defaults to fallbackMode:"paste" — these subcommands inherit
365
+ // that, so the user-facing path is always: login (print loginUrl) →
366
+ // fetch cookies in external browser → register --cookies <string>.
367
+
368
+ async function cmdAIChatList(options) {
369
+ try {
370
+ const wiz =
371
+ options._wizard ||
372
+ getAIChatWizard({ hubDir: (await (options._getHub || getHub)()).hubDir });
373
+ // Probe each known vendor for current health
374
+ const items = [];
375
+ for (const vendor of options._knownVendors || _defaultKnownVendors()) {
376
+ const opened = await wiz.openVendorLogin({ vendor });
377
+ items.push({
378
+ vendor,
379
+ displayName: opened.notes ? opened.notes.split(";")[0] : vendor,
380
+ loginUrl: opened.loginUrl,
381
+ fallbackMode: opened.fallbackMode,
382
+ requiredCookies: opened.requiredCookies || [],
383
+ });
384
+ }
385
+ if (options.json) {
386
+ printJson({ vendors: items });
387
+ } else {
388
+ logger.log(chalk.bold("可注册 vendor 列表:"));
389
+ for (const v of items) {
390
+ logger.log(` • ${chalk.cyan(v.vendor)} ${chalk.gray(v.loginUrl)}`);
391
+ }
392
+ }
393
+ } catch (err) {
394
+ fail(null, err, options.json);
395
+ }
396
+ }
397
+
398
+ async function cmdAIChatLogin(vendor, options) {
399
+ try {
400
+ const wiz =
401
+ options._wizard ||
402
+ getAIChatWizard({ hubDir: (await (options._getHub || getHub)()).hubDir });
403
+ const r = await wiz.openVendorLogin({ vendor });
404
+ if (options.json) {
405
+ printJson(r);
406
+ return;
407
+ }
408
+ if (!r.ok) {
409
+ logger.error(chalk.red(`✗ ${r.reason || "open failed"}`));
410
+ process.exit(1);
411
+ }
412
+ logger.log(chalk.bold(`vendor: ${vendor}`));
413
+ logger.log(` loginUrl: ${chalk.cyan(r.loginUrl)}`);
414
+ if (r.helpText) logger.log(` ${chalk.gray(r.helpText)}`);
415
+ if (r.requiredCookies && r.requiredCookies.length) {
416
+ logger.log(
417
+ chalk.gray(
418
+ ` required: ${r.requiredCookies.join(", ")} (至少识别 1 个)`,
419
+ ),
420
+ );
421
+ }
422
+ logger.log(
423
+ chalk.gray(
424
+ "\n登录完成后从浏览器 DevTools 复制 cookie,再跑:\n cc hub aichat register " +
425
+ vendor +
426
+ ' --cookies "<paste>"',
427
+ ),
428
+ );
429
+ } catch (err) {
430
+ fail(null, err, options.json);
431
+ }
432
+ }
433
+
434
+ async function cmdAIChatProbe(vendor, options) {
435
+ try {
436
+ const wiz =
437
+ options._wizard ||
438
+ getAIChatWizard({ hubDir: (await (options._getHub || getHub)()).hubDir });
439
+ if (!options.cookies) {
440
+ throw new Error('--cookies <string> required (e.g. "a=1; b=2")');
441
+ }
442
+ const r = await wiz.probeCookies({ vendor, cookieHeader: options.cookies });
443
+ if (options.json) {
444
+ printJson(r);
445
+ return;
446
+ }
447
+ if (!r.ok) {
448
+ logger.error(
449
+ chalk.red(
450
+ `✗ ${r.reason || "incomplete"} — missing: ${(r.missingRequired || []).join(", ") || "(none)"}`,
451
+ ),
452
+ );
453
+ process.exit(1);
454
+ }
455
+ logger.log(chalk.green(`✓ ${vendor} cookies look valid`));
456
+ logger.log(` found: ${(r.foundRequired || []).join(", ")}`);
457
+ if (r.foundOptional && r.foundOptional.length) {
458
+ logger.log(` +opt: ${r.foundOptional.join(", ")}`);
459
+ }
460
+ } catch (err) {
461
+ fail(null, err, options.json);
462
+ }
463
+ }
464
+
465
+ async function cmdAIChatRegister(vendor, options) {
466
+ try {
467
+ if (!options.cookies) {
468
+ throw new Error("--cookies <string> required");
469
+ }
470
+ const wiz =
471
+ options._wizard ||
472
+ getAIChatWizard({ hubDir: (await (options._getHub || getHub)()).hubDir });
473
+ const r = await wiz.registerVendor({ vendor, cookies: options.cookies });
474
+ if (options.json) {
475
+ printJson(r);
476
+ return;
477
+ }
478
+ if (!r.ok) {
479
+ logger.error(chalk.red(`✗ ${r.reason || "register failed"}`));
480
+ if (r.missingRequired && r.missingRequired.length) {
481
+ logger.error(chalk.gray(` missing: ${r.missingRequired.join(", ")}`));
482
+ }
483
+ process.exit(1);
484
+ }
485
+ logger.log(chalk.green(`✓ registered ${vendor} (${r.accountId})`));
486
+ } catch (err) {
487
+ fail(null, err, options.json);
488
+ }
489
+ }
490
+
491
+ async function cmdAIChatHealth(options) {
492
+ // One-shot health-checker pass (does NOT start the periodic loop —
493
+ // that's owned by long-running processes like the cc ui server). This
494
+ // is the manual/scripted equivalent.
495
+ try {
496
+ const factoryDeps = options._factoryDeps || {};
497
+ const hubDir =
498
+ factoryDeps.hubDir || (await (options._getHub || getHub)()).hubDir;
499
+ const { createAIChatHealthChecker } =
500
+ await import("@chainlesschain/personal-data-hub/adapters/ai-chat-history/health-checker");
501
+ const { createAccountsStore, createVendorAdapterBridge } =
502
+ await import("../lib/personal-data-hub-aichat-wizard.js");
503
+ const accountsStore =
504
+ factoryDeps.accountsStore || createAccountsStore({ hubDir });
505
+ const vendorAdapter =
506
+ factoryDeps.vendorAdapter || createVendorAdapterBridge();
507
+ const hc = createAIChatHealthChecker({
508
+ accountsStore,
509
+ vendorAdapter,
510
+ _deps: factoryDeps.timerDeps,
511
+ });
512
+ const r = await hc.runOnce();
513
+ if (options.json) {
514
+ printJson(r);
515
+ return;
516
+ }
517
+ logger.log(
518
+ `checked=${r.checked} ${chalk.green("ok=" + r.ok)} ${chalk.red("failed=" + r.failed)} ${chalk.yellow("mismatch=" + r.mismatch)}`,
519
+ );
520
+ } catch (err) {
521
+ fail(null, err, options.json);
522
+ }
523
+ }
524
+
525
+ async function cmdAIChatUnregister(vendor, options) {
526
+ try {
527
+ const factoryDeps = options._factoryDeps || {};
528
+ const hubDir =
529
+ factoryDeps.hubDir || (await (options._getHub || getHub)()).hubDir;
530
+ const { createAccountsStore } =
531
+ await import("../lib/personal-data-hub-aichat-wizard.js");
532
+ const accountsStore =
533
+ factoryDeps.accountsStore || createAccountsStore({ hubDir });
534
+ const existing = await accountsStore.get(vendor);
535
+ if (!existing) {
536
+ const result = { ok: false, reason: "NOT_REGISTERED", vendor };
537
+ if (options.json) printJson(result);
538
+ else logger.error(chalk.red(`✗ ${vendor} is not registered`));
539
+ process.exit(1);
540
+ }
541
+ await accountsStore.delete(vendor);
542
+ if (options.json) {
543
+ printJson({ ok: true, vendor, removed: true });
544
+ } else {
545
+ logger.log(chalk.green(`✓ removed ${vendor} from aichat-accounts.json`));
546
+ }
547
+ } catch (err) {
548
+ fail(null, err, options.json);
549
+ }
550
+ }
551
+
552
+ // ─── Phase 12.6.9 — cc hub wechat <verb> subcommand surface ─────────
553
+ //
554
+ // Mirrors the WS topics (personal-data-hub.wechat-env-probe /
555
+ // register-wechat / unregister-wechat / list-wechat-accounts). Useful
556
+ // for headless / scripted setup on a rooted Android attached to a Mac
557
+ // where the user doesn't want to bring up the Vue page just to wire
558
+ // the adapter (cf. cc hub aichat).
559
+
560
+ async function cmdWechatEnvProbe(options) {
561
+ try {
562
+ const hub = await (options._getHub || getHub)();
563
+ const probe = await hub.probeWechatEnv();
564
+ if (options.json) {
565
+ printJson(probe);
566
+ return;
567
+ }
568
+ 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)}`);
576
+ } catch (err) {
577
+ fail(null, err, options.json);
578
+ }
579
+ }
580
+
581
+ async function cmdWechatRegister(options) {
582
+ try {
583
+ if (!options.uin) {
584
+ throw new Error("--uin <wxid-or-uin> required");
585
+ }
586
+ const hub = await (options._getHub || getHub)();
587
+ const r = await hub.registerWechatAdapter({
588
+ account: { uin: options.uin },
589
+ dbPath: options.db || null,
590
+ wechatDataPath: options.wechatDataPath || null,
591
+ keyProviderOverride: options.forceProvider || null,
592
+ fridaOpts: options.fridaDeviceId ? { deviceId: options.fridaDeviceId } : null,
593
+ });
594
+ if (options.json) {
595
+ printJson(r);
596
+ return;
597
+ }
598
+ if (!r.ok) {
599
+ logger.error(chalk.red(`✗ ${r.reason || "register failed"}: ${r.message || ""}`));
600
+ if (r.probe) {
601
+ for (const reason of r.probe.reasons || []) logger.error(chalk.gray(" · " + reason));
602
+ }
603
+ process.exit(1);
604
+ }
605
+ logger.log(chalk.green(`✓ wechat registered (uin=${options.uin})`));
606
+ logger.log(` provider: ${chalk.cyan(r.chosenKeyProvider)}`);
607
+ logger.log(` sensitivity: ${r.sensitivity}`);
608
+ } catch (err) {
609
+ fail(null, err, options.json);
610
+ }
611
+ }
612
+
613
+ async function cmdWechatList(options) {
614
+ try {
615
+ const hub = await (options._getHub || getHub)();
616
+ const rows = hub.listWechatAccounts();
617
+ if (options.json) {
618
+ printJson({ accounts: rows });
619
+ return;
620
+ }
621
+ if (rows.length === 0) {
622
+ logger.log(chalk.gray("(no registered WeChat accounts)"));
623
+ return;
624
+ }
625
+ logger.log(chalk.bold("Registered WeChat accounts:"));
626
+ 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() : "?"}`);
628
+ }
629
+ } catch (err) {
630
+ fail(null, err, options.json);
631
+ }
632
+ }
633
+
634
+ async function cmdWechatUnregister(uin, options) {
635
+ try {
636
+ if (!uin) throw new Error("uin argument required");
637
+ const hub = await (options._getHub || getHub)();
638
+ const r = await hub.unregisterWechatAdapter(uin);
639
+ if (options.json) {
640
+ printJson(r);
641
+ return;
642
+ }
643
+ if (!r.ok) {
644
+ logger.error(chalk.red(`✗ ${r.reason || "unregister failed"}`));
645
+ process.exit(1);
646
+ }
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)`));
649
+ } catch (err) {
650
+ fail(null, err, options.json);
651
+ }
652
+ }
653
+
654
+ function _defaultKnownVendors() {
655
+ // Match KNOWN_VENDORS in the hub package — kept inline so this file
656
+ // doesn't have to dynamic-import that module on the hot path.
657
+ return [
658
+ "deepseek",
659
+ "kimi",
660
+ "tongyi",
661
+ "zhipu",
662
+ "hunyuan",
663
+ "qianfan",
664
+ "coze",
665
+ "dreamina",
666
+ "doubao",
667
+ ];
668
+ }
669
+
670
+ // ─── Commander wire-up ───────────────────────────────────────────────
671
+
672
+ export function registerHubCommand(program) {
673
+ const hub = program
674
+ .command("hub")
675
+ .description(
676
+ "Personal Data Hub — local vault + adapters + AnalysisEngine on this machine",
677
+ );
678
+
679
+ hub
680
+ .command("ask <question>")
681
+ .description("Natural-language question over your local vault")
682
+ .option("--use-rag", "Enable RAG retrieval (default true)", true)
683
+ .option("--no-use-rag", "Disable RAG retrieval")
684
+ .option(
685
+ "--accept-non-local",
686
+ "Allow non-local LLM (sends data off device — required when provider is not Ollama/vLLM)",
687
+ )
688
+ .option("--json", "Output JSON")
689
+ .action(cmdAsk);
690
+
691
+ hub
692
+ .command("stats")
693
+ .description("Vault row counts + registered adapter list + hub paths")
694
+ .option("--json", "Output JSON")
695
+ .action(cmdStats);
696
+
697
+ hub
698
+ .command("health")
699
+ .description("Component health: vault / llm / kgSink / ragSink")
700
+ .option("--json", "Output JSON")
701
+ .action(cmdHealth);
702
+
703
+ hub
704
+ .command("list-adapters")
705
+ .description("List registered adapters")
706
+ .option("--json", "Output JSON")
707
+ .action(cmdListAdapters);
708
+
709
+ hub
710
+ .command("sync-adapter <name>")
711
+ .description("Run one adapter's ingest pipeline")
712
+ .option("--since <ms>", "Override watermark — sync from this unix-ms")
713
+ .option("--until <ms>", "Stop at this unix-ms")
714
+ .option("--limit <n>", "Cap ingested events")
715
+ .option("--json", "Output JSON")
716
+ .action(cmdSyncAdapter);
717
+
718
+ hub
719
+ .command("sync-all")
720
+ .description("Run all registered adapters in series")
721
+ .option("--since <ms>", "Override watermark for all")
722
+ .option("--until <ms>", "Stop at this unix-ms")
723
+ .option("--limit <n>", "Cap each adapter")
724
+ .option("--json", "Output JSON")
725
+ .action(cmdSyncAll);
726
+
727
+ hub
728
+ .command("query-events")
729
+ .description("Query vault events with filters")
730
+ .option("--subtype <t>", "Event subtype filter")
731
+ .option("--since <ms>", "Start of time window (unix-ms)")
732
+ .option("--until <ms>", "End of time window (unix-ms)")
733
+ .option("--actor <id>", "Actor person id filter")
734
+ .option("--adapter <name>", "Adapter origin filter")
735
+ .option("--limit <n>", "Max rows", "100")
736
+ .option("--json", "Output JSON")
737
+ .action(cmdQueryEvents);
738
+
739
+ hub
740
+ .command("recent-audit")
741
+ .description("Recent audit log entries")
742
+ .option("--since <ms>", "Start of time window (unix-ms)")
743
+ .option("--action <a>", "Filter by action (ingest / ask / register / ...)")
744
+ .option("--limit <n>", "Max rows", "50")
745
+ .option("--json", "Output JSON")
746
+ .action(cmdRecentAudit);
747
+
748
+ hub
749
+ .command("register-mock")
750
+ .description("Register MockAdapter (dev/smoke only)")
751
+ .option("--name <n>", "Adapter name", "mock")
752
+ .option("--count <n>", "How many fake events per sync", "20")
753
+ .option("--seed <n>", "Deterministic seed", "1")
754
+ .option("--json", "Output JSON")
755
+ .action(cmdRegisterMock);
756
+
757
+ hub
758
+ .command("run-skill <name>")
759
+ .description(
760
+ "Run one of the built-in analysis skills (spending/relations/footprint/interests/timeline)",
761
+ )
762
+ .option("--since <ms>", "Start of time window")
763
+ .option("--until <ms>", "End of time window")
764
+ .option("--json", "Output JSON")
765
+ .action(cmdRunSkill);
766
+
767
+ hub
768
+ .command("destroy")
769
+ .description(
770
+ "DESTRUCTIVE: wipe vault.db + WAL. Requires --confirm. Adapters / accounts files preserved.",
771
+ )
772
+ .option("--confirm", "Required to proceed")
773
+ .option("--json", "Output JSON")
774
+ .action(cmdDestroy);
775
+
776
+ // Phase 10.3 — AIChat WebView wizard CLI surface (paste-mode on cc ui).
777
+ const aichat = hub
778
+ .command("aichat")
779
+ .description(
780
+ "AIChat WebView 鉴权向导 — list / login / probe / register / health / unregister 9 家国产 AI",
781
+ );
782
+
783
+ aichat
784
+ .command("list")
785
+ .description("List the 9 known AIChat vendors with their login URLs")
786
+ .option("--json", "Output JSON")
787
+ .action(cmdAIChatList);
788
+
789
+ aichat
790
+ .command("login <vendor>")
791
+ .description("Print the vendor's login URL + paste-fallback help text")
792
+ .option("--json", "Output JSON")
793
+ .action(cmdAIChatLogin);
794
+
795
+ aichat
796
+ .command("probe <vendor>")
797
+ .description(
798
+ "Classify a pasted cookie string against the vendor spec (dry-run)",
799
+ )
800
+ .option("--cookies <string>", 'Raw cookie header (e.g. "a=1; b=2")')
801
+ .option("--json", "Output JSON")
802
+ .action(cmdAIChatProbe);
803
+
804
+ aichat
805
+ .command("register <vendor>")
806
+ .description(
807
+ "Register the vendor — runs validateCookie + persists aichat-accounts.json",
808
+ )
809
+ .option("--cookies <string>", "Raw cookie header from the browser")
810
+ .option("--json", "Output JSON")
811
+ .action(cmdAIChatRegister);
812
+
813
+ aichat
814
+ .command("health")
815
+ .description("Run one HealthChecker pass over registered AIChat vendors")
816
+ .option("--json", "Output JSON")
817
+ .action(cmdAIChatHealth);
818
+
819
+ aichat
820
+ .command("unregister <vendor>")
821
+ .description(
822
+ "Remove a registered AIChat vendor entry (does not touch vault data)",
823
+ )
824
+ .option("--json", "Output JSON")
825
+ .action(cmdAIChatUnregister);
826
+
827
+ // Phase 12.6.9 — cc hub wechat <verb> mirror of WS topics.
828
+ const wechat = hub
829
+ .command("wechat")
830
+ .description(
831
+ "WeChat adapter — env-probe / register / list / unregister (rooted Android required for 8.0+)",
832
+ );
833
+
834
+ wechat
835
+ .command("env-probe")
836
+ .description("Probe attached Android device for adb / root / frida-server / WeChat version")
837
+ .option("--json", "Output JSON")
838
+ .action(cmdWechatEnvProbe);
839
+
840
+ wechat
841
+ .command("register")
842
+ .description(
843
+ "Bootstrap a WeChat adapter (chooses md5 or frida path per env-probe) and persist wechat-accounts.json",
844
+ )
845
+ .requiredOption("--uin <id>", "WeChat numeric UIN (≤ 8.0) or wxid (8.0+)")
846
+ .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)")
848
+ .option("--force-provider <md5|frida>", "Override env-probe suggestion")
849
+ .option("--frida-device-id <id>", "Frida device id (defaults to first USB device)")
850
+ .option("--json", "Output JSON")
851
+ .action(cmdWechatRegister);
852
+
853
+ wechat
854
+ .command("list")
855
+ .description("List registered WeChat accounts (scrubbed)")
856
+ .option("--json", "Output JSON")
857
+ .action(cmdWechatList);
858
+
859
+ wechat
860
+ .command("unregister <uin>")
861
+ .description("Remove a registered WeChat account (does not touch vault data)")
862
+ .option("--json", "Output JSON")
863
+ .action(cmdWechatUnregister);
864
+ }
865
+
866
+ // exported for tests — handler functions can be invoked directly with
867
+ // `_wizard` / `_factoryDeps` / `_knownVendors` injected, bypassing the
868
+ // real `getHub()` call. The commander wiring above is the runtime path.
869
+ export const _internal = {
870
+ cmdAIChatList,
871
+ cmdAIChatLogin,
872
+ cmdAIChatProbe,
873
+ cmdAIChatRegister,
874
+ cmdAIChatHealth,
875
+ cmdAIChatUnregister,
876
+ cmdWechatEnvProbe,
877
+ cmdWechatRegister,
878
+ cmdWechatList,
879
+ cmdWechatUnregister,
880
+ _defaultKnownVendors,
881
+ };