@bolloon/bolloon-agent 0.5.3 → 0.5.4
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/dist/agents/pi-sdk-tools.js +67 -15
- package/dist/agents/pi-sdk.js +7 -1
- package/dist/agents/session-store.js +144 -3
- package/dist/agents/wallet-tools.js +63 -0
- package/dist/cli/ink-app.js +79 -27
- package/dist/cli/input-history.js +119 -0
- package/dist/cli/loading-tui.js +66 -6
- package/dist/cli/log-gate.js +39 -1
- package/dist/cli/session-summary.js +129 -0
- package/dist/cli/session-tree.js +141 -0
- package/dist/cli/status-segments.js +70 -0
- package/dist/cli/stores.js +16 -0
- package/dist/cli/wallet-store.js +159 -0
- package/dist/cli/wallet-table.js +46 -0
- package/dist/cli-entry.js +22 -0
- package/dist/electron-build/agents/pi-sdk-tools.js +68 -15
- package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk.js +7 -1
- package/dist/electron-build/agents/pi-sdk.js.map +1 -1
- package/dist/electron-build/agents/session-store.js +144 -3
- package/dist/electron-build/agents/session-store.js.map +1 -1
- package/dist/electron-build/agents/wallet-tools.js +102 -0
- package/dist/electron-build/agents/wallet-tools.js.map +1 -0
- package/dist/electron-build/cli/wallet-store.js +200 -0
- package/dist/electron-build/cli/wallet-store.js.map +1 -0
- package/dist/electron-build/llm/pi-ai.js +25 -0
- package/dist/electron-build/llm/pi-ai.js.map +1 -1
- package/dist/index.js +826 -101
- package/dist/llm/pi-ai.js +23 -0
- package/dist/web/bolloon-web.json +3 -3
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wallet-store.ts — bolloon 自己的钱包台账: **一个目录, 一钱包一文件** (leo 2026-09-30)
|
|
3
|
+
*
|
|
4
|
+
* 事实与边界:
|
|
5
|
+
* · agent 工具 `wallet_create` / `wallet_import` 只**生成/解析**钱包, **从不落盘**
|
|
6
|
+
* (`constraint-runtime/src/tools/WalletTools/createWallet.ts` 纯 `Wallet.createRandom()`) ⇒
|
|
7
|
+
* `/wallet` 一直是「钱包 (0)」不是路径写错, 是**从来没写过文件**。
|
|
8
|
+
* · 台账目录 = `~/.bolloon/wallets/`, **一钱包一文件** `<名字>.json` (**0600**)。
|
|
9
|
+
* 放这儿而不是仓库里: 仓库是公开的 (`private:false`), 私钥进 git 就完了。
|
|
10
|
+
* · 文件里允许有私钥/助记词 (签名要用), 但**命令输出永不回显** —— 只给地址 + 路径。
|
|
11
|
+
* · 不读别的 agent 的钱包目录 (`~/.hermes/wallets/…`): `chain-config.ts` 定的硬规则 ——
|
|
12
|
+
* 那不属于本进程, 读了就是越权。要收编必须由**用户显式给路径** (`/wallet import <path>`)。
|
|
13
|
+
* · 老的单文件台账 (`~/.bolloon/wallets.json`) 若存在 ⇒ 一次性拆进目录, 原文件改名 `.migrated`
|
|
14
|
+
* (不删: 用户的数据, 出问题能翻回去)。
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from 'fs/promises';
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
/** 一钱包一文件所在目录 */
|
|
19
|
+
export function walletsDir() {
|
|
20
|
+
return path.join(process.env.HOME || '/tmp', '.bolloon', 'wallets');
|
|
21
|
+
}
|
|
22
|
+
/** 文件名安全化 (只保留字母数字与 - _ .) */
|
|
23
|
+
function safeName(name, fallback = 'wallet') {
|
|
24
|
+
const s = String(name || '').trim().replace(/[^A-Za-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
25
|
+
return s || fallback;
|
|
26
|
+
}
|
|
27
|
+
async function readOne(file) {
|
|
28
|
+
try {
|
|
29
|
+
const raw = JSON.parse(await fs.readFile(file, 'utf-8'));
|
|
30
|
+
const address = raw?.address;
|
|
31
|
+
if (typeof address !== 'string' || !address.startsWith('0x'))
|
|
32
|
+
return null;
|
|
33
|
+
return {
|
|
34
|
+
name: String(raw?.name || path.basename(file).replace(/\.json$/, '')),
|
|
35
|
+
address,
|
|
36
|
+
network: String(raw?.network || '—'),
|
|
37
|
+
source: raw?.source === 'imported' ? 'imported' : 'created',
|
|
38
|
+
createdAt: String(raw?.createdAt || ''),
|
|
39
|
+
privateKey: typeof raw?.privateKey === 'string' ? raw.privateKey : undefined,
|
|
40
|
+
mnemonic: typeof raw?.mnemonic === 'string' ? raw.mnemonic : undefined,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null; // 坏文件不挡列表 —— 但下面 loadWallets 会把"读不了的"计数报出去
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** 老单文件台账拆进目录 (一次性; 原文件改名 .migrated 保留) */
|
|
48
|
+
async function migrateLegacySingleFile(dir) {
|
|
49
|
+
const legacy = path.join(process.env.HOME || '/tmp', '.bolloon', 'wallets.json');
|
|
50
|
+
let raw;
|
|
51
|
+
try {
|
|
52
|
+
raw = JSON.parse(await fs.readFile(legacy, 'utf-8'));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
const list = Array.isArray(raw) ? raw : Array.isArray(raw?.wallets) ? raw.wallets : [];
|
|
58
|
+
let n = 0;
|
|
59
|
+
for (const w of list) {
|
|
60
|
+
if (!w?.address)
|
|
61
|
+
continue;
|
|
62
|
+
const rec = {
|
|
63
|
+
name: safeName(w.name || `wallet-${n + 1}`), address: String(w.address),
|
|
64
|
+
network: String(w.network || '—'), source: w.source === 'imported' ? 'imported' : 'created',
|
|
65
|
+
createdAt: String(w.createdAt || new Date().toISOString()),
|
|
66
|
+
privateKey: typeof w.privateKey === 'string' ? w.privateKey : undefined,
|
|
67
|
+
mnemonic: typeof w.mnemonic === 'string' ? w.mnemonic : undefined,
|
|
68
|
+
};
|
|
69
|
+
await writeWalletFile(dir, rec);
|
|
70
|
+
n++;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
await fs.rename(legacy, `${legacy}.migrated`);
|
|
74
|
+
}
|
|
75
|
+
catch { /* 改名失败就留着, 下次再试 */ }
|
|
76
|
+
return n;
|
|
77
|
+
}
|
|
78
|
+
async function writeWalletFile(dir, rec) {
|
|
79
|
+
await fs.mkdir(dir, { recursive: true });
|
|
80
|
+
const file = path.join(dir, `${safeName(rec.name)}.json`);
|
|
81
|
+
const tmp = `${file}.tmp`;
|
|
82
|
+
await fs.writeFile(tmp, JSON.stringify(rec, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
83
|
+
await fs.rename(tmp, file);
|
|
84
|
+
try {
|
|
85
|
+
await fs.chmod(file, 0o600);
|
|
86
|
+
}
|
|
87
|
+
catch { /* 已足够 */ }
|
|
88
|
+
return file;
|
|
89
|
+
}
|
|
90
|
+
/** 迁移只做一次 (模块级旗标) —— 每次进程首次读台账时试一次 */
|
|
91
|
+
let legacyMigrated = false;
|
|
92
|
+
/** 读全部钱包 (一钱包一文件, 按文件名排序) */
|
|
93
|
+
export async function loadWallets() {
|
|
94
|
+
const dir = walletsDir();
|
|
95
|
+
await fs.mkdir(dir, { recursive: true }).catch(() => { });
|
|
96
|
+
// 老单文件台账: 每进程首次读的时候试一次迁移 (旗标防重复; 迁移里自己判断有没有老文件)
|
|
97
|
+
if (!legacyMigrated) {
|
|
98
|
+
legacyMigrated = true;
|
|
99
|
+
const n = await migrateLegacySingleFile(dir);
|
|
100
|
+
if (n > 0) { /* 迁移来的文件就在下面一起读 */ }
|
|
101
|
+
}
|
|
102
|
+
let names = [];
|
|
103
|
+
try {
|
|
104
|
+
names = (await fs.readdir(dir)).filter(n => n.endsWith('.json') && !n.endsWith('.tmp')).sort();
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
throw new Error(`钱包目录读不了 (${dir}): ${String(e?.message || e).slice(0, 120)}`);
|
|
108
|
+
}
|
|
109
|
+
const out = [];
|
|
110
|
+
for (const n of names) {
|
|
111
|
+
const rec = await readOne(path.join(dir, n));
|
|
112
|
+
if (rec)
|
|
113
|
+
out.push(rec);
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 写一个钱包进目录。
|
|
119
|
+
* · 同地址**不新建文件**: 合并进那条 (不覆盖已有私钥/助记词)
|
|
120
|
+
* · 名字撞了就自动加后缀 (-2, -3 …)
|
|
121
|
+
*/
|
|
122
|
+
export async function addWallet(rec) {
|
|
123
|
+
const dir = walletsDir();
|
|
124
|
+
const existing = await loadWallets();
|
|
125
|
+
const dup = existing.find(w => w.address.toLowerCase() === rec.address.toLowerCase());
|
|
126
|
+
if (dup) {
|
|
127
|
+
const merged = {
|
|
128
|
+
...rec,
|
|
129
|
+
name: dup.name, // 保留原来的文件名/名字
|
|
130
|
+
privateKey: dup.privateKey || rec.privateKey,
|
|
131
|
+
mnemonic: dup.mnemonic || rec.mnemonic,
|
|
132
|
+
createdAt: dup.createdAt || rec.createdAt,
|
|
133
|
+
};
|
|
134
|
+
const file = await writeWalletFile(dir, merged);
|
|
135
|
+
return { rec: merged, replaced: true, file };
|
|
136
|
+
}
|
|
137
|
+
let name = safeName(rec.name);
|
|
138
|
+
const used = new Set(existing.map(w => w.name));
|
|
139
|
+
for (let i = 2; used.has(name); i++)
|
|
140
|
+
name = `${safeName(rec.name)}-${i}`;
|
|
141
|
+
const out = { ...rec, name };
|
|
142
|
+
const file = await writeWalletFile(dir, out);
|
|
143
|
+
return { rec: out, replaced: false, file };
|
|
144
|
+
}
|
|
145
|
+
/** 按 # / 地址 / 名字挑一个 */
|
|
146
|
+
export function pickWallet(list, spec) {
|
|
147
|
+
if (!spec)
|
|
148
|
+
return list[0];
|
|
149
|
+
const n = Number(spec);
|
|
150
|
+
if (Number.isFinite(n) && n >= 1)
|
|
151
|
+
return list[n - 1];
|
|
152
|
+
if (spec.startsWith('0x'))
|
|
153
|
+
return list.find(w => w.address.toLowerCase() === spec.toLowerCase());
|
|
154
|
+
return list.find(w => w.name === spec);
|
|
155
|
+
}
|
|
156
|
+
/** 地址缩写 (单行显示用) */
|
|
157
|
+
export function shortAddr(a) {
|
|
158
|
+
return a && a.length > 12 ? `${a.slice(0, 6)}…${a.slice(-4)}` : String(a || '?');
|
|
159
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wallet-table.ts — `/wallet` 的钱包表格渲染 (leo 2026-09-30: 「钱包格式也统一一个表格」)
|
|
3
|
+
*
|
|
4
|
+
* 为什么单独抽出来: 表格的**对齐/裁宽**必须能确定性验证 —— 之前它内联在 CLI 的 /wallet 分支里,
|
|
5
|
+
* 只能靠 PTY 抓包看 (那工具这轮抽风 6 次只成 2 次), 验证变成了碰运气。
|
|
6
|
+
* 抽成纯函数 (进 list, 出 string[] 无 ANSI) ⇒ 可以直接跑、直接看列宽对不对 (含中文名列)。
|
|
7
|
+
*
|
|
8
|
+
* 与 `/sessions` 同一套口径: 列宽按终端宽动态分配; 中文按**显示宽**对齐 (dispWidth);
|
|
9
|
+
* 地址优先给满 42 字符 (能直接复制), 列宽不足才退回缩写 (0x1234…abcd) —— 不截半截地址。
|
|
10
|
+
*/
|
|
11
|
+
import { dispWidth, truncate } from './loading-tui.js';
|
|
12
|
+
/**
|
|
13
|
+
* 返回表格行 (无 ANSI): [表头, 分隔线, ...数据行]
|
|
14
|
+
* 调用方负责加颜色前缀/缩进。
|
|
15
|
+
*/
|
|
16
|
+
export function renderWalletTable(list, opts) {
|
|
17
|
+
const zh = opts.lang !== 'en';
|
|
18
|
+
const PAD = (v, w) => v + ' '.repeat(Math.max(0, w - dispWidth(v)));
|
|
19
|
+
const avail = Math.max(60, opts.width - 2);
|
|
20
|
+
const wN = 3, wChain = 8, wSrc = 6, wDate = 10;
|
|
21
|
+
const fixed = wN + wChain + wSrc + wDate + 5; // 5 = 列间空格
|
|
22
|
+
const wAddr = Math.max(13, Math.min(42, Math.floor((avail - fixed) * 0.55)));
|
|
23
|
+
const wName = Math.max(12, avail - fixed - wAddr);
|
|
24
|
+
const cols = [wN, wName, wAddr, wChain, wSrc, wDate];
|
|
25
|
+
const hdr = [
|
|
26
|
+
PAD('#', wN),
|
|
27
|
+
PAD(zh ? '名字' : 'Name', wName),
|
|
28
|
+
PAD(zh ? '地址' : 'Address', wAddr),
|
|
29
|
+
PAD(zh ? '链' : 'Chain', wChain),
|
|
30
|
+
PAD(zh ? '来源' : 'Source', wSrc),
|
|
31
|
+
PAD(zh ? '建立' : 'Created', wDate),
|
|
32
|
+
].join(' ');
|
|
33
|
+
const rule = cols.map(w => '─'.repeat(w)).join(' ');
|
|
34
|
+
const rows = list.map((w, k) => {
|
|
35
|
+
const addr = wAddr >= 42 ? w.address : opts.shortAddr(w.address);
|
|
36
|
+
return [
|
|
37
|
+
PAD(String(k + 1), wN),
|
|
38
|
+
PAD(truncate(w.name, wName), wName),
|
|
39
|
+
PAD(truncate(addr, wAddr), wAddr),
|
|
40
|
+
PAD(truncate(w.network || '—', wChain), wChain),
|
|
41
|
+
PAD(w.source === 'imported' ? (zh ? '导入' : 'imported') : (zh ? '生成' : 'created'), wSrc),
|
|
42
|
+
PAD(String(w.createdAt).slice(0, 10), wDate),
|
|
43
|
+
].join(' ');
|
|
44
|
+
});
|
|
45
|
+
return [hdr, rule, ...rows];
|
|
46
|
+
}
|
package/dist/cli-entry.js
CHANGED
|
@@ -21,6 +21,8 @@ import { printBanner } from './cli/loading-tui.js';
|
|
|
21
21
|
import { discoverEngines, delegateToEngine } from './external-engines/index.js';
|
|
22
22
|
import { x402CheckBalance, x402Fetch } from './agents/x402/x402Pay.js';
|
|
23
23
|
import { runVersionCommand, runUpdateCommand, runDoctorCommand, runRuntimeCommand, UPDATE_HELP } from './cli/update-commands.js';
|
|
24
|
+
import { installStartupLogGate } from './cli/log-gate.js';
|
|
25
|
+
import { waitForTermSize } from './cli/loading-tui.js';
|
|
24
26
|
import { collectVersionInfo } from './utils/version-info.js';
|
|
25
27
|
// 2026-09-27: 启动前言的默认静默 + 显式查询命令 (与 `src/index.ts` 同一套判断)
|
|
26
28
|
import { startupPreambleVisible } from './cli/startup-notice.js';
|
|
@@ -965,6 +967,26 @@ async function main() {
|
|
|
965
967
|
await startWebServer(args);
|
|
966
968
|
break;
|
|
967
969
|
case 'cli':
|
|
970
|
+
/**
|
|
971
|
+
* 2026-09-30 修复 (leo 报「开启过程的日志没去掉」): 启动日志门原先在 `index.ts` 很靠后的位置才装,
|
|
972
|
+
* 于是**早于它**发生的第三方输出会直接落到 tty, 出现在启动面板之前 (实测 SDK 的 ipfs 装配会打
|
|
973
|
+
* `[warn]: ipfs init 失败…` 两行 + 它自己的子进程输出, 共 4 行泄漏).
|
|
974
|
+
* 门覆盖 `process.stdout.write` / `process.stderr.write` / `console.*` 三层, 且**是单例**
|
|
975
|
+
* (index.ts 里那次 `installStartupLogGate` 会直接复用它) ⇒ 在这里提前装, 行为不变、只是更早生效.
|
|
976
|
+
* 与 index 侧同一套跳过条件: `--json` (stdout 是机器数据本体) / 一次性工具 / 命令式诊断 不装门.
|
|
977
|
+
*/
|
|
978
|
+
{
|
|
979
|
+
const a = args;
|
|
980
|
+
const isOneShotTool = !!(a.tool || a.prompt);
|
|
981
|
+
const isCommandLike = !!(a.supervise || a.setupStatus || a.setupResume || a.setupRepair
|
|
982
|
+
|| a.setupReconfigure || a.setupTest);
|
|
983
|
+
if (!a.json && !isOneShotTool && !isCommandLike) {
|
|
984
|
+
installStartupLogGate({ mode: 'cli-interactive', args: process.argv.slice(2) });
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
// 2026-09-30: 先等真实终端尺寸就位再出第一帧 —— 否则面板会先按兜底 80 列渲染一次,
|
|
988
|
+
// 拿到真尺寸后再渲染一次, 而静态输出覆盖不掉旧内容 ⇒ 顶框被重复打印 + 宽度乱变.
|
|
989
|
+
await waitForTermSize();
|
|
968
990
|
await startCLI(args);
|
|
969
991
|
break;
|
|
970
992
|
case 'engine':
|
|
@@ -37,11 +37,13 @@ exports.IdempotencyCache = exports.SIDE_EFFECT_TOOLS = void 0;
|
|
|
37
37
|
exports.runCodeSnippet = runCodeSnippet;
|
|
38
38
|
exports.runTerminalCommand = runTerminalCommand;
|
|
39
39
|
exports.registerBuiltinTools = registerBuiltinTools;
|
|
40
|
+
exports.kuboAlreadyRunning = kuboAlreadyRunning;
|
|
40
41
|
exports.ensureKuboReady = ensureKuboReady;
|
|
41
42
|
exports.kuboApi = kuboApi;
|
|
42
43
|
exports.registerWalletTools = registerWalletTools;
|
|
43
44
|
exports.setupInboxListener = setupInboxListener;
|
|
44
45
|
const fs = __importStar(require("fs/promises"));
|
|
46
|
+
const wallet_tools_js_1 = require("./wallet-tools.js");
|
|
45
47
|
const fsSync = __importStar(require("fs"));
|
|
46
48
|
const path = __importStar(require("path"));
|
|
47
49
|
const version_info_js_1 = require("../utils/version-info.js");
|
|
@@ -3023,13 +3025,16 @@ function registerBuiltinTools(ctx) {
|
|
|
3023
3025
|
const { KeyManager } = await Promise.resolve().then(() => __importStar(require('@diap/sdk')));
|
|
3024
3026
|
const kp = KeyManager.fromPrivateKey(Buffer.from(identity.privateKey, 'hex'));
|
|
3025
3027
|
const displayName = args.name ? String(args.name) : agentId || 'bolloon-agent';
|
|
3026
|
-
// 1. 确保本地 Kubo (自动安装 + 启动)
|
|
3028
|
+
// 1. 确保本地 Kubo (自动安装 + 启动) —— **已经在跑就别调 SDK 那次注定失败的 init**
|
|
3029
|
+
// (`sdk` 后面发布 IPNS 还要用, 所以模块导入留在函数作用域, 只把这一次调用关进短路里)
|
|
3027
3030
|
const sdk = await Promise.resolve().then(() => __importStar(require('@diap/sdk')));
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3031
|
+
if (!(await kuboAlreadyRunning())) {
|
|
3032
|
+
const checkKuboSetup = sdk.checkKuboSetup;
|
|
3033
|
+
if (typeof checkKuboSetup === 'function') {
|
|
3034
|
+
const setup = await checkKuboSetup(true, true);
|
|
3035
|
+
if (!setup?.ready || !setup?.daemonRunning) {
|
|
3036
|
+
return { success: false, error: '本地 Kubo 不可用 (自动安装失败), 无法发布到 IPFS' };
|
|
3037
|
+
}
|
|
3033
3038
|
}
|
|
3034
3039
|
}
|
|
3035
3040
|
// 2. 注册 agent → 上传 DID 文档 → CID
|
|
@@ -3485,7 +3490,26 @@ function registerBuiltinTools(ctx) {
|
|
|
3485
3490
|
}
|
|
3486
3491
|
// ─── IPFS/IPNS 通用 helper (2026-08-04) ─────────────────────────────────────
|
|
3487
3492
|
// 复用 publish_did 的 checkKuboSetup 自动安装/启动本地 Kubo (darwin-arm64 v0.28.0)
|
|
3493
|
+
/**
|
|
3494
|
+
* Kubo 本地节点已经在跑吗 (只读探测: POST /api/v0/version —— Kubo 只接受 POST).
|
|
3495
|
+
*
|
|
3496
|
+
* 2026-09-30 新增 (leo 报「开启过程的日志没去掉」): SDK 的 `checkKuboSetup(true, true)` 即使守护进程
|
|
3497
|
+
* 已经在跑, 仍会跑一遍 `ipfs init` ⇒ 抛 "daemon is running" ⇒ **打一行 [warn](消息里含"失败"二字,
|
|
3498
|
+
* 于是被启动日志门按"错误不许被吞"放行到屏上)** 外加子进程输出, 两行都落在启动面板之前.
|
|
3499
|
+
* 启动路径上所有调用点都先用这个探测短路: 已经在跑就没有任何可装配的.
|
|
3500
|
+
*/
|
|
3501
|
+
async function kuboAlreadyRunning(timeoutMs = 1500) {
|
|
3502
|
+
try {
|
|
3503
|
+
await kuboApi('/api/v0/version', undefined, timeoutMs);
|
|
3504
|
+
return true;
|
|
3505
|
+
}
|
|
3506
|
+
catch {
|
|
3507
|
+
return false;
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3488
3510
|
async function ensureKuboReady() {
|
|
3511
|
+
if (await kuboAlreadyRunning())
|
|
3512
|
+
return; // 已在跑 ⇒ 没有可装配的, 也别触发那次注定失败的 init
|
|
3489
3513
|
const sdk = await Promise.resolve().then(() => __importStar(require('@diap/sdk')));
|
|
3490
3514
|
const checkKuboSetup = sdk.checkKuboSetup;
|
|
3491
3515
|
if (typeof checkKuboSetup === 'function') {
|
|
@@ -3525,9 +3549,24 @@ function registerWalletTools(ctx) {
|
|
|
3525
3549
|
parameters: {},
|
|
3526
3550
|
execute: async () => {
|
|
3527
3551
|
try {
|
|
3528
|
-
const { createWallet } = await
|
|
3552
|
+
const { createWallet } = await (0, wallet_tools_js_1.loadWalletTool)('createWallet');
|
|
3529
3553
|
const r = await createWallet();
|
|
3530
|
-
|
|
3554
|
+
// 2026-09-30: **落台账** (以前只生成不存 ⇒ /wallet 永远「钱包 (0)」)。
|
|
3555
|
+
// 落 ~/.bolloon/wallets/<名字>.json (0600); 输出只给地址 + 路径, 私钥/助记词不回显。
|
|
3556
|
+
let savedPath = '';
|
|
3557
|
+
try {
|
|
3558
|
+
const { addWallet } = await Promise.resolve().then(() => __importStar(require('../cli/wallet-store.js')));
|
|
3559
|
+
const { file } = await addWallet({
|
|
3560
|
+
name: `agent-${new Date().toISOString().slice(0, 19).replace(/[:T]/g, '')}`,
|
|
3561
|
+
address: r.address, network: '—', source: 'created',
|
|
3562
|
+
createdAt: r.createdAt || new Date().toISOString(), privateKey: r.privateKey, mnemonic: r.mnemonic,
|
|
3563
|
+
});
|
|
3564
|
+
savedPath = file;
|
|
3565
|
+
}
|
|
3566
|
+
catch (e) {
|
|
3567
|
+
return { success: true, output: `⚠️ 钱包已生成但**没落台账**: ${String(e?.message || e).slice(0, 120)}\n address: ${r.address}\n (私钥只在本次输出里, 请立刻自己存好)` };
|
|
3568
|
+
}
|
|
3569
|
+
return { success: true, output: `✅ 钱包创建成功 (已落台账)\n address: ${r.address}\n 台账: ${savedPath} (0600 — 私钥/助记词已存, 不回显)\n createdAt: ${r.createdAt}` };
|
|
3531
3570
|
}
|
|
3532
3571
|
catch (e) {
|
|
3533
3572
|
return { success: false, error: `创建失败: ${String(e.message || e)}` };
|
|
@@ -3540,9 +3579,23 @@ function registerWalletTools(ctx) {
|
|
|
3540
3579
|
parameters: { mnemonic: '可选, 12/15/18/21/24 词助记词', privateKey: '可选, 0x 开头的私钥' },
|
|
3541
3580
|
execute: async (args) => {
|
|
3542
3581
|
try {
|
|
3543
|
-
const { importWallet } = await
|
|
3582
|
+
const { importWallet } = await (0, wallet_tools_js_1.loadWalletTool)('importWallet');
|
|
3544
3583
|
const r = await importWallet({ mnemonic: args.mnemonic, privateKey: args.privateKey });
|
|
3545
|
-
|
|
3584
|
+
// 2026-09-30: 同 wallet_create —— 导入也落台账 (0600), 输出不回显私钥
|
|
3585
|
+
let impPath = '';
|
|
3586
|
+
try {
|
|
3587
|
+
const { addWallet } = await Promise.resolve().then(() => __importStar(require('../cli/wallet-store.js')));
|
|
3588
|
+
const { file } = await addWallet({
|
|
3589
|
+
name: `imported-${new Date().toISOString().slice(0, 19).replace(/[:T]/g, '')}`,
|
|
3590
|
+
address: r.address, network: '—', source: 'imported',
|
|
3591
|
+
createdAt: new Date().toISOString(), privateKey: args.privateKey, mnemonic: args.mnemonic,
|
|
3592
|
+
});
|
|
3593
|
+
impPath = file;
|
|
3594
|
+
}
|
|
3595
|
+
catch (e) {
|
|
3596
|
+
return { success: true, output: `⚠️ 钱包已导入但**没落台账**: ${String(e?.message || e).slice(0, 120)}\n address: ${r.address}\n (私钥请自己存好, 不会再有第二遍)` };
|
|
3597
|
+
}
|
|
3598
|
+
return { success: true, output: `✅ 钱包导入成功 (已落台账)\n address: ${r.address}\n 台账: ${impPath} (0600 — 私钥已存, 不回显)\n source: ${r.source}` };
|
|
3546
3599
|
}
|
|
3547
3600
|
catch (e) {
|
|
3548
3601
|
return { success: false, error: `导入失败: ${String(e.message || e)}` };
|
|
@@ -3555,7 +3608,7 @@ function registerWalletTools(ctx) {
|
|
|
3555
3608
|
parameters: { address: '0x 开头的 EVM 地址 (必填)', rpcUrl: '可选 RPC URL (默认 eth.llamarpc.com)' },
|
|
3556
3609
|
execute: async (args) => {
|
|
3557
3610
|
try {
|
|
3558
|
-
const { getBalance } = await
|
|
3611
|
+
const { getBalance } = await (0, wallet_tools_js_1.loadWalletTool)('getBalance');
|
|
3559
3612
|
const r = await getBalance({ address: String(args.address), rpcUrl: args.rpcUrl });
|
|
3560
3613
|
return { success: true, output: `💰 ${r.address}\n ${r.balanceEth} ${r.symbol} (${r.balance} wei)` };
|
|
3561
3614
|
}
|
|
@@ -3570,7 +3623,7 @@ function registerWalletTools(ctx) {
|
|
|
3570
3623
|
parameters: { message: '要签名的消息 (必填)', privateKey: '0x 开头的私钥 (必填)' },
|
|
3571
3624
|
execute: async (args) => {
|
|
3572
3625
|
try {
|
|
3573
|
-
const { signMessage } = await
|
|
3626
|
+
const { signMessage } = await (0, wallet_tools_js_1.loadWalletTool)('signMessage');
|
|
3574
3627
|
const r = await signMessage({ message: String(args.message), privateKey: String(args.privateKey) });
|
|
3575
3628
|
return { success: true, output: `✅ 签名完成:\n address: ${r.address}\n message: ${r.message}\n signature: ${r.signature}` };
|
|
3576
3629
|
}
|
|
@@ -3585,7 +3638,7 @@ function registerWalletTools(ctx) {
|
|
|
3585
3638
|
parameters: { privateKey: '私钥 (必填)', to: '接收地址 (必填)', value: '发送 wei 数量 (必填)', data: '可选 0x 开头的 calldata', rpcUrl: '可选 RPC URL' },
|
|
3586
3639
|
execute: async (args) => {
|
|
3587
3640
|
try {
|
|
3588
|
-
const { sendTransaction } = await
|
|
3641
|
+
const { sendTransaction } = await (0, wallet_tools_js_1.loadWalletTool)('sendTransaction');
|
|
3589
3642
|
const r = await sendTransaction({
|
|
3590
3643
|
privateKey: String(args.privateKey),
|
|
3591
3644
|
to: String(args.to),
|
|
@@ -3606,7 +3659,7 @@ function registerWalletTools(ctx) {
|
|
|
3606
3659
|
parameters: { privateKey: '私钥 (必填)', tokenAddress: 'ERC20 合约地址 (必填)', to: '接收地址 (必填)', amount: 'token 数量', decimals: '可选 token decimals', rpcUrl: '可选 RPC URL' },
|
|
3607
3660
|
execute: async (args) => {
|
|
3608
3661
|
try {
|
|
3609
|
-
const { transferToken } = await
|
|
3662
|
+
const { transferToken } = await (0, wallet_tools_js_1.loadWalletTool)('transferToken');
|
|
3610
3663
|
const r = await transferToken({
|
|
3611
3664
|
privateKey: String(args.privateKey),
|
|
3612
3665
|
tokenAddress: String(args.tokenAddress),
|
|
@@ -3628,7 +3681,7 @@ function registerWalletTools(ctx) {
|
|
|
3628
3681
|
parameters: { from: '付款方地址 (必填)', to: '收款方地址 (必填)', amount: '金额 (必填)', interval: '周期 (e.g. daily/weekly/monthly)', token: '可选, 默认 ETH' },
|
|
3629
3682
|
execute: async (args) => {
|
|
3630
3683
|
try {
|
|
3631
|
-
const mod = await
|
|
3684
|
+
const mod = await (0, wallet_tools_js_1.loadWalletTool)('autoPay');
|
|
3632
3685
|
const fn = mod.autoPay || mod.setAutoPay || mod.default;
|
|
3633
3686
|
if (!fn)
|
|
3634
3687
|
return { success: false, error: 'autoPay 接口未找到' };
|