@bolloon/bolloon-agent 0.4.13 → 0.4.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/README.md +127 -127
- package/bin/bolloon-cli.cjs +183 -183
- package/bin/bolloon-daemon.sh +207 -0
- package/bin/bolloon.cjs +0 -0
- package/dist/agents/agent-gateway.js +130 -0
- package/dist/agents/constraint-layer.js +19 -19
- package/dist/agents/gateway-group.js +261 -0
- package/dist/agents/gateway-network.js +278 -0
- package/dist/agents/judgment-protocol.js +14 -14
- package/dist/agents/pi-sdk-tools.js +117 -0
- package/dist/agents/pi-sdk-types.js +12 -12
- package/dist/agents/pi-sdk.js +101 -101
- package/dist/agents/skill-organizer.js +19 -19
- package/dist/agents/workflow-pivot-loop.js +2 -2
- package/dist/agents/x402/x402Pay.js +4 -0
- package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
- package/dist/bollharness-integration/context-router-judgment.js +4 -4
- package/dist/bollharness-integration/context-router.js +292 -292
- package/dist/bollharness-integration/gate-state-machine.js +13 -13
- package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
- package/dist/bollharness-integration/skill-adapter.js +21 -21
- package/dist/bootstrap/chat-archiver.js +14 -14
- package/dist/bootstrap/context-os.js +24 -24
- package/dist/bootstrap/memory-compressor.js +13 -13
- package/dist/cli-entry.js +36 -36
- package/dist/constraint-runtime/reference_data/archive_surface_snapshot.json +63 -0
- package/dist/constraint-runtime/reference_data/commands_snapshot.json +1037 -0
- package/dist/constraint-runtime/reference_data/subsystems/assistant.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/bootstrap.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/bridge.json +32 -0
- package/dist/constraint-runtime/reference_data/subsystems/buddy.json +13 -0
- package/dist/constraint-runtime/reference_data/subsystems/cli.json +26 -0
- package/dist/constraint-runtime/reference_data/subsystems/components.json +32 -0
- package/dist/constraint-runtime/reference_data/subsystems/constants.json +28 -0
- package/dist/constraint-runtime/reference_data/subsystems/coordinator.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/entrypoints.json +15 -0
- package/dist/constraint-runtime/reference_data/subsystems/hooks.json +32 -0
- package/dist/constraint-runtime/reference_data/subsystems/keybindings.json +21 -0
- package/dist/constraint-runtime/reference_data/subsystems/memdir.json +15 -0
- package/dist/constraint-runtime/reference_data/subsystems/migrations.json +18 -0
- package/dist/constraint-runtime/reference_data/subsystems/moreright.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/native_ts.json +11 -0
- package/dist/constraint-runtime/reference_data/subsystems/outputStyles.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/plugins.json +9 -0
- package/dist/constraint-runtime/reference_data/subsystems/remote.json +11 -0
- package/dist/constraint-runtime/reference_data/subsystems/schemas.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/screens.json +10 -0
- package/dist/constraint-runtime/reference_data/subsystems/server.json +10 -0
- package/dist/constraint-runtime/reference_data/subsystems/services.json +32 -0
- package/dist/constraint-runtime/reference_data/subsystems/skills.json +27 -0
- package/dist/constraint-runtime/reference_data/subsystems/state.json +13 -0
- package/dist/constraint-runtime/reference_data/subsystems/types.json +18 -0
- package/dist/constraint-runtime/reference_data/subsystems/upstreamproxy.json +9 -0
- package/dist/constraint-runtime/reference_data/subsystems/utils.json +32 -0
- package/dist/constraint-runtime/reference_data/subsystems/vim.json +12 -0
- package/dist/constraint-runtime/reference_data/subsystems/voice.json +8 -0
- package/dist/constraint-runtime/reference_data/tools_snapshot.json +922 -0
- package/dist/constraint-runtime/tools/PolymarketSDK/econ-integration.d.ts +55 -0
- package/dist/constraint-runtime/tools/PolymarketSDK/econ-integration.js +59 -0
- package/dist/context-compaction/auto-compact.js +7 -7
- package/dist/electron/config.js.map +1 -0
- package/dist/electron/dialogs.js.map +1 -0
- package/dist/electron/first-run.js +54 -54
- package/dist/electron/first-run.js.map +1 -0
- package/dist/electron/ipc.js.map +1 -0
- package/dist/electron/logger.js.map +1 -0
- package/dist/electron/main.js.map +1 -0
- package/dist/electron/menu.js.map +1 -0
- package/dist/electron/paths.js.map +1 -0
- package/dist/electron/server.js.map +1 -0
- package/dist/electron/tray.js.map +1 -0
- package/dist/electron/window.js.map +1 -0
- package/dist/electron-build/electron/first-run.js +54 -54
- package/dist/electron-preload.js.map +1 -1
- package/dist/electron.js.map +1 -1
- package/dist/index.js +112 -112
- package/dist/llm/llm-judgment-client.js +102 -102
- package/dist/llm/pi-ai.js +48 -48
- package/dist/llm/system-prompt/layers/channel/human-async.md +41 -41
- package/dist/llm/system-prompt/layers/channel/local.md +14 -14
- package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
- package/dist/llm/system-prompt/layers/channel/p2p-peer-sync.md +51 -51
- package/dist/llm/system-prompt/layers/channel/p2p-proactive.md +43 -43
- package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
- package/dist/llm/system-prompt/layers/channel/session-handoff.md +61 -61
- package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
- package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
- package/dist/llm/system-prompt/layers/core/external-engagement.md +73 -73
- package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
- package/dist/llm/system-prompt/layers/core/identity.md +48 -48
- package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
- package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
- package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
- package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
- package/dist/llm/system-prompt/layers/core/tone.md +31 -31
- package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
- package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
- package/dist/llm/system-prompt/layers/role/architect.md +20 -20
- package/dist/llm/system-prompt/layers/role/expert.md +19 -19
- package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
- package/dist/llm/system-prompt/layers/role/security.md +15 -15
- package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
- package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
- package/dist/llm/system-prompt/layers/tool/goal_handoff.md +77 -77
- package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
- package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
- package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
- package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
- package/dist/llm/system-prompt/layers/tool/p2p_request.md +61 -61
- package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
- package/dist/llm/tool-manifest/ask_user_input.js +1 -1
- package/dist/llm/tool-manifest/bash.js +3 -3
- package/dist/llm/tool-manifest/create_file.js +4 -4
- package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
- package/dist/llm/tool-manifest/image_search.js +2 -2
- package/dist/llm/tool-manifest/index.js +59 -0
- package/dist/llm/tool-manifest/mcp.js +2 -2
- package/dist/llm/tool-manifest/message_compose.js +3 -3
- package/dist/llm/tool-manifest/places.js +2 -2
- package/dist/llm/tool-manifest/present_files.js +1 -1
- package/dist/llm/tool-manifest/recipe.js +2 -2
- package/dist/llm/tool-manifest/str_replace.js +5 -5
- package/dist/llm/tool-manifest/view.js +3 -3
- package/dist/llm/tool-manifest/weather.js +4 -4
- package/dist/llm/tool-manifest/web.js +4 -4
- package/dist/network/p2p.js +78 -4
- package/dist/orbitdb/cid-database.js +39 -10
- package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
- package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
- package/dist/pi-ecosystem-judgment/distillation.js +14 -14
- package/dist/pi-ecosystem-judgment/index.js +1 -1
- package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
- package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
- package/dist/security/context-router-tool.js +15 -15
- package/dist/utils/auto-update.js.map +1 -0
- package/dist/web/a2ui-client.js +1594 -223
- package/dist/web/api-config.html +779 -779
- package/dist/web/client-hearth.js +10 -10
- package/dist/web/client.js +273 -0
- package/dist/web/client.js.map +7 -0
- package/dist/web/components/wallet-viem.mjs +118 -118
- package/dist/web/index.html +502 -485
- package/dist/web/manifest.json +20 -20
- package/dist/web/mobile-agent.js +397 -0
- package/dist/web/mobile-core.js +86235 -0
- package/dist/web/mobile-data.js +346 -0
- package/dist/web/mobile-http-api.js +71 -0
- package/dist/web/mobile-p2p.js +250 -0
- package/dist/web/mobile-payments.js +80 -0
- package/dist/web/mobile-wallet.js +226 -0
- package/dist/web/mobile.css +483 -6
- package/dist/web/mobile.html +99 -50
- package/dist/web/mobile.js +1040 -187
- package/dist/web/server.js +196 -13
- package/dist/web/style.css +5062 -4946
- package/package.json +201 -198
- package/scripts/build-cli.js +215 -215
- package/scripts/build-web.ts +162 -149
- package/scripts/postinstall.js +152 -152
- package/bin/bolloon.js +0 -157
- package/dist/agents/python-exec.js +0 -115
- package/dist/pi-ecosystem-colony/index.js +0 -365
- package/dist/social/ant-colony/AdaptiveHeartbeat.js +0 -101
- package/dist/social/ant-colony/PheromoneEngine.js +0 -227
- package/dist/social/ant-colony/index.js +0 -6
- package/dist/social/ant-colony/types.js +0 -24
- package/dist/storage/trajectory.js +0 -101
- package/dist/test/ai-judgment-test.js +0 -80
- package/dist/test/bollharness-integration.test.js +0 -318
- package/dist/test/channel-agent-multi-dialogue.js +0 -205
- package/dist/test/channel-heartbeat-agent-test.js +0 -201
- package/dist/test/constraint-layer.test.js +0 -164
- package/dist/test/diap-identity-test.js +0 -172
- package/dist/test/diap-quick-test.js +0 -62
- package/dist/test/global-shared-context.test.js +0 -315
- package/dist/test/harness-judgment-injection.test.js +0 -246
- package/dist/test/harness-workflow-integrator-test.js +0 -228
- package/dist/test/human-value-store.test.js +0 -243
- package/dist/test/hybrid-integration-test.js +0 -118
- package/dist/test/hybrid-messenger-verify.js +0 -55
- package/dist/test/iroh-bistream-debug.js +0 -38
- package/dist/test/iroh-communication.test.js +0 -66
- package/dist/test/iroh-debug-test.js +0 -57
- package/dist/test/iroh-diap-test.js +0 -71
- package/dist/test/iroh-direct-connect.js +0 -55
- package/dist/test/iroh-e2e-fixed.js +0 -89
- package/dist/test/iroh-e2e-same-process.js +0 -63
- package/dist/test/iroh-e2e.js +0 -66
- package/dist/test/iroh-final-e2e.js +0 -72
- package/dist/test/iroh-relay-test.js +0 -37
- package/dist/test/iroh-simple-test.js +0 -41
- package/dist/test/iroh-transport-verify.js +0 -54
- package/dist/test/iroh-transport.test.js +0 -37
- package/dist/test/iroh-two-nodes.js +0 -70
- package/dist/test/iroh-verify.js +0 -44
- package/dist/test/judgment-decision.test.js +0 -219
- package/dist/test/llm-judgment-integration.test.js +0 -220
- package/dist/test/p2p-agent-complex-dialogue.js +0 -385
- package/dist/test/p2p-agent-dialogue.js +0 -341
- package/dist/test/p2p-agent-full-bidirectional.js +0 -510
- package/dist/test/p2p-agent-harness-flow.js +0 -437
- package/dist/test/p2p-agent-harness-single.js +0 -143
- package/dist/test/p2p-ai-dialogue-test.js +0 -318
- package/dist/test/p2p-cid-connect-test.js +0 -195
- package/dist/test/p2p-connect-receiver.js +0 -69
- package/dist/test/p2p-doc-transfer.js +0 -110
- package/dist/test/p2p-identity-page-test.js +0 -77
- package/dist/test/p2p-iroh-test.js +0 -171
- package/dist/test/p2p-minimal-test.js +0 -241
- package/dist/test/p2p-node-1.js +0 -148
- package/dist/test/p2p-node-2.js +0 -148
- package/dist/test/p2p-server.js +0 -281
- package/dist/test/p2p-two-nodes-test.js +0 -438
- package/dist/test/pi-sdk.test.js +0 -44
- package/dist/test/set-persona.js +0 -40
- package/dist/test/simple.test.js +0 -9
- package/dist/test/storage-integration.test.js +0 -150
- package/dist/test/subagent-manager.test.js +0 -276
- package/dist/test/test-gate-flow.test.js +0 -81
- package/dist/test/workflow-engine.test.js +0 -87
- package/dist/test/workflow-pivot-loop.test.js +0 -246
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mobile-payments.ts — 手机端支付审批 (2026-08-15)
|
|
3
|
+
*
|
|
4
|
+
* 独立能力: 支付审批 (与数据同步 / agent 功能并列, 但不属于任一层).
|
|
5
|
+
* 存储: IndexedDB (独立库, 与 data/agent 分开).
|
|
6
|
+
*/
|
|
7
|
+
const DB_NAME = 'bolloon-mobile-payments';
|
|
8
|
+
let _db = null;
|
|
9
|
+
function openDb() {
|
|
10
|
+
if (_db)
|
|
11
|
+
return Promise.resolve(_db);
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const req = indexedDB.open(DB_NAME, 1);
|
|
14
|
+
req.onupgradeneeded = () => {
|
|
15
|
+
const db = req.result;
|
|
16
|
+
if (!db.objectStoreNames.contains('kv'))
|
|
17
|
+
db.createObjectStore('kv');
|
|
18
|
+
};
|
|
19
|
+
req.onsuccess = () => { _db = req.result; resolve(_db); };
|
|
20
|
+
req.onerror = () => reject(req.error);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async function idbGet(key) {
|
|
24
|
+
const db = await openDb();
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
const tx = db.transaction('kv', 'readonly');
|
|
27
|
+
const r = tx.objectStore('kv').get(key);
|
|
28
|
+
r.onsuccess = () => resolve(r.result ?? null);
|
|
29
|
+
r.onerror = () => resolve(null);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async function idbSet(key, val) {
|
|
33
|
+
const db = await openDb();
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const tx = db.transaction('kv', 'readwrite');
|
|
36
|
+
tx.objectStore('kv').put(val, key);
|
|
37
|
+
tx.oncomplete = () => resolve();
|
|
38
|
+
tx.onerror = () => reject(tx.error);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export async function loadApprovals() {
|
|
42
|
+
return (await idbGet('approvals')) || [];
|
|
43
|
+
}
|
|
44
|
+
async function saveApprovals(list) {
|
|
45
|
+
await idbSet('approvals', list);
|
|
46
|
+
}
|
|
47
|
+
export async function addApproval(a) {
|
|
48
|
+
const all = await loadApprovals();
|
|
49
|
+
all.push(a);
|
|
50
|
+
await saveApprovals(all);
|
|
51
|
+
}
|
|
52
|
+
export async function approveApproval(id, approved) {
|
|
53
|
+
const all = await loadApprovals();
|
|
54
|
+
const a = all.find((x) => x.id === id);
|
|
55
|
+
if (a) {
|
|
56
|
+
a.resolved = true;
|
|
57
|
+
a.resolvedAt = Date.now();
|
|
58
|
+
a.approved = approved;
|
|
59
|
+
await saveApprovals(all);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** 测试用: 关闭并清空支付库 */
|
|
63
|
+
export async function resetPaymentsDb() {
|
|
64
|
+
if (_db) {
|
|
65
|
+
_db.close();
|
|
66
|
+
_db = null;
|
|
67
|
+
}
|
|
68
|
+
await new Promise((resolve) => {
|
|
69
|
+
try {
|
|
70
|
+
const req = indexedDB.deleteDatabase(DB_NAME);
|
|
71
|
+
req.onsuccess = () => resolve();
|
|
72
|
+
req.onerror = () => resolve();
|
|
73
|
+
req.onblocked = () => resolve();
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
resolve();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
export default { loadApprovals, addApproval, approveApproval };
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mobile-wallet.ts — 手机端加密钱包 v2 (多钱包, EVM, 只读 MVP)
|
|
3
|
+
*
|
|
4
|
+
* 用户需求 (2026-09-05):
|
|
5
|
+
* - 多个加密钱包, 可创建新钱包
|
|
6
|
+
* - 本地自动加载 (无口令) — auto 模式: 用设备本地密钥 (deviceSecret) 加密, 打开即解锁
|
|
7
|
+
* - 或 口令解锁 — passphrase 模式: 用户口令 (PBKDF2) 加密, 需输入口令 (生物识别为 native 后置)
|
|
8
|
+
* - 每个钱包可授权给多个本地智能体 (allowedAgents), 仅本地创建的智能体能调自己的钱包
|
|
9
|
+
*
|
|
10
|
+
* 被 mobile-core.ts import → esbuild bundle 进 mobile-core.js (离线可用).
|
|
11
|
+
*/
|
|
12
|
+
import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39';
|
|
13
|
+
import { wordlist as english } from '@scure/bip39/wordlists/english';
|
|
14
|
+
import { HDKey } from '@scure/bip32';
|
|
15
|
+
import { keccak_256 } from '@noble/hashes/sha3.js';
|
|
16
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
17
|
+
// ============ IndexedDB (自持 kv) ============
|
|
18
|
+
const DB_NAME = 'bolloon';
|
|
19
|
+
const STORE = 'kv';
|
|
20
|
+
function openDb() {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const rq = indexedDB.open(DB_NAME, 1);
|
|
23
|
+
rq.onupgradeneeded = () => { if (!rq.result.objectStoreNames.contains(STORE))
|
|
24
|
+
rq.result.createObjectStore(STORE); };
|
|
25
|
+
rq.onsuccess = () => resolve(rq.result);
|
|
26
|
+
rq.onerror = () => reject(rq.error);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async function idbGet(key) {
|
|
30
|
+
const db = await openDb();
|
|
31
|
+
return new Promise((res, rej) => {
|
|
32
|
+
const t = db.transaction(STORE, 'readonly').objectStore(STORE).get(key);
|
|
33
|
+
t.onsuccess = () => res(t.result);
|
|
34
|
+
t.onerror = () => rej(t.error);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async function idbSet(key, val) {
|
|
38
|
+
const db = await openDb();
|
|
39
|
+
return new Promise((res, rej) => {
|
|
40
|
+
const t = db.transaction(STORE, 'readwrite').objectStore(STORE).put(val, key);
|
|
41
|
+
t.onsuccess = () => res();
|
|
42
|
+
t.onerror = () => rej(t.error);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const WKEY = 'wallets'; // WalletRecord[]
|
|
46
|
+
const DSKEY = 'device_secret'; // 设备本地密钥 (auto 模式用, hex)
|
|
47
|
+
const DEFAULT_RPC = 'https://eth.llamarpc.com';
|
|
48
|
+
// 内存解锁态: id → privKey (仅当前会话)
|
|
49
|
+
const unlockedKeys = new Map();
|
|
50
|
+
// ============ hex/b64 工具 ============
|
|
51
|
+
function bytesToHex(b) { return Array.from(b).map((x) => x.toString(16).padStart(2, '0')).join(''); }
|
|
52
|
+
function hexToBytes(h) {
|
|
53
|
+
const s = h.startsWith('0x') ? h.slice(2) : h;
|
|
54
|
+
if (s.length % 2 !== 0)
|
|
55
|
+
throw new Error('hex 长度需为偶数');
|
|
56
|
+
const out = new Uint8Array(s.length / 2);
|
|
57
|
+
for (let i = 0; i < out.length; i++)
|
|
58
|
+
out[i] = parseInt(s.slice(i * 2, i * 2 + 2), 16);
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
function bytesToB64(b) { let s = ''; b.forEach((x) => { s += String.fromCharCode(x); }); return btoa(s); }
|
|
62
|
+
function b64ToBytes(s) { return Uint8Array.from(atob(s), (c) => c.charCodeAt(0)); }
|
|
63
|
+
// ============ 派生 ============
|
|
64
|
+
function derivePrivateKey(mnemonic) {
|
|
65
|
+
const seed = mnemonicToSeedSync(mnemonic, '');
|
|
66
|
+
const hd = HDKey.fromMasterSeed(seed);
|
|
67
|
+
const pk = hd.derive("m/44'/60'/0'/0/0").privateKey;
|
|
68
|
+
if (!pk || pk.length !== 32)
|
|
69
|
+
throw new Error('派生私钥失败');
|
|
70
|
+
return pk;
|
|
71
|
+
}
|
|
72
|
+
function privToAddress(priv) {
|
|
73
|
+
const pub = secp256k1.getPublicKey(priv, false);
|
|
74
|
+
const hash = keccak_256(pub.subarray(1));
|
|
75
|
+
return '0x' + bytesToHex(hash.subarray(12));
|
|
76
|
+
}
|
|
77
|
+
// ============ WebCrypto ============
|
|
78
|
+
async function pbkdf2Key(pass, salt) {
|
|
79
|
+
const km = await crypto.subtle.importKey('raw', new TextEncoder().encode(pass), 'PBKDF2', false, ['deriveKey']);
|
|
80
|
+
return crypto.subtle.deriveKey({ name: 'PBKDF2', salt: salt, iterations: 100000, hash: 'SHA-256' }, km, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
|
|
81
|
+
}
|
|
82
|
+
async function rawKey(bytes) {
|
|
83
|
+
return crypto.subtle.importKey('raw', bytes, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
|
|
84
|
+
}
|
|
85
|
+
async function encryptWith(key, plain) {
|
|
86
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
87
|
+
const ct = await crypto.subtle.encrypt({ name: 'AES-GCM', iv: iv }, key, plain);
|
|
88
|
+
return { iv: bytesToB64(iv), data: bytesToB64(new Uint8Array(ct)) };
|
|
89
|
+
}
|
|
90
|
+
async function decryptWith(key, enc) {
|
|
91
|
+
const iv = b64ToBytes(enc.iv);
|
|
92
|
+
const ct = b64ToBytes(enc.data);
|
|
93
|
+
const pt = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: iv }, key, ct);
|
|
94
|
+
return new Uint8Array(pt);
|
|
95
|
+
}
|
|
96
|
+
// ============ 设备密钥 与 钱包列表 ============
|
|
97
|
+
async function deviceSecretBytes() {
|
|
98
|
+
let hex = await idbGet(DSKEY);
|
|
99
|
+
if (!hex) {
|
|
100
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
101
|
+
hex = bytesToHex(bytes);
|
|
102
|
+
await idbSet(DSKEY, hex);
|
|
103
|
+
}
|
|
104
|
+
return hexToBytes(hex);
|
|
105
|
+
}
|
|
106
|
+
async function loadWallets() { return (await idbGet(WKEY)) || []; }
|
|
107
|
+
async function saveWallets(list) { await idbSet(WKEY, list); }
|
|
108
|
+
// ============ 公开 API ============
|
|
109
|
+
export async function listWallets() {
|
|
110
|
+
const list = await loadWallets();
|
|
111
|
+
const wallets = list.map((w) => ({
|
|
112
|
+
id: w.id, name: w.name, address: w.address, mode: w.mode,
|
|
113
|
+
unlocked: unlockedKeys.has(w.id), allowedAgents: w.allowedAgents || [], createdAt: w.createdAt,
|
|
114
|
+
}));
|
|
115
|
+
return { exists: list.length > 0, wallets };
|
|
116
|
+
}
|
|
117
|
+
export async function createWallet(name, mode, pass) {
|
|
118
|
+
const mnemonic = generateMnemonic(english);
|
|
119
|
+
const priv = derivePrivateKey(mnemonic);
|
|
120
|
+
const address = privToAddress(priv);
|
|
121
|
+
let enc;
|
|
122
|
+
if (mode === 'auto') {
|
|
123
|
+
const key = await rawKey(await deviceSecretBytes());
|
|
124
|
+
enc = await encryptWith(key, priv);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
if (!pass)
|
|
128
|
+
throw new Error('口令模式需要口令');
|
|
129
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
130
|
+
const key = await pbkdf2Key(pass, salt);
|
|
131
|
+
const r = await encryptWith(key, priv);
|
|
132
|
+
enc = { iv: r.iv, data: r.data, salt: bytesToB64(salt) };
|
|
133
|
+
}
|
|
134
|
+
const id = 'wallet-' + Date.now().toString(36) + Math.floor(Math.random() * 1000);
|
|
135
|
+
const rec = { id, name: name || ('钱包 ' + ((await loadWallets()).length + 1)), address, enc, mode, allowedAgents: [], createdAt: Date.now(), chain: '1' };
|
|
136
|
+
const list = await loadWallets();
|
|
137
|
+
list.push(rec);
|
|
138
|
+
await saveWallets(list);
|
|
139
|
+
if (mode === 'auto')
|
|
140
|
+
unlockedKeys.set(id, priv);
|
|
141
|
+
return { id, address, mnemonic, name: rec.name };
|
|
142
|
+
}
|
|
143
|
+
export async function importWallet(input, name, mode, pass) {
|
|
144
|
+
const s = (input || '').trim();
|
|
145
|
+
let priv;
|
|
146
|
+
if (validateMnemonic(s, english))
|
|
147
|
+
priv = derivePrivateKey(s);
|
|
148
|
+
else {
|
|
149
|
+
priv = hexToBytes(s);
|
|
150
|
+
if (priv.length !== 32)
|
|
151
|
+
throw new Error('私钥需为 32 字节 (64 位 hex) 或有效助记词');
|
|
152
|
+
}
|
|
153
|
+
const address = privToAddress(priv);
|
|
154
|
+
let enc;
|
|
155
|
+
if (mode === 'auto')
|
|
156
|
+
enc = await encryptWith(await rawKey(await deviceSecretBytes()), priv);
|
|
157
|
+
else {
|
|
158
|
+
if (!pass)
|
|
159
|
+
throw new Error('口令模式需要口令');
|
|
160
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
161
|
+
const key = await pbkdf2Key(pass, salt);
|
|
162
|
+
const r = await encryptWith(key, priv);
|
|
163
|
+
enc = { iv: r.iv, data: r.data, salt: bytesToB64(salt) };
|
|
164
|
+
}
|
|
165
|
+
const id = 'wallet-' + Date.now().toString(36) + Math.floor(Math.random() * 1000);
|
|
166
|
+
const list = await loadWallets();
|
|
167
|
+
const rec = { id, name: name || ('钱包 ' + (list.length + 1)), address, enc, mode, allowedAgents: [], createdAt: Date.now(), chain: '1' };
|
|
168
|
+
list.push(rec);
|
|
169
|
+
await saveWallets(list);
|
|
170
|
+
if (mode === 'auto')
|
|
171
|
+
unlockedKeys.set(id, priv);
|
|
172
|
+
return { id, address, name: rec.name };
|
|
173
|
+
}
|
|
174
|
+
export async function unlockWallet(id, pass) {
|
|
175
|
+
const list = await loadWallets();
|
|
176
|
+
const w = list.find((x) => x.id === id);
|
|
177
|
+
if (!w)
|
|
178
|
+
throw new Error('钱包不存在');
|
|
179
|
+
const salt = w.enc.salt ? b64ToBytes(w.enc.salt) : crypto.getRandomValues(new Uint8Array(16));
|
|
180
|
+
const key = await pbkdf2Key(pass, salt);
|
|
181
|
+
const priv = await decryptWith(key, w.enc);
|
|
182
|
+
unlockedKeys.set(id, priv);
|
|
183
|
+
return { address: w.address };
|
|
184
|
+
}
|
|
185
|
+
export function lockWallet(id) { unlockedKeys.delete(id); }
|
|
186
|
+
export async function grantWallet(id, agentId, allow) {
|
|
187
|
+
const list = await loadWallets();
|
|
188
|
+
const w = list.find((x) => x.id === id);
|
|
189
|
+
if (!w)
|
|
190
|
+
throw new Error('钱包不存在');
|
|
191
|
+
const set = new Set(w.allowedAgents || []);
|
|
192
|
+
if (allow)
|
|
193
|
+
set.add(agentId);
|
|
194
|
+
else
|
|
195
|
+
set.delete(agentId);
|
|
196
|
+
w.allowedAgents = Array.from(set);
|
|
197
|
+
await saveWallets(list);
|
|
198
|
+
return { ok: true, allowedAgents: w.allowedAgents };
|
|
199
|
+
}
|
|
200
|
+
/** 某本地智能体可调用的钱包 (仅其被授予的) — agent 工具使用 */
|
|
201
|
+
export async function walletForAgent(agentId) {
|
|
202
|
+
const list = await loadWallets();
|
|
203
|
+
const mine = list.filter((w) => (w.allowedAgents || []).includes(agentId));
|
|
204
|
+
return {
|
|
205
|
+
exists: mine.length > 0,
|
|
206
|
+
agentId,
|
|
207
|
+
wallets: mine.map((w) => ({ id: w.id, name: w.name, address: w.address, unlocked: unlockedKeys.has(w.id) })),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
export async function walletBalance(id, rpcUrl) {
|
|
211
|
+
const list = await loadWallets();
|
|
212
|
+
let w;
|
|
213
|
+
if (id)
|
|
214
|
+
w = list.find((x) => x.id === id);
|
|
215
|
+
else
|
|
216
|
+
w = list.filter((x) => unlockedKeys.has(x.id))[0] || list[0];
|
|
217
|
+
if (!w)
|
|
218
|
+
throw new Error('钱包未创建');
|
|
219
|
+
const res = await fetch(rpcUrl || DEFAULT_RPC, {
|
|
220
|
+
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
221
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_getBalance', params: [w.address, 'latest'] }),
|
|
222
|
+
});
|
|
223
|
+
const j = await res.json();
|
|
224
|
+
const wei = BigInt(j && j.result ? String(j.result) : '0x0');
|
|
225
|
+
return (Number(wei) / 1e18).toFixed(6) + ' ETH';
|
|
226
|
+
}
|