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.
- package/README.md +30 -25
- package/package.json +4 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-ebtJGjAG.js → AIOps-Bq_zxhCr.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CypkRN-G.js → ActionButton-CaevDm9t.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-B2JMlIng.js → Analytics-B0gOmwPw.js} +1 -1
- package/src/assets/web-panel/assets/{AppLayout-B8QQ4pk7.js → AppLayout-DWhZiV0Q.js} +2 -2
- package/src/assets/web-panel/assets/{Audit-BoYaAyFa.js → Audit-ZuZJBCxo.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BfackGZ5.js → Backup-CX7jhH5l.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C06FUpDz.js → BaseInput-CVLx7HVq.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BWxMkBYZ.js → Chat-C6yL5tRD.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-XJMvS3PV.js → Checkbox-BePhbqVq.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CzR462RK.js → Codegen-B5E4x1Lm.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BQHpLNCA.js → Col-CWhNU6A7.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BWRRbJYd.js → Community-mSEAuJhp.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BunoKIy9.js → Compact-DSudHzX3.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CtJfZctm.js → Compliance-CxJLYjyn.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-ER5-_bod.js → Cowork-C-trppQj.js} +1 -1
- package/src/assets/web-panel/assets/{Cron-C80jYBw1.js → Cron-_Ij4v5fY.js} +1 -1
- package/src/assets/web-panel/assets/{Crosschain-fEMlCNsL.js → Crosschain-eLHXzp5T.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BZpctKmU.js → DID-BP04AUUB.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-RQhZmLi4.js → Dashboard-CzEeQv62.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CrpwS84l.js → Dropdown-C_SGOB22.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BEQyZtdR.js → Federation-BgaP4BOv.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DCwvl6Vh.js → FormItemContext-BxGLLt9r.js} +1 -1
- package/src/assets/web-panel/assets/{Git-6FihOxMK.js → Git-Bt_uM_Gw.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DlBLHSlJ.js → Governance-N2-0RG_o.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DdSokzV0.js → Inference-eS3g-CzP.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-bg8GBHMr.js → KnowledgeGraph-CUuvNVah.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DdFYdLQ-.js → Logs-wPbwEt2r.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-DjnlAeYF.js → Marketplace-DH91kTwo.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Czs41YUh.js → McpTools-D-GyyBrI.js} +1 -1
- package/src/assets/web-panel/assets/{Memory-CX0b3c8D.js → Memory-BOtUy-tw.js} +1 -1
- package/src/assets/web-panel/assets/{MobileBridge-BoFGb9Mm.js → MobileBridge-DIP__XQd.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B8qQ9H-0.js → MobileProjects-1nqr1UsU.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CRF1NLae.js → Mtc-CCE0x7h2.js} +1 -1
- package/src/assets/web-panel/assets/{MtcAudit-CdCm70cJ.js → MtcAudit-BBkz0XUO.js} +1 -1
- package/src/assets/web-panel/assets/{Multisig-yoZlpq2Y.js → Multisig-CIKSJvTY.js} +2 -2
- package/src/assets/web-panel/assets/{NLProgramming-hFCgqDxJ.js → NLProgramming-BDkgeFcq.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DC8pnxs-.js → Notes-ONiUxfN1.js} +1 -1
- package/src/assets/web-panel/assets/{NotificationSettings-DDVg5Nc8.js → NotificationSettings-CGcyKEIe.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-0YCtAFMS.js → Organization-BZtMYBJt.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DhPLoAdz.js → Overflow-C4LfFZAI.js} +1 -1
- package/src/assets/web-panel/assets/{OverrideContext-x9ZzjLwk.js → OverrideContext-C_4H9tGA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BWpuJhkD.js → P2P-D71Cpk-m.js} +1 -1
- package/src/assets/web-panel/assets/{Permissions-B4IrizO9.js → Permissions-DRGYF29v.js} +1 -1
- package/src/assets/web-panel/assets/PersonalDataHub-CfRYoIua.js +1 -0
- package/src/assets/web-panel/assets/PersonalDataHub-Dvaa8niQ.css +1 -0
- package/src/assets/web-panel/assets/{Pipeline-M65jR6sq.js → Pipeline-BOyp0_Qo.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BeO8zLup.js → Privacy-a_AcphvF.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Ck_ZjrVZ.js → ProjectInit-CFu1grYt.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectSettings-ijn-97s0.js → ProjectSettings-p54Eivhh.js} +1 -1
- package/src/assets/web-panel/assets/{Projects-BsNBemeh.js → Projects-DkB88XAu.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CI7UxKVO.js → Providers-EK7f8DEd.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-dE2M1KOB.js → QuickAsk-CpO0w3iP.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-B30EgbKS.js → Recommend-BUJVQdv9.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-CV6n7wMx.js → Reputation-kU2fOuZt.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DSaoTjlN.js → Row-oGWRbk6g.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-_NgBmHaC.js → RssFeed-saC_46Yo.js} +1 -1
- package/src/assets/web-panel/assets/{Search-B341ooTV.js → Search-CjtRqFUu.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CqCQD8hf.js → Security-BX0NBVfQ.js} +1 -1
- package/src/assets/web-panel/assets/{Services-Cju_95rB.js → Services-Bgxsnei_.js} +1 -1
- package/src/assets/web-panel/assets/{Skeleton-kh_uW22l.js → Skeleton-CwBb3k2a.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BVRPgciI.js → Skills-CZCMYH6Z.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-y-vKFYkI.js → Sla-Djy1uHnZ.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BXJm9zyo.js → SpeechSettings-CGUI_Uyh.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BmHZR-Kv.js → SyncSettings-DK2CKHRD.js} +1 -1
- package/src/assets/web-panel/assets/{Tasks-C7zmYW9f.js → Tasks-BKiOzeIO.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DVEG7FdA.js → Templates-CnQpleXj.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CpvjzPCo.js → Tenant-DwKz0cjm.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-D_Wpp2iE.js → Terminal-A7t_wsR8.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-QBrjdNqi.js → Tokens-BqYY9l44.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BhR_VEvQ.js → Trigger-BI4bXFmi.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-C0xhM2lC.js → Trust-yMynKTRG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BnyP-W3-.js → UkeySign-Br4IScM6.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C5Y8MyEK.js → VideoEditing-CWcThGsP.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DzCPCQNF.js → Wallet-CZcAtjxj.js} +1 -1
- package/src/assets/web-panel/assets/{WebAuthn-6X5bLtHU.js → WebAuthn-BnTZFMA0.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-ekS27G9f.js → WorkflowEditor-N7gGz3_n.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BikodUwh.js → chat-D175ZIO0.js} +1 -1
- package/src/assets/web-panel/assets/{collapseMotion-CjFH_Jop.js → collapseMotion-DfnRZex1.js} +1 -1
- package/src/assets/web-panel/assets/{colors-8yIg5K7E.js → colors-LKhZyttv.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-MLWo5-GY.js → compact-item-CsJSebxT.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-nir7ccDv.js → createContext-BJ_CPYFC.js} +1 -1
- package/src/assets/web-panel/assets/{echarts-Bq-n0MtJ.js → echarts-Dj_pBaVI.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-DxUIHW2P.js → hasIn-CzD3IqH8.js} +1 -1
- package/src/assets/web-panel/assets/{icons-CLQTHa5-.js → icons-BOPtEWK4.js} +4 -4
- package/src/assets/web-panel/assets/{index-vVrIg9Jk.js → index-6qPbrYF7.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCGf6IJj.js → index-B7UYymse.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSjWvxVr.js → index-BDSZDDb2.js} +4 -4
- package/src/assets/web-panel/assets/{index-BURKtxBq.js → index-BEDFHKO3.js} +1 -1
- package/src/assets/web-panel/assets/{index-q3Lr2UzW.js → index-BMvdoiFr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BgQtoOHc.js → index-BNVLVzN5.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bn5VWKW1.js → index-BSNibAqz.js} +1 -1
- package/src/assets/web-panel/assets/{index-4SFekeAy.js → index-BV-__mlC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfeuuE7v.js → index-BXH9ujMW.js} +1 -1
- package/src/assets/web-panel/assets/{index-h05fIj9Q.js → index-BZluCuTH.js} +1 -1
- package/src/assets/web-panel/assets/{index-9Y0IyfeM.js → index-BhiZDGg7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkMtxzcM.js → index-BjOrt4vw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOIryna2.js → index-BmJdof_c.js} +2 -2
- package/src/assets/web-panel/assets/{index-TB5vrA0Z.js → index-BsirlkJ0.js} +1 -1
- package/src/assets/web-panel/assets/{index-BgHPrMXP.js → index-BvF2tC6C.js} +1 -1
- package/src/assets/web-panel/assets/{index-BSIaRmzU.js → index-C2HBKw07.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkSN2Ki_.js → index-CKjBAdm0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLiexKJ2.js → index-CRGNuUIM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeX-HLIi.js → index-CTIkCKav.js} +1 -1
- package/src/assets/web-panel/assets/index-CY1mQA2I.js +1 -0
- package/src/assets/web-panel/assets/{index-78olN7S9.js → index-CyHdYUeZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcyG-9vV.js → index-D401L3yx.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_W1kVtY.js → index-D5IZCkZG.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bpa9senE.js → index-D7ZcBI5s.js} +1 -1
- package/src/assets/web-panel/assets/{index-dgZZAsxo.js → index-D8kltMTW.js} +1 -1
- package/src/assets/web-panel/assets/index-D9nXHfUB.js +1 -0
- package/src/assets/web-panel/assets/{index-DkIon-Gv.js → index-DJVkBmSc.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-UB9bYd.js → index-DM3uBEWD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dx5xZmzt.js → index-DOO73rHE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVLJ1iGu.js → index-DXp1jVsK.js} +1 -1
- package/src/assets/web-panel/assets/{index-S8mYImvf.js → index-Dd7dICwB.js} +1 -1
- package/src/assets/web-panel/assets/{index-SUYLhwZI.js → index-Dm-3kvtD.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_Xi08tu.js → index-DnPt5OdD.js} +1 -1
- package/src/assets/web-panel/assets/{index-hu-wjfWv.js → index-E7t1hAnk.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cp-YnzHN.js → index-JTX9A7w0.js} +1 -1
- package/src/assets/web-panel/assets/{index-8qrwsaKy.js → index-Kn-Of5ew.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dl-O2OkQ.js → index-R1cFADfk.js} +1 -1
- package/src/assets/web-panel/assets/{index-yfNusVbo.js → index-RIO4JKMP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDmc4cig.js → index-uTEVWPYA.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-xUjF_bq0.js → initDefaultProps-CBW0okek.js} +1 -1
- package/src/assets/web-panel/assets/{motion-B019-Q6h.js → motion-DGAffQ0Z.js} +1 -1
- package/src/assets/web-panel/assets/{move-D2XYj_gA.js → move-DFJ0-5IW.js} +1 -1
- package/src/assets/web-panel/assets/{omit-AIzzlguv.js → omit-AvrDghg1.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CRkEQaLs.js → pickAttrs-D7csw9i1.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-s4kAStH6.js → placementArrow-hZ6Lg6kG.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BCsWrTkb.js → responsiveObserve-DLLx5VvS.js} +1 -1
- package/src/assets/web-panel/assets/{slide-B_Hggtvv.js → slide-BaRIT3ev.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DnNf15VW.js → statusUtils-Cdjyuhrz.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CSQdy9SQ.js → styleChecker-CbrNybTt.js} +1 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-B8gAhiRC.js +1 -0
- package/src/assets/web-panel/assets/{useFs-D78PlgeG.js → useFs-BZPy4ICP.js} +1 -1
- package/src/assets/web-panel/assets/{useMergedState-O7QXt4P5.js → useMergedState-WwedrFR0.js} +1 -1
- package/src/assets/web-panel/assets/{useRefs-0J6m8UWN.js → useRefs-Cdq8EWeF.js} +1 -1
- package/src/assets/web-panel/assets/{useState-CSzR8F8O.js → useState-DGS1NOyn.js} +1 -1
- package/src/assets/web-panel/assets/{vendor-M5lGV-wr.js → vendor-DhFY8mDK.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BTMmpsWu.js → vnode-6Y0NDMVv.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CwOTbvKc.js → zoom-DTbMGsSH.js} +1 -1
- package/src/assets/web-panel/index.html +3 -3
- package/src/commands/__tests__/hub-aichat.test.js +277 -0
- package/src/commands/__tests__/hub-wechat.test.js +243 -0
- package/src/commands/hub.js +881 -0
- package/src/commands/sync-providers.js +436 -0
- package/src/gateways/ws/personal-data-hub-protocol.js +68 -0
- package/src/index.js +6 -0
- package/src/lib/__tests__/personal-data-hub-aichat-wizard.test.js +209 -0
- package/src/lib/__tests__/sync-credentials.test.js +265 -0
- package/src/lib/__tests__/sync-engine-cli.test.js +293 -0
- package/src/lib/personal-data-hub-aichat-wizard.js +242 -0
- package/src/lib/personal-data-hub-wiring.js +189 -0
- package/src/lib/sync-cli-db.js +194 -0
- package/src/lib/sync-credentials.js +225 -0
- package/src/lib/sync-engine-cli.js +406 -0
- package/src/lib/sync-oss-client.js +273 -0
- package/src/lib/sync-webdav-client.js +194 -0
- package/src/assets/web-panel/assets/PersonalDataHub-BK7I0Rsb.css +0 -1
- package/src/assets/web-panel/assets/PersonalDataHub-ZbziiUr6.js +0 -1
- package/src/assets/web-panel/assets/index-BeA3spHc.js +0 -1
- package/src/assets/web-panel/assets/index-DoLRjAoc.js +0 -1
- 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
|
+
};
|