chainlesschain 0.162.14 → 0.162.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/assets/web-panel/assets/{AIOps-D34d_Nh1.js → AIOps-Dr5poTWh.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Br7HxCnl.js → ActionButton-DLOGPHQ-.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-bVKq79Xd.js → Analytics-DBlO3FPX.js} +1 -1
- package/src/assets/web-panel/assets/{AppLayout-CWSLIbAz.js → AppLayout-BrMarmWx.js} +2 -2
- package/src/assets/web-panel/assets/{Audit-Cmnu1qqa.js → Audit-DX6omFqA.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Rok20-TL.js → Backup-0QSLlQkB.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BJzs_ZtT.js → BaseInput-Dn4GFfR8.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CSYapbcq.js → Chat-CMTeA0Zo.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BEa7Sr7e.js → Checkbox-BQQxf6iA.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-C9M4e7ne.js → Codegen-D2vrazFC.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DU9NoUIi.js → Col-P1hVDVKm.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DA9uz_jP.js → Community-CPcDkABv.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-3_bEraVw.js → Compact-DvBQwE-b.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BtF8jWUQ.js → Compliance-GX2qsHkq.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BqvA7oaM.js → Cowork-CEEISrBb.js} +1 -1
- package/src/assets/web-panel/assets/{Cron-CxZy7Mzg.js → Cron-Y3D6wB4C.js} +1 -1
- package/src/assets/web-panel/assets/{Crosschain-1DB-XRGu.js → Crosschain-CnCjDKKD.js} +1 -1
- package/src/assets/web-panel/assets/{DID-B6Ezp1pt.js → DID-CJ2EFB5b.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-QDJ6VVsn.js → Dashboard-CWujrs70.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CovsWjxG.js → Dropdown-BzKSQA5P.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DbRxS4Y4.js → Federation-DgaAWIwd.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-9E9dNGtx.js → FormItemContext-BitgB4Gb.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CqEpyxRZ.js → Git-Bzj4vLLf.js} +1 -1
- package/src/assets/web-panel/assets/{Governance-On47KtGq.js → Governance-D7DEdSBD.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-RZcjcyaq.js → Inference-CVsGLfcX.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-C-1rRAM9.js → KnowledgeGraph-CF6C6ZNo.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BuunmG_r.js → Logs-C0Er-tt8.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CromymyA.js → Marketplace-_E7uwUUU.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-5XlFExh7.js → McpTools-dHbktLMV.js} +1 -1
- package/src/assets/web-panel/assets/{Memory-DjnUT7YM.js → Memory-CaQ4UY7b.js} +1 -1
- package/src/assets/web-panel/assets/{MobileBridge-BrYIgLg6.js → MobileBridge-BbOh0uJt.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CL5V3fTm.js → MobileProjects-CfdNEimZ.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CHYJq6zK.js → Mtc-HLvyepRP.js} +1 -1
- package/src/assets/web-panel/assets/{MtcAudit-BZxUO0qt.js → MtcAudit-DMaHGoIr.js} +1 -1
- package/src/assets/web-panel/assets/{Multisig-FZTmJgW1.js → Multisig-DFNZf0AB.js} +1 -1
- package/src/assets/web-panel/assets/{NLProgramming-C9Mhefph.js → NLProgramming-cZwXhWy2.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-W7usj-Ar.js → Notes-U_3n8Zid.js} +1 -1
- package/src/assets/web-panel/assets/{NotificationSettings-PBuYv_Bh.js → NotificationSettings-B4VvhkKf.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CuYCE-rF.js → Organization-BIwBFrdX.js} +1 -1
- package/src/assets/web-panel/assets/{Overflow-Dojx-kzE.js → Overflow-_zxR8gF2.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BgIaSrLX.js → P2P-DJXmWhLC.js} +1 -1
- package/src/assets/web-panel/assets/{Permissions-Byj2dkF_.js → Permissions-DwJ35K8Z.js} +1 -1
- package/src/assets/web-panel/assets/{PersonalDataHub-CMOOI13-.js → PersonalDataHub-CzZXLXGN.js} +1 -1
- package/src/assets/web-panel/assets/{Pipeline-CWwEOF09.js → Pipeline-DjtRGM8X.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-VT7gldcN.js → Privacy-BJhsIhGf.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-7UH3c3p7.js → ProjectInit-DEx5SDLh.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectSettings-DqLp-72a.js → ProjectSettings-B6qDg7nn.js} +1 -1
- package/src/assets/web-panel/assets/{Projects-B_54eDhH.js → Projects-CKkTgrBf.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BIrNfNpc.js → Providers-3GEshcW_.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BbYPwCso.js → QuickAsk-BfUXbKxa.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BF4qBssF.js → Recommend-DgxYasSJ.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-DPEzlC2V.js → Reputation-CWvVg_V1.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DjHxhH1L.js → Row-42M5ot1v.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-D0_j678P.js → RssFeed-Do3isL1x.js} +1 -1
- package/src/assets/web-panel/assets/{Search-DctfGehu.js → Search-_7wgwjaR.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BFHggeYM.js → Security-DKB6pe8t.js} +1 -1
- package/src/assets/web-panel/assets/{Services-CmrFMukV.js → Services-BHrBJAcB.js} +1 -1
- package/src/assets/web-panel/assets/{Skeleton-DR4vn_nS.js → Skeleton-C8L-EfGp.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DlXG2yyV.js → Skills-CU66huTh.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-4PPGL3SE.js → Sla-DA8AKNmI.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-D9EhJOqm.js → SpeechSettings-CufjYTzV.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Dasmbi0p.js → SyncSettings-D_VAYHIg.js} +1 -1
- package/src/assets/web-panel/assets/{Tasks-vilEiuPA.js → Tasks-BE6nYh9k.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Ca9Rvktn.js → Templates-BfRbttd6.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CEZb9gfK.js → Tenant-YO71CL80.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DanCBdbD.js → Terminal-D1mQaE_Z.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-SPkClW2d.js → Tokens-CvnNQtX4.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-B645yL7g.js → Trigger-BnN19FJt.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-D9sM_Ig0.js → Trust-C6oZLHqp.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B_Nr2K-u.js → UkeySign-BOhFYuWu.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-U01Lea8j.js → VideoEditing-NMc6-qc-.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-6xBySVV8.js → Wallet-BALmcKtd.js} +1 -1
- package/src/assets/web-panel/assets/{WebAuthn-DbgMoBu6.js → WebAuthn-6IVe-W6O.js} +1 -1
- package/src/assets/web-panel/assets/{WorkflowEditor-Bz-Y6IR2.js → WorkflowEditor-BIv20DXb.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BC_O9hag.js → chat-CiceyA69.js} +1 -1
- package/src/assets/web-panel/assets/{colors-ChlOGOvr.js → colors-CxwvpRW0.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BSbAYGGF.js → compact-item-N6ASDseQ.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CFcZly5M.js → createContext-CQ8PEhyP.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-BomYwwYE.js → hasIn-DDiIuryC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DXgE2VW6.js → index-1jBrqw2R.js} +1 -1
- package/src/assets/web-panel/assets/index-1losWCP0.js +1 -0
- package/src/assets/web-panel/assets/{index-DNkth8dM.js → index-92sMXPmR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHGsFwYW.js → index-BCgZTQf8.js} +1 -1
- package/src/assets/web-panel/assets/{index-gUACAWbM.js → index-BKLatStI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CoF95pYK.js → index-BKmY57ry.js} +1 -1
- package/src/assets/web-panel/assets/{index-DW-Ji07y.js → index-BZ4O1Vp7.js} +1 -1
- package/src/assets/web-panel/assets/{index-onW325hZ.js → index-BdmcwwMp.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnxlKTDK.js → index-Bj-mAQFK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BI1jAWcc.js → index-BjvirvV4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEJ6YiLI.js → index-Bw6UqIkJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCRSz2cR.js → index-BzdEj9_B.js} +1 -1
- package/src/assets/web-panel/assets/{index-slYX2rCE.js → index-C43WIe_p.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGUbtM3R.js → index-CHsNn-Qv.js} +1 -1
- package/src/assets/web-panel/assets/{index-CBhoZhCO.js → index-COIxnEwP.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHi69MHF.js → index-C_dbCu-F.js} +1 -1
- package/src/assets/web-panel/assets/{index-b8GbH2Yi.js → index-Cb0QVAZL.js} +3 -3
- package/src/assets/web-panel/assets/{index-za1GUJBG.js → index-CcUK2M7W.js} +1 -1
- package/src/assets/web-panel/assets/{index-T5Y_9IPv.js → index-ClASVywF.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6LJHQoE.js → index-CnArbjXg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZGdjNLA.js → index-Cur_KKpV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ctx97mH-.js → index-Cwk0olXV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsNQ2hqI.js → index-D0yG93O4.js} +1 -1
- package/src/assets/web-panel/assets/{index-5Ewm6KZA.js → index-D58a5SL3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BwFykZ5U.js → index-DEjAgx2R.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cj47XwJQ.js → index-DUKjS4kF.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0rr1X9W.js → index-DWKigrAM.js} +1 -1
- package/src/assets/web-panel/assets/{index-EY733h9z.js → index-DcMESTJs.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRp5_Xns.js → index-DgwpVvQq.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cmzh8gKL.js → index-Dj2wgF3A.js} +1 -1
- package/src/assets/web-panel/assets/{index-ozVPr1gj.js → index-DobYLmfZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-ByZQNO0A.js → index-Dp3r80qO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dbf5YmDX.js → index-DtpWqJ-2.js} +1 -1
- package/src/assets/web-panel/assets/index-EoP_WtDt.js +1 -0
- package/src/assets/web-panel/assets/{index-D0vX9jQA.js → index-_BHtKVC_.js} +1 -1
- package/src/assets/web-panel/assets/{index-t9u2bHpH.js → index-bYorCa3r.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYDvb1pi.js → index-hn9LVkIY.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_0B3CiU.js → index-mmpauJ3E.js} +1 -1
- package/src/assets/web-panel/assets/{index-QD_n54XT.js → index-xQlQRo2j.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DnadEaxu.js → initDefaultProps-1MKZtqXZ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CC_Na0Tl.js → motion-DFBQpRS8.js} +1 -1
- package/src/assets/web-panel/assets/{move-C2d9Mkk9.js → move-CiI8Ada0.js} +1 -1
- package/src/assets/web-panel/assets/{omit-QvpKbF8p.js → omit-CjeFoPcC.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Dm8r3X1_.js → pickAttrs-Be3kefJq.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DaqaVfoX.js → placementArrow-Be5Ra1_B.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Iida9fIn.js → responsiveObserve-NCu3YHiX.js} +1 -1
- package/src/assets/web-panel/assets/{slide-YqHexXQD.js → slide-f23lSB4X.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BGKLoeEt.js → statusUtils-Dq99US_U.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-aI-gsQO8.js → styleChecker-B-UUq5Ww.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BiOsz4rc.js → useFlexGapSupport-D6aUzeVO.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CZy7Zo2X.js → useFs-DU-R5a4I.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-B6WqjmE4.js → vnode-N7r8LSGe.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DTeTrJ2z.js → zoom-07xoxB1t.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/__tests__/hub-wechat.test.js +186 -15
- package/src/commands/hub.js +206 -2
- package/src/lib/__tests__/cc-android-bridge.test.js +245 -0
- package/src/lib/cc-android-bridge.js +99 -55
- package/src/lib/personal-data-hub-wiring.js +24 -2
- package/src/assets/web-panel/assets/index-BIRYt1of.js +0 -1
- package/src/assets/web-panel/assets/index-CZfySmWX.js +0 -1
|
@@ -41,19 +41,27 @@ function fakeHub(overrides = {}) {
|
|
|
41
41
|
...overrides.register,
|
|
42
42
|
};
|
|
43
43
|
}),
|
|
44
|
-
listWechatAccounts: vi.fn(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
listWechatAccounts: vi.fn(
|
|
45
|
+
() =>
|
|
46
|
+
overrides.list || [
|
|
47
|
+
{
|
|
48
|
+
uin: "1234567890",
|
|
49
|
+
dbPath: "/tmp/EnMicroMsg.db",
|
|
50
|
+
hasWechatDataPath: true,
|
|
51
|
+
chosenKeyProvider: "md5",
|
|
52
|
+
registeredAt: 1716280000000,
|
|
53
|
+
lastSyncAt: null,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
),
|
|
54
57
|
unregisterWechatAdapter: vi.fn(async (uin) => {
|
|
55
58
|
if (!uin) return { ok: false, reason: "UIN_REQUIRED" };
|
|
56
|
-
return {
|
|
59
|
+
return {
|
|
60
|
+
ok: true,
|
|
61
|
+
removed: uin === "1234567890",
|
|
62
|
+
uin,
|
|
63
|
+
...overrides.unregister,
|
|
64
|
+
};
|
|
57
65
|
}),
|
|
58
66
|
};
|
|
59
67
|
}
|
|
@@ -106,9 +114,21 @@ describe("cc hub wechat register", () => {
|
|
|
106
114
|
_internal.cmdWechatRegister({ _getHub: async () => hub, json: true }),
|
|
107
115
|
).rejects.toThrow(/process\.exit/);
|
|
108
116
|
const allOut =
|
|
109
|
-
logSpy.mock.calls
|
|
117
|
+
logSpy.mock.calls
|
|
118
|
+
.map((c) =>
|
|
119
|
+
c
|
|
120
|
+
.map((p) => (typeof p === "string" ? p : JSON.stringify(p)))
|
|
121
|
+
.join(" "),
|
|
122
|
+
)
|
|
123
|
+
.join("\n") +
|
|
110
124
|
"\n" +
|
|
111
|
-
errSpy.mock.calls
|
|
125
|
+
errSpy.mock.calls
|
|
126
|
+
.map((c) =>
|
|
127
|
+
c
|
|
128
|
+
.map((p) => (typeof p === "string" ? p : JSON.stringify(p)))
|
|
129
|
+
.join(" "),
|
|
130
|
+
)
|
|
131
|
+
.join("\n");
|
|
112
132
|
expect(allOut).toMatch(/--uin|uin/i);
|
|
113
133
|
});
|
|
114
134
|
|
|
@@ -217,7 +237,9 @@ describe("cc hub wechat list", () => {
|
|
|
217
237
|
describe("cc hub wechat unregister", () => {
|
|
218
238
|
it("calls hub with uin + reports removed", async () => {
|
|
219
239
|
const hub = fakeHub();
|
|
220
|
-
await _internal.cmdWechatUnregister("1234567890", {
|
|
240
|
+
await _internal.cmdWechatUnregister("1234567890", {
|
|
241
|
+
_getHub: async () => hub,
|
|
242
|
+
});
|
|
221
243
|
expect(hub.unregisterWechatAdapter).toHaveBeenCalledWith("1234567890");
|
|
222
244
|
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
223
245
|
expect(out).toMatch(/removed wechat account.*1234567890/);
|
|
@@ -238,6 +260,155 @@ describe("cc hub wechat unregister", () => {
|
|
|
238
260
|
});
|
|
239
261
|
const out = logSpy.mock.calls.map((c) => c[0]).join("\n");
|
|
240
262
|
const parsed = JSON.parse(out);
|
|
241
|
-
expect(parsed).toEqual(
|
|
263
|
+
expect(parsed).toEqual(
|
|
264
|
+
expect.objectContaining({ ok: true, removed: true, uin: "1234567890" }),
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// ─── Phase 12.9 — `cc hub wechat doctor` ──────────────────────────────
|
|
270
|
+
|
|
271
|
+
describe("interpretWechatProbe (pure)", () => {
|
|
272
|
+
const { interpretWechatProbe } = _internal;
|
|
273
|
+
|
|
274
|
+
it("device unreachable → blocked + adb hint", () => {
|
|
275
|
+
const r = interpretWechatProbe({
|
|
276
|
+
device: { reachable: false },
|
|
277
|
+
wechat: {},
|
|
278
|
+
root: {},
|
|
279
|
+
frida: {},
|
|
280
|
+
});
|
|
281
|
+
expect(r.readiness).toBe("blocked");
|
|
282
|
+
expect(r.blockers.join(" ")).toMatch(/adb/);
|
|
283
|
+
expect(r.nextSteps.join(" ")).toMatch(/adb devices/);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("WeChat not installed → blocked + install hint", () => {
|
|
287
|
+
const r = interpretWechatProbe({
|
|
288
|
+
device: { reachable: true },
|
|
289
|
+
wechat: { installed: false },
|
|
290
|
+
root: {},
|
|
291
|
+
frida: {},
|
|
292
|
+
});
|
|
293
|
+
expect(r.readiness).toBe("blocked");
|
|
294
|
+
expect(r.blockers.join(" ")).toMatch(/com\.tencent\.mm/);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("WeChat < 8 + md5 path → ready with adb pull instructions", () => {
|
|
298
|
+
const r = interpretWechatProbe({
|
|
299
|
+
device: { reachable: true },
|
|
300
|
+
wechat: { installed: true, majorVersion: 7, versionName: "7.0.22" },
|
|
301
|
+
root: { detected: false },
|
|
302
|
+
frida: {},
|
|
303
|
+
suggestedKeyProvider: "md5",
|
|
304
|
+
});
|
|
305
|
+
expect(r.readiness).toBe("ready");
|
|
306
|
+
expect(r.nextSteps.some((s) => /adb pull/.test(s))).toBe(true);
|
|
307
|
+
expect(r.nextSteps.some((s) => /cc hub wechat register/.test(s))).toBe(
|
|
308
|
+
true,
|
|
309
|
+
);
|
|
310
|
+
// non-root warning when MD5 path lacks root (backup subset only)
|
|
311
|
+
expect(r.warnings.some((w) => /非 root|backup 子集/.test(w))).toBe(true);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("WeChat ≥ 8 + no root → blocked with root requirement", () => {
|
|
315
|
+
const r = interpretWechatProbe({
|
|
316
|
+
device: { reachable: true },
|
|
317
|
+
wechat: { installed: true, majorVersion: 8, versionName: "8.0.45" },
|
|
318
|
+
root: { detected: false },
|
|
319
|
+
frida: { serverRunning: false },
|
|
320
|
+
suggestedKeyProvider: "frida",
|
|
321
|
+
});
|
|
322
|
+
expect(r.readiness).toBe("blocked");
|
|
323
|
+
expect(r.blockers.join(" ")).toMatch(/root/);
|
|
324
|
+
expect(r.nextSteps.join(" ")).toMatch(/Magisk/);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it("WeChat ≥ 8 + root + no frida → partial with setup pointer", () => {
|
|
328
|
+
const r = interpretWechatProbe({
|
|
329
|
+
device: { reachable: true },
|
|
330
|
+
wechat: { installed: true, majorVersion: 8, versionName: "8.0.45" },
|
|
331
|
+
root: { detected: true },
|
|
332
|
+
frida: { serverRunning: false },
|
|
333
|
+
suggestedKeyProvider: "frida",
|
|
334
|
+
});
|
|
335
|
+
expect(r.readiness).toBe("partial");
|
|
336
|
+
expect(r.blockers.join(" ")).toMatch(/Frida server/i);
|
|
337
|
+
expect(r.nextSteps.join(" ")).toMatch(/Frida_Setup/);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it("WeChat ≥ 8 + root + frida running → ready", () => {
|
|
341
|
+
const r = interpretWechatProbe({
|
|
342
|
+
device: { reachable: true },
|
|
343
|
+
wechat: { installed: true, majorVersion: 8, versionName: "8.0.45" },
|
|
344
|
+
root: { detected: true },
|
|
345
|
+
frida: { serverRunning: true, port: 27042 },
|
|
346
|
+
suggestedKeyProvider: "frida",
|
|
347
|
+
});
|
|
348
|
+
expect(r.readiness).toBe("ready");
|
|
349
|
+
expect(r.nextSteps.some((s) => /register/.test(s))).toBe(true);
|
|
350
|
+
expect(r.nextSteps.some((s) => /前台运行/.test(s))).toBe(true);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it("env-probe judges unsupported → blocked with reasons", () => {
|
|
354
|
+
const r = interpretWechatProbe({
|
|
355
|
+
device: { reachable: true },
|
|
356
|
+
wechat: { installed: true, majorVersion: 8, versionName: "8.0.50" },
|
|
357
|
+
root: { detected: true },
|
|
358
|
+
frida: { serverRunning: true },
|
|
359
|
+
suggestedKeyProvider: "unsupported",
|
|
360
|
+
reasons: [
|
|
361
|
+
"MMKV-only storage on this build — neither MD5 nor Frida path works",
|
|
362
|
+
],
|
|
363
|
+
});
|
|
364
|
+
expect(r.readiness).toBe("blocked");
|
|
365
|
+
expect(r.blockers.join(" ")).toMatch(/MMKV-only/);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
describe("cc hub wechat doctor", () => {
|
|
370
|
+
it("--json includes probe + doctor.readiness/blockers/nextSteps", async () => {
|
|
371
|
+
const hub = fakeHub();
|
|
372
|
+
await _internal.cmdWechatDoctor({ _getHub: async () => hub, json: true });
|
|
373
|
+
const out = logSpy.mock.calls.map((c) => c[0]).join("\n");
|
|
374
|
+
const parsed = JSON.parse(out);
|
|
375
|
+
expect(parsed.probe).toEqual(
|
|
376
|
+
expect.objectContaining({ suggestedKeyProvider: "md5" }),
|
|
377
|
+
);
|
|
378
|
+
expect(parsed.doctor).toEqual(
|
|
379
|
+
expect.objectContaining({
|
|
380
|
+
readiness: "ready",
|
|
381
|
+
nextSteps: expect.any(Array),
|
|
382
|
+
}),
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it("human-readable surfaces trap table reference + telemetry capture cmd", async () => {
|
|
387
|
+
const hub = fakeHub();
|
|
388
|
+
await _internal.cmdWechatDoctor({ _getHub: async () => hub });
|
|
389
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
390
|
+
expect(out).toMatch(/Doctor:/);
|
|
391
|
+
expect(out).toMatch(/READY|PARTIAL|BLOCKED/);
|
|
392
|
+
expect(out).toMatch(/fridaTelemetry/);
|
|
393
|
+
expect(out).toMatch(/trap table|§5\.1|Runbook/i);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it("device unreachable: omits telemetry capture section (no point if blocked)", async () => {
|
|
397
|
+
const hub = fakeHub({
|
|
398
|
+
probe: {
|
|
399
|
+
ok: false,
|
|
400
|
+
suggestedKeyProvider: "unsupported",
|
|
401
|
+
device: { reachable: false },
|
|
402
|
+
wechat: {},
|
|
403
|
+
root: {},
|
|
404
|
+
frida: {},
|
|
405
|
+
reasons: [],
|
|
406
|
+
warnings: [],
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
await _internal.cmdWechatDoctor({ _getHub: async () => hub });
|
|
410
|
+
const out = logSpy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
411
|
+
expect(out).toMatch(/BLOCKED/);
|
|
412
|
+
expect(out).not.toMatch(/fridaTelemetry/);
|
|
242
413
|
});
|
|
243
414
|
});
|
package/src/commands/hub.js
CHANGED
|
@@ -499,8 +499,13 @@ async function cmdAIChatHealth(options) {
|
|
|
499
499
|
const factoryDeps = options._factoryDeps || {};
|
|
500
500
|
const hubDir =
|
|
501
501
|
factoryDeps.hubDir || (await (options._getHub || getHub)()).hubDir;
|
|
502
|
-
|
|
503
|
-
|
|
502
|
+
// Bypass vite import-analysis (which can't resolve subpath exports
|
|
503
|
+
// for dynamic imports in vitest SSR mode) by composing the specifier
|
|
504
|
+
// at runtime — the static analyzer skips non-literal arguments.
|
|
505
|
+
const hcSpecifier =
|
|
506
|
+
"@chainlesschain/personal-data-hub" +
|
|
507
|
+
"/adapters/ai-chat-history/health-checker";
|
|
508
|
+
const { createAIChatHealthChecker } = await import(hcSpecifier);
|
|
504
509
|
const { createAccountsStore, createVendorAdapterBridge } =
|
|
505
510
|
await import("../lib/personal-data-hub-aichat-wizard.js");
|
|
506
511
|
const accountsStore =
|
|
@@ -653,6 +658,193 @@ async function cmdWechatList(options) {
|
|
|
653
658
|
}
|
|
654
659
|
}
|
|
655
660
|
|
|
661
|
+
/**
|
|
662
|
+
* `cc hub wechat doctor` — env-probe + actionable interpretation +
|
|
663
|
+
* inline reference to the Phase 12.9 §5.1 Frida hook trap table.
|
|
664
|
+
*
|
|
665
|
+
* Designed to be the single command user runs on a rooted Android during
|
|
666
|
+
* Phase 12.9 real-device E2E to figure out "what should I do next?" —
|
|
667
|
+
* combines env-probe output + readiness checklist (per md5 vs frida
|
|
668
|
+
* path) + post-register telemetry fields to capture if hook fails.
|
|
669
|
+
*
|
|
670
|
+
* Returns the same JSON as env-probe + a `doctor` block under --json so
|
|
671
|
+
* scripts can branch on `doctor.readiness === 'ready' | 'blocked' | 'partial'`.
|
|
672
|
+
*/
|
|
673
|
+
async function cmdWechatDoctor(options) {
|
|
674
|
+
try {
|
|
675
|
+
const hub = await (options._getHub || getHub)();
|
|
676
|
+
const probe = await hub.probeWechatEnv();
|
|
677
|
+
|
|
678
|
+
// Determine readiness + concrete next-action based on probe shape.
|
|
679
|
+
const advice = interpretWechatProbe(probe);
|
|
680
|
+
|
|
681
|
+
if (options.json) {
|
|
682
|
+
printJson({ probe, doctor: advice });
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Human-readable: re-use env-probe formatting then append advice.
|
|
687
|
+
logger.log(chalk.bold("WeChat env-probe:"));
|
|
688
|
+
logger.log(
|
|
689
|
+
` ${probe.ok ? chalk.green("✓") : chalk.red("✗")} suggested: ${chalk.cyan(probe.suggestedKeyProvider)}`,
|
|
690
|
+
);
|
|
691
|
+
logger.log(
|
|
692
|
+
` device: ${probe.device.reachable ? chalk.green("reachable") : chalk.red("unreachable")}${probe.device.serial ? " (" + probe.device.serial + ")" : ""} abi=${probe.device.abi || "?"}`,
|
|
693
|
+
);
|
|
694
|
+
logger.log(
|
|
695
|
+
` root: ${probe.root.detected ? chalk.green("yes") : chalk.gray("no")} magisk=${probe.root.magiskInstalled ? "yes" : "no"}`,
|
|
696
|
+
);
|
|
697
|
+
logger.log(
|
|
698
|
+
` frida-server: ${probe.frida.serverRunning ? chalk.green("running") : chalk.gray("not running")}${probe.frida.port ? " :" + probe.frida.port : ""}`,
|
|
699
|
+
);
|
|
700
|
+
logger.log(
|
|
701
|
+
` wechat: ${probe.wechat.installed ? chalk.green(probe.wechat.versionName) : chalk.gray("not installed")}`,
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
logger.log("");
|
|
705
|
+
const statusColor =
|
|
706
|
+
advice.readiness === "ready"
|
|
707
|
+
? chalk.green
|
|
708
|
+
: advice.readiness === "partial"
|
|
709
|
+
? chalk.yellow
|
|
710
|
+
: chalk.red;
|
|
711
|
+
logger.log(
|
|
712
|
+
chalk.bold(`Doctor: ${statusColor(advice.readiness.toUpperCase())}`),
|
|
713
|
+
);
|
|
714
|
+
for (const blocker of advice.blockers) {
|
|
715
|
+
logger.log(` ${chalk.red("✗")} ${blocker}`);
|
|
716
|
+
}
|
|
717
|
+
for (const w of advice.warnings) {
|
|
718
|
+
logger.log(` ${chalk.yellow("!")} ${w}`);
|
|
719
|
+
}
|
|
720
|
+
for (const step of advice.nextSteps) {
|
|
721
|
+
logger.log(` ${chalk.cyan("→")} ${step}`);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if (advice.readiness !== "blocked") {
|
|
725
|
+
logger.log("");
|
|
726
|
+
logger.log(
|
|
727
|
+
chalk.bold("After `cc hub wechat register`, capture telemetry:"),
|
|
728
|
+
);
|
|
729
|
+
logger.log(
|
|
730
|
+
chalk.gray(
|
|
731
|
+
" cc hub wechat register --uin <UIN> --db ... --json | jq '.fridaTelemetry'",
|
|
732
|
+
),
|
|
733
|
+
);
|
|
734
|
+
logger.log(
|
|
735
|
+
chalk.gray(
|
|
736
|
+
" Expected fields: hooked / keySource / keySig / keyFormat / keyLength / keyAlt / errors / durationMs",
|
|
737
|
+
),
|
|
738
|
+
);
|
|
739
|
+
logger.log("");
|
|
740
|
+
logger.log(
|
|
741
|
+
chalk.bold("If hook fails, match telemetry against trap table:"),
|
|
742
|
+
);
|
|
743
|
+
logger.log(
|
|
744
|
+
chalk.gray(
|
|
745
|
+
" A — hooked:[] empty → libWCDB.so module name (try OEM custom names)",
|
|
746
|
+
),
|
|
747
|
+
);
|
|
748
|
+
logger.log(
|
|
749
|
+
chalk.gray(
|
|
750
|
+
" B — keySig:v2 but DB won't open → sqlite3_key_v2 args index wrong",
|
|
751
|
+
),
|
|
752
|
+
);
|
|
753
|
+
logger.log(
|
|
754
|
+
chalk.gray(
|
|
755
|
+
" C — keyFormat:raw-bytes but len=64 → ascii-hex path missed",
|
|
756
|
+
),
|
|
757
|
+
);
|
|
758
|
+
logger.log(
|
|
759
|
+
chalk.gray(
|
|
760
|
+
" Full table: docs/design/Personal_Data_Hub_Phase_12_9_*Runbook.md §5.1",
|
|
761
|
+
),
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
} catch (err) {
|
|
765
|
+
fail(null, err, options.json);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Interpret env-probe result into { readiness, blockers, warnings, nextSteps }.
|
|
771
|
+
* Pure function — testable without device.
|
|
772
|
+
*
|
|
773
|
+
* @param {object} probe output of hub.probeWechatEnv()
|
|
774
|
+
* @returns {{readiness: 'ready'|'partial'|'blocked', blockers: string[],
|
|
775
|
+
* warnings: string[], nextSteps: string[]}}
|
|
776
|
+
*/
|
|
777
|
+
function interpretWechatProbe(probe) {
|
|
778
|
+
const blockers = [];
|
|
779
|
+
const warnings = [];
|
|
780
|
+
const nextSteps = [];
|
|
781
|
+
|
|
782
|
+
if (!probe || !probe.device || !probe.device.reachable) {
|
|
783
|
+
blockers.push("adb 设备未连接 (USB 调试 / drivers / authorize?)");
|
|
784
|
+
nextSteps.push("`adb devices` 应列出至少一台 device 状态");
|
|
785
|
+
return { readiness: "blocked", blockers, warnings, nextSteps };
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (!probe.wechat || !probe.wechat.installed) {
|
|
789
|
+
blockers.push("WeChat (com.tencent.mm) 未安装");
|
|
790
|
+
nextSteps.push("先把 WeChat 装上 + 登一次产生 EnMicroMsg.db");
|
|
791
|
+
return { readiness: "blocked", blockers, warnings, nextSteps };
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const ver = probe.wechat.majorVersion || 0;
|
|
795
|
+
const suggested = probe.suggestedKeyProvider;
|
|
796
|
+
|
|
797
|
+
if (ver < 8 && suggested === "md5") {
|
|
798
|
+
nextSteps.push(
|
|
799
|
+
"MD5 path: `adb pull /data/data/com.tencent.mm/ /tmp/wechat-data/`",
|
|
800
|
+
);
|
|
801
|
+
nextSteps.push(
|
|
802
|
+
"拉到本地后跑: `cc hub wechat register --uin <UIN> --db /tmp/wechat-data/MicroMsg/<md5-uin>/EnMicroMsg.db --wechat-data-path /tmp/wechat-data/`",
|
|
803
|
+
);
|
|
804
|
+
if (!probe.root.detected) {
|
|
805
|
+
warnings.push(
|
|
806
|
+
"非 root 设备只能 adb pull WeChat backup 子集 — 可能拿不到全部 db / 文件",
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
return { readiness: "ready", blockers, warnings, nextSteps };
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
if (ver >= 8 && suggested === "frida") {
|
|
813
|
+
if (!probe.root.detected) {
|
|
814
|
+
blockers.push("WeChat ≥ 8.0 必须 root + Frida hook,当前设备未 root");
|
|
815
|
+
nextSteps.push("Magisk 刷入 → 重启 → 重跑 doctor");
|
|
816
|
+
return { readiness: "blocked", blockers, warnings, nextSteps };
|
|
817
|
+
}
|
|
818
|
+
if (!probe.frida.serverRunning) {
|
|
819
|
+
blockers.push("Frida server 未运行");
|
|
820
|
+
nextSteps.push(
|
|
821
|
+
"见 docs/design/Adapter_WeChat_SQLCipher_Frida_Setup.md §2 启 frida-server",
|
|
822
|
+
);
|
|
823
|
+
return { readiness: "partial", blockers, warnings, nextSteps };
|
|
824
|
+
}
|
|
825
|
+
nextSteps.push(
|
|
826
|
+
"Frida path 就绪。register: `cc hub wechat register --uin <wxid>`(无需 --db / --wechat-data-path)",
|
|
827
|
+
);
|
|
828
|
+
nextSteps.push("WeChat 必须前台运行(已登录),register 期间不要切走");
|
|
829
|
+
return { readiness: "ready", blockers, warnings, nextSteps };
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
if (suggested === "unsupported") {
|
|
833
|
+
blockers.push("env-probe 判定为 unsupported");
|
|
834
|
+
for (const reason of probe.reasons || []) blockers.push(reason);
|
|
835
|
+
nextSteps.push(
|
|
836
|
+
"见 docs/design/Adapter_WeChat_SQLCipher.md §13 — 检查 WeChat 版本兼容矩阵",
|
|
837
|
+
);
|
|
838
|
+
return { readiness: "blocked", blockers, warnings, nextSteps };
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Fallback — probe shape we don't recognize
|
|
842
|
+
warnings.push(
|
|
843
|
+
`未识别的 suggested='${suggested}' (version=${probe.wechat.versionName || "?"})`,
|
|
844
|
+
);
|
|
845
|
+
return { readiness: "partial", blockers, warnings, nextSteps };
|
|
846
|
+
}
|
|
847
|
+
|
|
656
848
|
async function cmdWechatUnregister(uin, options) {
|
|
657
849
|
try {
|
|
658
850
|
if (!uin) throw new Error("uin argument required");
|
|
@@ -901,6 +1093,16 @@ export function registerHubCommand(program) {
|
|
|
901
1093
|
)
|
|
902
1094
|
.option("--json", "Output JSON")
|
|
903
1095
|
.action(cmdWechatUnregister);
|
|
1096
|
+
|
|
1097
|
+
// Phase 12.9 — diagnostic helper for real-device E2E. Combines env-probe
|
|
1098
|
+
// with actionable readiness checklist + inline §5.1 Frida trap reference.
|
|
1099
|
+
wechat
|
|
1100
|
+
.command("doctor")
|
|
1101
|
+
.description(
|
|
1102
|
+
"Diagnose WeChat setup — env-probe + readiness checklist + Phase 12.9 trap reference",
|
|
1103
|
+
)
|
|
1104
|
+
.option("--json", "Output JSON")
|
|
1105
|
+
.action(cmdWechatDoctor);
|
|
904
1106
|
}
|
|
905
1107
|
|
|
906
1108
|
// exported for tests — handler functions can be invoked directly with
|
|
@@ -917,5 +1119,7 @@ export const _internal = {
|
|
|
917
1119
|
cmdWechatRegister,
|
|
918
1120
|
cmdWechatList,
|
|
919
1121
|
cmdWechatUnregister,
|
|
1122
|
+
cmdWechatDoctor,
|
|
1123
|
+
interpretWechatProbe,
|
|
920
1124
|
_defaultKnownVendors,
|
|
921
1125
|
};
|