chainlesschain 0.162.14 → 0.162.16
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-6yP2WySy.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Br7HxCnl.js → ActionButton-BvpIMxiU.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-bVKq79Xd.js → Analytics-Bvg88oOa.js} +1 -1
- package/src/assets/web-panel/assets/{AppLayout-CWSLIbAz.js → AppLayout-Do30UWbY.js} +2 -2
- package/src/assets/web-panel/assets/{Audit-Cmnu1qqa.js → Audit-Bfshrs5j.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Rok20-TL.js → Backup-yKQbHMJr.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BJzs_ZtT.js → BaseInput-CxpV1ZXM.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CSYapbcq.js → Chat-y1SfGDl1.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BEa7Sr7e.js → Checkbox-Dpoth43k.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-C9M4e7ne.js → Codegen-DbwH-QDz.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DU9NoUIi.js → Col-B-MGUxt2.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DA9uz_jP.js → Community-B521TAcS.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-3_bEraVw.js → Compact-CislP3fF.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BtF8jWUQ.js → Compliance-BXqrD-tO.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BqvA7oaM.js → Cowork-DtRMjAz_.js} +1 -1
- package/src/assets/web-panel/assets/{Cron-CxZy7Mzg.js → Cron-CX8dYcdW.js} +1 -1
- package/src/assets/web-panel/assets/{Crosschain-1DB-XRGu.js → Crosschain-DeEwabS0.js} +1 -1
- package/src/assets/web-panel/assets/{DID-B6Ezp1pt.js → DID-DjDE7pgB.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-QDJ6VVsn.js → Dashboard-pr7dCgLq.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CovsWjxG.js → Dropdown-DuDw9Nfe.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DbRxS4Y4.js → Federation-BiDyaRHe.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-9E9dNGtx.js → FormItemContext-BEF_PW-L.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CqEpyxRZ.js → Git-DIbvoylw.js} +1 -1
- package/src/assets/web-panel/assets/{Governance-On47KtGq.js → Governance-FFY2QV_a.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-RZcjcyaq.js → Inference-Clgxm2a1.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-C-1rRAM9.js → KnowledgeGraph-CNmtI1QB.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BuunmG_r.js → Logs-ibszFmVA.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CromymyA.js → Marketplace-CeBhDY6f.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-5XlFExh7.js → McpTools-Bl4misFt.js} +1 -1
- package/src/assets/web-panel/assets/{Memory-DjnUT7YM.js → Memory-CTGi-Zcb.js} +1 -1
- package/src/assets/web-panel/assets/{MobileBridge-BrYIgLg6.js → MobileBridge-F7jJrt63.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CL5V3fTm.js → MobileProjects--p2v4WUQ.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CHYJq6zK.js → Mtc-Cw0DfWPd.js} +1 -1
- package/src/assets/web-panel/assets/{MtcAudit-BZxUO0qt.js → MtcAudit-BNAv66VX.js} +1 -1
- package/src/assets/web-panel/assets/{Multisig-FZTmJgW1.js → Multisig-B4IxV-6K.js} +1 -1
- package/src/assets/web-panel/assets/{NLProgramming-C9Mhefph.js → NLProgramming-J5Nfy4jx.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-W7usj-Ar.js → Notes-BQl5wafk.js} +1 -1
- package/src/assets/web-panel/assets/{NotificationSettings-PBuYv_Bh.js → NotificationSettings-1Sur2hZk.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CuYCE-rF.js → Organization-DSJGSd5V.js} +1 -1
- package/src/assets/web-panel/assets/{Overflow-Dojx-kzE.js → Overflow-6TgFQZGw.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BgIaSrLX.js → P2P-Cb-eWAUF.js} +1 -1
- package/src/assets/web-panel/assets/{Permissions-Byj2dkF_.js → Permissions-C_xekWqT.js} +1 -1
- package/src/assets/web-panel/assets/{PersonalDataHub-CMOOI13-.js → PersonalDataHub-C6X0ilxN.js} +1 -1
- package/src/assets/web-panel/assets/{Pipeline-CWwEOF09.js → Pipeline-CQlD8pLe.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-VT7gldcN.js → Privacy-DlTQcdGg.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-7UH3c3p7.js → ProjectInit-BicKKZtR.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectSettings-DqLp-72a.js → ProjectSettings-CXTbWgIg.js} +1 -1
- package/src/assets/web-panel/assets/{Projects-B_54eDhH.js → Projects-Diq_IZJO.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BIrNfNpc.js → Providers-CKXjLBNU.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BbYPwCso.js → QuickAsk-BnUavy-a.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BF4qBssF.js → Recommend-qbDO-M0M.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-DPEzlC2V.js → Reputation-BnkI4I_D.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DjHxhH1L.js → Row-sVpTpVTK.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-D0_j678P.js → RssFeed-CN85L1Ph.js} +1 -1
- package/src/assets/web-panel/assets/{Search-DctfGehu.js → Search-Dxxlr3ob.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BFHggeYM.js → Security-CVjWa1ad.js} +1 -1
- package/src/assets/web-panel/assets/{Services-CmrFMukV.js → Services-BPiPhH6q.js} +1 -1
- package/src/assets/web-panel/assets/{Skeleton-DR4vn_nS.js → Skeleton-BpJEB3Px.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DlXG2yyV.js → Skills-Cjiy1gsC.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-4PPGL3SE.js → Sla-CW2YJRZw.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-D9EhJOqm.js → SpeechSettings-DBGDc9cz.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Dasmbi0p.js → SyncSettings-CWiQLmKL.js} +1 -1
- package/src/assets/web-panel/assets/{Tasks-vilEiuPA.js → Tasks-CVsvO2yt.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Ca9Rvktn.js → Templates-BEXmGilK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CEZb9gfK.js → Tenant-Dd_xNf2O.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DanCBdbD.js → Terminal-JIkt-KLk.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-SPkClW2d.js → Tokens-Ca8aA-4X.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-B645yL7g.js → Trigger-CLDtLBuR.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-D9sM_Ig0.js → Trust-DSzmH0eN.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B_Nr2K-u.js → UkeySign-D7yOYajn.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-U01Lea8j.js → VideoEditing-Dk3jUxyG.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-6xBySVV8.js → Wallet-BSB2tO_C.js} +1 -1
- package/src/assets/web-panel/assets/{WebAuthn-DbgMoBu6.js → WebAuthn-DAPZsZNw.js} +1 -1
- package/src/assets/web-panel/assets/{WorkflowEditor-Bz-Y6IR2.js → WorkflowEditor-DinHN1vM.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BC_O9hag.js → chat-BLyN3kuS.js} +1 -1
- package/src/assets/web-panel/assets/{colors-ChlOGOvr.js → colors-B-IfN2x7.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BSbAYGGF.js → compact-item-tVJNTa_f.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CFcZly5M.js → createContext-CeHSYhwo.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-BomYwwYE.js → hasIn-CP8akQuY.js} +1 -1
- package/src/assets/web-panel/assets/{index-QD_n54XT.js → index-3T9u2Hlb.js} +1 -1
- package/src/assets/web-panel/assets/{index-ozVPr1gj.js → index-B6RV21Hn.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnxlKTDK.js → index-BAhYPqmm.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNkth8dM.js → index-BDS9QmmD.js} +1 -1
- package/src/assets/web-panel/assets/{index-CoF95pYK.js → index-BRH7hf5q.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsNQ2hqI.js → index-BZhvUfFb.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ctx97mH-.js → index-B_pAhN3p.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHGsFwYW.js → index-BsMpKD71.js} +1 -1
- package/src/assets/web-panel/assets/{index-T5Y_9IPv.js → index-ByqjsQPb.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_0B3CiU.js → index-C44pVyK1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BwFykZ5U.js → index-C6NyhfFI.js} +1 -1
- package/src/assets/web-panel/assets/index-C6dKiBgm.js +1 -0
- package/src/assets/web-panel/assets/{index-EY733h9z.js → index-COqTEK1x.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6LJHQoE.js → index-CP5ax2Ox.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZGdjNLA.js → index-CPdpqzTz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DW-Ji07y.js → index-C_sA6V0-.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEJ6YiLI.js → index-C_we-zLv.js} +1 -1
- package/src/assets/web-panel/assets/{index-5Ewm6KZA.js → index-Cjg_lXkn.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0rr1X9W.js → index-CkMJbSJn.js} +1 -1
- package/src/assets/web-panel/assets/{index-BI1jAWcc.js → index-CmFox476.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cmzh8gKL.js → index-CrxfAG6t.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHi69MHF.js → index-CtgPPk47.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0vX9jQA.js → index-CxRjf-uA.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCRSz2cR.js → index-D0Lw2-GE.js} +1 -1
- package/src/assets/web-panel/assets/{index-za1GUJBG.js → index-D338fQ78.js} +1 -1
- package/src/assets/web-panel/assets/{index-slYX2rCE.js → index-D7xlQtKS.js} +1 -1
- package/src/assets/web-panel/assets/{index-CBhoZhCO.js → index-DISTBJJ4.js} +1 -1
- package/src/assets/web-panel/assets/{index-b8GbH2Yi.js → index-DL2e2Kwh.js} +3 -3
- package/src/assets/web-panel/assets/{index-DXgE2VW6.js → index-DNpXi3KX.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRp5_Xns.js → index-DVbSr5gk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYDvb1pi.js → index-DYEooXtQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGUbtM3R.js → index-DaXMEgmQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dbf5YmDX.js → index-DfzQT6CX.js} +1 -1
- package/src/assets/web-panel/assets/{index-gUACAWbM.js → index-Dk_cKbpp.js} +1 -1
- package/src/assets/web-panel/assets/{index-ByZQNO0A.js → index-pO_TKKcK.js} +1 -1
- package/src/assets/web-panel/assets/{index-onW325hZ.js → index-q4cwusSB.js} +1 -1
- package/src/assets/web-panel/assets/{index-t9u2bHpH.js → index-rdeSvPj9.js} +1 -1
- package/src/assets/web-panel/assets/index-vl01xWcc.js +1 -0
- package/src/assets/web-panel/assets/{index-Cj47XwJQ.js → index-xaqrvGuG.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DnadEaxu.js → initDefaultProps-Cw37W7y2.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CC_Na0Tl.js → motion-DOWpjqMY.js} +1 -1
- package/src/assets/web-panel/assets/{move-C2d9Mkk9.js → move-Ha0Ha2IK.js} +1 -1
- package/src/assets/web-panel/assets/{omit-QvpKbF8p.js → omit-DwGrzMoG.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Dm8r3X1_.js → pickAttrs-BRVWfNub.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DaqaVfoX.js → placementArrow-hV_RKr6c.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Iida9fIn.js → responsiveObserve-PWGubgde.js} +1 -1
- package/src/assets/web-panel/assets/{slide-YqHexXQD.js → slide-TRaJdEGF.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BGKLoeEt.js → statusUtils-Dtpf-U70.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-aI-gsQO8.js → styleChecker-pWt7CTDQ.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BiOsz4rc.js → useFlexGapSupport-BojArhOx.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CZy7Zo2X.js → useFs-ZGDRvFRC.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-B6WqjmE4.js → vnode-CBtr3ooD.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DTeTrJ2z.js → zoom-DyGC413M.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 +368 -3
- 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-aichat-wizard.js +29 -15
- 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
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
4
|
+
import bridgeDefault, {
|
|
5
|
+
invoke,
|
|
6
|
+
caps,
|
|
7
|
+
AndroidBridgeUnavailableError,
|
|
8
|
+
detectAndroid,
|
|
9
|
+
loadBridgeConfig,
|
|
10
|
+
_deps,
|
|
11
|
+
} from "../cc-android-bridge.js";
|
|
12
|
+
|
|
13
|
+
// ─── env restore helpers ──────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
const ENV_KEYS = [
|
|
16
|
+
"CC_ANDROID_BRIDGE_OVERRIDE",
|
|
17
|
+
"CC_ANDROID_BRIDGE_CONFIG_DIR",
|
|
18
|
+
"PREFIX",
|
|
19
|
+
];
|
|
20
|
+
let envSnapshot;
|
|
21
|
+
let depsSnapshot;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
envSnapshot = {};
|
|
25
|
+
for (const k of ENV_KEYS) envSnapshot[k] = process.env[k];
|
|
26
|
+
depsSnapshot = {
|
|
27
|
+
detectAndroid: _deps.detectAndroid,
|
|
28
|
+
loadBridgeConfig: _deps.loadBridgeConfig,
|
|
29
|
+
fetch: _deps.fetch,
|
|
30
|
+
testInvoke: _deps.testInvoke,
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
for (const k of ENV_KEYS) {
|
|
36
|
+
if (envSnapshot[k] === undefined) delete process.env[k];
|
|
37
|
+
else process.env[k] = envSnapshot[k];
|
|
38
|
+
}
|
|
39
|
+
_deps.detectAndroid = depsSnapshot.detectAndroid;
|
|
40
|
+
_deps.loadBridgeConfig = depsSnapshot.loadBridgeConfig;
|
|
41
|
+
_deps.fetch = depsSnapshot.fetch;
|
|
42
|
+
_deps.testInvoke = depsSnapshot.testInvoke;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// ─── detectAndroid ────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
describe("detectAndroid", () => {
|
|
48
|
+
it("returns false on non-Android host", () => {
|
|
49
|
+
delete process.env.CC_ANDROID_BRIDGE_OVERRIDE;
|
|
50
|
+
delete process.env.PREFIX;
|
|
51
|
+
// platform = "win32" / "linux" / "darwin" — none is "android"
|
|
52
|
+
expect(detectAndroid()).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("returns true under CC_ANDROID_BRIDGE_OVERRIDE=1", () => {
|
|
56
|
+
process.env.CC_ANDROID_BRIDGE_OVERRIDE = "1";
|
|
57
|
+
expect(detectAndroid()).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("returns true when PREFIX matches Termux Android prefix", () => {
|
|
61
|
+
delete process.env.CC_ANDROID_BRIDGE_OVERRIDE;
|
|
62
|
+
process.env.PREFIX = "/data/data/com.chainlesschain.android/files/usr";
|
|
63
|
+
expect(detectAndroid()).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// ─── loadBridgeConfig ─────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
describe("loadBridgeConfig", () => {
|
|
70
|
+
it("returns null off-device with no PREFIX + no override dir", () => {
|
|
71
|
+
delete process.env.CC_ANDROID_BRIDGE_CONFIG_DIR;
|
|
72
|
+
delete process.env.PREFIX;
|
|
73
|
+
expect(loadBridgeConfig()).toBeNull();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("returns parsed config from CC_ANDROID_BRIDGE_CONFIG_DIR", async () => {
|
|
77
|
+
const { mkdtempSync, writeFileSync, rmSync } = await import("node:fs");
|
|
78
|
+
const { join } = await import("node:path");
|
|
79
|
+
const { tmpdir } = await import("node:os");
|
|
80
|
+
|
|
81
|
+
const dir = mkdtempSync(join(tmpdir(), "cc-bridge-cfg-"));
|
|
82
|
+
writeFileSync(join(dir, "port"), "12345\n", "utf-8");
|
|
83
|
+
writeFileSync(join(dir, "token"), "deadbeef\n", "utf-8");
|
|
84
|
+
process.env.CC_ANDROID_BRIDGE_CONFIG_DIR = dir;
|
|
85
|
+
|
|
86
|
+
const cfg = loadBridgeConfig();
|
|
87
|
+
expect(cfg).toEqual({
|
|
88
|
+
port: 12345,
|
|
89
|
+
token: "deadbeef",
|
|
90
|
+
baseUrl: "http://127.0.0.1:12345",
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
rmSync(dir, { recursive: true, force: true });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("returns null when port file missing", async () => {
|
|
97
|
+
const { mkdtempSync, writeFileSync, rmSync } = await import("node:fs");
|
|
98
|
+
const { join } = await import("node:path");
|
|
99
|
+
const { tmpdir } = await import("node:os");
|
|
100
|
+
const dir = mkdtempSync(join(tmpdir(), "cc-bridge-cfg-"));
|
|
101
|
+
writeFileSync(join(dir, "token"), "x", "utf-8");
|
|
102
|
+
process.env.CC_ANDROID_BRIDGE_CONFIG_DIR = dir;
|
|
103
|
+
expect(loadBridgeConfig()).toBeNull();
|
|
104
|
+
rmSync(dir, { recursive: true, force: true });
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ─── invoke ───────────────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
describe("invoke — error paths", () => {
|
|
111
|
+
it("throws TypeError on empty method", async () => {
|
|
112
|
+
await expect(invoke("")).rejects.toThrow(TypeError);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("rejects ANDROID_BRIDGE_NOT_AVAILABLE off-device", async () => {
|
|
116
|
+
delete process.env.CC_ANDROID_BRIDGE_OVERRIDE;
|
|
117
|
+
_deps.detectAndroid = () => false;
|
|
118
|
+
await expect(invoke("contacts.query")).rejects.toBeInstanceOf(
|
|
119
|
+
AndroidBridgeUnavailableError,
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("rejects when bridge config missing on-device", async () => {
|
|
124
|
+
delete process.env.CC_ANDROID_BRIDGE_OVERRIDE;
|
|
125
|
+
_deps.detectAndroid = () => true;
|
|
126
|
+
_deps.loadBridgeConfig = () => null;
|
|
127
|
+
await expect(invoke("contacts.query")).rejects.toMatchObject({
|
|
128
|
+
code: "ANDROID_BRIDGE_NOT_AVAILABLE",
|
|
129
|
+
reason: expect.stringContaining("bridge config missing"),
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("rejects on fetch network error", async () => {
|
|
134
|
+
delete process.env.CC_ANDROID_BRIDGE_OVERRIDE;
|
|
135
|
+
_deps.detectAndroid = () => true;
|
|
136
|
+
_deps.loadBridgeConfig = () => ({
|
|
137
|
+
port: 8237,
|
|
138
|
+
token: "x",
|
|
139
|
+
baseUrl: "http://127.0.0.1:8237",
|
|
140
|
+
});
|
|
141
|
+
_deps.fetch = vi.fn(async () => {
|
|
142
|
+
throw new Error("ECONNREFUSED");
|
|
143
|
+
});
|
|
144
|
+
await expect(invoke("contacts.query")).rejects.toMatchObject({
|
|
145
|
+
code: "ANDROID_BRIDGE_NOT_AVAILABLE",
|
|
146
|
+
reason: expect.stringContaining("ECONNREFUSED"),
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("rejects on non-2xx response", async () => {
|
|
151
|
+
delete process.env.CC_ANDROID_BRIDGE_OVERRIDE;
|
|
152
|
+
_deps.detectAndroid = () => true;
|
|
153
|
+
_deps.loadBridgeConfig = () => ({
|
|
154
|
+
port: 8237,
|
|
155
|
+
token: "x",
|
|
156
|
+
baseUrl: "http://127.0.0.1:8237",
|
|
157
|
+
});
|
|
158
|
+
_deps.fetch = vi.fn(async () => ({
|
|
159
|
+
ok: false,
|
|
160
|
+
status: 401,
|
|
161
|
+
text: async () => "",
|
|
162
|
+
}));
|
|
163
|
+
await expect(invoke("contacts.query")).rejects.toMatchObject({
|
|
164
|
+
reason: expect.stringContaining("HTTP 401"),
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe("invoke — happy path", () => {
|
|
170
|
+
it("POSTs /invoke with Bearer token + JSON body, returns parsed JSON", async () => {
|
|
171
|
+
delete process.env.CC_ANDROID_BRIDGE_OVERRIDE;
|
|
172
|
+
_deps.detectAndroid = () => true;
|
|
173
|
+
_deps.loadBridgeConfig = () => ({
|
|
174
|
+
port: 8237,
|
|
175
|
+
token: "secret-token",
|
|
176
|
+
baseUrl: "http://127.0.0.1:8237",
|
|
177
|
+
});
|
|
178
|
+
const fetchSpy = vi.fn(async (url, init) => ({
|
|
179
|
+
ok: true,
|
|
180
|
+
status: 200,
|
|
181
|
+
text: async () => JSON.stringify({ contacts: [{ name: "Alice" }] }),
|
|
182
|
+
}));
|
|
183
|
+
_deps.fetch = fetchSpy;
|
|
184
|
+
|
|
185
|
+
const result = await invoke("contacts.query", { since: 0 });
|
|
186
|
+
expect(result).toEqual({ contacts: [{ name: "Alice" }] });
|
|
187
|
+
expect(fetchSpy).toHaveBeenCalledOnce();
|
|
188
|
+
const [url, init] = fetchSpy.mock.calls[0];
|
|
189
|
+
expect(url).toBe("http://127.0.0.1:8237/invoke?method=contacts.query");
|
|
190
|
+
expect(init.method).toBe("POST");
|
|
191
|
+
expect(init.headers["Authorization"]).toBe("Bearer secret-token");
|
|
192
|
+
expect(init.headers["Content-Type"]).toBe("application/json");
|
|
193
|
+
expect(JSON.parse(init.body)).toEqual({ since: 0 });
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("uses testInvoke when CC_ANDROID_BRIDGE_OVERRIDE=1", async () => {
|
|
197
|
+
process.env.CC_ANDROID_BRIDGE_OVERRIDE = "1";
|
|
198
|
+
_deps.testInvoke = vi.fn(async (m, p) => ({ echo: m, ...p }));
|
|
199
|
+
const result = await invoke("foo.bar", { x: 1 });
|
|
200
|
+
expect(result).toEqual({ echo: "foo.bar", x: 1 });
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// ─── caps ─────────────────────────────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
describe("caps", () => {
|
|
207
|
+
it("returns available=false off-device", () => {
|
|
208
|
+
delete process.env.CC_ANDROID_BRIDGE_OVERRIDE;
|
|
209
|
+
_deps.detectAndroid = () => false;
|
|
210
|
+
const r = caps();
|
|
211
|
+
expect(r.available).toBe(false);
|
|
212
|
+
expect(r.reason).toContain("not-on-android");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("returns available=false when bridge server not started", () => {
|
|
216
|
+
delete process.env.CC_ANDROID_BRIDGE_OVERRIDE;
|
|
217
|
+
_deps.detectAndroid = () => true;
|
|
218
|
+
_deps.loadBridgeConfig = () => null;
|
|
219
|
+
const r = caps();
|
|
220
|
+
expect(r.available).toBe(false);
|
|
221
|
+
expect(r.reason).toContain("bridge-server-not-started");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("returns available=true with port when bridge is up", () => {
|
|
225
|
+
delete process.env.CC_ANDROID_BRIDGE_OVERRIDE;
|
|
226
|
+
_deps.detectAndroid = () => true;
|
|
227
|
+
_deps.loadBridgeConfig = () => ({ port: 8237, token: "x" });
|
|
228
|
+
const r = caps();
|
|
229
|
+
expect(r.available).toBe(true);
|
|
230
|
+
expect(r.port).toBe(8237);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// ─── default export ───────────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
describe("default export", () => {
|
|
237
|
+
it("exposes invoke / caps / Error class / _deps", () => {
|
|
238
|
+
expect(bridgeDefault.invoke).toBe(invoke);
|
|
239
|
+
expect(bridgeDefault.caps).toBe(caps);
|
|
240
|
+
expect(bridgeDefault.AndroidBridgeUnavailableError).toBe(
|
|
241
|
+
AndroidBridgeUnavailableError,
|
|
242
|
+
);
|
|
243
|
+
expect(bridgeDefault._deps).toBe(_deps);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* cc-android-bridge — Node-side facade for the Android JNI bridge.
|
|
3
3
|
*
|
|
4
|
-
* Plan A Sub-Phase A6
|
|
5
|
-
* §4.2 + §6.1). The eventual native binding (`cc-android-bridge.node`,
|
|
6
|
-
* loaded from `android-app/app/src/main/cpp/`) exposes Java/Kotlin APIs to
|
|
7
|
-
* the in-APK Node runtime: ContentResolver query, PackageManager listings,
|
|
8
|
-
* Runtime.exec / Shizuku / Magisk su, Accessibility node tree, SAF DocumentFile
|
|
9
|
-
* read.
|
|
4
|
+
* Plan A Sub-Phase A6 (see `docs/design/Personal_Data_Hub_Android_Standalone_Cc.md`).
|
|
10
5
|
*
|
|
11
|
-
* **
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* (
|
|
15
|
-
* the
|
|
16
|
-
*
|
|
6
|
+
* **A6a transport (current)**: HTTP localhost. The Android app starts a
|
|
7
|
+
* tiny HTTP server (CcAndroidBridgeServer.kt) at App.onCreate; this file
|
|
8
|
+
* reads the port + token written to filesDir/.chainlesschain/bridge/ and
|
|
9
|
+
* forwards `invoke(method, params)` as `POST /invoke?method=<kebab>` with
|
|
10
|
+
* the JSON params as body and `Authorization: Bearer <token>`.
|
|
11
|
+
*
|
|
12
|
+
* Why HTTP not JNI: the cc subprocess is a separate Linux process (per
|
|
13
|
+
* memory android_cc_subprocess_execve_via_mksh) — JNI cannot bridge
|
|
14
|
+
* process boundaries. HTTP localhost gives the same UX with zero NDK
|
|
15
|
+
* toolchain dependency. The .so + Unix-socket path stays on the roadmap
|
|
16
|
+
* (A6b) for the in-process feel but is not blocking the feature.
|
|
17
17
|
*
|
|
18
18
|
* Detection precedence (highest first):
|
|
19
|
-
* 1. process.env.CC_ANDROID_BRIDGE_OVERRIDE === "1" — test override
|
|
20
|
-
* reports "available" and routes through `_deps.testInvoke` (which tests
|
|
21
|
-
* replace with a mock). Lets us exercise the command code paths without
|
|
22
|
-
* a real device.
|
|
19
|
+
* 1. process.env.CC_ANDROID_BRIDGE_OVERRIDE === "1" — test override
|
|
23
20
|
* 2. process.platform === "android"
|
|
24
21
|
* 3. process.env.PREFIX startsWith "/data/data/com.chainlesschain.android/"
|
|
25
22
|
* (Termux $PREFIX pattern in the bundled cc).
|
|
26
23
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
24
|
+
* Bridge config discovery (Android only):
|
|
25
|
+
* - filesDir/.chainlesschain/bridge/port — "8237" etc.
|
|
26
|
+
* - filesDir/.chainlesschain/bridge/token — 48-hex-char auth token
|
|
27
|
+
* filesDir resolves to `$PREFIX/..` (Termux convention: filesDir is the
|
|
28
|
+
* parent of usr/).
|
|
29
|
+
*
|
|
30
|
+
* Method names are kebab-case to match the `cc android <verb>` CLI surface.
|
|
30
31
|
*/
|
|
31
32
|
|
|
32
|
-
import {
|
|
33
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
34
|
+
import { join, dirname } from "node:path";
|
|
33
35
|
|
|
34
36
|
const ANDROID_PREFIX = "/data/data/com.chainlesschain.android/";
|
|
35
|
-
const nodeRequire = createRequire(import.meta.url);
|
|
36
37
|
|
|
37
38
|
export function detectAndroid() {
|
|
38
39
|
if (process.env.CC_ANDROID_BRIDGE_OVERRIDE === "1") return true;
|
|
@@ -47,19 +48,40 @@ export function detectAndroid() {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
* Locate the Android app's filesDir from inside the in-APK cc subprocess.
|
|
52
|
+
* Termux convention: $PREFIX = filesDir/usr → filesDir = dirname($PREFIX).
|
|
53
|
+
* Falls back to env var CC_ANDROID_BRIDGE_CONFIG_DIR (LAN / desktop case
|
|
54
|
+
* where caller has discovered the Android filesDir some other way).
|
|
55
|
+
*/
|
|
56
|
+
function resolveBridgeConfigDir() {
|
|
57
|
+
if (process.env.CC_ANDROID_BRIDGE_CONFIG_DIR) {
|
|
58
|
+
return process.env.CC_ANDROID_BRIDGE_CONFIG_DIR;
|
|
59
|
+
}
|
|
60
|
+
if (
|
|
61
|
+
typeof process.env.PREFIX === "string" &&
|
|
62
|
+
process.env.PREFIX.startsWith(ANDROID_PREFIX)
|
|
63
|
+
) {
|
|
64
|
+
return join(dirname(process.env.PREFIX), ".chainlesschain", "bridge");
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Read the port + token written by CcAndroidBridgeServer.start() in
|
|
71
|
+
* filesDir/.chainlesschain/bridge/. Returns null if either file is missing
|
|
72
|
+
* (server not started yet, or running off-device without override).
|
|
57
73
|
*/
|
|
58
|
-
export function
|
|
59
|
-
|
|
60
|
-
if (
|
|
74
|
+
export function loadBridgeConfig() {
|
|
75
|
+
const dir = resolveBridgeConfigDir();
|
|
76
|
+
if (!dir) return null;
|
|
61
77
|
try {
|
|
62
|
-
|
|
78
|
+
const portPath = join(dir, "port");
|
|
79
|
+
const tokenPath = join(dir, "token");
|
|
80
|
+
if (!existsSync(portPath) || !existsSync(tokenPath)) return null;
|
|
81
|
+
const port = parseInt(readFileSync(portPath, "utf-8").trim(), 10);
|
|
82
|
+
const token = readFileSync(tokenPath, "utf-8").trim();
|
|
83
|
+
if (!Number.isFinite(port) || port <= 0 || !token) return null;
|
|
84
|
+
return { port, token, baseUrl: `http://127.0.0.1:${port}` };
|
|
63
85
|
} catch (_e) {
|
|
64
86
|
return null;
|
|
65
87
|
}
|
|
@@ -74,17 +96,17 @@ export class AndroidBridgeUnavailableError extends Error {
|
|
|
74
96
|
}
|
|
75
97
|
|
|
76
98
|
/**
|
|
77
|
-
* Invoke a bridge method.
|
|
99
|
+
* Invoke a bridge method via the HTTP transport.
|
|
78
100
|
*
|
|
79
|
-
* @param {string} method — kebab-case method name (e.g. "contacts.query"
|
|
80
|
-
* "fs.read", "a11y.query", "app.list").
|
|
101
|
+
* @param {string} method — kebab-case method name (e.g. "contacts.query")
|
|
81
102
|
* @param {object} [params]
|
|
82
|
-
* @returns {Promise<any>}
|
|
103
|
+
* @returns {Promise<any>} Parsed JSON response from the bridge.
|
|
83
104
|
*
|
|
84
105
|
* Rejects with AndroidBridgeUnavailableError when:
|
|
85
|
-
* - Not running on Android
|
|
86
|
-
* - Running on Android but
|
|
87
|
-
* -
|
|
106
|
+
* - Not running on Android and CC_ANDROID_BRIDGE_OVERRIDE != 1
|
|
107
|
+
* - Running on Android but bridge config (port/token) missing
|
|
108
|
+
* - HTTP request fails or server returns non-200
|
|
109
|
+
* - Method name unknown (server returns {"error": "UNKNOWN_METHOD"})
|
|
88
110
|
*/
|
|
89
111
|
export async function invoke(method, params = {}) {
|
|
90
112
|
if (typeof method !== "string" || method.length === 0) {
|
|
@@ -93,7 +115,7 @@ export async function invoke(method, params = {}) {
|
|
|
93
115
|
);
|
|
94
116
|
}
|
|
95
117
|
|
|
96
|
-
// Test path — _deps.testInvoke
|
|
118
|
+
// Test path — harness replaces _deps.testInvoke.
|
|
97
119
|
if (
|
|
98
120
|
process.env.CC_ANDROID_BRIDGE_OVERRIDE === "1" &&
|
|
99
121
|
typeof _deps.testInvoke === "function"
|
|
@@ -107,23 +129,45 @@ export async function invoke(method, params = {}) {
|
|
|
107
129
|
);
|
|
108
130
|
}
|
|
109
131
|
|
|
110
|
-
const
|
|
111
|
-
if (!
|
|
132
|
+
const config = _deps.loadBridgeConfig();
|
|
133
|
+
if (!config) {
|
|
134
|
+
throw new AndroidBridgeUnavailableError(
|
|
135
|
+
"bridge config missing — CcAndroidBridgeServer not started? (filesDir/.chainlesschain/bridge/{port,token})",
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const url = `${config.baseUrl}/invoke?method=${encodeURIComponent(method)}`;
|
|
140
|
+
let resp;
|
|
141
|
+
try {
|
|
142
|
+
resp = await _deps.fetch(url, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: {
|
|
145
|
+
"Content-Type": "application/json",
|
|
146
|
+
Authorization: `Bearer ${config.token}`,
|
|
147
|
+
},
|
|
148
|
+
body: JSON.stringify(params || {}),
|
|
149
|
+
});
|
|
150
|
+
} catch (e) {
|
|
112
151
|
throw new AndroidBridgeUnavailableError(
|
|
113
|
-
|
|
152
|
+
`HTTP transport error: ${e && e.message ? e.message : String(e)}`,
|
|
114
153
|
);
|
|
115
154
|
}
|
|
116
|
-
if (
|
|
155
|
+
if (!resp.ok) {
|
|
156
|
+
throw new AndroidBridgeUnavailableError(`bridge HTTP ${resp.status}`);
|
|
157
|
+
}
|
|
158
|
+
const text = await resp.text();
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse(text);
|
|
161
|
+
} catch (e) {
|
|
117
162
|
throw new AndroidBridgeUnavailableError(
|
|
118
|
-
|
|
163
|
+
`bridge returned non-JSON: ${text.slice(0, 200)}`,
|
|
119
164
|
);
|
|
120
165
|
}
|
|
121
|
-
return await native.invoke(method, params);
|
|
122
166
|
}
|
|
123
167
|
|
|
124
168
|
/**
|
|
125
|
-
* Check whether the bridge is
|
|
126
|
-
* Returns `{ available: boolean, reason?: string }`.
|
|
169
|
+
* Check whether the bridge is reachable right now without throwing.
|
|
170
|
+
* Returns `{ available: boolean, reason?: string, port?: number }`.
|
|
127
171
|
*/
|
|
128
172
|
export function caps() {
|
|
129
173
|
if (process.env.CC_ANDROID_BRIDGE_OVERRIDE === "1") {
|
|
@@ -135,22 +179,22 @@ export function caps() {
|
|
|
135
179
|
reason: `not-on-android (platform=${process.platform})`,
|
|
136
180
|
};
|
|
137
181
|
}
|
|
138
|
-
const
|
|
139
|
-
if (!
|
|
182
|
+
const config = _deps.loadBridgeConfig();
|
|
183
|
+
if (!config) {
|
|
140
184
|
return {
|
|
141
185
|
available: false,
|
|
142
|
-
reason: "
|
|
186
|
+
reason: "bridge-server-not-started (port/token files missing)",
|
|
143
187
|
};
|
|
144
188
|
}
|
|
145
|
-
return { available: true };
|
|
189
|
+
return { available: true, port: config.port };
|
|
146
190
|
}
|
|
147
191
|
|
|
148
192
|
// _deps injection seam (per [[feedback-vi-mock-cjs-interop]]). Tests reach in
|
|
149
|
-
// and replace these to exercise
|
|
150
|
-
// a real Android device or the unloaded native binding.
|
|
193
|
+
// and replace these to exercise success / error paths without a real device.
|
|
151
194
|
export const _deps = {
|
|
152
195
|
detectAndroid,
|
|
153
|
-
|
|
196
|
+
loadBridgeConfig,
|
|
197
|
+
fetch: globalThis.fetch.bind(globalThis),
|
|
154
198
|
testInvoke: null,
|
|
155
199
|
};
|
|
156
200
|
|
|
@@ -19,14 +19,26 @@ import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "node:fs";
|
|
|
19
19
|
import { join } from "node:path";
|
|
20
20
|
import { createRequire } from "node:module";
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
DEFAULT_VENDOR_SPECS,
|
|
24
|
-
HttpClient,
|
|
25
|
-
CookieAuthSession,
|
|
26
|
-
} from "@chainlesschain/personal-data-hub/adapters/ai-chat-history";
|
|
27
|
-
|
|
28
22
|
const _require = createRequire(import.meta.url);
|
|
29
23
|
|
|
24
|
+
// Lazy-load `@chainlesschain/personal-data-hub/adapters/ai-chat-history` —
|
|
25
|
+
// older nested copies of the hub package (e.g. PDH 0.2.0 left in
|
|
26
|
+
// `packages/cli/node_modules/@chainlesschain/personal-data-hub/` by a stale
|
|
27
|
+
// install) lack this subpath export. A static ESM import would crash the
|
|
28
|
+
// whole wiring chain at module load time; lazy `_require` lets the error
|
|
29
|
+
// surface only when actually called, and tolerates an older nested copy as
|
|
30
|
+
// long as Node's resolution eventually walks up to a version that exports
|
|
31
|
+
// the subpath. See sibling lazy-load at line 217 / 228 for cookie-capture-
|
|
32
|
+
// spec + wizard-controller (same defence).
|
|
33
|
+
let _aichatModule = null;
|
|
34
|
+
function _loadAichatModule() {
|
|
35
|
+
if (_aichatModule) return _aichatModule;
|
|
36
|
+
_aichatModule = _require(
|
|
37
|
+
"@chainlesschain/personal-data-hub/adapters/ai-chat-history",
|
|
38
|
+
);
|
|
39
|
+
return _aichatModule;
|
|
40
|
+
}
|
|
41
|
+
|
|
30
42
|
export const ACCOUNTS_FILE = "aichat-accounts.json";
|
|
31
43
|
|
|
32
44
|
/**
|
|
@@ -99,16 +111,14 @@ export function createAccountsStore({ hubDir }) {
|
|
|
99
111
|
* spec.validateCookie(). Kept duplicated so the cli build stays ESM-pure
|
|
100
112
|
* without reaching into desktop-app-vue.
|
|
101
113
|
*/
|
|
102
|
-
export function createVendorAdapterBridge({
|
|
103
|
-
specs = DEFAULT_VENDOR_SPECS,
|
|
104
|
-
_httpClientFactory,
|
|
105
|
-
} = {}) {
|
|
114
|
+
export function createVendorAdapterBridge({ specs, _httpClientFactory } = {}) {
|
|
106
115
|
// DEFAULT_VENDOR_SPECS is shipped as a vendor-keyed object; tests pass an
|
|
107
116
|
// array. Normalize to an array first so byVendor lookup works either way.
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
117
|
+
const effectiveSpecs = specs ?? _loadAichatModule().DEFAULT_VENDOR_SPECS;
|
|
118
|
+
const arr = Array.isArray(effectiveSpecs)
|
|
119
|
+
? effectiveSpecs
|
|
120
|
+
: effectiveSpecs && typeof effectiveSpecs === "object"
|
|
121
|
+
? Object.values(effectiveSpecs)
|
|
112
122
|
: null;
|
|
113
123
|
if (!arr || arr.length === 0) {
|
|
114
124
|
throw new Error("aichat-wizard-cli: specs required");
|
|
@@ -119,7 +129,10 @@ export function createVendorAdapterBridge({
|
|
|
119
129
|
}
|
|
120
130
|
const buildClient =
|
|
121
131
|
_httpClientFactory ||
|
|
122
|
-
((vendor, spec) =>
|
|
132
|
+
((vendor, spec) => {
|
|
133
|
+
const { HttpClient } = _loadAichatModule();
|
|
134
|
+
return new HttpClient({ vendor, rateLimits: spec.rateLimits });
|
|
135
|
+
});
|
|
123
136
|
|
|
124
137
|
async function registerVendor(vendor, cookies, _opts = {}) {
|
|
125
138
|
const spec = byVendor.get(vendor);
|
|
@@ -137,6 +150,7 @@ export function createVendorAdapterBridge({
|
|
|
137
150
|
error: err.message,
|
|
138
151
|
};
|
|
139
152
|
}
|
|
153
|
+
const { CookieAuthSession } = _loadAichatModule();
|
|
140
154
|
const session = new CookieAuthSession({
|
|
141
155
|
vendor,
|
|
142
156
|
cookies: _jarToArray(cookies),
|
|
@@ -45,6 +45,7 @@ const {
|
|
|
45
45
|
EmailAdapter,
|
|
46
46
|
AlipayBillAdapter,
|
|
47
47
|
SystemDataAndroidAdapter,
|
|
48
|
+
BilibiliAdapter,
|
|
48
49
|
EntityResolver,
|
|
49
50
|
EntityResolverEmbeddingStage,
|
|
50
51
|
EntityResolverLLMStage,
|
|
@@ -61,8 +62,13 @@ import {
|
|
|
61
62
|
} from "./personal-data-hub-aichat-wizard.js";
|
|
62
63
|
|
|
63
64
|
async function loadAIChatHealthChecker() {
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
// Composed-at-runtime specifier so vite import-analysis (which fails
|
|
66
|
+
// to resolve subpath exports for dynamic imports in vitest SSR mode)
|
|
67
|
+
// skips this — the static scanner only descends into string literals.
|
|
68
|
+
const spec =
|
|
69
|
+
"@chainlesschain/personal-data-hub" +
|
|
70
|
+
"/adapters/ai-chat-history/health-checker";
|
|
71
|
+
const mod = await import(spec);
|
|
66
72
|
return (mod.default || mod).createAIChatHealthChecker;
|
|
67
73
|
}
|
|
68
74
|
|
|
@@ -263,6 +269,22 @@ async function initHub() {
|
|
|
263
269
|
// surface the absence via list-adapters.
|
|
264
270
|
}
|
|
265
271
|
|
|
272
|
+
// A8 v0.1 (2026-05-22) — social adapters in snapshot mode. Stateless: the
|
|
273
|
+
// Android UI captures a cookie via in-app WebView, runs OkHttp against the
|
|
274
|
+
// platform's HTTP API, parses the JSON response, writes a snapshot JSON to
|
|
275
|
+
// filesDir/.chainlesschain/staging/, then calls `cc hub sync-adapter <name>
|
|
276
|
+
// --input <path> --json` via LocalCcRunner. The adapter reads the file and
|
|
277
|
+
// yields events; the same registry handles KG + RAG + vault writes — no
|
|
278
|
+
// desktop connection required (this is the desktop-independent path the
|
|
279
|
+
// user repeatedly asked about). Each adapter no-ops at boot when no
|
|
280
|
+
// snapshot has been produced yet.
|
|
281
|
+
try {
|
|
282
|
+
const bilibili = new BilibiliAdapter();
|
|
283
|
+
if (!registry.has(bilibili.name)) registry.register(bilibili);
|
|
284
|
+
} catch (_err) {
|
|
285
|
+
// Continue boot
|
|
286
|
+
}
|
|
287
|
+
|
|
266
288
|
// Phase 6: auto-register persisted Alipay accounts.
|
|
267
289
|
const alipayAccountsPath = join(hubDir, "alipay-accounts.json");
|
|
268
290
|
const alipayAccounts = loadAlipayAccounts(alipayAccountsPath);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{A as o}from"./Row-DjHxhH1L.js";import{S as t}from"./index-b8GbH2Yi.js";import"./responsiveObserve-Iida9fIn.js";import"./vendor-DhFY8mDK.js";import"./useFlexGapSupport-BiOsz4rc.js";import"./styleChecker-aI-gsQO8.js";import"./index-gUACAWbM.js";import"./icons-BOPtEWK4.js";const l=t(o);export{l as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{C as o}from"./Col-DU9NoUIi.js";import{S as t}from"./index-b8GbH2Yi.js";import"./index-gUACAWbM.js";import"./vendor-DhFY8mDK.js";import"./icons-BOPtEWK4.js";const s=t(o);export{s as default};
|