@vultisig/cli 0.5.0 → 0.7.0

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.
Files changed (4) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +39 -0
  3. package/dist/index.js +2934 -258
  4. package/package.json +18 -17
package/dist/index.js CHANGED
@@ -11,9 +11,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
11
11
  if (typeof require !== "undefined") return require.apply(this, arguments);
12
12
  throw Error('Dynamic require of "' + x + '" is not supported');
13
13
  });
14
+ var __esm = (fn, res) => function __init() {
15
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
16
+ };
14
17
  var __commonJS = (cb, mod) => function __require2() {
15
18
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
19
  };
20
+ var __export = (target, all) => {
21
+ for (var name in all)
22
+ __defProp(target, name, { get: all[name], enumerable: true });
23
+ };
17
24
  var __copyProps = (to, from, except, desc) => {
18
25
  if (from && typeof from === "object" || typeof from === "function") {
19
26
  for (let key of __getOwnPropNames(from))
@@ -1101,11 +1108,333 @@ var require_main = __commonJS({
1101
1108
  }
1102
1109
  });
1103
1110
 
1111
+ // ../../node_modules/@noble/hashes/esm/_u64.js
1112
+ function fromBig(n, le = false) {
1113
+ if (le)
1114
+ return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
1115
+ return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
1116
+ }
1117
+ function split(lst, le = false) {
1118
+ const len = lst.length;
1119
+ let Ah = new Uint32Array(len);
1120
+ let Al = new Uint32Array(len);
1121
+ for (let i = 0; i < len; i++) {
1122
+ const { h, l } = fromBig(lst[i], le);
1123
+ [Ah[i], Al[i]] = [h, l];
1124
+ }
1125
+ return [Ah, Al];
1126
+ }
1127
+ var U32_MASK64, _32n, rotlSH, rotlSL, rotlBH, rotlBL;
1128
+ var init_u64 = __esm({
1129
+ "../../node_modules/@noble/hashes/esm/_u64.js"() {
1130
+ U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
1131
+ _32n = /* @__PURE__ */ BigInt(32);
1132
+ rotlSH = (h, l, s) => h << s | l >>> 32 - s;
1133
+ rotlSL = (h, l, s) => l << s | h >>> 32 - s;
1134
+ rotlBH = (h, l, s) => l << s - 32 | h >>> 64 - s;
1135
+ rotlBL = (h, l, s) => h << s - 32 | l >>> 64 - s;
1136
+ }
1137
+ });
1138
+
1139
+ // ../../node_modules/@noble/hashes/esm/utils.js
1140
+ function isBytes(a) {
1141
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
1142
+ }
1143
+ function anumber(n) {
1144
+ if (!Number.isSafeInteger(n) || n < 0)
1145
+ throw new Error("positive integer expected, got " + n);
1146
+ }
1147
+ function abytes(b, ...lengths) {
1148
+ if (!isBytes(b))
1149
+ throw new Error("Uint8Array expected");
1150
+ if (lengths.length > 0 && !lengths.includes(b.length))
1151
+ throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
1152
+ }
1153
+ function aexists(instance, checkFinished = true) {
1154
+ if (instance.destroyed)
1155
+ throw new Error("Hash instance has been destroyed");
1156
+ if (checkFinished && instance.finished)
1157
+ throw new Error("Hash#digest() has already been called");
1158
+ }
1159
+ function aoutput(out, instance) {
1160
+ abytes(out);
1161
+ const min = instance.outputLen;
1162
+ if (out.length < min) {
1163
+ throw new Error("digestInto() expects output buffer of length at least " + min);
1164
+ }
1165
+ }
1166
+ function u32(arr) {
1167
+ return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
1168
+ }
1169
+ function clean(...arrays) {
1170
+ for (let i = 0; i < arrays.length; i++) {
1171
+ arrays[i].fill(0);
1172
+ }
1173
+ }
1174
+ function byteSwap(word) {
1175
+ return word << 24 & 4278190080 | word << 8 & 16711680 | word >>> 8 & 65280 | word >>> 24 & 255;
1176
+ }
1177
+ function byteSwap32(arr) {
1178
+ for (let i = 0; i < arr.length; i++) {
1179
+ arr[i] = byteSwap(arr[i]);
1180
+ }
1181
+ return arr;
1182
+ }
1183
+ function utf8ToBytes(str) {
1184
+ if (typeof str !== "string")
1185
+ throw new Error("string expected");
1186
+ return new Uint8Array(new TextEncoder().encode(str));
1187
+ }
1188
+ function toBytes(data) {
1189
+ if (typeof data === "string")
1190
+ data = utf8ToBytes(data);
1191
+ abytes(data);
1192
+ return data;
1193
+ }
1194
+ function createHasher(hashCons) {
1195
+ const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
1196
+ const tmp = hashCons();
1197
+ hashC.outputLen = tmp.outputLen;
1198
+ hashC.blockLen = tmp.blockLen;
1199
+ hashC.create = () => hashCons();
1200
+ return hashC;
1201
+ }
1202
+ function createXOFer(hashCons) {
1203
+ const hashC = (msg, opts) => hashCons(opts).update(toBytes(msg)).digest();
1204
+ const tmp = hashCons({});
1205
+ hashC.outputLen = tmp.outputLen;
1206
+ hashC.blockLen = tmp.blockLen;
1207
+ hashC.create = (opts) => hashCons(opts);
1208
+ return hashC;
1209
+ }
1210
+ var isLE, swap32IfBE, Hash;
1211
+ var init_utils = __esm({
1212
+ "../../node_modules/@noble/hashes/esm/utils.js"() {
1213
+ isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)();
1214
+ swap32IfBE = isLE ? (u) => u : byteSwap32;
1215
+ Hash = class {
1216
+ };
1217
+ }
1218
+ });
1219
+
1220
+ // ../../node_modules/@noble/hashes/esm/sha3.js
1221
+ var sha3_exports = {};
1222
+ __export(sha3_exports, {
1223
+ Keccak: () => Keccak,
1224
+ keccakP: () => keccakP,
1225
+ keccak_224: () => keccak_224,
1226
+ keccak_256: () => keccak_256,
1227
+ keccak_384: () => keccak_384,
1228
+ keccak_512: () => keccak_512,
1229
+ sha3_224: () => sha3_224,
1230
+ sha3_256: () => sha3_256,
1231
+ sha3_384: () => sha3_384,
1232
+ sha3_512: () => sha3_512,
1233
+ shake128: () => shake128,
1234
+ shake256: () => shake256
1235
+ });
1236
+ function keccakP(s, rounds = 24) {
1237
+ const B = new Uint32Array(5 * 2);
1238
+ for (let round = 24 - rounds; round < 24; round++) {
1239
+ for (let x = 0; x < 10; x++)
1240
+ B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
1241
+ for (let x = 0; x < 10; x += 2) {
1242
+ const idx1 = (x + 8) % 10;
1243
+ const idx0 = (x + 2) % 10;
1244
+ const B0 = B[idx0];
1245
+ const B1 = B[idx0 + 1];
1246
+ const Th = rotlH(B0, B1, 1) ^ B[idx1];
1247
+ const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
1248
+ for (let y = 0; y < 50; y += 10) {
1249
+ s[x + y] ^= Th;
1250
+ s[x + y + 1] ^= Tl;
1251
+ }
1252
+ }
1253
+ let curH = s[2];
1254
+ let curL = s[3];
1255
+ for (let t = 0; t < 24; t++) {
1256
+ const shift = SHA3_ROTL[t];
1257
+ const Th = rotlH(curH, curL, shift);
1258
+ const Tl = rotlL(curH, curL, shift);
1259
+ const PI = SHA3_PI[t];
1260
+ curH = s[PI];
1261
+ curL = s[PI + 1];
1262
+ s[PI] = Th;
1263
+ s[PI + 1] = Tl;
1264
+ }
1265
+ for (let y = 0; y < 50; y += 10) {
1266
+ for (let x = 0; x < 10; x++)
1267
+ B[x] = s[y + x];
1268
+ for (let x = 0; x < 10; x++)
1269
+ s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
1270
+ }
1271
+ s[0] ^= SHA3_IOTA_H[round];
1272
+ s[1] ^= SHA3_IOTA_L[round];
1273
+ }
1274
+ clean(B);
1275
+ }
1276
+ var _0n, _1n, _2n, _7n, _256n, _0x71n, SHA3_PI, SHA3_ROTL, _SHA3_IOTA, IOTAS, SHA3_IOTA_H, SHA3_IOTA_L, rotlH, rotlL, Keccak, gen, sha3_224, sha3_256, sha3_384, sha3_512, keccak_224, keccak_256, keccak_384, keccak_512, genShake, shake128, shake256;
1277
+ var init_sha3 = __esm({
1278
+ "../../node_modules/@noble/hashes/esm/sha3.js"() {
1279
+ init_u64();
1280
+ init_utils();
1281
+ _0n = BigInt(0);
1282
+ _1n = BigInt(1);
1283
+ _2n = BigInt(2);
1284
+ _7n = BigInt(7);
1285
+ _256n = BigInt(256);
1286
+ _0x71n = BigInt(113);
1287
+ SHA3_PI = [];
1288
+ SHA3_ROTL = [];
1289
+ _SHA3_IOTA = [];
1290
+ for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
1291
+ [x, y] = [y, (2 * x + 3 * y) % 5];
1292
+ SHA3_PI.push(2 * (5 * y + x));
1293
+ SHA3_ROTL.push((round + 1) * (round + 2) / 2 % 64);
1294
+ let t = _0n;
1295
+ for (let j = 0; j < 7; j++) {
1296
+ R = (R << _1n ^ (R >> _7n) * _0x71n) % _256n;
1297
+ if (R & _2n)
1298
+ t ^= _1n << (_1n << /* @__PURE__ */ BigInt(j)) - _1n;
1299
+ }
1300
+ _SHA3_IOTA.push(t);
1301
+ }
1302
+ IOTAS = split(_SHA3_IOTA, true);
1303
+ SHA3_IOTA_H = IOTAS[0];
1304
+ SHA3_IOTA_L = IOTAS[1];
1305
+ rotlH = (h, l, s) => s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s);
1306
+ rotlL = (h, l, s) => s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s);
1307
+ Keccak = class _Keccak extends Hash {
1308
+ // NOTE: we accept arguments in bytes instead of bits here.
1309
+ constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {
1310
+ super();
1311
+ this.pos = 0;
1312
+ this.posOut = 0;
1313
+ this.finished = false;
1314
+ this.destroyed = false;
1315
+ this.enableXOF = false;
1316
+ this.blockLen = blockLen;
1317
+ this.suffix = suffix;
1318
+ this.outputLen = outputLen;
1319
+ this.enableXOF = enableXOF;
1320
+ this.rounds = rounds;
1321
+ anumber(outputLen);
1322
+ if (!(0 < blockLen && blockLen < 200))
1323
+ throw new Error("only keccak-f1600 function is supported");
1324
+ this.state = new Uint8Array(200);
1325
+ this.state32 = u32(this.state);
1326
+ }
1327
+ clone() {
1328
+ return this._cloneInto();
1329
+ }
1330
+ keccak() {
1331
+ swap32IfBE(this.state32);
1332
+ keccakP(this.state32, this.rounds);
1333
+ swap32IfBE(this.state32);
1334
+ this.posOut = 0;
1335
+ this.pos = 0;
1336
+ }
1337
+ update(data) {
1338
+ aexists(this);
1339
+ data = toBytes(data);
1340
+ abytes(data);
1341
+ const { blockLen, state } = this;
1342
+ const len = data.length;
1343
+ for (let pos = 0; pos < len; ) {
1344
+ const take = Math.min(blockLen - this.pos, len - pos);
1345
+ for (let i = 0; i < take; i++)
1346
+ state[this.pos++] ^= data[pos++];
1347
+ if (this.pos === blockLen)
1348
+ this.keccak();
1349
+ }
1350
+ return this;
1351
+ }
1352
+ finish() {
1353
+ if (this.finished)
1354
+ return;
1355
+ this.finished = true;
1356
+ const { state, suffix, pos, blockLen } = this;
1357
+ state[pos] ^= suffix;
1358
+ if ((suffix & 128) !== 0 && pos === blockLen - 1)
1359
+ this.keccak();
1360
+ state[blockLen - 1] ^= 128;
1361
+ this.keccak();
1362
+ }
1363
+ writeInto(out) {
1364
+ aexists(this, false);
1365
+ abytes(out);
1366
+ this.finish();
1367
+ const bufferOut = this.state;
1368
+ const { blockLen } = this;
1369
+ for (let pos = 0, len = out.length; pos < len; ) {
1370
+ if (this.posOut >= blockLen)
1371
+ this.keccak();
1372
+ const take = Math.min(blockLen - this.posOut, len - pos);
1373
+ out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
1374
+ this.posOut += take;
1375
+ pos += take;
1376
+ }
1377
+ return out;
1378
+ }
1379
+ xofInto(out) {
1380
+ if (!this.enableXOF)
1381
+ throw new Error("XOF is not possible for this instance");
1382
+ return this.writeInto(out);
1383
+ }
1384
+ xof(bytes) {
1385
+ anumber(bytes);
1386
+ return this.xofInto(new Uint8Array(bytes));
1387
+ }
1388
+ digestInto(out) {
1389
+ aoutput(out, this);
1390
+ if (this.finished)
1391
+ throw new Error("digest() was already called");
1392
+ this.writeInto(out);
1393
+ this.destroy();
1394
+ return out;
1395
+ }
1396
+ digest() {
1397
+ return this.digestInto(new Uint8Array(this.outputLen));
1398
+ }
1399
+ destroy() {
1400
+ this.destroyed = true;
1401
+ clean(this.state);
1402
+ }
1403
+ _cloneInto(to) {
1404
+ const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
1405
+ to || (to = new _Keccak(blockLen, suffix, outputLen, enableXOF, rounds));
1406
+ to.state32.set(this.state32);
1407
+ to.pos = this.pos;
1408
+ to.posOut = this.posOut;
1409
+ to.finished = this.finished;
1410
+ to.rounds = rounds;
1411
+ to.suffix = suffix;
1412
+ to.outputLen = outputLen;
1413
+ to.enableXOF = enableXOF;
1414
+ to.destroyed = this.destroyed;
1415
+ return to;
1416
+ }
1417
+ };
1418
+ gen = (suffix, blockLen, outputLen) => createHasher(() => new Keccak(blockLen, suffix, outputLen));
1419
+ sha3_224 = /* @__PURE__ */ (() => gen(6, 144, 224 / 8))();
1420
+ sha3_256 = /* @__PURE__ */ (() => gen(6, 136, 256 / 8))();
1421
+ sha3_384 = /* @__PURE__ */ (() => gen(6, 104, 384 / 8))();
1422
+ sha3_512 = /* @__PURE__ */ (() => gen(6, 72, 512 / 8))();
1423
+ keccak_224 = /* @__PURE__ */ (() => gen(1, 144, 224 / 8))();
1424
+ keccak_256 = /* @__PURE__ */ (() => gen(1, 136, 256 / 8))();
1425
+ keccak_384 = /* @__PURE__ */ (() => gen(1, 104, 384 / 8))();
1426
+ keccak_512 = /* @__PURE__ */ (() => gen(1, 72, 512 / 8))();
1427
+ genShake = (suffix, blockLen, outputLen) => createXOFer((opts = {}) => new Keccak(blockLen, suffix, opts.dkLen === void 0 ? outputLen : opts.dkLen, true));
1428
+ shake128 = /* @__PURE__ */ (() => genShake(31, 168, 128 / 8))();
1429
+ shake256 = /* @__PURE__ */ (() => genShake(31, 136, 256 / 8))();
1430
+ }
1431
+ });
1432
+
1104
1433
  // src/index.ts
1105
1434
  import "dotenv/config";
1106
1435
  import { promises as fs3 } from "node:fs";
1107
- import { parseKeygenQR, Vultisig as Vultisig5 } from "@vultisig/sdk";
1108
- import chalk13 from "chalk";
1436
+ import { parseKeygenQR, Vultisig as Vultisig7 } from "@vultisig/sdk";
1437
+ import chalk14 from "chalk";
1109
1438
  import { program } from "commander";
1110
1439
  import inquirer8 from "inquirer";
1111
1440
 
@@ -2327,6 +2656,66 @@ async function executeBroadcast(ctx2, params) {
2327
2656
  }
2328
2657
  }
2329
2658
 
2659
+ // src/commands/tx-status.ts
2660
+ import { Chain as Chain5, Vultisig as Vultisig5 } from "@vultisig/sdk";
2661
+ var POLL_INTERVAL_MS = 5e3;
2662
+ async function executeTxStatus(ctx2, params) {
2663
+ const vault = await ctx2.ensureActiveVault();
2664
+ if (!Object.values(Chain5).includes(params.chain)) {
2665
+ throw new Error(`Invalid chain: ${params.chain}`);
2666
+ }
2667
+ const spinner = createSpinner("Checking transaction status...");
2668
+ try {
2669
+ let result = await vault.getTxStatus({ chain: params.chain, txHash: params.txHash });
2670
+ if (!params.noWait) {
2671
+ let polls = 1;
2672
+ while (result.status === "pending") {
2673
+ spinner.text = `Transaction pending... (${polls * 5}s)`;
2674
+ await sleep(POLL_INTERVAL_MS);
2675
+ result = await vault.getTxStatus({ chain: params.chain, txHash: params.txHash });
2676
+ polls++;
2677
+ }
2678
+ }
2679
+ spinner.succeed(`Transaction status: ${result.status}`);
2680
+ displayResult(params.chain, params.txHash, result);
2681
+ return result;
2682
+ } catch (error2) {
2683
+ spinner.fail("Failed to check transaction status");
2684
+ throw error2;
2685
+ }
2686
+ }
2687
+ function displayResult(chain, txHash, result) {
2688
+ if (isJsonOutput()) {
2689
+ outputJson({
2690
+ chain,
2691
+ txHash,
2692
+ status: result.status,
2693
+ receipt: result.receipt ? {
2694
+ feeAmount: result.receipt.feeAmount.toString(),
2695
+ feeDecimals: result.receipt.feeDecimals,
2696
+ feeTicker: result.receipt.feeTicker
2697
+ } : void 0,
2698
+ explorerUrl: Vultisig5.getTxExplorerUrl(chain, txHash)
2699
+ });
2700
+ } else {
2701
+ printResult(`Status: ${result.status}`);
2702
+ if (result.receipt) {
2703
+ const fee = formatFee(result.receipt.feeAmount, result.receipt.feeDecimals);
2704
+ printResult(`Fee: ${fee} ${result.receipt.feeTicker}`);
2705
+ }
2706
+ printResult(`Explorer: ${Vultisig5.getTxExplorerUrl(chain, txHash)}`);
2707
+ }
2708
+ }
2709
+ function formatFee(amount, decimals) {
2710
+ const str = amount.toString().padStart(decimals + 1, "0");
2711
+ const whole = str.slice(0, -decimals) || "0";
2712
+ const frac = str.slice(-decimals).replace(/0+$/, "");
2713
+ return frac ? `${whole}.${frac}` : whole;
2714
+ }
2715
+ function sleep(ms) {
2716
+ return new Promise((resolve) => setTimeout(resolve, ms));
2717
+ }
2718
+
2330
2719
  // src/commands/vault-management.ts
2331
2720
  var import_qrcode_terminal4 = __toESM(require_main(), 1);
2332
2721
  import chalk5 from "chalk";
@@ -2344,13 +2733,14 @@ function withAbortSignal(promise, signal) {
2344
2733
  ]);
2345
2734
  }
2346
2735
  async function executeCreateFast(ctx2, options) {
2347
- const { name, password, email, signal } = options;
2736
+ const { name, password, email, signal, twoStep } = options;
2348
2737
  const spinner = createSpinner("Creating vault...");
2349
2738
  const vaultId = await withAbortSignal(
2350
2739
  ctx2.sdk.createFastVault({
2351
2740
  name,
2352
2741
  password,
2353
2742
  email,
2743
+ persistPending: twoStep,
2354
2744
  onProgress: (step) => {
2355
2745
  spinner.text = `${step.message} (${step.progress}%)`;
2356
2746
  }
@@ -2358,6 +2748,17 @@ async function executeCreateFast(ctx2, options) {
2358
2748
  signal
2359
2749
  );
2360
2750
  spinner.succeed(`Vault keys generated: ${name}`);
2751
+ if (twoStep) {
2752
+ success("\n+ Vault created and saved to disk (pending verification)");
2753
+ info(`
2754
+ Vault ID: ${vaultId}`);
2755
+ info("\nA verification code has been sent to your email.");
2756
+ info("To complete setup, run:");
2757
+ printResult(chalk5.cyan(` vultisig verify ${vaultId} --code <OTP>`));
2758
+ info("\nOr to resend the verification email:");
2759
+ printResult(chalk5.cyan(` vultisig verify ${vaultId} --resend --email ${email} --password <password>`));
2760
+ return void 0;
2761
+ }
2361
2762
  warn("\nA verification code has been sent to your email.");
2362
2763
  info("Please check your inbox and enter the code.");
2363
2764
  const MAX_VERIFY_ATTEMPTS = 5;
@@ -3268,7 +3669,7 @@ async function executeSwap(ctx2, options) {
3268
3669
  }
3269
3670
 
3270
3671
  // src/commands/settings.ts
3271
- import { Chain as Chain5, fiatCurrencies as fiatCurrencies2, fiatCurrencyNameRecord as fiatCurrencyNameRecord3 } from "@vultisig/sdk";
3672
+ import { Chain as Chain6, fiatCurrencies as fiatCurrencies2, fiatCurrencyNameRecord as fiatCurrencyNameRecord3 } from "@vultisig/sdk";
3272
3673
  import chalk6 from "chalk";
3273
3674
  import inquirer5 from "inquirer";
3274
3675
  async function executeCurrency(ctx2, newCurrency) {
@@ -3336,7 +3737,7 @@ async function executeAddressBook(ctx2, options = {}) {
3336
3737
  type: "list",
3337
3738
  name: "chain",
3338
3739
  message: "Select chain:",
3339
- choices: Object.values(Chain5)
3740
+ choices: Object.values(Chain6)
3340
3741
  });
3341
3742
  }
3342
3743
  if (!address) {
@@ -3696,130 +4097,2339 @@ function displayDiscountTier(tierInfo) {
3696
4097
  printResult("");
3697
4098
  }
3698
4099
 
3699
- // src/interactive/completer.ts
3700
- import { Chain as Chain6 } from "@vultisig/sdk";
3701
- import fs2 from "fs";
3702
- import path2 from "path";
3703
- var COMMANDS = [
3704
- // Vault management
3705
- "vaults",
3706
- "vault",
3707
- "import",
3708
- "delete",
3709
- "create-from-seedphrase",
3710
- "create",
3711
- "join",
3712
- "info",
3713
- "export",
3714
- // Wallet operations
3715
- "balance",
3716
- "bal",
3717
- "send",
3718
- "portfolio",
3719
- "addresses",
3720
- "chains",
3721
- "tokens",
3722
- // Swap operations
3723
- "swap-chains",
3724
- "swap-quote",
3725
- "swap",
3726
- // Session commands (shell-only)
3727
- "lock",
3728
- "unlock",
3729
- "status",
3730
- // Settings
3731
- "currency",
3732
- "server",
3733
- "address-book",
3734
- // Help
3735
- "help",
3736
- "?",
3737
- // REPL commands
3738
- ".help",
3739
- ".clear",
3740
- ".exit"
3741
- ];
3742
- function createCompleter(ctx2) {
3743
- return function completer(line) {
4100
+ // src/agent/auth.ts
4101
+ init_sha3();
4102
+ import { randomBytes } from "node:crypto";
4103
+ import { Chain as Chain7 } from "@vultisig/sdk";
4104
+ async function authenticateVault(client, vault, password, maxAttempts = 3) {
4105
+ const publicKey = vault.publicKeys.ecdsa;
4106
+ const chainCode = vault.hexChainCode;
4107
+ const ethAddress = await vault.address(Chain7.Ethereum);
4108
+ const nonce = "0x" + randomBytes(16).toString("hex");
4109
+ const expiresAt = new Date(Date.now() + 15 * 60 * 1e3).toISOString();
4110
+ const authMessage = JSON.stringify({
4111
+ message: "Sign into Vultisig Plugin Marketplace",
4112
+ nonce,
4113
+ expiresAt,
4114
+ address: ethAddress
4115
+ });
4116
+ const messageHash = computePersonalSignHash(authMessage);
4117
+ let lastError = null;
4118
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
3744
4119
  try {
3745
- const parts = line.split(/\s+/);
3746
- const command = parts[0]?.toLowerCase();
3747
- if ((command === "import" || command === "export") && parts.length > 1) {
3748
- const partial = parts.slice(1).join(" ");
3749
- return completeFilePath(partial, command === "import");
4120
+ if (attempt > 1) {
4121
+ process.stderr.write(` Retry ${attempt}/${maxAttempts}...
4122
+ `);
3750
4123
  }
3751
- if (command === "vault" && parts.length > 1) {
3752
- const partial = parts.slice(1).join(" ");
3753
- return completeVaultName(ctx2, partial);
4124
+ const signature = await vault.signBytes(
4125
+ { data: Buffer.from(messageHash), chain: Chain7.Ethereum },
4126
+ {}
4127
+ );
4128
+ const sigHex = formatSignature65(signature.signature, signature.recovery ?? 0);
4129
+ const authResponse = await client.authenticate({
4130
+ public_key: publicKey,
4131
+ chain_code_hex: chainCode,
4132
+ message: authMessage,
4133
+ signature: sigHex
4134
+ });
4135
+ return {
4136
+ token: authResponse.token,
4137
+ expiresAt: authResponse.expires_at
4138
+ };
4139
+ } catch (err) {
4140
+ lastError = err;
4141
+ if (attempt < maxAttempts && err.message?.includes("timeout")) {
4142
+ continue;
3754
4143
  }
3755
- if (command === "chains" && parts.length >= 2) {
3756
- const lastPart = parts[parts.length - 1] || "";
3757
- const lastPartLower = lastPart.toLowerCase();
3758
- if (lastPartLower.startsWith("-")) {
3759
- const flags = ["--add", "--add-all", "--remove"];
3760
- const matches = flags.filter((f) => f.startsWith(lastPartLower));
3761
- return [matches.length ? matches : flags, lastPart];
4144
+ throw err;
4145
+ }
4146
+ }
4147
+ throw lastError || new Error("Authentication failed after all attempts");
4148
+ }
4149
+ function computePersonalSignHash(message) {
4150
+ const messageBytes = new TextEncoder().encode(message);
4151
+ const prefix = `Ethereum Signed Message:
4152
+ ${messageBytes.length}`;
4153
+ const prefixBytes = new TextEncoder().encode(prefix);
4154
+ const combined = new Uint8Array(prefixBytes.length + messageBytes.length);
4155
+ combined.set(prefixBytes);
4156
+ combined.set(messageBytes, prefixBytes.length);
4157
+ return keccak_256(combined);
4158
+ }
4159
+ function formatSignature65(sigHex, recovery) {
4160
+ const hex = sigHex.startsWith("0x") ? sigHex.slice(2) : sigHex;
4161
+ const bytes = Buffer.from(hex, "hex");
4162
+ if (bytes[0] === 48) {
4163
+ const { r, s } = decodeDERSignature(bytes);
4164
+ const v = (recovery + 27).toString(16).padStart(2, "0");
4165
+ return r + s + v;
4166
+ }
4167
+ if (hex.length >= 128) {
4168
+ const rs = hex.slice(0, 128);
4169
+ const v = (recovery + 27).toString(16).padStart(2, "0");
4170
+ return rs + v;
4171
+ }
4172
+ throw new Error(`Cannot format signature: unrecognized format (${hex.length} hex chars)`);
4173
+ }
4174
+ function decodeDERSignature(der) {
4175
+ let offset = 0;
4176
+ if (der[offset++] !== 48) throw new Error("Invalid DER: expected SEQUENCE");
4177
+ offset++;
4178
+ if (der[offset++] !== 2) throw new Error("Invalid DER: expected INTEGER for r");
4179
+ const rLen = der[offset++];
4180
+ const rBytes = der.subarray(offset, offset + rLen);
4181
+ offset += rLen;
4182
+ if (der[offset++] !== 2) throw new Error("Invalid DER: expected INTEGER for s");
4183
+ const sLen = der[offset++];
4184
+ const sBytes = der.subarray(offset, offset + sLen);
4185
+ const r = padTo32Bytes(stripLeadingZeros(rBytes));
4186
+ const s = padTo32Bytes(stripLeadingZeros(sBytes));
4187
+ return { r, s };
4188
+ }
4189
+ function stripLeadingZeros(buf) {
4190
+ let start = 0;
4191
+ while (start < buf.length - 1 && buf[start] === 0) start++;
4192
+ return Buffer.from(buf.subarray(start));
4193
+ }
4194
+ function padTo32Bytes(buf) {
4195
+ if (buf.length > 32) {
4196
+ return buf.subarray(buf.length - 32).toString("hex");
4197
+ }
4198
+ return buf.toString("hex").padStart(64, "0");
4199
+ }
4200
+
4201
+ // src/agent/client.ts
4202
+ var AgentClient = class {
4203
+ baseUrl;
4204
+ authToken = null;
4205
+ verbose = false;
4206
+ constructor(baseUrl) {
4207
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
4208
+ }
4209
+ setAuthToken(token) {
4210
+ this.authToken = token;
4211
+ }
4212
+ // ============================================================================
4213
+ // Authentication
4214
+ // ============================================================================
4215
+ async authenticate(req) {
4216
+ const res = await fetch(`${this.baseUrl}/auth/token`, {
4217
+ method: "POST",
4218
+ headers: { "Content-Type": "application/json" },
4219
+ body: JSON.stringify(req)
4220
+ });
4221
+ if (!res.ok) {
4222
+ const body = await res.json().catch(() => ({ error: res.statusText }));
4223
+ throw new Error(`Auth failed (${res.status}): ${body.error || res.statusText}`);
4224
+ }
4225
+ const data = await res.json();
4226
+ this.authToken = data.token;
4227
+ return data;
4228
+ }
4229
+ // ============================================================================
4230
+ // Health
4231
+ // ============================================================================
4232
+ async healthCheck() {
4233
+ try {
4234
+ const res = await fetch(`${this.baseUrl}/healthz`);
4235
+ return res.ok;
4236
+ } catch {
4237
+ return false;
4238
+ }
4239
+ }
4240
+ // ============================================================================
4241
+ // Conversations
4242
+ // ============================================================================
4243
+ async createConversation(publicKey) {
4244
+ const req = { public_key: publicKey };
4245
+ return this.post("/agent/conversations", req);
4246
+ }
4247
+ async listConversations(publicKey, skip = 0, take = 20) {
4248
+ const req = { public_key: publicKey, skip, take };
4249
+ return this.post("/agent/conversations/list", req);
4250
+ }
4251
+ async getConversation(conversationId, publicKey) {
4252
+ const req = { public_key: publicKey };
4253
+ return this.post(`/agent/conversations/${conversationId}`, req);
4254
+ }
4255
+ async deleteConversation(conversationId, publicKey) {
4256
+ await this.delete(`/agent/conversations/${conversationId}`, { public_key: publicKey });
4257
+ }
4258
+ // ============================================================================
4259
+ // Messages - JSON mode
4260
+ // ============================================================================
4261
+ async sendMessage(conversationId, req) {
4262
+ return this.post(`/agent/conversations/${conversationId}/messages`, req);
4263
+ }
4264
+ // ============================================================================
4265
+ // Messages - SSE Streaming mode
4266
+ // ============================================================================
4267
+ async sendMessageStream(conversationId, req, callbacks, signal) {
4268
+ const res = await fetch(`${this.baseUrl}/agent/conversations/${conversationId}/messages`, {
4269
+ method: "POST",
4270
+ headers: {
4271
+ "Content-Type": "application/json",
4272
+ Accept: "text/event-stream",
4273
+ ...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {}
4274
+ },
4275
+ body: JSON.stringify(req),
4276
+ signal
4277
+ });
4278
+ if (!res.ok) {
4279
+ const body = await res.json().catch(() => ({ error: res.statusText }));
4280
+ throw new Error(`Message failed (${res.status}): ${body.error || res.statusText}`);
4281
+ }
4282
+ if (!res.body) {
4283
+ throw new Error("No response body for SSE stream");
4284
+ }
4285
+ const result = {
4286
+ fullText: "",
4287
+ actions: [],
4288
+ suggestions: [],
4289
+ transactions: [],
4290
+ message: null
4291
+ };
4292
+ const reader = res.body.getReader();
4293
+ const decoder = new TextDecoder();
4294
+ let buffer = "";
4295
+ try {
4296
+ while (true) {
4297
+ const { done, value } = await reader.read();
4298
+ if (done) break;
4299
+ buffer += decoder.decode(value, { stream: true });
4300
+ const lines = buffer.split("\n");
4301
+ buffer = lines.pop() || "";
4302
+ let currentEvent = "";
4303
+ let currentData = "";
4304
+ for (const line of lines) {
4305
+ if (line.startsWith("event: ")) {
4306
+ currentEvent = line.slice(7).trim();
4307
+ } else if (line.startsWith("data: ")) {
4308
+ currentData += (currentData ? "\n" : "") + line.slice(6);
4309
+ } else if (line === "") {
4310
+ if (currentEvent && currentData) {
4311
+ this.handleSSEEvent(currentEvent, currentData, result, callbacks);
4312
+ }
4313
+ currentEvent = "";
4314
+ currentData = "";
4315
+ } else if (line.startsWith(": ")) {
4316
+ }
3762
4317
  }
3763
- const flag = parts[parts.length - 2]?.toLowerCase();
3764
- if (flag === "--add" || flag === "--remove") {
3765
- return completeChainName(lastPart);
4318
+ if (currentEvent && currentData) {
4319
+ this.handleSSEEvent(currentEvent, currentData, result, callbacks);
3766
4320
  }
3767
4321
  }
3768
- if (["balance", "bal", "tokens", "send", "swap", "swap-quote"].includes(command) && parts.length === 2) {
3769
- const partial = parts[1] || "";
3770
- return completeChainName(partial);
3771
- }
3772
- if ((command === "create" || command === "create-from-seedphrase") && parts.length === 2) {
3773
- const types = ["fast", "secure"];
3774
- const partial = parts[1] || "";
3775
- const partialLower = partial.toLowerCase();
3776
- const matches = types.filter((t) => t.startsWith(partialLower));
3777
- return [matches.length ? matches : types, partial];
3778
- }
3779
- if (command === "join" && parts.length === 2) {
3780
- const types = ["secure"];
3781
- const partial = parts[1] || "";
3782
- const partialLower = partial.toLowerCase();
3783
- const matches = types.filter((t) => t.startsWith(partialLower));
3784
- return [matches.length ? matches : types, partial];
4322
+ } finally {
4323
+ reader.releaseLock();
4324
+ }
4325
+ return result;
4326
+ }
4327
+ handleSSEEvent(event, data, result, callbacks) {
4328
+ try {
4329
+ const parsed = JSON.parse(data);
4330
+ switch (event) {
4331
+ case "text_delta":
4332
+ result.fullText += parsed.delta;
4333
+ callbacks.onTextDelta?.(parsed.delta);
4334
+ break;
4335
+ case "tool_progress":
4336
+ if (this.verbose) process.stderr.write(`[SSE:tool_progress] raw: ${data.slice(0, 1e3)}
4337
+ `);
4338
+ callbacks.onToolProgress?.(parsed.tool, parsed.status, parsed.label);
4339
+ break;
4340
+ case "title":
4341
+ callbacks.onTitle?.(parsed.title);
4342
+ break;
4343
+ case "actions":
4344
+ if (this.verbose) process.stderr.write(`[SSE:actions] raw: ${data.slice(0, 1e3)}
4345
+ `);
4346
+ result.actions.push(...parsed.actions || []);
4347
+ callbacks.onActions?.(parsed.actions || []);
4348
+ break;
4349
+ case "suggestions":
4350
+ result.suggestions.push(...parsed.suggestions || []);
4351
+ callbacks.onSuggestions?.(parsed.suggestions || []);
4352
+ break;
4353
+ case "tx_ready":
4354
+ if (this.verbose) process.stderr.write(`[SSE:tx_ready] raw: ${data.slice(0, 2e3)}
4355
+ `);
4356
+ result.transactions.push(parsed);
4357
+ callbacks.onTxReady?.(parsed);
4358
+ break;
4359
+ case "message":
4360
+ result.message = parsed.message || parsed;
4361
+ callbacks.onMessage?.(result.message);
4362
+ break;
4363
+ case "error":
4364
+ callbacks.onError?.(parsed.error);
4365
+ break;
4366
+ case "done":
4367
+ break;
3785
4368
  }
3786
- const hits = COMMANDS.filter((c) => c.startsWith(line));
3787
- const show = hits.length ? hits : COMMANDS;
3788
- return [show, line];
3789
4369
  } catch {
3790
- return [[], line];
3791
4370
  }
4371
+ }
4372
+ // ============================================================================
4373
+ // Private helpers
4374
+ // ============================================================================
4375
+ async post(path3, body) {
4376
+ const res = await fetch(`${this.baseUrl}${path3}`, {
4377
+ method: "POST",
4378
+ headers: {
4379
+ "Content-Type": "application/json",
4380
+ ...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {}
4381
+ },
4382
+ body: JSON.stringify(body)
4383
+ });
4384
+ if (!res.ok) {
4385
+ const errorBody = await res.json().catch(() => ({ error: res.statusText }));
4386
+ throw new Error(`Request failed (${res.status}): ${errorBody.error || res.statusText}`);
4387
+ }
4388
+ return await res.json();
4389
+ }
4390
+ async delete(path3, body) {
4391
+ const res = await fetch(`${this.baseUrl}${path3}`, {
4392
+ method: "DELETE",
4393
+ headers: {
4394
+ "Content-Type": "application/json",
4395
+ ...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {}
4396
+ },
4397
+ body: JSON.stringify(body)
4398
+ });
4399
+ if (!res.ok) {
4400
+ const errorBody = await res.json().catch(() => ({ error: res.statusText }));
4401
+ throw new Error(`Delete failed (${res.status}): ${errorBody.error || res.statusText}`);
4402
+ }
4403
+ }
4404
+ };
4405
+
4406
+ // src/agent/context.ts
4407
+ import { Chain as Chain8 } from "@vultisig/sdk";
4408
+ async function buildMessageContext(vault) {
4409
+ const context = {
4410
+ vault_address: vault.publicKeys.ecdsa,
4411
+ vault_name: vault.name
3792
4412
  };
3793
- }
3794
- function completeFilePath(partial, filterVult) {
3795
4413
  try {
3796
- const endsWithSeparator = partial.endsWith("/") || partial.endsWith(path2.sep);
3797
- let dir;
3798
- let basename;
3799
- if (endsWithSeparator) {
3800
- dir = partial;
3801
- basename = "";
3802
- } else {
3803
- dir = path2.dirname(partial);
3804
- basename = path2.basename(partial);
3805
- if (fs2.existsSync(partial) && fs2.statSync(partial).isDirectory()) {
3806
- dir = partial;
3807
- basename = "";
4414
+ const chains = vault.chains;
4415
+ const addressEntries = await Promise.allSettled(
4416
+ chains.map(async (chain) => ({
4417
+ chain: chain.toString(),
4418
+ address: await vault.address(chain)
4419
+ }))
4420
+ );
4421
+ const addresses = {};
4422
+ for (const result of addressEntries) {
4423
+ if (result.status === "fulfilled") {
4424
+ addresses[result.value.chain] = result.value.address;
3808
4425
  }
3809
4426
  }
3810
- const resolvedDir = path2.resolve(dir);
3811
- if (!fs2.existsSync(resolvedDir) || !fs2.statSync(resolvedDir).isDirectory()) {
3812
- return [[], partial];
4427
+ context.addresses = addresses;
4428
+ } catch {
4429
+ }
4430
+ try {
4431
+ const balanceRecord = await vault.balances();
4432
+ const balanceInfos = [];
4433
+ for (const [key, balance] of Object.entries(balanceRecord)) {
4434
+ balanceInfos.push({
4435
+ chain: balance.chainId || key.split(":")[0] || "",
4436
+ asset: balance.symbol || "",
4437
+ symbol: balance.symbol || "",
4438
+ amount: balance.formattedAmount || balance.amount?.toString() || "0",
4439
+ decimals: balance.decimals || 18
4440
+ });
3813
4441
  }
3814
- const files = fs2.readdirSync(resolvedDir);
3815
- const matches = files.filter((file) => file.startsWith(basename)).map((file) => {
3816
- const fullPath = path2.join(dir, file);
3817
- const stats = fs2.statSync(path2.join(resolvedDir, file));
3818
- if (stats.isDirectory()) {
3819
- return fullPath + "/";
3820
- }
3821
- if (filterVult) {
3822
- if (file.endsWith(".vult") || stats.isDirectory()) {
4442
+ context.balances = balanceInfos;
4443
+ } catch {
4444
+ }
4445
+ try {
4446
+ const coins = [];
4447
+ const chains = vault.chains;
4448
+ for (const chain of chains) {
4449
+ coins.push({
4450
+ chain: chain.toString(),
4451
+ ticker: getNativeTokenTicker(chain),
4452
+ is_native_token: true,
4453
+ decimals: getNativeTokenDecimals(chain)
4454
+ });
4455
+ const tokens = vault.tokens[chain] || [];
4456
+ for (const token of tokens) {
4457
+ coins.push({
4458
+ chain: chain.toString(),
4459
+ ticker: token.symbol || "",
4460
+ contract_address: token.contractAddress || token.id,
4461
+ is_native_token: false,
4462
+ decimals: token.decimals || 18
4463
+ });
4464
+ }
4465
+ }
4466
+ context.coins = coins;
4467
+ } catch {
4468
+ }
4469
+ return context;
4470
+ }
4471
+ function getNativeTokenTicker(chain) {
4472
+ const tickers = {
4473
+ [Chain8.Ethereum]: "ETH",
4474
+ [Chain8.Bitcoin]: "BTC",
4475
+ [Chain8.Solana]: "SOL",
4476
+ [Chain8.THORChain]: "RUNE",
4477
+ [Chain8.Cosmos]: "ATOM",
4478
+ [Chain8.Avalanche]: "AVAX",
4479
+ [Chain8.BSC]: "BNB",
4480
+ [Chain8.Polygon]: "MATIC",
4481
+ [Chain8.Arbitrum]: "ETH",
4482
+ [Chain8.Optimism]: "ETH",
4483
+ [Chain8.Base]: "ETH",
4484
+ [Chain8.Blast]: "ETH",
4485
+ [Chain8.Litecoin]: "LTC",
4486
+ [Chain8.Dogecoin]: "DOGE",
4487
+ [Chain8.Dash]: "DASH",
4488
+ [Chain8.MayaChain]: "CACAO",
4489
+ [Chain8.Polkadot]: "DOT",
4490
+ [Chain8.Sui]: "SUI",
4491
+ [Chain8.Ton]: "TON",
4492
+ [Chain8.Tron]: "TRX",
4493
+ [Chain8.Ripple]: "XRP",
4494
+ [Chain8.Dydx]: "DYDX",
4495
+ [Chain8.Osmosis]: "OSMO",
4496
+ [Chain8.Terra]: "LUNA",
4497
+ [Chain8.Noble]: "USDC",
4498
+ [Chain8.Kujira]: "KUJI",
4499
+ [Chain8.Zksync]: "ETH",
4500
+ [Chain8.CronosChain]: "CRO"
4501
+ };
4502
+ return tickers[chain] || chain.toString();
4503
+ }
4504
+ function getNativeTokenDecimals(chain) {
4505
+ const decimals = {
4506
+ [Chain8.Bitcoin]: 8,
4507
+ [Chain8.Litecoin]: 8,
4508
+ [Chain8.Dogecoin]: 8,
4509
+ [Chain8.Dash]: 8,
4510
+ [Chain8.Solana]: 9,
4511
+ [Chain8.Sui]: 9,
4512
+ [Chain8.Ton]: 9,
4513
+ [Chain8.Polkadot]: 10,
4514
+ [Chain8.Cosmos]: 6,
4515
+ [Chain8.THORChain]: 8,
4516
+ [Chain8.MayaChain]: 10,
4517
+ [Chain8.Osmosis]: 6,
4518
+ [Chain8.Dydx]: 18,
4519
+ [Chain8.Tron]: 6,
4520
+ [Chain8.Ripple]: 6,
4521
+ [Chain8.Noble]: 6,
4522
+ [Chain8.Kujira]: 6,
4523
+ [Chain8.Terra]: 6
4524
+ };
4525
+ return decimals[chain] || 18;
4526
+ }
4527
+
4528
+ // src/agent/executor.ts
4529
+ import { Chain as Chain9, Vultisig as Vultisig6 } from "@vultisig/sdk";
4530
+
4531
+ // src/agent/types.ts
4532
+ var AUTO_EXECUTE_ACTIONS = /* @__PURE__ */ new Set([
4533
+ "add_chain",
4534
+ "add_coin",
4535
+ "remove_coin",
4536
+ "remove_chain",
4537
+ "address_book_add",
4538
+ "address_book_remove",
4539
+ "get_address_book",
4540
+ "get_market_price",
4541
+ "get_balances",
4542
+ "get_portfolio",
4543
+ "search_token",
4544
+ "list_vaults",
4545
+ "build_swap_tx",
4546
+ "build_send_tx",
4547
+ "build_custom_tx",
4548
+ "build_tx",
4549
+ "sign_tx",
4550
+ "sign_typed_data",
4551
+ "read_evm_contract",
4552
+ "scan_tx",
4553
+ "thorchain_query"
4554
+ ]);
4555
+ var PASSWORD_REQUIRED_ACTIONS = /* @__PURE__ */ new Set(["sign_tx", "sign_typed_data", "build_custom_tx"]);
4556
+
4557
+ // src/agent/executor.ts
4558
+ var AgentExecutor = class {
4559
+ vault;
4560
+ pendingPayloads = /* @__PURE__ */ new Map();
4561
+ password = null;
4562
+ verbose;
4563
+ constructor(vault, verbose = false) {
4564
+ this.vault = vault;
4565
+ this.verbose = verbose;
4566
+ }
4567
+ setPassword(password) {
4568
+ this.password = password;
4569
+ }
4570
+ /**
4571
+ * Store a server-built transaction (from tx_ready SSE event).
4572
+ * This allows sign_tx to find and sign it when the backend requests signing.
4573
+ */
4574
+ storeServerTransaction(txReadyData) {
4575
+ if (this.verbose) process.stderr.write(`[executor] storeServerTransaction called, keys: ${Object.keys(txReadyData || {}).join(",")}
4576
+ `);
4577
+ const swapTx = txReadyData.swap_tx || txReadyData.send_tx || txReadyData.tx;
4578
+ if (!swapTx) {
4579
+ if (this.verbose) process.stderr.write(`[executor] storeServerTransaction: no swap_tx/send_tx/tx found in data
4580
+ `);
4581
+ return;
4582
+ }
4583
+ const chain = resolveChainFromTxReady(txReadyData) || Chain9.Ethereum;
4584
+ this.pendingPayloads.set("latest", {
4585
+ payload: { __serverTx: true, ...txReadyData },
4586
+ coin: { chain, address: "", decimals: 18, ticker: "" },
4587
+ chain,
4588
+ timestamp: Date.now()
4589
+ });
4590
+ if (this.verbose) process.stderr.write(`[executor] Stored server tx for chain ${chain}, pendingPayloads size=${this.pendingPayloads.size}
4591
+ `);
4592
+ }
4593
+ hasPendingTransaction() {
4594
+ return this.pendingPayloads.has("latest");
4595
+ }
4596
+ shouldAutoExecute(action) {
4597
+ return action.auto_execute === true || AUTO_EXECUTE_ACTIONS.has(action.type);
4598
+ }
4599
+ requiresPassword(action) {
4600
+ return PASSWORD_REQUIRED_ACTIONS.has(action.type);
4601
+ }
4602
+ /**
4603
+ * Execute a single action and return the result.
4604
+ */
4605
+ async executeAction(action) {
4606
+ try {
4607
+ const data = await this.dispatch(action);
4608
+ return {
4609
+ action: action.type,
4610
+ action_id: action.id,
4611
+ success: true,
4612
+ data
4613
+ };
4614
+ } catch (err) {
4615
+ return {
4616
+ action: action.type,
4617
+ action_id: action.id,
4618
+ success: false,
4619
+ error: err.message || String(err)
4620
+ };
4621
+ }
4622
+ }
4623
+ async dispatch(action) {
4624
+ if (this.verbose) process.stderr.write(`[dispatch] action.type=${action.type} action.id=${action.id}
4625
+ `);
4626
+ const params = action.params || {};
4627
+ switch (action.type) {
4628
+ case "get_balances":
4629
+ return this.getBalances(params);
4630
+ case "get_portfolio":
4631
+ return this.getPortfolio(params);
4632
+ case "add_chain":
4633
+ return this.addChain(params);
4634
+ case "remove_chain":
4635
+ return this.removeChain(params);
4636
+ case "add_coin":
4637
+ return this.addCoin(params);
4638
+ case "remove_coin":
4639
+ return this.removeCoin(params);
4640
+ case "build_send_tx":
4641
+ return this.buildSendTx(params);
4642
+ case "build_swap_tx":
4643
+ return this.buildSwapTx(params);
4644
+ case "build_tx":
4645
+ case "build_custom_tx":
4646
+ return this.buildTx(params);
4647
+ case "sign_tx":
4648
+ return this.signTx(params);
4649
+ case "get_address_book":
4650
+ return this.getAddressBook();
4651
+ case "address_book_add":
4652
+ return this.addAddressBookEntry(params);
4653
+ case "address_book_remove":
4654
+ return this.removeAddressBookEntry(params);
4655
+ case "search_token":
4656
+ return this.searchToken(params);
4657
+ case "list_vaults":
4658
+ return this.listVaults();
4659
+ case "sign_typed_data":
4660
+ return this.signTypedData(params);
4661
+ case "scan_tx":
4662
+ return this.scanTx(params);
4663
+ case "read_evm_contract":
4664
+ return this.readEvmContract(params);
4665
+ default:
4666
+ return { message: `Action type '${action.type}' not implemented locally` };
4667
+ }
4668
+ }
4669
+ // ============================================================================
4670
+ // Balance & Portfolio
4671
+ // ============================================================================
4672
+ async getBalances(params) {
4673
+ const chainFilter = params.chain;
4674
+ const tickerFilter = params.ticker;
4675
+ const balanceRecord = await this.vault.balances();
4676
+ let entries = Object.entries(balanceRecord).map(([key, b]) => ({
4677
+ chain: b.chainId || key.split(":")[0] || "",
4678
+ symbol: b.symbol || "",
4679
+ amount: b.formattedAmount || b.amount?.toString() || "0",
4680
+ decimals: b.decimals || 18,
4681
+ raw_amount: b.amount?.toString()
4682
+ }));
4683
+ if (chainFilter) {
4684
+ const chain = resolveChain(chainFilter);
4685
+ if (chain) {
4686
+ entries = entries.filter((b) => b.chain.toLowerCase() === chain.toLowerCase());
4687
+ }
4688
+ }
4689
+ if (tickerFilter) {
4690
+ entries = entries.filter((b) => b.symbol.toLowerCase() === tickerFilter.toLowerCase());
4691
+ }
4692
+ return { balances: entries };
4693
+ }
4694
+ async getPortfolio(_params) {
4695
+ const balanceRecord = await this.vault.balances();
4696
+ const entries = Object.entries(balanceRecord).map(([key, b]) => ({
4697
+ chain: b.chainId || key.split(":")[0] || "",
4698
+ symbol: b.symbol || "",
4699
+ amount: b.formattedAmount || b.amount?.toString() || "0",
4700
+ decimals: b.decimals || 18
4701
+ }));
4702
+ return { balances: entries };
4703
+ }
4704
+ // ============================================================================
4705
+ // Chain & Token Management
4706
+ // ============================================================================
4707
+ async addChain(params) {
4708
+ const chains = params.chains;
4709
+ if (chains && Array.isArray(chains)) {
4710
+ const results = [];
4711
+ for (const c of chains) {
4712
+ const name = typeof c === "string" ? c : c.chain;
4713
+ const chain2 = resolveChain(name);
4714
+ if (!chain2) throw new Error(`Unknown chain: ${name}`);
4715
+ await this.vault.addChain(chain2);
4716
+ const address2 = await this.vault.address(chain2);
4717
+ results.push({ chain: chain2.toString(), address: address2 });
4718
+ }
4719
+ return { added: results };
4720
+ }
4721
+ const chainName = params.chain;
4722
+ const chain = resolveChain(chainName);
4723
+ if (!chain) throw new Error(`Unknown chain: ${chainName}`);
4724
+ await this.vault.addChain(chain);
4725
+ const address = await this.vault.address(chain);
4726
+ return { chain: chain.toString(), address, added: true };
4727
+ }
4728
+ async removeChain(params) {
4729
+ const chainName = params.chain;
4730
+ const chain = resolveChain(chainName);
4731
+ if (!chain) throw new Error(`Unknown chain: ${chainName}`);
4732
+ await this.vault.removeChain(chain);
4733
+ return { chain: chain.toString(), removed: true };
4734
+ }
4735
+ async addCoin(params) {
4736
+ const tokens = params.tokens;
4737
+ if (tokens && Array.isArray(tokens)) {
4738
+ const results = [];
4739
+ for (const t of tokens) {
4740
+ const chain2 = resolveChain(t.chain);
4741
+ if (!chain2) throw new Error(`Unknown chain: ${t.chain}`);
4742
+ const symbol2 = t.symbol || t.ticker || "";
4743
+ await this.vault.addToken(chain2, {
4744
+ id: t.contract_address || t.contractAddress || "",
4745
+ symbol: symbol2,
4746
+ name: t.name || symbol2,
4747
+ decimals: t.decimals || 18,
4748
+ contractAddress: t.contract_address || t.contractAddress,
4749
+ chainId: chain2.toString()
4750
+ });
4751
+ results.push({ chain: chain2.toString(), symbol: symbol2 });
4752
+ }
4753
+ return { added: results };
4754
+ }
4755
+ const chainName = params.chain;
4756
+ const chain = resolveChain(chainName);
4757
+ if (!chain) throw new Error(`Unknown chain: ${chainName}`);
4758
+ const symbol = params.symbol || params.ticker;
4759
+ await this.vault.addToken(chain, {
4760
+ id: params.contract_address || params.contractAddress || "",
4761
+ symbol,
4762
+ name: params.name || symbol,
4763
+ decimals: params.decimals || 18,
4764
+ contractAddress: params.contract_address || params.contractAddress,
4765
+ chainId: chain.toString()
4766
+ });
4767
+ return { chain: chain.toString(), symbol, added: true };
4768
+ }
4769
+ async removeCoin(params) {
4770
+ const chainName = params.chain;
4771
+ const chain = resolveChain(chainName);
4772
+ if (!chain) throw new Error(`Unknown chain: ${chainName}`);
4773
+ const tokenId = params.token_id || params.id || params.contract_address;
4774
+ await this.vault.removeToken(chain, tokenId);
4775
+ return { chain: chain.toString(), removed: true };
4776
+ }
4777
+ // ============================================================================
4778
+ // Transaction Building
4779
+ // ============================================================================
4780
+ async buildSendTx(params) {
4781
+ const chainName = params.chain || params.from_chain;
4782
+ const chain = resolveChain(chainName);
4783
+ if (!chain) throw new Error(`Unknown chain: ${chainName}`);
4784
+ const symbol = params.symbol || params.ticker;
4785
+ const toAddress = params.address || params.to || params.destination;
4786
+ const amountStr = params.amount;
4787
+ if (!toAddress) throw new Error("Destination address is required");
4788
+ if (!amountStr) throw new Error("Amount is required");
4789
+ const address = await this.vault.address(chain);
4790
+ const balance = await this.vault.balance(chain, params.token_id);
4791
+ const coin = {
4792
+ chain,
4793
+ address,
4794
+ decimals: balance.decimals,
4795
+ ticker: symbol || balance.symbol,
4796
+ id: params.token_id
4797
+ };
4798
+ const amount = parseAmount(amountStr, balance.decimals);
4799
+ const memo = params.memo;
4800
+ const payload = await this.vault.prepareSendTx({ coin, receiver: toAddress, amount, memo });
4801
+ const payloadId = `tx_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
4802
+ this.pendingPayloads.set(payloadId, { payload, coin, chain, timestamp: Date.now() });
4803
+ this.pendingPayloads.set("latest", { payload, coin, chain, timestamp: Date.now() });
4804
+ const messageHashes = await this.vault.extractMessageHashes(payload);
4805
+ return {
4806
+ keysign_payload: payloadId,
4807
+ from_chain: chain.toString(),
4808
+ from_symbol: coin.ticker,
4809
+ amount: amountStr,
4810
+ sender: address,
4811
+ destination: toAddress,
4812
+ memo: memo || void 0,
4813
+ message_hashes: messageHashes,
4814
+ tx_details: {
4815
+ chain: chain.toString(),
4816
+ from: address,
4817
+ to: toAddress,
4818
+ amount: amountStr,
4819
+ symbol: coin.ticker
4820
+ }
4821
+ };
4822
+ }
4823
+ async buildSwapTx(params) {
4824
+ if (this.verbose) process.stderr.write(`[build_swap_tx] called with params: ${JSON.stringify(params).slice(0, 500)}
4825
+ `);
4826
+ const fromChainName = params.from_chain || params.chain;
4827
+ const toChainName = params.to_chain;
4828
+ const fromChain = resolveChain(fromChainName);
4829
+ const toChain = toChainName ? resolveChain(toChainName) : null;
4830
+ if (!fromChain) throw new Error(`Unknown from_chain: ${fromChainName}`);
4831
+ const amountStr = params.amount;
4832
+ const fromSymbol = params.from_symbol || params.from_token || "";
4833
+ const toSymbol = params.to_symbol || params.to_token || "";
4834
+ const fromToken = params.from_contract || params.from_token_id;
4835
+ const toToken = params.to_contract || params.to_token_id;
4836
+ const fromCoin = { chain: fromChain, token: fromToken || void 0 };
4837
+ const toCoin = { chain: toChain || fromChain, token: toToken || void 0 };
4838
+ const quote = await this.vault.getSwapQuote({
4839
+ fromCoin,
4840
+ toCoin,
4841
+ amount: parseFloat(amountStr)
4842
+ });
4843
+ const swapResult = await this.vault.prepareSwapTx({
4844
+ fromCoin,
4845
+ toCoin,
4846
+ amount: parseFloat(amountStr),
4847
+ swapQuote: quote,
4848
+ autoApprove: true
4849
+ });
4850
+ const chain = fromChain;
4851
+ const payload = swapResult.keysignPayload;
4852
+ const payloadId = `swap_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
4853
+ this.pendingPayloads.set(payloadId, { payload, coin: { chain, address: "", decimals: 18, ticker: fromSymbol }, chain, timestamp: Date.now() });
4854
+ this.pendingPayloads.set("latest", { payload, coin: { chain, address: "", decimals: 18, ticker: fromSymbol }, chain, timestamp: Date.now() });
4855
+ const messageHashes = await this.vault.extractMessageHashes(payload);
4856
+ return {
4857
+ keysign_payload: payloadId,
4858
+ from_chain: fromChain.toString(),
4859
+ to_chain: (toChain || fromChain).toString(),
4860
+ from_symbol: fromSymbol,
4861
+ to_symbol: toSymbol,
4862
+ amount: amountStr,
4863
+ estimated_output: quote.estimatedOutput?.toString(),
4864
+ provider: quote.provider,
4865
+ message_hashes: messageHashes
4866
+ };
4867
+ }
4868
+ async buildTx(params) {
4869
+ if (params.function_name && params.contract_address) {
4870
+ return this.buildContractCallTx(params);
4871
+ }
4872
+ if (params.data || params.calldata || params.hex_payload) {
4873
+ const txData = {
4874
+ to: params.to || params.address || params.contract,
4875
+ value: params.value || "0",
4876
+ data: params.data || params.calldata || params.hex_payload,
4877
+ chain: params.chain,
4878
+ chain_id: params.chain_id
4879
+ };
4880
+ this.storeServerTransaction({
4881
+ tx: txData,
4882
+ chain: params.chain,
4883
+ from_chain: params.chain
4884
+ });
4885
+ const chain = resolveChain(params.chain) || Chain9.Ethereum;
4886
+ const address = await this.vault.address(chain);
4887
+ return {
4888
+ status: "ready",
4889
+ chain: chain.toString(),
4890
+ from: address,
4891
+ to: txData.to,
4892
+ value: txData.value,
4893
+ has_calldata: true,
4894
+ message: "Transaction built. Ready to sign."
4895
+ };
4896
+ }
4897
+ return this.buildSendTx(params);
4898
+ }
4899
+ /**
4900
+ * Build, sign, and broadcast an EVM contract call transaction from structured params.
4901
+ * Encodes function_name + typed params into ABI calldata, then signs via signServerTx.
4902
+ */
4903
+ async buildContractCallTx(params) {
4904
+ const chainName = params.chain || "Ethereum";
4905
+ const chain = resolveChain(chainName);
4906
+ if (!chain) throw new Error(`Unknown chain: ${chainName}`);
4907
+ const contractAddress = params.contract_address;
4908
+ const functionName = params.function_name;
4909
+ const typedParams = params.params;
4910
+ const value = params.value || "0";
4911
+ const calldata = await encodeContractCall(functionName, typedParams || []);
4912
+ if (this.verbose) process.stderr.write(`[build_contract_tx] ${functionName}(${(typedParams || []).map((p) => p.type).join(",")}) on ${contractAddress} chain=${chain}
4913
+ `);
4914
+ const serverTxData = {
4915
+ __serverTx: true,
4916
+ tx: {
4917
+ to: contractAddress,
4918
+ value,
4919
+ data: calldata
4920
+ },
4921
+ chain: chainName,
4922
+ from_chain: chainName
4923
+ };
4924
+ this.pendingPayloads.set("latest", {
4925
+ payload: serverTxData,
4926
+ coin: { chain, address: "", decimals: 18, ticker: "" },
4927
+ chain,
4928
+ timestamp: Date.now()
4929
+ });
4930
+ return this.signServerTx(serverTxData, chain, { chain: chainName });
4931
+ }
4932
+ // ============================================================================
4933
+ // Transaction Signing
4934
+ // ============================================================================
4935
+ async signTx(params) {
4936
+ if (this.verbose) process.stderr.write(`[sign_tx] params: ${JSON.stringify(params).slice(0, 500)}
4937
+ `);
4938
+ if (this.verbose) process.stderr.write(`[sign_tx] pendingPayloads keys: ${[...this.pendingPayloads.keys()].join(", ")}
4939
+ `);
4940
+ const payloadId = params.keysign_payload || params.payload_id || "latest";
4941
+ const stored = this.pendingPayloads.get(payloadId);
4942
+ if (!stored) {
4943
+ throw new Error("No pending transaction to sign. Build a transaction first.");
4944
+ }
4945
+ const { payload, chain } = stored;
4946
+ if (payload.__serverTx) {
4947
+ return this.signServerTx(payload, chain, params);
4948
+ }
4949
+ return this.signSdkTx(payload, chain, payloadId);
4950
+ }
4951
+ /**
4952
+ * Sign and broadcast an SDK-built transaction (keysign payload from local build methods).
4953
+ */
4954
+ async signSdkTx(payload, chain, payloadId) {
4955
+ if (this.vault.isEncrypted && !this.vault.isUnlocked?.()) {
4956
+ if (this.password) {
4957
+ await this.vault.unlock?.(this.password);
4958
+ }
4959
+ }
4960
+ const messageHashes = await this.vault.extractMessageHashes(payload);
4961
+ const signature = await this.vault.sign(
4962
+ {
4963
+ transaction: payload,
4964
+ chain: payload.coin?.chain || chain,
4965
+ messageHashes
4966
+ },
4967
+ {}
4968
+ );
4969
+ const txHash = await this.vault.broadcastTx({
4970
+ chain,
4971
+ keysignPayload: payload,
4972
+ signature
4973
+ });
4974
+ this.pendingPayloads.delete(payloadId);
4975
+ if (payloadId !== "latest") {
4976
+ this.pendingPayloads.delete("latest");
4977
+ }
4978
+ const explorerUrl = Vultisig6.getTxExplorerUrl(chain, txHash);
4979
+ return {
4980
+ tx_hash: txHash,
4981
+ chain: chain.toString(),
4982
+ status: "pending",
4983
+ explorer_url: explorerUrl
4984
+ };
4985
+ }
4986
+ /**
4987
+ * Sign and broadcast a server-built transaction (raw EVM tx from tx_ready SSE).
4988
+ * Uses vault.prepareSendTx with memo field to carry the calldata.
4989
+ */
4990
+ async signServerTx(serverTxData, defaultChain, params) {
4991
+ const swapTx = serverTxData.swap_tx || serverTxData.send_tx || serverTxData.tx;
4992
+ if (!swapTx?.to) {
4993
+ throw new Error("Server transaction missing required fields (to)");
4994
+ }
4995
+ const chainName = params.chain || serverTxData.chain || serverTxData.from_chain;
4996
+ const chainId = serverTxData.chain_id || swapTx.chainId;
4997
+ let chain = defaultChain;
4998
+ if (chainName) {
4999
+ chain = resolveChain(chainName) || defaultChain;
5000
+ } else if (chainId) {
5001
+ chain = resolveChainId(chainId) || defaultChain;
5002
+ }
5003
+ const address = await this.vault.address(chain);
5004
+ const balance = await this.vault.balance(chain);
5005
+ const coin = {
5006
+ chain,
5007
+ address,
5008
+ decimals: balance.decimals || 18,
5009
+ ticker: balance.symbol || chain.toString()
5010
+ };
5011
+ const amount = BigInt(swapTx.value || "0");
5012
+ const hasCalldata = !!(swapTx.data && swapTx.data !== "0x");
5013
+ if (this.verbose) process.stderr.write(`[sign_server_tx] chain=${chain}, to=${swapTx.to}, value=${swapTx.value}, amount=${amount}, hasCalldata=${hasCalldata}
5014
+ `);
5015
+ if (this.vault.isEncrypted && !this.vault.isUnlocked?.()) {
5016
+ if (this.password) {
5017
+ await this.vault.unlock?.(this.password);
5018
+ }
5019
+ }
5020
+ const buildAmount = amount === 0n && hasCalldata ? 1n : amount;
5021
+ const keysignPayload = await this.vault.prepareSendTx({
5022
+ coin,
5023
+ receiver: swapTx.to,
5024
+ amount: buildAmount,
5025
+ memo: swapTx.data
5026
+ });
5027
+ if (amount === 0n && hasCalldata) {
5028
+ ;
5029
+ keysignPayload.toAmount = "0";
5030
+ }
5031
+ const messageHashes = await this.vault.extractMessageHashes(keysignPayload);
5032
+ const signature = await this.vault.sign(
5033
+ {
5034
+ transaction: keysignPayload,
5035
+ chain,
5036
+ messageHashes
5037
+ },
5038
+ {}
5039
+ );
5040
+ const txHash = await this.vault.broadcastTx({
5041
+ chain,
5042
+ keysignPayload,
5043
+ signature
5044
+ });
5045
+ this.pendingPayloads.delete("latest");
5046
+ const explorerUrl = Vultisig6.getTxExplorerUrl(chain, txHash);
5047
+ return {
5048
+ tx_hash: txHash,
5049
+ chain: chain.toString(),
5050
+ status: "pending",
5051
+ explorer_url: explorerUrl
5052
+ };
5053
+ }
5054
+ // ============================================================================
5055
+ // EIP-712 Typed Data Signing
5056
+ // ============================================================================
5057
+ /**
5058
+ * Sign EIP-712 typed data. Computes the EIP-712 hash and signs with vault.signBytes().
5059
+ * Supports two formats:
5060
+ * - Flat: { domain, types, message, primaryType } — single typed data
5061
+ * - Payloads array: { payloads: [{id, domain, types, message, primaryType, chain}, ...] }
5062
+ * Used by Polymarket which requires signing both an Order and a ClobAuth.
5063
+ */
5064
+ async signTypedData(params) {
5065
+ if (this.vault.isEncrypted && !this.vault.isUnlocked?.()) {
5066
+ if (this.password) {
5067
+ await this.vault.unlock?.(this.password);
5068
+ }
5069
+ }
5070
+ const payloads = params.payloads;
5071
+ if (payloads && Array.isArray(payloads)) {
5072
+ if (this.verbose) process.stderr.write(`[sign_typed_data] payloads mode, ${payloads.length} items
5073
+ `);
5074
+ const signatures = [];
5075
+ for (let i = 0; i < payloads.length; i++) {
5076
+ const payload = payloads[i];
5077
+ const id = payload.id || payload.name || "default";
5078
+ if (i > 0) {
5079
+ if (this.verbose) process.stderr.write(`[sign_typed_data] waiting 5s between MPC sessions...
5080
+ `);
5081
+ await new Promise((r) => setTimeout(r, 5e3));
5082
+ }
5083
+ const sig = await this.signSingleTypedData(payload);
5084
+ signatures.push({ id, ...sig });
5085
+ if (this.verbose) process.stderr.write(`[sign_typed_data] signed payload "${id}"
5086
+ `);
5087
+ }
5088
+ return {
5089
+ signatures,
5090
+ pm_order_ref: params.pm_order_ref,
5091
+ auto_submit: !!(params.__pm_auto_submit || params.auto_submit)
5092
+ };
5093
+ }
5094
+ return this.signSingleTypedData(params);
5095
+ }
5096
+ /**
5097
+ * Sign a single EIP-712 typed data object.
5098
+ */
5099
+ async signSingleTypedData(params) {
5100
+ const domain = params.domain;
5101
+ const types = params.types;
5102
+ const message = params.message;
5103
+ const primaryType = params.primaryType || params.primary_type;
5104
+ if (!domain || !types || !message || !primaryType) {
5105
+ throw new Error("sign_typed_data requires domain, types, message, and primaryType");
5106
+ }
5107
+ if (this.verbose) process.stderr.write(`[sign_typed_data] primaryType=${primaryType} domain.name=${domain.name}
5108
+ `);
5109
+ const eip712Hash = await computeEIP712Hash(domain, types, primaryType, message);
5110
+ if (this.verbose) process.stderr.write(`[sign_typed_data] hash=${eip712Hash}
5111
+ `);
5112
+ const chainName = params.chain;
5113
+ const chainId = domain.chainId;
5114
+ let chain = Chain9.Ethereum;
5115
+ if (chainName) {
5116
+ chain = resolveChain(chainName) || Chain9.Ethereum;
5117
+ } else if (chainId) {
5118
+ chain = resolveChainId(chainId) || Chain9.Ethereum;
5119
+ }
5120
+ const sigResult = await this.vault.signBytes({
5121
+ data: eip712Hash,
5122
+ chain
5123
+ });
5124
+ if (this.verbose) process.stderr.write(`[sign_typed_data] signed, format=${sigResult.format}, recovery=${sigResult.recovery}
5125
+ `);
5126
+ const { r, s } = parseDERSignature(sigResult.signature);
5127
+ const v = (sigResult.recovery ?? 0) + 27;
5128
+ const ethSignature = "0x" + r + s + v.toString(16).padStart(2, "0");
5129
+ if (this.verbose) process.stderr.write(`[sign_typed_data] r=${r.slice(0, 16)}... s=${s.slice(0, 16)}... v=${v}
5130
+ `);
5131
+ return {
5132
+ signature: ethSignature,
5133
+ r: "0x" + r,
5134
+ s: "0x" + s,
5135
+ v,
5136
+ recovery: sigResult.recovery,
5137
+ hash: eip712Hash
5138
+ };
5139
+ }
5140
+ // ============================================================================
5141
+ // Address Book
5142
+ // ============================================================================
5143
+ async getAddressBook() {
5144
+ return { entries: [], message: "Address book retrieved" };
5145
+ }
5146
+ async addAddressBookEntry(params) {
5147
+ return { added: true, name: params.name, address: params.address, chain: params.chain };
5148
+ }
5149
+ async removeAddressBookEntry(params) {
5150
+ return { removed: true, address: params.address };
5151
+ }
5152
+ // ============================================================================
5153
+ // Token Search & Other
5154
+ // ============================================================================
5155
+ async searchToken(params) {
5156
+ return { message: `Token search for '${params.query || params.symbol}' - delegated to backend` };
5157
+ }
5158
+ async listVaults() {
5159
+ return {
5160
+ vaults: [{
5161
+ name: this.vault.name,
5162
+ id: this.vault.id,
5163
+ type: this.vault.type,
5164
+ chains: this.vault.chains.map((c) => c.toString())
5165
+ }]
5166
+ };
5167
+ }
5168
+ async scanTx(params) {
5169
+ return { message: `Transaction scan for ${params.tx_hash || "unknown"} - delegated to backend` };
5170
+ }
5171
+ async readEvmContract(params) {
5172
+ return { message: `EVM contract read for ${params.contract || "unknown"} - delegated to backend` };
5173
+ }
5174
+ };
5175
+ async function encodeContractCall(functionName, params) {
5176
+ const types = params.map((p) => p.type);
5177
+ const sig = `${functionName}(${types.join(",")})`;
5178
+ const selector = await keccak256Selector(sig);
5179
+ let encoded = "";
5180
+ for (const param of params) {
5181
+ encoded += abiEncodeParam(param.type, param.value);
5182
+ }
5183
+ return "0x" + selector + encoded;
5184
+ }
5185
+ async function keccak256Selector(sig) {
5186
+ const { keccak_256: keccak_2562 } = await Promise.resolve().then(() => (init_sha3(), sha3_exports));
5187
+ const hash = keccak_2562(new TextEncoder().encode(sig));
5188
+ return Buffer.from(hash).toString("hex").slice(0, 8);
5189
+ }
5190
+ function abiEncodeParam(type, value) {
5191
+ if (type === "address") {
5192
+ const addr = value.startsWith("0x") ? value.slice(2) : value;
5193
+ return addr.toLowerCase().padStart(64, "0");
5194
+ }
5195
+ if (type.startsWith("uint") || type.startsWith("int")) {
5196
+ const n = BigInt(value);
5197
+ const hex = n.toString(16);
5198
+ return hex.padStart(64, "0");
5199
+ }
5200
+ if (type === "bool") {
5201
+ return (value === "true" || value === "1" ? "1" : "0").padStart(64, "0");
5202
+ }
5203
+ if (type === "bytes32") {
5204
+ const b2 = value.startsWith("0x") ? value.slice(2) : value;
5205
+ return b2.padEnd(64, "0");
5206
+ }
5207
+ const b = value.startsWith("0x") ? value.slice(2) : Buffer.from(value).toString("hex");
5208
+ return b.padStart(64, "0");
5209
+ }
5210
+ function resolveChain(name) {
5211
+ if (!name) return null;
5212
+ if (Object.values(Chain9).includes(name)) {
5213
+ return name;
5214
+ }
5215
+ const lower = name.toLowerCase();
5216
+ for (const [, value] of Object.entries(Chain9)) {
5217
+ if (typeof value === "string" && value.toLowerCase() === lower) {
5218
+ return value;
5219
+ }
5220
+ }
5221
+ const aliases = {
5222
+ eth: "Ethereum",
5223
+ btc: "Bitcoin",
5224
+ sol: "Solana",
5225
+ bnb: "BSC",
5226
+ avax: "Avalanche",
5227
+ matic: "Polygon",
5228
+ arb: "Arbitrum",
5229
+ op: "Optimism",
5230
+ ltc: "Litecoin",
5231
+ doge: "Dogecoin",
5232
+ dot: "Polkadot",
5233
+ atom: "Cosmos",
5234
+ rune: "THORChain",
5235
+ thor: "THORChain",
5236
+ sui: "Sui",
5237
+ ton: "Ton",
5238
+ trx: "Tron",
5239
+ xrp: "Ripple"
5240
+ };
5241
+ const aliased = aliases[lower];
5242
+ if (aliased && Object.values(Chain9).includes(aliased)) {
5243
+ return aliased;
5244
+ }
5245
+ return null;
5246
+ }
5247
+ function parseAmount(amountStr, decimals) {
5248
+ const [whole, frac = ""] = amountStr.split(".");
5249
+ const paddedFrac = frac.slice(0, decimals).padEnd(decimals, "0");
5250
+ return BigInt(whole || "0") * 10n ** BigInt(decimals) + BigInt(paddedFrac || "0");
5251
+ }
5252
+ function resolveChainFromTxReady(txReadyData) {
5253
+ if (txReadyData.chain) {
5254
+ const chain = resolveChain(txReadyData.chain);
5255
+ if (chain) return chain;
5256
+ }
5257
+ if (txReadyData.from_chain) {
5258
+ const chain = resolveChain(txReadyData.from_chain);
5259
+ if (chain) return chain;
5260
+ }
5261
+ if (txReadyData.chain_id) {
5262
+ const chain = resolveChainId(txReadyData.chain_id);
5263
+ if (chain) return chain;
5264
+ }
5265
+ const swapTx = txReadyData.swap_tx || txReadyData.send_tx || txReadyData.tx;
5266
+ if (swapTx?.chainId) {
5267
+ const chain = resolveChainId(swapTx.chainId);
5268
+ if (chain) return chain;
5269
+ }
5270
+ return null;
5271
+ }
5272
+ function resolveChainId(chainId) {
5273
+ const id = typeof chainId === "string" ? parseInt(chainId, 10) : chainId;
5274
+ if (isNaN(id)) return null;
5275
+ const chainIdMap = {
5276
+ 1: Chain9.Ethereum,
5277
+ 56: Chain9.BSC,
5278
+ 137: Chain9.Polygon,
5279
+ 43114: Chain9.Avalanche,
5280
+ 42161: Chain9.Arbitrum,
5281
+ 10: Chain9.Optimism,
5282
+ 8453: Chain9.Base,
5283
+ 81457: Chain9.Blast,
5284
+ 324: Chain9.Zksync,
5285
+ 25: Chain9.CronosChain
5286
+ };
5287
+ return chainIdMap[id] || null;
5288
+ }
5289
+ async function computeEIP712Hash(domain, types, primaryType, message) {
5290
+ const { keccak_256: keccak_2562 } = await Promise.resolve().then(() => (init_sha3(), sha3_exports));
5291
+ const domainSeparator = hashStruct("EIP712Domain", domain, types, keccak_2562);
5292
+ const messageHash = hashStruct(primaryType, message, types, keccak_2562);
5293
+ const prefix = new Uint8Array([25, 1]);
5294
+ const combined = new Uint8Array(2 + 32 + 32);
5295
+ combined.set(prefix, 0);
5296
+ combined.set(domainSeparator, 2);
5297
+ combined.set(messageHash, 34);
5298
+ const finalHash = keccak_2562(combined);
5299
+ return "0x" + Buffer.from(finalHash).toString("hex");
5300
+ }
5301
+ function hashStruct(typeName, data, types, keccak) {
5302
+ const typeHash = hashType(typeName, types, keccak);
5303
+ const encodedData = encodeData(typeName, data, types, keccak);
5304
+ const combined = new Uint8Array(32 + encodedData.length);
5305
+ combined.set(typeHash, 0);
5306
+ combined.set(encodedData, 32);
5307
+ return keccak(combined);
5308
+ }
5309
+ function hashType(typeName, types, keccak) {
5310
+ const encoded = encodeType(typeName, types);
5311
+ return keccak(new TextEncoder().encode(encoded));
5312
+ }
5313
+ function encodeType(typeName, types) {
5314
+ const fields = getTypeFields(typeName, types);
5315
+ if (!fields) return "";
5316
+ const refs = /* @__PURE__ */ new Set();
5317
+ findReferencedTypes(typeName, types, refs);
5318
+ refs.delete(typeName);
5319
+ const sortedRefs = [...refs].sort();
5320
+ let result = `${typeName}(${fields.map((f) => `${f.type} ${f.name}`).join(",")})`;
5321
+ for (const ref of sortedRefs) {
5322
+ const refFields = getTypeFields(ref, types);
5323
+ if (refFields) {
5324
+ result += `${ref}(${refFields.map((f) => `${f.type} ${f.name}`).join(",")})`;
5325
+ }
5326
+ }
5327
+ return result;
5328
+ }
5329
+ function findReferencedTypes(typeName, types, refs) {
5330
+ if (refs.has(typeName)) return;
5331
+ const fields = getTypeFields(typeName, types);
5332
+ if (!fields) return;
5333
+ refs.add(typeName);
5334
+ for (const field of fields) {
5335
+ const baseType = field.type.replace(/\[\d*\]$/, "");
5336
+ if (types[baseType]) {
5337
+ findReferencedTypes(baseType, types, refs);
5338
+ }
5339
+ }
5340
+ }
5341
+ function getTypeFields(typeName, types) {
5342
+ if (types[typeName]) return types[typeName];
5343
+ if (typeName === "EIP712Domain") {
5344
+ return [
5345
+ { name: "name", type: "string" },
5346
+ { name: "version", type: "string" },
5347
+ { name: "chainId", type: "uint256" },
5348
+ { name: "verifyingContract", type: "address" }
5349
+ ];
5350
+ }
5351
+ return void 0;
5352
+ }
5353
+ function encodeData(typeName, data, types, keccak) {
5354
+ const fields = getTypeFields(typeName, types);
5355
+ if (!fields) return new Uint8Array(0);
5356
+ const chunks = [];
5357
+ for (const field of fields) {
5358
+ const value = data[field.name];
5359
+ if (value === void 0 || value === null) continue;
5360
+ chunks.push(encodeField(field.type, value, types, keccak));
5361
+ }
5362
+ const totalLen = chunks.reduce((sum, c) => sum + c.length, 0);
5363
+ const result = new Uint8Array(totalLen);
5364
+ let offset = 0;
5365
+ for (const chunk of chunks) {
5366
+ result.set(chunk, offset);
5367
+ offset += chunk.length;
5368
+ }
5369
+ return result;
5370
+ }
5371
+ function encodeField(type, value, types, keccak) {
5372
+ if (type === "string") {
5373
+ return keccak(new TextEncoder().encode(value));
5374
+ }
5375
+ if (type === "bytes") {
5376
+ const hex2 = value.startsWith("0x") ? value.slice(2) : value;
5377
+ const bytes2 = hexToBytes(hex2);
5378
+ return keccak(bytes2);
5379
+ }
5380
+ const baseType = type.replace(/\[\d*\]$/, "");
5381
+ if (types[baseType] && !type.endsWith("]")) {
5382
+ return hashStruct(baseType, value, types, keccak);
5383
+ }
5384
+ if (type.endsWith("]")) {
5385
+ const arr = value;
5386
+ const elementType = type.replace(/\[\d*\]$/, "");
5387
+ const encodedElements = arr.map((el) => encodeField(elementType, el, types, keccak));
5388
+ const totalLen = encodedElements.reduce((sum, e) => sum + e.length, 0);
5389
+ const concat = new Uint8Array(totalLen);
5390
+ let off = 0;
5391
+ for (const el of encodedElements) {
5392
+ concat.set(el, off);
5393
+ off += el.length;
5394
+ }
5395
+ return keccak(concat);
5396
+ }
5397
+ const result = new Uint8Array(32);
5398
+ if (type === "address") {
5399
+ const addr = value.startsWith("0x") ? value.slice(2) : value;
5400
+ const bytes2 = hexToBytes(addr.toLowerCase());
5401
+ result.set(bytes2, 32 - bytes2.length);
5402
+ return result;
5403
+ }
5404
+ if (type === "bool") {
5405
+ if (value === true || value === "true" || value === 1 || value === "1") {
5406
+ result[31] = 1;
5407
+ }
5408
+ return result;
5409
+ }
5410
+ if (type.startsWith("uint") || type.startsWith("int")) {
5411
+ const n2 = BigInt(value);
5412
+ const hex2 = n2.toString(16).padStart(64, "0");
5413
+ const bytes2 = hexToBytes(hex2);
5414
+ result.set(bytes2, 32 - bytes2.length);
5415
+ return result;
5416
+ }
5417
+ if (type.startsWith("bytes")) {
5418
+ const hex2 = value.startsWith("0x") ? value.slice(2) : value;
5419
+ const bytes2 = hexToBytes(hex2);
5420
+ result.set(bytes2, 0);
5421
+ return result;
5422
+ }
5423
+ const n = BigInt(value);
5424
+ const hex = n.toString(16).padStart(64, "0");
5425
+ const bytes = hexToBytes(hex);
5426
+ result.set(bytes, 32 - bytes.length);
5427
+ return result;
5428
+ }
5429
+ function hexToBytes(hex) {
5430
+ const clean2 = hex.startsWith("0x") ? hex.slice(2) : hex;
5431
+ const padded = clean2.length % 2 === 1 ? "0" + clean2 : clean2;
5432
+ const bytes = new Uint8Array(padded.length / 2);
5433
+ for (let i = 0; i < bytes.length; i++) {
5434
+ bytes[i] = parseInt(padded.slice(i * 2, i * 2 + 2), 16);
5435
+ }
5436
+ return bytes;
5437
+ }
5438
+ function parseDERSignature(sigHex) {
5439
+ const raw = sigHex.startsWith("0x") ? sigHex.slice(2) : sigHex;
5440
+ if (raw.length === 128) {
5441
+ return { r: raw.slice(0, 64), s: raw.slice(64) };
5442
+ }
5443
+ let offset = 0;
5444
+ if (raw.slice(offset, offset + 2) !== "30") {
5445
+ return { r: raw.slice(0, 64).padStart(64, "0"), s: raw.slice(64).padStart(64, "0") };
5446
+ }
5447
+ offset += 2;
5448
+ offset += 2;
5449
+ if (raw.slice(offset, offset + 2) !== "02") throw new Error("Invalid DER: expected 02 for R");
5450
+ offset += 2;
5451
+ const rLen = parseInt(raw.slice(offset, offset + 2), 16);
5452
+ offset += 2;
5453
+ let rHex = raw.slice(offset, offset + rLen * 2);
5454
+ offset += rLen * 2;
5455
+ if (rHex.length > 64 && rHex.startsWith("00")) {
5456
+ rHex = rHex.slice(rHex.length - 64);
5457
+ }
5458
+ rHex = rHex.padStart(64, "0");
5459
+ if (raw.slice(offset, offset + 2) !== "02") throw new Error("Invalid DER: expected 02 for S");
5460
+ offset += 2;
5461
+ const sLen = parseInt(raw.slice(offset, offset + 2), 16);
5462
+ offset += 2;
5463
+ let sHex = raw.slice(offset, offset + sLen * 2);
5464
+ if (sHex.length > 64 && sHex.startsWith("00")) {
5465
+ sHex = sHex.slice(sHex.length - 64);
5466
+ }
5467
+ sHex = sHex.padStart(64, "0");
5468
+ return { r: rHex, s: sHex };
5469
+ }
5470
+
5471
+ // src/agent/pipe.ts
5472
+ import * as readline from "node:readline";
5473
+ var PipeInterface = class {
5474
+ session;
5475
+ rl = null;
5476
+ stopped = false;
5477
+ pendingPasswordResolve = null;
5478
+ pendingConfirmResolve = null;
5479
+ constructor(session) {
5480
+ this.session = session;
5481
+ process.stdin.pause();
5482
+ }
5483
+ /**
5484
+ * Start the pipe interface.
5485
+ */
5486
+ async start(vaultName, addresses) {
5487
+ this.rl = readline.createInterface({
5488
+ input: process.stdin,
5489
+ output: void 0,
5490
+ // Don't write prompts to stdout
5491
+ terminal: false
5492
+ });
5493
+ this.emit({ type: "ready", vault: vaultName, addresses });
5494
+ const lines = [];
5495
+ let inputDone = false;
5496
+ let processing = false;
5497
+ this.rl.on("line", async (line) => {
5498
+ const trimmed = line.trim();
5499
+ if (!trimmed) return;
5500
+ lines.push(trimmed);
5501
+ if (!processing) {
5502
+ processing = true;
5503
+ while (lines.length > 0) {
5504
+ const nextLine = lines.shift();
5505
+ try {
5506
+ const cmd = JSON.parse(nextLine);
5507
+ await this.handleCommand(cmd);
5508
+ } catch (err) {
5509
+ this.emit({ type: "error", message: `Invalid input: ${err.message}` });
5510
+ }
5511
+ }
5512
+ processing = false;
5513
+ if (inputDone && lines.length === 0) {
5514
+ this.stop();
5515
+ }
5516
+ }
5517
+ });
5518
+ this.rl.on("close", () => {
5519
+ inputDone = true;
5520
+ if (!processing && lines.length === 0) {
5521
+ this.stop();
5522
+ }
5523
+ });
5524
+ await new Promise((resolve) => {
5525
+ const check = setInterval(() => {
5526
+ if (this.stopped) {
5527
+ clearInterval(check);
5528
+ resolve();
5529
+ }
5530
+ }, 100);
5531
+ });
5532
+ }
5533
+ stop() {
5534
+ if (this.stopped) return;
5535
+ this.stopped = true;
5536
+ this.rl?.close();
5537
+ this.session.dispose();
5538
+ }
5539
+ /**
5540
+ * Get UI callbacks for the session.
5541
+ */
5542
+ getCallbacks() {
5543
+ return {
5544
+ onTextDelta: (delta) => {
5545
+ this.emit({ type: "text_delta", delta });
5546
+ },
5547
+ onToolCall: (id, action, params) => {
5548
+ this.emit({ type: "tool_call", id, action, params, status: "running" });
5549
+ },
5550
+ onToolResult: (id, action, success2, data, error2) => {
5551
+ this.emit({ type: "tool_result", id, action, success: success2, data, error: error2 });
5552
+ },
5553
+ onAssistantMessage: (content) => {
5554
+ this.emit({ type: "assistant", content });
5555
+ },
5556
+ onSuggestions: (suggestions) => {
5557
+ this.emit({ type: "suggestions", suggestions });
5558
+ },
5559
+ onTxStatus: (txHash, chain, status, explorerUrl) => {
5560
+ this.emit({
5561
+ type: "tx_status",
5562
+ tx_hash: txHash,
5563
+ chain,
5564
+ status,
5565
+ explorer_url: explorerUrl
5566
+ });
5567
+ },
5568
+ onError: (message) => {
5569
+ this.emit({ type: "error", message });
5570
+ },
5571
+ onDone: () => {
5572
+ this.emit({ type: "done" });
5573
+ },
5574
+ requestPassword: async () => {
5575
+ return new Promise((resolve) => {
5576
+ this.pendingPasswordResolve = resolve;
5577
+ this.emit({ type: "error", message: "PASSWORD_REQUIRED" });
5578
+ });
5579
+ },
5580
+ requestConfirmation: async (message) => {
5581
+ return new Promise((resolve) => {
5582
+ this.pendingConfirmResolve = resolve;
5583
+ this.emit({ type: "error", message: `CONFIRMATION_REQUIRED: ${message}` });
5584
+ });
5585
+ }
5586
+ };
5587
+ }
5588
+ async handleCommand(cmd) {
5589
+ switch (cmd.type) {
5590
+ case "message": {
5591
+ const callbacks = this.getCallbacks();
5592
+ try {
5593
+ await this.session.sendMessage(cmd.content, callbacks);
5594
+ } catch (err) {
5595
+ this.emit({ type: "error", message: err.message });
5596
+ this.emit({ type: "done" });
5597
+ }
5598
+ break;
5599
+ }
5600
+ case "password": {
5601
+ if (this.pendingPasswordResolve) {
5602
+ this.pendingPasswordResolve(cmd.password);
5603
+ this.pendingPasswordResolve = null;
5604
+ }
5605
+ break;
5606
+ }
5607
+ case "confirm": {
5608
+ if (this.pendingConfirmResolve) {
5609
+ this.pendingConfirmResolve(cmd.confirmed);
5610
+ this.pendingConfirmResolve = null;
5611
+ }
5612
+ break;
5613
+ }
5614
+ default:
5615
+ this.emit({ type: "error", message: `Unknown command type: ${cmd.type}` });
5616
+ }
5617
+ }
5618
+ emit(event) {
5619
+ process.stdout.write(JSON.stringify(event) + "\n");
5620
+ }
5621
+ };
5622
+
5623
+ // src/agent/session.ts
5624
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5625
+ import { homedir } from "node:os";
5626
+ import { join } from "node:path";
5627
+ var AgentSession = class {
5628
+ client;
5629
+ vault;
5630
+ executor;
5631
+ config;
5632
+ conversationId = null;
5633
+ publicKey;
5634
+ cachedContext = null;
5635
+ abortController = null;
5636
+ constructor(vault, config) {
5637
+ this.vault = vault;
5638
+ this.config = config;
5639
+ this.client = new AgentClient(config.backendUrl);
5640
+ this.client.verbose = !!config.verbose;
5641
+ this.executor = new AgentExecutor(vault, !!config.verbose);
5642
+ this.publicKey = vault.publicKeys.ecdsa;
5643
+ if (config.password) {
5644
+ this.executor.setPassword(config.password);
5645
+ }
5646
+ }
5647
+ /**
5648
+ * Initialize the session: health check, authenticate, create conversation.
5649
+ */
5650
+ async initialize(ui) {
5651
+ const healthy = await this.client.healthCheck();
5652
+ if (!healthy) {
5653
+ throw new Error(`Agent backend unreachable at ${this.config.backendUrl}`);
5654
+ }
5655
+ try {
5656
+ if (this.vault.isEncrypted) {
5657
+ const password = this.config.password || await ui.requestPassword();
5658
+ await this.vault.unlock?.(password);
5659
+ this.executor.setPassword(password);
5660
+ }
5661
+ const cached = loadCachedToken(this.publicKey);
5662
+ if (cached) {
5663
+ this.client.setAuthToken(cached);
5664
+ } else {
5665
+ const auth = await authenticateVault(this.client, this.vault, this.config.password);
5666
+ this.client.setAuthToken(auth.token);
5667
+ saveCachedToken(this.publicKey, auth.token, auth.expiresAt);
5668
+ }
5669
+ } catch (err) {
5670
+ throw new Error(`Authentication failed: ${err.message}`);
5671
+ }
5672
+ if (this.config.conversationId) {
5673
+ this.conversationId = this.config.conversationId;
5674
+ } else {
5675
+ const conv = await this.client.createConversation(this.publicKey);
5676
+ this.conversationId = conv.id;
5677
+ }
5678
+ this.cachedContext = await buildMessageContext(this.vault);
5679
+ }
5680
+ getConversationId() {
5681
+ return this.conversationId;
5682
+ }
5683
+ getVaultAddresses() {
5684
+ return this.cachedContext?.addresses || {};
5685
+ }
5686
+ /**
5687
+ * Send a user message and process the full response cycle.
5688
+ *
5689
+ * Flow:
5690
+ * 1. Send message to backend via SSE stream
5691
+ * 2. Collect text deltas and actions
5692
+ * 3. Execute auto-execute actions locally
5693
+ * 4. Report results back to backend
5694
+ * 5. Repeat if backend sends more actions
5695
+ */
5696
+ async sendMessage(content, ui) {
5697
+ if (!this.conversationId) {
5698
+ throw new Error("Session not initialized");
5699
+ }
5700
+ this.abortController = new AbortController();
5701
+ try {
5702
+ this.cachedContext = await buildMessageContext(this.vault);
5703
+ } catch {
5704
+ }
5705
+ try {
5706
+ await this.processMessageLoop(content, null, ui);
5707
+ } catch (err) {
5708
+ if (err.message?.includes("401") || err.message?.includes("403")) {
5709
+ clearCachedToken(this.publicKey);
5710
+ const auth = await authenticateVault(this.client, this.vault, this.config.password);
5711
+ this.client.setAuthToken(auth.token);
5712
+ saveCachedToken(this.publicKey, auth.token, auth.expiresAt);
5713
+ await this.processMessageLoop(content, null, ui);
5714
+ } else {
5715
+ throw err;
5716
+ }
5717
+ } finally {
5718
+ this.abortController = null;
5719
+ }
5720
+ }
5721
+ /**
5722
+ * Core message processing loop.
5723
+ * Sends content or action results, executes returned actions, repeats.
5724
+ */
5725
+ async processMessageLoop(content, actionResults, ui) {
5726
+ if (!this.conversationId) return;
5727
+ const request = {
5728
+ public_key: this.publicKey,
5729
+ context: this.cachedContext
5730
+ };
5731
+ if (content) {
5732
+ request.content = content;
5733
+ }
5734
+ if (actionResults && actionResults.length > 0) {
5735
+ const result = actionResults[0];
5736
+ request.action_result = {
5737
+ action: result.action,
5738
+ action_id: result.action_id,
5739
+ success: result.success,
5740
+ data: result.data || {},
5741
+ error: result.error || ""
5742
+ };
5743
+ }
5744
+ const streamResult = await this.client.sendMessageStream(
5745
+ this.conversationId,
5746
+ request,
5747
+ {
5748
+ onTextDelta: (delta) => ui.onTextDelta(delta),
5749
+ onToolProgress: (tool, status, label) => {
5750
+ if (status === "running") {
5751
+ ui.onToolCall(`mcp-${tool}`, tool);
5752
+ } else {
5753
+ ui.onToolResult(`mcp-${tool}`, tool, true, { label });
5754
+ }
5755
+ },
5756
+ onTitle: (_title) => {
5757
+ },
5758
+ onActions: (_actions) => {
5759
+ },
5760
+ onSuggestions: (suggestions) => {
5761
+ ui.onSuggestions(suggestions);
5762
+ },
5763
+ onTxReady: (tx) => {
5764
+ this.executor.storeServerTransaction(tx);
5765
+ if (this.config.password) {
5766
+ this.executor.setPassword(this.config.password);
5767
+ }
5768
+ },
5769
+ onMessage: (_msg) => {
5770
+ },
5771
+ onError: (error2) => {
5772
+ ui.onError(error2);
5773
+ }
5774
+ },
5775
+ this.abortController?.signal
5776
+ );
5777
+ const responseText = streamResult.fullText || streamResult.message?.content || "";
5778
+ const inlineActions = parseInlineToolCalls(responseText);
5779
+ if (inlineActions.length > 0) {
5780
+ const cleanText = responseText.replace(/<invoke\s+name="[^"]*">[\s\S]*?<\/invoke>/g, "").replace(/<\/?minimax:tool_call>/g, "").trim();
5781
+ if (cleanText) {
5782
+ ui.onAssistantMessage(cleanText);
5783
+ }
5784
+ streamResult.actions.push(...inlineActions);
5785
+ } else if (responseText) {
5786
+ ui.onAssistantMessage(responseText);
5787
+ }
5788
+ const nonSignActions = streamResult.actions.filter((a) => a.type !== "sign_tx");
5789
+ const backendSignActions = streamResult.actions.filter((a) => a.type === "sign_tx");
5790
+ if (nonSignActions.length > 0) {
5791
+ const results = await this.executeActions(nonSignActions, ui);
5792
+ const hasBuildSuccess = results.some(
5793
+ (r) => r.success && r.action.startsWith("build_")
5794
+ );
5795
+ if (hasBuildSuccess && this.executor.hasPendingTransaction()) {
5796
+ if (this.config.verbose) process.stderr.write(`[session] build_* action produced pending tx, auto-chaining sign_tx
5797
+ `);
5798
+ const signAction = {
5799
+ id: `tx_sign_${Date.now()}`,
5800
+ type: "sign_tx",
5801
+ title: "Sign transaction",
5802
+ params: {},
5803
+ auto_execute: true
5804
+ };
5805
+ const signResults = await this.executeActions([signAction], ui);
5806
+ const allResults = [...results, ...signResults];
5807
+ for (const result of allResults) {
5808
+ await this.processMessageLoop(null, [result], ui);
5809
+ }
5810
+ return;
5811
+ }
5812
+ if (results.length > 0) {
5813
+ for (const result of results) {
5814
+ await this.processMessageLoop(null, [result], ui);
5815
+ }
5816
+ return;
5817
+ }
5818
+ }
5819
+ if (streamResult.transactions.length > 0 && this.executor.hasPendingTransaction()) {
5820
+ if (this.config.verbose) process.stderr.write(`[session] ${streamResult.transactions.length} tx_ready events, signing client-side
5821
+ `);
5822
+ const signAction = {
5823
+ id: `tx_sign_${Date.now()}`,
5824
+ type: "sign_tx",
5825
+ title: "Sign transaction",
5826
+ params: {},
5827
+ auto_execute: true
5828
+ };
5829
+ const results = await this.executeActions([signAction], ui);
5830
+ if (results.length > 0) {
5831
+ for (const result of results) {
5832
+ await this.processMessageLoop(null, [result], ui);
5833
+ }
5834
+ return;
5835
+ }
5836
+ } else if (backendSignActions.length > 0 && this.executor.hasPendingTransaction()) {
5837
+ if (this.config.verbose) process.stderr.write(`[session] Backend sent sign_tx action, using it
5838
+ `);
5839
+ const results = await this.executeActions(backendSignActions, ui);
5840
+ if (results.length > 0) {
5841
+ for (const result of results) {
5842
+ await this.processMessageLoop(null, [result], ui);
5843
+ }
5844
+ return;
5845
+ }
5846
+ } else if (backendSignActions.length > 0 && !this.executor.hasPendingTransaction()) {
5847
+ if (this.config.verbose) process.stderr.write(`[session] Backend sent sign_tx but no pending tx, reporting error
5848
+ `);
5849
+ const errorResult = {
5850
+ action: "sign_tx",
5851
+ action_id: backendSignActions[0].id,
5852
+ success: false,
5853
+ error: "No pending transaction. The swap transaction data was not received."
5854
+ };
5855
+ await this.processMessageLoop(null, [errorResult], ui);
5856
+ return;
5857
+ }
5858
+ ui.onDone();
5859
+ }
5860
+ /**
5861
+ * Execute a list of actions, handling password requirements.
5862
+ */
5863
+ async executeActions(actions, ui) {
5864
+ const results = [];
5865
+ for (const action of actions) {
5866
+ if (!this.executor.shouldAutoExecute(action)) {
5867
+ continue;
5868
+ }
5869
+ if (PASSWORD_REQUIRED_ACTIONS.has(action.type)) {
5870
+ if (!this.config.password) {
5871
+ try {
5872
+ const password = await ui.requestPassword();
5873
+ this.executor.setPassword(password);
5874
+ this.config.password = password;
5875
+ } catch {
5876
+ results.push({
5877
+ action: action.type,
5878
+ action_id: action.id,
5879
+ success: false,
5880
+ error: "Password not provided"
5881
+ });
5882
+ continue;
5883
+ }
5884
+ }
5885
+ }
5886
+ ui.onToolCall(action.id, action.type, action.params);
5887
+ const result = await this.executor.executeAction(action);
5888
+ results.push(result);
5889
+ ui.onToolResult(action.id, action.type, result.success, result.data, result.error);
5890
+ if (action.type === "sign_tx" && result.success && result.data) {
5891
+ const txHash = result.data.tx_hash;
5892
+ const chain = result.data.chain;
5893
+ const explorerUrl = result.data.explorer_url;
5894
+ if (txHash) {
5895
+ ui.onTxStatus(txHash, chain, "pending", explorerUrl);
5896
+ }
5897
+ }
5898
+ }
5899
+ return results;
5900
+ }
5901
+ /**
5902
+ * Cancel the current operation.
5903
+ */
5904
+ cancel() {
5905
+ this.abortController?.abort();
5906
+ }
5907
+ /**
5908
+ * Clean up session resources.
5909
+ */
5910
+ dispose() {
5911
+ this.cancel();
5912
+ this.cachedContext = null;
5913
+ this.conversationId = null;
5914
+ }
5915
+ };
5916
+ function parseInlineToolCalls(text) {
5917
+ const actions = [];
5918
+ const invokeRegex = /<invoke\s+name="([^"]+)">([\s\S]*?)<\/invoke>/g;
5919
+ let match;
5920
+ while ((match = invokeRegex.exec(text)) !== null) {
5921
+ const actionType = match[1];
5922
+ const body = match[2];
5923
+ const params = {};
5924
+ const paramRegex = /<parameter\s+name="([^"]+)">([\s\S]*?)<\/parameter>/g;
5925
+ let paramMatch;
5926
+ while ((paramMatch = paramRegex.exec(body)) !== null) {
5927
+ const key = paramMatch[1];
5928
+ const value = paramMatch[2];
5929
+ try {
5930
+ params[key] = JSON.parse(value);
5931
+ } catch {
5932
+ params[key] = value;
5933
+ }
5934
+ }
5935
+ actions.push({
5936
+ id: `inline_${actionType}_${Date.now()}`,
5937
+ type: actionType,
5938
+ title: actionType,
5939
+ params,
5940
+ auto_execute: true
5941
+ });
5942
+ }
5943
+ return actions;
5944
+ }
5945
+ function getTokenCachePath() {
5946
+ const dir = process.env.VULTISIG_CONFIG_DIR ?? join(homedir(), ".vultisig");
5947
+ return join(dir, "agent-tokens.json");
5948
+ }
5949
+ function readTokenStore() {
5950
+ try {
5951
+ const path3 = getTokenCachePath();
5952
+ if (!existsSync(path3)) return {};
5953
+ return JSON.parse(readFileSync(path3, "utf-8"));
5954
+ } catch {
5955
+ return {};
5956
+ }
5957
+ }
5958
+ function writeTokenStore(store) {
5959
+ const path3 = getTokenCachePath();
5960
+ const dir = join(path3, "..");
5961
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
5962
+ writeFileSync(path3, JSON.stringify(store, null, 2), { mode: 384 });
5963
+ }
5964
+ function loadCachedToken(publicKey) {
5965
+ const store = readTokenStore();
5966
+ const entry = store[publicKey];
5967
+ if (!entry) return null;
5968
+ const now = Date.now();
5969
+ const expiresMs = entry.expiresAt * (entry.expiresAt < 1e12 ? 1e3 : 1);
5970
+ if (now >= expiresMs - 6e4) {
5971
+ delete store[publicKey];
5972
+ try {
5973
+ writeTokenStore(store);
5974
+ } catch {
5975
+ }
5976
+ return null;
5977
+ }
5978
+ return entry.token;
5979
+ }
5980
+ function saveCachedToken(publicKey, token, expiresAt) {
5981
+ const store = readTokenStore();
5982
+ store[publicKey] = { token, expiresAt };
5983
+ try {
5984
+ writeTokenStore(store);
5985
+ } catch {
5986
+ }
5987
+ }
5988
+ function clearCachedToken(publicKey) {
5989
+ const store = readTokenStore();
5990
+ delete store[publicKey];
5991
+ try {
5992
+ writeTokenStore(store);
5993
+ } catch {
5994
+ }
5995
+ }
5996
+
5997
+ // src/agent/tui.ts
5998
+ import * as readline2 from "node:readline";
5999
+ import chalk8 from "chalk";
6000
+ var ChatTUI = class {
6001
+ rl;
6002
+ session;
6003
+ isStreaming = false;
6004
+ currentStreamText = "";
6005
+ vaultName;
6006
+ stopped = false;
6007
+ verbose;
6008
+ constructor(session, vaultName, verbose = false) {
6009
+ this.session = session;
6010
+ this.vaultName = vaultName;
6011
+ this.verbose = verbose;
6012
+ this.rl = readline2.createInterface({
6013
+ input: process.stdin,
6014
+ output: process.stdout,
6015
+ prompt: "",
6016
+ terminal: true
6017
+ });
6018
+ }
6019
+ /**
6020
+ * Start the interactive chat loop.
6021
+ */
6022
+ async start() {
6023
+ this.printHeader();
6024
+ this.printHelp();
6025
+ this.showPrompt();
6026
+ this.rl.on("line", async (line) => {
6027
+ const input = line.trim();
6028
+ readline2.moveCursor(process.stdout, 0, -1);
6029
+ readline2.clearLine(process.stdout, 0);
6030
+ if (!input) {
6031
+ this.showPrompt();
6032
+ return;
6033
+ }
6034
+ if (input === "/quit" || input === "/exit" || input === "/q") {
6035
+ this.stop();
6036
+ return;
6037
+ }
6038
+ if (input === "/help" || input === "/h") {
6039
+ this.printHelp();
6040
+ this.showPrompt();
6041
+ return;
6042
+ }
6043
+ if (input === "/clear") {
6044
+ console.clear();
6045
+ this.printHeader();
6046
+ this.showPrompt();
6047
+ return;
6048
+ }
6049
+ this.printUserMessage(input);
6050
+ await this.handleMessage(input);
6051
+ this.showPrompt();
6052
+ });
6053
+ this.rl.on("close", () => {
6054
+ this.stop();
6055
+ });
6056
+ process.on("SIGINT", () => {
6057
+ if (this.isStreaming) {
6058
+ this.session.cancel();
6059
+ this.isStreaming = false;
6060
+ console.log(chalk8.yellow("\n [cancelled]"));
6061
+ this.showPrompt();
6062
+ } else {
6063
+ this.stop();
6064
+ }
6065
+ });
6066
+ await new Promise((resolve) => {
6067
+ const check = setInterval(() => {
6068
+ if (this.stopped) {
6069
+ clearInterval(check);
6070
+ resolve();
6071
+ }
6072
+ }, 100);
6073
+ });
6074
+ }
6075
+ stop() {
6076
+ if (this.stopped) return;
6077
+ this.stopped = true;
6078
+ console.log(chalk8.gray("\n Goodbye!\n"));
6079
+ this.rl.close();
6080
+ this.session.dispose();
6081
+ }
6082
+ /**
6083
+ * Get UI callbacks for the session.
6084
+ */
6085
+ getCallbacks() {
6086
+ return {
6087
+ onTextDelta: (delta) => {
6088
+ if (!this.isStreaming) {
6089
+ this.isStreaming = true;
6090
+ this.currentStreamText = "";
6091
+ const ts = this.timestamp();
6092
+ process.stdout.write(`${chalk8.gray(ts)} ${chalk8.cyan.bold("Agent")}: `);
6093
+ }
6094
+ this.currentStreamText += delta;
6095
+ },
6096
+ onToolCall: (_id, action, params) => {
6097
+ if (this.isStreaming) {
6098
+ process.stdout.write("\n");
6099
+ this.isStreaming = false;
6100
+ }
6101
+ if (this.verbose) {
6102
+ const paramStr = params ? chalk8.gray(` ${JSON.stringify(params).slice(0, 80)}`) : "";
6103
+ console.log(` ${chalk8.yellow("\u26A1")} ${chalk8.yellow(action)}${paramStr} ${chalk8.gray("...")}`);
6104
+ } else {
6105
+ console.log(` ${chalk8.yellow("\u26A1")} ${chalk8.yellow(action)} ${chalk8.gray("...")}`);
6106
+ }
6107
+ },
6108
+ onToolResult: (_id, action, success2, data, error2) => {
6109
+ if (success2) {
6110
+ if (this.verbose) {
6111
+ const summary = data ? summarizeData(data) : "";
6112
+ console.log(` ${chalk8.green("\u2713")} ${chalk8.green(action)}${summary ? chalk8.gray(` \u2192 ${summary}`) : ""}`);
6113
+ } else {
6114
+ console.log(` ${chalk8.green("\u2713")} ${chalk8.green(action)}`);
6115
+ }
6116
+ } else {
6117
+ console.log(` ${chalk8.red("\u2717")} ${chalk8.red(action)}: ${chalk8.red(error2 || "failed")}`);
6118
+ }
6119
+ },
6120
+ onAssistantMessage: (content) => {
6121
+ if (this.isStreaming) {
6122
+ process.stdout.write(renderMarkdown(this.currentStreamText) + "\n");
6123
+ this.isStreaming = false;
6124
+ } else if (content && content !== this.currentStreamText) {
6125
+ const ts = this.timestamp();
6126
+ console.log(`${chalk8.gray(ts)} ${chalk8.cyan.bold("Agent")}: ${renderMarkdown(content)}`);
6127
+ }
6128
+ this.currentStreamText = "";
6129
+ },
6130
+ onSuggestions: (suggestions) => {
6131
+ if (suggestions.length > 0) {
6132
+ console.log(chalk8.gray(" Suggestions:"));
6133
+ for (const s of suggestions) {
6134
+ console.log(chalk8.gray(` \u2022 ${s.title}`));
6135
+ }
6136
+ }
6137
+ },
6138
+ onTxStatus: (txHash, chain, status, explorerUrl) => {
6139
+ const statusIcon = status === "confirmed" ? chalk8.green("\u2713") : status === "failed" ? chalk8.red("\u2717") : chalk8.yellow("\u23F3");
6140
+ console.log(` ${statusIcon} ${chalk8.bold("TX")} [${chain}]: ${txHash.slice(0, 12)}...${txHash.slice(-8)}`);
6141
+ if (explorerUrl) {
6142
+ console.log(` ${chalk8.blue.underline(explorerUrl)}`);
6143
+ }
6144
+ },
6145
+ onError: (message) => {
6146
+ if (this.isStreaming) {
6147
+ process.stdout.write("\n");
6148
+ this.isStreaming = false;
6149
+ }
6150
+ console.log(` ${chalk8.red("Error")}: ${message}`);
6151
+ },
6152
+ onDone: () => {
6153
+ if (this.isStreaming) {
6154
+ process.stdout.write(renderMarkdown(this.currentStreamText) + "\n");
6155
+ this.isStreaming = false;
6156
+ this.currentStreamText = "";
6157
+ }
6158
+ },
6159
+ requestPassword: async () => {
6160
+ return new Promise((resolve, reject) => {
6161
+ const rl2 = readline2.createInterface({
6162
+ input: process.stdin,
6163
+ output: process.stdout,
6164
+ terminal: true
6165
+ });
6166
+ if (process.stdin.isTTY) {
6167
+ process.stdout.write(chalk8.yellow(" \u{1F510} Enter vault password: "));
6168
+ const wasRaw = process.stdin.isRaw;
6169
+ process.stdin.setRawMode(true);
6170
+ let password = "";
6171
+ const onData = (key) => {
6172
+ const ch = key.toString();
6173
+ if (ch === "\r" || ch === "\n") {
6174
+ process.stdin.setRawMode(wasRaw || false);
6175
+ process.stdin.removeListener("data", onData);
6176
+ process.stdout.write("\n");
6177
+ rl2.close();
6178
+ resolve(password);
6179
+ } else if (ch === "") {
6180
+ process.stdin.setRawMode(wasRaw || false);
6181
+ process.stdin.removeListener("data", onData);
6182
+ rl2.close();
6183
+ reject(new Error("Password input cancelled"));
6184
+ } else if (ch === "\x7F" || ch === "\b") {
6185
+ if (password.length > 0) {
6186
+ password = password.slice(0, -1);
6187
+ process.stdout.write("\b \b");
6188
+ }
6189
+ } else if (ch.charCodeAt(0) >= 32) {
6190
+ password += ch;
6191
+ process.stdout.write("*");
6192
+ }
6193
+ };
6194
+ process.stdin.on("data", onData);
6195
+ } else {
6196
+ rl2.question("Password: ", (answer) => {
6197
+ rl2.close();
6198
+ resolve(answer.trim());
6199
+ });
6200
+ }
6201
+ });
6202
+ },
6203
+ requestConfirmation: async (message) => {
6204
+ return new Promise((resolve) => {
6205
+ this.rl.question(chalk8.yellow(` ${message} (y/N): `), (answer) => {
6206
+ resolve(answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes");
6207
+ });
6208
+ });
6209
+ }
6210
+ };
6211
+ }
6212
+ async handleMessage(content) {
6213
+ const callbacks = this.getCallbacks();
6214
+ this.isStreaming = false;
6215
+ try {
6216
+ await this.session.sendMessage(content, callbacks);
6217
+ } catch (err) {
6218
+ if (err.name === "AbortError") {
6219
+ console.log(chalk8.yellow(" [cancelled]"));
6220
+ } else {
6221
+ console.log(chalk8.red(` Error: ${err.message}`));
6222
+ }
6223
+ }
6224
+ }
6225
+ printHeader() {
6226
+ console.log("");
6227
+ console.log(chalk8.bold.cyan(` \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`));
6228
+ console.log(chalk8.bold.cyan(` \u2551`) + chalk8.bold(` Vultisig Agent - ${this.vaultName}`.padEnd(38).slice(0, 38)) + chalk8.bold.cyan(`\u2551`));
6229
+ console.log(chalk8.bold.cyan(` \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
6230
+ console.log("");
6231
+ }
6232
+ printHelp() {
6233
+ console.log(chalk8.gray(" Commands: /help, /clear, /quit"));
6234
+ console.log(chalk8.gray(" Press Ctrl+C to cancel a response, or to exit"));
6235
+ console.log("");
6236
+ }
6237
+ printUserMessage(content) {
6238
+ const ts = this.timestamp();
6239
+ console.log(`${chalk8.gray(ts)} ${chalk8.green.bold("You")}: ${content}`);
6240
+ }
6241
+ showPrompt() {
6242
+ if (this.stopped) return;
6243
+ const prompt = chalk8.gray(`${this.timestamp()} `) + chalk8.green.bold("You") + ": ";
6244
+ this.rl.setPrompt(prompt);
6245
+ this.rl.prompt();
6246
+ }
6247
+ timestamp() {
6248
+ const now = /* @__PURE__ */ new Date();
6249
+ return `[${now.getHours().toString().padStart(2, "0")}:${now.getMinutes().toString().padStart(2, "0")}:${now.getSeconds().toString().padStart(2, "0")}]`;
6250
+ }
6251
+ };
6252
+ function renderMarkdown(text) {
6253
+ return text.replace(/\*\*(.+?)\*\*/g, (_m, p1) => chalk8.bold(p1)).replace(/__(.+?)__/g, (_m, p1) => chalk8.bold(p1)).replace(/(?<!\w)\*([^*]+?)\*(?!\w)/g, (_m, p1) => chalk8.italic(p1)).replace(/(?<!\w)_([^_]+?)_(?!\w)/g, (_m, p1) => chalk8.italic(p1)).replace(/`([^`]+?)`/g, (_m, p1) => chalk8.cyan(p1)).replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, p1, p2) => `${p1} ${chalk8.blue.underline(`(${p2})`)}`);
6254
+ }
6255
+ function summarizeData(data) {
6256
+ if (data.balances && Array.isArray(data.balances)) {
6257
+ const balances = data.balances;
6258
+ if (balances.length === 1) {
6259
+ return `${balances[0].amount} ${balances[0].symbol}`;
6260
+ }
6261
+ return `${balances.length} balances`;
6262
+ }
6263
+ if (data.tx_hash) {
6264
+ return `tx: ${data.tx_hash.slice(0, 12)}...`;
6265
+ }
6266
+ if (data.added) return "added";
6267
+ if (data.removed) return "removed";
6268
+ if (data.message) return data.message;
6269
+ return "";
6270
+ }
6271
+
6272
+ // src/commands/agent.ts
6273
+ async function executeAgent(ctx2, options) {
6274
+ const vault = await ctx2.ensureActiveVault();
6275
+ const config = {
6276
+ backendUrl: options.backendUrl || process.env.VULTISIG_AGENT_URL || "http://localhost:9998",
6277
+ vaultName: vault.name,
6278
+ password: options.password,
6279
+ viaAgent: options.viaAgent,
6280
+ conversationId: options.conversationId,
6281
+ verbose: options.verbose
6282
+ };
6283
+ const session = new AgentSession(vault, config);
6284
+ if (options.viaAgent) {
6285
+ const pipe = new PipeInterface(session);
6286
+ const callbacks = pipe.getCallbacks();
6287
+ try {
6288
+ await session.initialize(callbacks);
6289
+ const addresses = session.getVaultAddresses();
6290
+ await pipe.start(vault.name, addresses);
6291
+ } catch (err) {
6292
+ process.stdout.write(JSON.stringify({ type: "error", message: err.message }) + "\n");
6293
+ process.exit(1);
6294
+ }
6295
+ } else {
6296
+ const tui = new ChatTUI(session, vault.name, config.verbose);
6297
+ const callbacks = tui.getCallbacks();
6298
+ try {
6299
+ await session.initialize(callbacks);
6300
+ await tui.start();
6301
+ } catch (err) {
6302
+ console.error(`Agent error: ${err.message}`);
6303
+ process.exit(1);
6304
+ }
6305
+ }
6306
+ }
6307
+
6308
+ // src/interactive/completer.ts
6309
+ import { Chain as Chain10 } from "@vultisig/sdk";
6310
+ import fs2 from "fs";
6311
+ import path2 from "path";
6312
+ var COMMANDS = [
6313
+ // Vault management
6314
+ "vaults",
6315
+ "vault",
6316
+ "import",
6317
+ "delete",
6318
+ "create-from-seedphrase",
6319
+ "create",
6320
+ "join",
6321
+ "info",
6322
+ "export",
6323
+ // Wallet operations
6324
+ "balance",
6325
+ "bal",
6326
+ "send",
6327
+ "tx-status",
6328
+ "portfolio",
6329
+ "addresses",
6330
+ "chains",
6331
+ "tokens",
6332
+ // Swap operations
6333
+ "swap-chains",
6334
+ "swap-quote",
6335
+ "swap",
6336
+ // Session commands (shell-only)
6337
+ "lock",
6338
+ "unlock",
6339
+ "status",
6340
+ // Settings
6341
+ "currency",
6342
+ "server",
6343
+ "address-book",
6344
+ // Help
6345
+ "help",
6346
+ "?",
6347
+ // REPL commands
6348
+ ".help",
6349
+ ".clear",
6350
+ ".exit"
6351
+ ];
6352
+ function createCompleter(ctx2) {
6353
+ return function completer(line) {
6354
+ try {
6355
+ const parts = line.split(/\s+/);
6356
+ const command = parts[0]?.toLowerCase();
6357
+ if ((command === "import" || command === "export") && parts.length > 1) {
6358
+ const partial = parts.slice(1).join(" ");
6359
+ return completeFilePath(partial, command === "import");
6360
+ }
6361
+ if (command === "vault" && parts.length > 1) {
6362
+ const partial = parts.slice(1).join(" ");
6363
+ return completeVaultName(ctx2, partial);
6364
+ }
6365
+ if (command === "chains" && parts.length >= 2) {
6366
+ const lastPart = parts[parts.length - 1] || "";
6367
+ const lastPartLower = lastPart.toLowerCase();
6368
+ if (lastPartLower.startsWith("-")) {
6369
+ const flags = ["--add", "--add-all", "--remove"];
6370
+ const matches = flags.filter((f) => f.startsWith(lastPartLower));
6371
+ return [matches.length ? matches : flags, lastPart];
6372
+ }
6373
+ const flag = parts[parts.length - 2]?.toLowerCase();
6374
+ if (flag === "--add" || flag === "--remove") {
6375
+ return completeChainName(lastPart);
6376
+ }
6377
+ }
6378
+ if (["balance", "bal", "tokens", "send", "swap", "swap-quote", "tx-status"].includes(command) && parts.length === 2) {
6379
+ const partial = parts[1] || "";
6380
+ return completeChainName(partial);
6381
+ }
6382
+ if ((command === "create" || command === "create-from-seedphrase") && parts.length === 2) {
6383
+ const types = ["fast", "secure"];
6384
+ const partial = parts[1] || "";
6385
+ const partialLower = partial.toLowerCase();
6386
+ const matches = types.filter((t) => t.startsWith(partialLower));
6387
+ return [matches.length ? matches : types, partial];
6388
+ }
6389
+ if (command === "join" && parts.length === 2) {
6390
+ const types = ["secure"];
6391
+ const partial = parts[1] || "";
6392
+ const partialLower = partial.toLowerCase();
6393
+ const matches = types.filter((t) => t.startsWith(partialLower));
6394
+ return [matches.length ? matches : types, partial];
6395
+ }
6396
+ const hits = COMMANDS.filter((c) => c.startsWith(line));
6397
+ const show = hits.length ? hits : COMMANDS;
6398
+ return [show, line];
6399
+ } catch {
6400
+ return [[], line];
6401
+ }
6402
+ };
6403
+ }
6404
+ function completeFilePath(partial, filterVult) {
6405
+ try {
6406
+ const endsWithSeparator = partial.endsWith("/") || partial.endsWith(path2.sep);
6407
+ let dir;
6408
+ let basename;
6409
+ if (endsWithSeparator) {
6410
+ dir = partial;
6411
+ basename = "";
6412
+ } else {
6413
+ dir = path2.dirname(partial);
6414
+ basename = path2.basename(partial);
6415
+ if (fs2.existsSync(partial) && fs2.statSync(partial).isDirectory()) {
6416
+ dir = partial;
6417
+ basename = "";
6418
+ }
6419
+ }
6420
+ const resolvedDir = path2.resolve(dir);
6421
+ if (!fs2.existsSync(resolvedDir) || !fs2.statSync(resolvedDir).isDirectory()) {
6422
+ return [[], partial];
6423
+ }
6424
+ const files = fs2.readdirSync(resolvedDir);
6425
+ const matches = files.filter((file) => file.startsWith(basename)).map((file) => {
6426
+ const fullPath = path2.join(dir, file);
6427
+ const stats = fs2.statSync(path2.join(resolvedDir, file));
6428
+ if (stats.isDirectory()) {
6429
+ return fullPath + "/";
6430
+ }
6431
+ if (filterVult) {
6432
+ if (file.endsWith(".vult") || stats.isDirectory()) {
3823
6433
  return fullPath;
3824
6434
  }
3825
6435
  return null;
@@ -3840,7 +6450,7 @@ function completeVaultName(ctx2, partial) {
3840
6450
  return [show, partial];
3841
6451
  }
3842
6452
  function completeChainName(partial) {
3843
- const allChains = Object.values(Chain6);
6453
+ const allChains = Object.values(Chain10);
3844
6454
  const partialLower = partial.toLowerCase();
3845
6455
  const matches = allChains.filter((chain) => chain.toLowerCase().startsWith(partialLower));
3846
6456
  matches.sort();
@@ -3848,14 +6458,14 @@ function completeChainName(partial) {
3848
6458
  return [show, partial];
3849
6459
  }
3850
6460
  function findChainByName(name) {
3851
- const allChains = Object.values(Chain6);
6461
+ const allChains = Object.values(Chain10);
3852
6462
  const nameLower = name.toLowerCase();
3853
6463
  const found = allChains.find((chain) => chain.toLowerCase() === nameLower);
3854
6464
  return found ? found : null;
3855
6465
  }
3856
6466
 
3857
6467
  // src/interactive/event-buffer.ts
3858
- import chalk8 from "chalk";
6468
+ import chalk9 from "chalk";
3859
6469
  var EventBuffer = class {
3860
6470
  eventBuffer = [];
3861
6471
  isCommandRunning = false;
@@ -3895,17 +6505,17 @@ var EventBuffer = class {
3895
6505
  displayEvent(message, type) {
3896
6506
  switch (type) {
3897
6507
  case "success":
3898
- console.log(chalk8.green(message));
6508
+ console.log(chalk9.green(message));
3899
6509
  break;
3900
6510
  case "warning":
3901
- console.log(chalk8.yellow(message));
6511
+ console.log(chalk9.yellow(message));
3902
6512
  break;
3903
6513
  case "error":
3904
- console.error(chalk8.red(message));
6514
+ console.error(chalk9.red(message));
3905
6515
  break;
3906
6516
  case "info":
3907
6517
  default:
3908
- console.log(chalk8.blue(message));
6518
+ console.log(chalk9.blue(message));
3909
6519
  break;
3910
6520
  }
3911
6521
  }
@@ -3916,13 +6526,13 @@ var EventBuffer = class {
3916
6526
  if (this.eventBuffer.length === 0) {
3917
6527
  return;
3918
6528
  }
3919
- console.log(chalk8.gray("\n--- Background Events ---"));
6529
+ console.log(chalk9.gray("\n--- Background Events ---"));
3920
6530
  this.eventBuffer.forEach((event) => {
3921
6531
  const timeStr = event.timestamp.toLocaleTimeString();
3922
6532
  const message = `[${timeStr}] ${event.message}`;
3923
6533
  this.displayEvent(message, event.type);
3924
6534
  });
3925
- console.log(chalk8.gray("--- End Events ---\n"));
6535
+ console.log(chalk9.gray("--- End Events ---\n"));
3926
6536
  }
3927
6537
  /**
3928
6538
  * Setup all vault event listeners
@@ -3939,6 +6549,12 @@ var EventBuffer = class {
3939
6549
  this.handleEvent(`+ Transaction broadcast on ${chain}`, "success");
3940
6550
  this.handleEvent(` TX Hash: ${txHash}`, "info");
3941
6551
  });
6552
+ vault.on("transactionConfirmed", ({ chain, txHash }) => {
6553
+ this.handleEvent(`+ Transaction confirmed on ${chain}: ${txHash}`, "success");
6554
+ });
6555
+ vault.on("transactionFailed", ({ chain, txHash }) => {
6556
+ this.handleEvent(`x Transaction failed on ${chain}: ${txHash}`, "error");
6557
+ });
3942
6558
  vault.on("signingProgress", ({ step }) => {
3943
6559
  this.handleEvent(`i Signing: ${step}`, "info");
3944
6560
  });
@@ -4002,6 +6618,8 @@ var EventBuffer = class {
4002
6618
  vault.removeAllListeners("balanceUpdated");
4003
6619
  vault.removeAllListeners("transactionSigned");
4004
6620
  vault.removeAllListeners("transactionBroadcast");
6621
+ vault.removeAllListeners("transactionConfirmed");
6622
+ vault.removeAllListeners("transactionFailed");
4005
6623
  vault.removeAllListeners("signingProgress");
4006
6624
  vault.removeAllListeners("chainAdded");
4007
6625
  vault.removeAllListeners("chainRemoved");
@@ -4024,12 +6642,12 @@ var EventBuffer = class {
4024
6642
 
4025
6643
  // src/interactive/session.ts
4026
6644
  import { fiatCurrencies as fiatCurrencies3 } from "@vultisig/sdk";
4027
- import chalk10 from "chalk";
6645
+ import chalk11 from "chalk";
4028
6646
  import ora3 from "ora";
4029
- import * as readline from "readline";
6647
+ import * as readline3 from "readline";
4030
6648
 
4031
6649
  // src/interactive/shell-commands.ts
4032
- import chalk9 from "chalk";
6650
+ import chalk10 from "chalk";
4033
6651
  import Table from "cli-table3";
4034
6652
  import inquirer6 from "inquirer";
4035
6653
  import ora2 from "ora";
@@ -4046,25 +6664,25 @@ function formatTimeRemaining(ms) {
4046
6664
  async function executeLock(ctx2) {
4047
6665
  const vault = ctx2.getActiveVault();
4048
6666
  if (!vault) {
4049
- console.log(chalk9.red("No active vault."));
4050
- console.log(chalk9.yellow('Use "vault <name>" to switch to a vault first.'));
6667
+ console.log(chalk10.red("No active vault."));
6668
+ console.log(chalk10.yellow('Use "vault <name>" to switch to a vault first.'));
4051
6669
  return;
4052
6670
  }
4053
6671
  ctx2.lockVault(vault.id);
4054
- console.log(chalk9.green("\n+ Vault locked"));
4055
- console.log(chalk9.gray("Password cache cleared. You will need to enter the password again."));
6672
+ console.log(chalk10.green("\n+ Vault locked"));
6673
+ console.log(chalk10.gray("Password cache cleared. You will need to enter the password again."));
4056
6674
  }
4057
6675
  async function executeUnlock(ctx2) {
4058
6676
  const vault = ctx2.getActiveVault();
4059
6677
  if (!vault) {
4060
- console.log(chalk9.red("No active vault."));
4061
- console.log(chalk9.yellow('Use "vault <name>" to switch to a vault first.'));
6678
+ console.log(chalk10.red("No active vault."));
6679
+ console.log(chalk10.yellow('Use "vault <name>" to switch to a vault first.'));
4062
6680
  return;
4063
6681
  }
4064
6682
  if (ctx2.isVaultUnlocked(vault.id)) {
4065
6683
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
4066
- console.log(chalk9.yellow("\nVault is already unlocked."));
4067
- console.log(chalk9.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
6684
+ console.log(chalk10.yellow("\nVault is already unlocked."));
6685
+ console.log(chalk10.gray(`Time remaining: ${formatTimeRemaining(timeRemaining)}`));
4068
6686
  return;
4069
6687
  }
4070
6688
  const { password } = await inquirer6.prompt([
@@ -4081,19 +6699,19 @@ async function executeUnlock(ctx2) {
4081
6699
  ctx2.cachePassword(vault.id, password);
4082
6700
  const timeRemaining = ctx2.getUnlockTimeRemaining(vault.id);
4083
6701
  spinner.succeed("Vault unlocked");
4084
- console.log(chalk9.green(`
6702
+ console.log(chalk10.green(`
4085
6703
  + Vault unlocked for ${formatTimeRemaining(timeRemaining)}`));
4086
6704
  } catch (err) {
4087
6705
  spinner.fail("Failed to unlock vault");
4088
- console.error(chalk9.red(`
6706
+ console.error(chalk10.red(`
4089
6707
  x ${err.message}`));
4090
6708
  }
4091
6709
  }
4092
6710
  async function executeStatus(ctx2) {
4093
6711
  const vault = ctx2.getActiveVault();
4094
6712
  if (!vault) {
4095
- console.log(chalk9.red("No active vault."));
4096
- console.log(chalk9.yellow('Use "vault <name>" to switch to a vault first.'));
6713
+ console.log(chalk10.red("No active vault."));
6714
+ console.log(chalk10.yellow('Use "vault <name>" to switch to a vault first.'));
4097
6715
  return;
4098
6716
  }
4099
6717
  const isUnlocked = ctx2.isVaultUnlocked(vault.id);
@@ -4124,30 +6742,30 @@ async function executeStatus(ctx2) {
4124
6742
  displayStatus(status);
4125
6743
  }
4126
6744
  function displayStatus(status) {
4127
- console.log(chalk9.cyan("\n+----------------------------------------+"));
4128
- console.log(chalk9.cyan("| Vault Status |"));
4129
- console.log(chalk9.cyan("+----------------------------------------+\n"));
4130
- console.log(chalk9.bold("Vault:"));
4131
- console.log(` Name: ${chalk9.green(status.name)}`);
6745
+ console.log(chalk10.cyan("\n+----------------------------------------+"));
6746
+ console.log(chalk10.cyan("| Vault Status |"));
6747
+ console.log(chalk10.cyan("+----------------------------------------+\n"));
6748
+ console.log(chalk10.bold("Vault:"));
6749
+ console.log(` Name: ${chalk10.green(status.name)}`);
4132
6750
  console.log(` ID: ${status.id}`);
4133
- console.log(` Type: ${chalk9.yellow(status.type)}`);
4134
- console.log(chalk9.bold("\nSecurity:"));
6751
+ console.log(` Type: ${chalk10.yellow(status.type)}`);
6752
+ console.log(chalk10.bold("\nSecurity:"));
4135
6753
  if (status.isUnlocked) {
4136
- console.log(` Status: ${chalk9.green("Unlocked")} ${chalk9.green("\u{1F513}")}`);
6754
+ console.log(` Status: ${chalk10.green("Unlocked")} ${chalk10.green("\u{1F513}")}`);
4137
6755
  console.log(` Expires: ${status.timeRemainingFormatted}`);
4138
6756
  } else {
4139
- console.log(` Status: ${chalk9.yellow("Locked")} ${chalk9.yellow("\u{1F512}")}`);
6757
+ console.log(` Status: ${chalk10.yellow("Locked")} ${chalk10.yellow("\u{1F512}")}`);
4140
6758
  }
4141
- console.log(` Encrypted: ${status.isEncrypted ? chalk9.green("Yes") : chalk9.gray("No")}`);
4142
- console.log(` Backed Up: ${status.isBackedUp ? chalk9.green("Yes") : chalk9.yellow("No")}`);
4143
- console.log(chalk9.bold("\nMPC Configuration:"));
6759
+ console.log(` Encrypted: ${status.isEncrypted ? chalk10.green("Yes") : chalk10.gray("No")}`);
6760
+ console.log(` Backed Up: ${status.isBackedUp ? chalk10.green("Yes") : chalk10.yellow("No")}`);
6761
+ console.log(chalk10.bold("\nMPC Configuration:"));
4144
6762
  console.log(` Library: ${status.libType}`);
4145
- console.log(` Threshold: ${chalk9.cyan(status.threshold)} of ${chalk9.cyan(status.totalSigners)}`);
4146
- console.log(chalk9.bold("\nSigning Modes:"));
6763
+ console.log(` Threshold: ${chalk10.cyan(status.threshold)} of ${chalk10.cyan(status.totalSigners)}`);
6764
+ console.log(chalk10.bold("\nSigning Modes:"));
4147
6765
  status.availableSigningModes.forEach((mode) => {
4148
6766
  console.log(` - ${mode}`);
4149
6767
  });
4150
- console.log(chalk9.bold("\nDetails:"));
6768
+ console.log(chalk10.bold("\nDetails:"));
4151
6769
  console.log(` Chains: ${status.chains}`);
4152
6770
  console.log(` Currency: ${status.currency.toUpperCase()}`);
4153
6771
  console.log(` Created: ${new Date(status.createdAt).toLocaleString()}`);
@@ -4156,7 +6774,7 @@ function displayStatus(status) {
4156
6774
  }
4157
6775
  function showHelp() {
4158
6776
  const table = new Table({
4159
- head: [chalk9.bold("Available Commands")],
6777
+ head: [chalk10.bold("Available Commands")],
4160
6778
  colWidths: [50],
4161
6779
  chars: {
4162
6780
  mid: "",
@@ -4170,7 +6788,7 @@ function showHelp() {
4170
6788
  }
4171
6789
  });
4172
6790
  table.push(
4173
- [chalk9.bold("Vault Management:")],
6791
+ [chalk10.bold("Vault Management:")],
4174
6792
  [" vaults - List all vaults"],
4175
6793
  [" vault <name> - Switch to vault"],
4176
6794
  [" import <file> - Import vault from file"],
@@ -4179,30 +6797,31 @@ function showHelp() {
4179
6797
  [" info - Show vault details"],
4180
6798
  [" export [path] - Export vault to file"],
4181
6799
  [""],
4182
- [chalk9.bold("Wallet Operations:")],
6800
+ [chalk10.bold("Wallet Operations:")],
4183
6801
  [" balance [chain] - Show balances"],
4184
6802
  [" send <chain> <to> <amount> - Send transaction"],
6803
+ [" tx-status <chain> <txHash> - Check transaction status"],
4185
6804
  [" portfolio [-c usd] - Show portfolio value"],
4186
6805
  [" addresses - Show all addresses"],
4187
6806
  [" chains [--add/--remove/--add-all] - Manage chains"],
4188
6807
  [" tokens <chain> - Manage tokens"],
4189
6808
  [""],
4190
- [chalk9.bold("Swap Operations:")],
6809
+ [chalk10.bold("Swap Operations:")],
4191
6810
  [" swap-chains - List swap-enabled chains"],
4192
6811
  [" swap-quote <from> <to> <amount> - Get quote"],
4193
6812
  [" swap <from> <to> <amount> - Execute swap"],
4194
6813
  [""],
4195
- [chalk9.bold("Session Commands (shell only):")],
6814
+ [chalk10.bold("Session Commands (shell only):")],
4196
6815
  [" lock - Lock vault"],
4197
6816
  [" unlock - Unlock vault"],
4198
6817
  [" status - Show vault status"],
4199
6818
  [""],
4200
- [chalk9.bold("Settings:")],
6819
+ [chalk10.bold("Settings:")],
4201
6820
  [" currency [code] - View/set currency"],
4202
6821
  [" server - Check server status"],
4203
6822
  [" address-book - Manage saved addresses"],
4204
6823
  [""],
4205
- [chalk9.bold("Help & Navigation:")],
6824
+ [chalk10.bold("Help & Navigation:")],
4206
6825
  [" help, ? - Show this help"],
4207
6826
  [" .clear - Clear screen"],
4208
6827
  [" .exit - Exit shell"]
@@ -4340,12 +6959,12 @@ var ShellSession = class {
4340
6959
  */
4341
6960
  async start() {
4342
6961
  console.clear();
4343
- console.log(chalk10.cyan.bold("\n=============================================="));
4344
- console.log(chalk10.cyan.bold(" Vultisig Interactive Shell"));
4345
- console.log(chalk10.cyan.bold("==============================================\n"));
6962
+ console.log(chalk11.cyan.bold("\n=============================================="));
6963
+ console.log(chalk11.cyan.bold(" Vultisig Interactive Shell"));
6964
+ console.log(chalk11.cyan.bold("==============================================\n"));
4346
6965
  await this.loadAllVaults();
4347
6966
  this.displayVaultList();
4348
- console.log(chalk10.gray('Type "help" for available commands, "exit" to quit\n'));
6967
+ console.log(chalk11.gray('Type "help" for available commands, "exit" to quit\n'));
4349
6968
  this.promptLoop().catch(() => {
4350
6969
  });
4351
6970
  }
@@ -4363,7 +6982,7 @@ var ShellSession = class {
4363
6982
  */
4364
6983
  readLine(prompt) {
4365
6984
  return new Promise((resolve) => {
4366
- const rl = readline.createInterface({
6985
+ const rl = readline3.createInterface({
4367
6986
  input: process.stdin,
4368
6987
  output: process.stdout,
4369
6988
  completer: (line, cb) => {
@@ -4379,12 +6998,12 @@ var ShellSession = class {
4379
6998
  const now = Date.now();
4380
6999
  if (now - this.lastSigintTime < this.DOUBLE_CTRL_C_TIMEOUT) {
4381
7000
  rl.close();
4382
- console.log(chalk10.yellow("\nGoodbye!"));
7001
+ console.log(chalk11.yellow("\nGoodbye!"));
4383
7002
  this.ctx.dispose();
4384
7003
  process.exit(0);
4385
7004
  }
4386
7005
  this.lastSigintTime = now;
4387
- console.log(chalk10.yellow("\n(Press Ctrl+C again to exit)"));
7006
+ console.log(chalk11.yellow("\n(Press Ctrl+C again to exit)"));
4388
7007
  rl.close();
4389
7008
  resolve("");
4390
7009
  });
@@ -4396,7 +7015,7 @@ var ShellSession = class {
4396
7015
  prompt(message, defaultValue) {
4397
7016
  return new Promise((resolve, reject) => {
4398
7017
  const displayPrompt = defaultValue ? `${message} [${defaultValue}]: ` : `${message}: `;
4399
- const rl = readline.createInterface({
7018
+ const rl = readline3.createInterface({
4400
7019
  input: process.stdin,
4401
7020
  output: process.stdout,
4402
7021
  terminal: true
@@ -4416,7 +7035,7 @@ var ShellSession = class {
4416
7035
  */
4417
7036
  promptPassword(message) {
4418
7037
  return new Promise((resolve, reject) => {
4419
- const rl = readline.createInterface({
7038
+ const rl = readline3.createInterface({
4420
7039
  input: process.stdin,
4421
7040
  output: process.stdout,
4422
7041
  terminal: true
@@ -4479,7 +7098,7 @@ var ShellSession = class {
4479
7098
  stopAllSpinners();
4480
7099
  process.stdout.write("\x1B[?25h");
4481
7100
  process.stdout.write("\r\x1B[K");
4482
- console.log(chalk10.yellow("\nCancelling operation..."));
7101
+ console.log(chalk11.yellow("\nCancelling operation..."));
4483
7102
  };
4484
7103
  const cleanup = () => {
4485
7104
  process.removeListener("SIGINT", onSigint);
@@ -4516,10 +7135,10 @@ var ShellSession = class {
4516
7135
  stopAllSpinners();
4517
7136
  process.stdout.write("\x1B[?25h");
4518
7137
  process.stdout.write("\r\x1B[K");
4519
- console.log(chalk10.yellow("Operation cancelled"));
7138
+ console.log(chalk11.yellow("Operation cancelled"));
4520
7139
  return;
4521
7140
  }
4522
- console.error(chalk10.red(`
7141
+ console.error(chalk11.red(`
4523
7142
  Error: ${error2.message}`));
4524
7143
  }
4525
7144
  }
@@ -4552,7 +7171,7 @@ Error: ${error2.message}`));
4552
7171
  break;
4553
7172
  case "rename":
4554
7173
  if (args.length === 0) {
4555
- console.log(chalk10.yellow("Usage: rename <newName>"));
7174
+ console.log(chalk11.yellow("Usage: rename <newName>"));
4556
7175
  return;
4557
7176
  }
4558
7177
  await executeRename(this.ctx, args.join(" "));
@@ -4572,6 +7191,9 @@ Error: ${error2.message}`));
4572
7191
  case "send":
4573
7192
  await this.runSend(args);
4574
7193
  break;
7194
+ case "tx-status":
7195
+ await this.runTxStatus(args);
7196
+ break;
4575
7197
  // Chain management
4576
7198
  case "addresses":
4577
7199
  await executeAddresses(this.ctx);
@@ -4625,41 +7247,41 @@ Error: ${error2.message}`));
4625
7247
  // Exit
4626
7248
  case "exit":
4627
7249
  case "quit":
4628
- console.log(chalk10.yellow("\nGoodbye!"));
7250
+ console.log(chalk11.yellow("\nGoodbye!"));
4629
7251
  this.ctx.dispose();
4630
7252
  process.exit(0);
4631
7253
  break;
4632
7254
  // eslint requires break even after process.exit
4633
7255
  default:
4634
- console.log(chalk10.yellow(`Unknown command: ${command}`));
4635
- console.log(chalk10.gray('Type "help" for available commands'));
7256
+ console.log(chalk11.yellow(`Unknown command: ${command}`));
7257
+ console.log(chalk11.gray('Type "help" for available commands'));
4636
7258
  break;
4637
7259
  }
4638
7260
  }
4639
7261
  // ===== Command Helpers =====
4640
7262
  async switchVault(args) {
4641
7263
  if (args.length === 0) {
4642
- console.log(chalk10.yellow("Usage: vault <name>"));
4643
- console.log(chalk10.gray('Run "vaults" to see available vaults'));
7264
+ console.log(chalk11.yellow("Usage: vault <name>"));
7265
+ console.log(chalk11.gray('Run "vaults" to see available vaults'));
4644
7266
  return;
4645
7267
  }
4646
7268
  const vaultName = args.join(" ");
4647
7269
  const vault = this.ctx.findVaultByName(vaultName);
4648
7270
  if (!vault) {
4649
- console.log(chalk10.red(`Vault not found: ${vaultName}`));
4650
- console.log(chalk10.gray('Run "vaults" to see available vaults'));
7271
+ console.log(chalk11.red(`Vault not found: ${vaultName}`));
7272
+ console.log(chalk11.gray('Run "vaults" to see available vaults'));
4651
7273
  return;
4652
7274
  }
4653
7275
  await this.ctx.setActiveVault(vault);
4654
- console.log(chalk10.green(`
7276
+ console.log(chalk11.green(`
4655
7277
  + Switched to: ${vault.name}`));
4656
7278
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
4657
- const status = isUnlocked ? chalk10.green("Unlocked") : chalk10.yellow("Locked");
7279
+ const status = isUnlocked ? chalk11.green("Unlocked") : chalk11.yellow("Locked");
4658
7280
  console.log(`Status: ${status}`);
4659
7281
  }
4660
7282
  async importVault(args) {
4661
7283
  if (args.length === 0) {
4662
- console.log(chalk10.yellow("Usage: import <file>"));
7284
+ console.log(chalk11.yellow("Usage: import <file>"));
4663
7285
  return;
4664
7286
  }
4665
7287
  const filePath = args.join(" ");
@@ -4674,45 +7296,45 @@ Error: ${error2.message}`));
4674
7296
  async createVault(args) {
4675
7297
  const type = args[0]?.toLowerCase();
4676
7298
  if (!type || type !== "fast" && type !== "secure") {
4677
- console.log(chalk10.yellow("Usage: create <fast|secure>"));
4678
- console.log(chalk10.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
4679
- console.log(chalk10.gray(" create secure - Create a secure vault (multi-device MPC)"));
7299
+ console.log(chalk11.yellow("Usage: create <fast|secure>"));
7300
+ console.log(chalk11.gray(" create fast - Create a fast vault (server-assisted 2-of-2)"));
7301
+ console.log(chalk11.gray(" create secure - Create a secure vault (multi-device MPC)"));
4680
7302
  return;
4681
7303
  }
4682
7304
  let vault;
4683
7305
  if (type === "fast") {
4684
7306
  const name = await this.prompt("Vault name");
4685
7307
  if (!name) {
4686
- console.log(chalk10.red("Name is required"));
7308
+ console.log(chalk11.red("Name is required"));
4687
7309
  return;
4688
7310
  }
4689
7311
  const password = await this.promptPassword("Vault password");
4690
7312
  if (!password) {
4691
- console.log(chalk10.red("Password is required"));
7313
+ console.log(chalk11.red("Password is required"));
4692
7314
  return;
4693
7315
  }
4694
7316
  const email = await this.prompt("Email for verification");
4695
7317
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
4696
- console.log(chalk10.red("Valid email is required"));
7318
+ console.log(chalk11.red("Valid email is required"));
4697
7319
  return;
4698
7320
  }
4699
7321
  vault = await this.withCancellation((signal) => executeCreateFast(this.ctx, { name, password, email, signal }));
4700
7322
  } else {
4701
7323
  const name = await this.prompt("Vault name");
4702
7324
  if (!name) {
4703
- console.log(chalk10.red("Name is required"));
7325
+ console.log(chalk11.red("Name is required"));
4704
7326
  return;
4705
7327
  }
4706
7328
  const sharesStr = await this.prompt("Total shares (devices)", "3");
4707
7329
  const shares = parseInt(sharesStr, 10);
4708
7330
  if (isNaN(shares) || shares < 2) {
4709
- console.log(chalk10.red("Must have at least 2 shares"));
7331
+ console.log(chalk11.red("Must have at least 2 shares"));
4710
7332
  return;
4711
7333
  }
4712
7334
  const thresholdStr = await this.prompt("Signing threshold", "2");
4713
7335
  const threshold = parseInt(thresholdStr, 10);
4714
7336
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
4715
- console.log(chalk10.red(`Threshold must be between 1 and ${shares}`));
7337
+ console.log(chalk11.red(`Threshold must be between 1 and ${shares}`));
4716
7338
  return;
4717
7339
  }
4718
7340
  const password = await this.promptPassword("Vault password (optional, press Enter to skip)");
@@ -4734,37 +7356,37 @@ Error: ${error2.message}`));
4734
7356
  async importSeedphrase(args) {
4735
7357
  const type = args[0]?.toLowerCase();
4736
7358
  if (!type || type !== "fast" && type !== "secure") {
4737
- console.log(chalk10.cyan("Usage: create-from-seedphrase <fast|secure>"));
4738
- console.log(chalk10.gray(" fast - Import with VultiServer (2-of-2)"));
4739
- console.log(chalk10.gray(" secure - Import with device coordination (N-of-M)"));
7359
+ console.log(chalk11.cyan("Usage: create-from-seedphrase <fast|secure>"));
7360
+ console.log(chalk11.gray(" fast - Import with VultiServer (2-of-2)"));
7361
+ console.log(chalk11.gray(" secure - Import with device coordination (N-of-M)"));
4740
7362
  return;
4741
7363
  }
4742
- console.log(chalk10.cyan("\nEnter your recovery phrase (words separated by spaces):"));
7364
+ console.log(chalk11.cyan("\nEnter your recovery phrase (words separated by spaces):"));
4743
7365
  const mnemonic = await this.promptPassword("Seedphrase");
4744
7366
  const validation = await this.ctx.sdk.validateSeedphrase(mnemonic);
4745
7367
  if (!validation.valid) {
4746
- console.log(chalk10.red(`Invalid seedphrase: ${validation.error}`));
7368
+ console.log(chalk11.red(`Invalid seedphrase: ${validation.error}`));
4747
7369
  if (validation.invalidWords?.length) {
4748
- console.log(chalk10.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
7370
+ console.log(chalk11.yellow(`Invalid words: ${validation.invalidWords.join(", ")}`));
4749
7371
  }
4750
7372
  return;
4751
7373
  }
4752
- console.log(chalk10.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
7374
+ console.log(chalk11.green(`\u2713 Valid ${validation.wordCount}-word seedphrase`));
4753
7375
  let vault;
4754
7376
  if (type === "fast") {
4755
7377
  const name = await this.prompt("Vault name");
4756
7378
  if (!name) {
4757
- console.log(chalk10.red("Name is required"));
7379
+ console.log(chalk11.red("Name is required"));
4758
7380
  return;
4759
7381
  }
4760
7382
  const password = await this.promptPassword("Vault password");
4761
7383
  if (!password) {
4762
- console.log(chalk10.red("Password is required"));
7384
+ console.log(chalk11.red("Password is required"));
4763
7385
  return;
4764
7386
  }
4765
7387
  const email = await this.prompt("Email for verification");
4766
7388
  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
4767
- console.log(chalk10.red("Valid email is required"));
7389
+ console.log(chalk11.red("Valid email is required"));
4768
7390
  return;
4769
7391
  }
4770
7392
  const discoverStr = await this.prompt("Discover chains with balances? (y/n)", "y");
@@ -4782,19 +7404,19 @@ Error: ${error2.message}`));
4782
7404
  } else {
4783
7405
  const name = await this.prompt("Vault name");
4784
7406
  if (!name) {
4785
- console.log(chalk10.red("Name is required"));
7407
+ console.log(chalk11.red("Name is required"));
4786
7408
  return;
4787
7409
  }
4788
7410
  const sharesStr = await this.prompt("Total shares (devices)", "3");
4789
7411
  const shares = parseInt(sharesStr, 10);
4790
7412
  if (isNaN(shares) || shares < 2) {
4791
- console.log(chalk10.red("Must have at least 2 shares"));
7413
+ console.log(chalk11.red("Must have at least 2 shares"));
4792
7414
  return;
4793
7415
  }
4794
7416
  const thresholdStr = await this.prompt("Signing threshold", "2");
4795
7417
  const threshold = parseInt(thresholdStr, 10);
4796
7418
  if (isNaN(threshold) || threshold < 1 || threshold > shares) {
4797
- console.log(chalk10.red(`Threshold must be between 1 and ${shares}`));
7419
+ console.log(chalk11.red(`Threshold must be between 1 and ${shares}`));
4798
7420
  return;
4799
7421
  }
4800
7422
  const password = await this.promptPassword("Vault password (optional, Enter to skip)");
@@ -4838,8 +7460,8 @@ Error: ${error2.message}`));
4838
7460
  }
4839
7461
  }
4840
7462
  if (!fiatCurrencies3.includes(currency)) {
4841
- console.log(chalk10.red(`Invalid currency: ${currency}`));
4842
- console.log(chalk10.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
7463
+ console.log(chalk11.red(`Invalid currency: ${currency}`));
7464
+ console.log(chalk11.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
4843
7465
  return;
4844
7466
  }
4845
7467
  const raw = args.includes("--raw");
@@ -4847,7 +7469,7 @@ Error: ${error2.message}`));
4847
7469
  }
4848
7470
  async runSend(args) {
4849
7471
  if (args.length < 3) {
4850
- console.log(chalk10.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
7472
+ console.log(chalk11.yellow("Usage: send <chain> <to> <amount> [--token <tokenId>] [--memo <memo>]"));
4851
7473
  return;
4852
7474
  }
4853
7475
  const [chainStr, to, amount, ...rest] = args;
@@ -4867,12 +7489,22 @@ Error: ${error2.message}`));
4867
7489
  await this.withAbortHandler((signal) => executeSend(this.ctx, { chain, to, amount, tokenId, memo, signal }));
4868
7490
  } catch (err) {
4869
7491
  if (err.message === "Transaction cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
4870
- console.log(chalk10.yellow("\nTransaction cancelled"));
7492
+ console.log(chalk11.yellow("\nTransaction cancelled"));
4871
7493
  return;
4872
7494
  }
4873
7495
  throw err;
4874
7496
  }
4875
7497
  }
7498
+ async runTxStatus(args) {
7499
+ if (args.length < 2) {
7500
+ console.log(chalk11.yellow("Usage: tx-status <chain> <txHash> [--no-wait]"));
7501
+ return;
7502
+ }
7503
+ const [chainStr, txHash, ...rest] = args;
7504
+ const chain = findChainByName(chainStr) || chainStr;
7505
+ const noWait = rest.includes("--no-wait");
7506
+ await this.withCancellation(() => executeTxStatus(this.ctx, { chain, txHash, noWait }));
7507
+ }
4876
7508
  async runChains(args) {
4877
7509
  let addChain;
4878
7510
  let removeChain;
@@ -4883,8 +7515,8 @@ Error: ${error2.message}`));
4883
7515
  } else if (args[i] === "--add" && i + 1 < args.length) {
4884
7516
  const chain = findChainByName(args[i + 1]);
4885
7517
  if (!chain) {
4886
- console.log(chalk10.red(`Unknown chain: ${args[i + 1]}`));
4887
- console.log(chalk10.gray("Use tab completion to see available chains"));
7518
+ console.log(chalk11.red(`Unknown chain: ${args[i + 1]}`));
7519
+ console.log(chalk11.gray("Use tab completion to see available chains"));
4888
7520
  return;
4889
7521
  }
4890
7522
  addChain = chain;
@@ -4892,8 +7524,8 @@ Error: ${error2.message}`));
4892
7524
  } else if (args[i] === "--remove" && i + 1 < args.length) {
4893
7525
  const chain = findChainByName(args[i + 1]);
4894
7526
  if (!chain) {
4895
- console.log(chalk10.red(`Unknown chain: ${args[i + 1]}`));
4896
- console.log(chalk10.gray("Use tab completion to see available chains"));
7527
+ console.log(chalk11.red(`Unknown chain: ${args[i + 1]}`));
7528
+ console.log(chalk11.gray("Use tab completion to see available chains"));
4897
7529
  return;
4898
7530
  }
4899
7531
  removeChain = chain;
@@ -4904,7 +7536,7 @@ Error: ${error2.message}`));
4904
7536
  }
4905
7537
  async runTokens(args) {
4906
7538
  if (args.length === 0) {
4907
- console.log(chalk10.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
7539
+ console.log(chalk11.yellow("Usage: tokens <chain> [--add <address>] [--remove <tokenId>]"));
4908
7540
  return;
4909
7541
  }
4910
7542
  const chainStr = args[0];
@@ -4925,7 +7557,7 @@ Error: ${error2.message}`));
4925
7557
  async runSwapQuote(args) {
4926
7558
  if (args.length < 3) {
4927
7559
  console.log(
4928
- chalk10.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
7560
+ chalk11.yellow("Usage: swap-quote <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>]")
4929
7561
  );
4930
7562
  return;
4931
7563
  }
@@ -4949,7 +7581,7 @@ Error: ${error2.message}`));
4949
7581
  async runSwap(args) {
4950
7582
  if (args.length < 3) {
4951
7583
  console.log(
4952
- chalk10.yellow(
7584
+ chalk11.yellow(
4953
7585
  "Usage: swap <fromChain> <toChain> <amount> [--from-token <addr>] [--to-token <addr>] [--slippage <pct>]"
4954
7586
  )
4955
7587
  );
@@ -4980,7 +7612,7 @@ Error: ${error2.message}`));
4980
7612
  );
4981
7613
  } catch (err) {
4982
7614
  if (err.message === "Swap cancelled by user" || err.message === "Operation cancelled" || err.message === "Operation aborted") {
4983
- console.log(chalk10.yellow("\nSwap cancelled"));
7615
+ console.log(chalk11.yellow("\nSwap cancelled"));
4984
7616
  return;
4985
7617
  }
4986
7618
  throw err;
@@ -5042,24 +7674,24 @@ Error: ${error2.message}`));
5042
7674
  }
5043
7675
  getPrompt() {
5044
7676
  const vault = this.ctx.getActiveVault();
5045
- if (!vault) return chalk10.cyan("wallet> ");
7677
+ if (!vault) return chalk11.cyan("wallet> ");
5046
7678
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
5047
- const status = isUnlocked ? chalk10.green("\u{1F513}") : chalk10.yellow("\u{1F512}");
5048
- return chalk10.cyan(`wallet[${vault.name}]${status}> `);
7679
+ const status = isUnlocked ? chalk11.green("\u{1F513}") : chalk11.yellow("\u{1F512}");
7680
+ return chalk11.cyan(`wallet[${vault.name}]${status}> `);
5049
7681
  }
5050
7682
  displayVaultList() {
5051
7683
  const vaults = Array.from(this.ctx.getVaults().values());
5052
7684
  const activeVault = this.ctx.getActiveVault();
5053
7685
  if (vaults.length === 0) {
5054
- console.log(chalk10.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
7686
+ console.log(chalk11.yellow('No vaults found. Use "create" or "import <file>" to add a vault.\n'));
5055
7687
  return;
5056
7688
  }
5057
- console.log(chalk10.cyan("Loaded Vaults:\n"));
7689
+ console.log(chalk11.cyan("Loaded Vaults:\n"));
5058
7690
  vaults.forEach((vault) => {
5059
7691
  const isActive = vault.id === activeVault?.id;
5060
7692
  const isUnlocked = this.ctx.isVaultUnlocked(vault.id);
5061
- const activeMarker = isActive ? chalk10.green(" (active)") : "";
5062
- const lockIcon = isUnlocked ? chalk10.green("\u{1F513}") : chalk10.yellow("\u{1F512}");
7693
+ const activeMarker = isActive ? chalk11.green(" (active)") : "";
7694
+ const lockIcon = isUnlocked ? chalk11.green("\u{1F513}") : chalk11.yellow("\u{1F512}");
5063
7695
  console.log(` ${lockIcon} ${vault.name}${activeMarker} - ${vault.type}`);
5064
7696
  });
5065
7697
  console.log();
@@ -5067,23 +7699,23 @@ Error: ${error2.message}`));
5067
7699
  };
5068
7700
 
5069
7701
  // src/lib/errors.ts
5070
- import chalk11 from "chalk";
7702
+ import chalk12 from "chalk";
5071
7703
 
5072
7704
  // src/lib/version.ts
5073
- import chalk12 from "chalk";
5074
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
5075
- import { homedir } from "os";
5076
- import { join } from "path";
7705
+ import chalk13 from "chalk";
7706
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
7707
+ import { homedir as homedir2 } from "os";
7708
+ import { join as join2 } from "path";
5077
7709
  var cachedVersion = null;
5078
7710
  function getVersion() {
5079
7711
  if (cachedVersion) return cachedVersion;
5080
7712
  if (true) {
5081
- cachedVersion = "0.5.0";
7713
+ cachedVersion = "0.7.0";
5082
7714
  return cachedVersion;
5083
7715
  }
5084
7716
  try {
5085
7717
  const packagePath = new URL("../../package.json", import.meta.url);
5086
- const pkg = JSON.parse(readFileSync(packagePath, "utf-8"));
7718
+ const pkg = JSON.parse(readFileSync2(packagePath, "utf-8"));
5087
7719
  cachedVersion = pkg.version;
5088
7720
  return cachedVersion;
5089
7721
  } catch {
@@ -5091,13 +7723,13 @@ function getVersion() {
5091
7723
  return cachedVersion;
5092
7724
  }
5093
7725
  }
5094
- var CACHE_DIR = join(homedir(), ".vultisig", "cache");
5095
- var VERSION_CACHE_FILE = join(CACHE_DIR, "version-check.json");
7726
+ var CACHE_DIR = join2(homedir2(), ".vultisig", "cache");
7727
+ var VERSION_CACHE_FILE = join2(CACHE_DIR, "version-check.json");
5096
7728
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
5097
7729
  function readVersionCache() {
5098
7730
  try {
5099
- if (!existsSync(VERSION_CACHE_FILE)) return null;
5100
- const data = readFileSync(VERSION_CACHE_FILE, "utf-8");
7731
+ if (!existsSync2(VERSION_CACHE_FILE)) return null;
7732
+ const data = readFileSync2(VERSION_CACHE_FILE, "utf-8");
5101
7733
  return JSON.parse(data);
5102
7734
  } catch {
5103
7735
  return null;
@@ -5105,10 +7737,10 @@ function readVersionCache() {
5105
7737
  }
5106
7738
  function writeVersionCache(cache) {
5107
7739
  try {
5108
- if (!existsSync(CACHE_DIR)) {
5109
- mkdirSync(CACHE_DIR, { recursive: true });
7740
+ if (!existsSync2(CACHE_DIR)) {
7741
+ mkdirSync2(CACHE_DIR, { recursive: true });
5110
7742
  }
5111
- writeFileSync(VERSION_CACHE_FILE, JSON.stringify(cache, null, 2));
7743
+ writeFileSync2(VERSION_CACHE_FILE, JSON.stringify(cache, null, 2));
5112
7744
  } catch {
5113
7745
  }
5114
7746
  }
@@ -5132,8 +7764,8 @@ async function fetchLatestVersion() {
5132
7764
  }
5133
7765
  function isNewerVersion(v1, v2) {
5134
7766
  const parse = (v) => {
5135
- const clean = v.replace(/^v/, "").replace(/-.*$/, "");
5136
- return clean.split(".").map((n) => parseInt(n, 10) || 0);
7767
+ const clean2 = v.replace(/^v/, "").replace(/-.*$/, "");
7768
+ return clean2.split(".").map((n) => parseInt(n, 10) || 0);
5137
7769
  };
5138
7770
  const p1 = parse(v1);
5139
7771
  const p2 = parse(v2);
@@ -5175,7 +7807,7 @@ function formatVersionShort() {
5175
7807
  }
5176
7808
  function formatVersionDetailed() {
5177
7809
  const lines = [];
5178
- lines.push(chalk12.bold(`Vultisig CLI v${getVersion()}`));
7810
+ lines.push(chalk13.bold(`Vultisig CLI v${getVersion()}`));
5179
7811
  lines.push("");
5180
7812
  lines.push(` Node.js: ${process.version}`);
5181
7813
  lines.push(` Platform: ${process.platform}-${process.arch}`);
@@ -5213,9 +7845,9 @@ function getUpdateCommand() {
5213
7845
  }
5214
7846
 
5215
7847
  // src/lib/completion.ts
5216
- import { homedir as homedir2 } from "os";
5217
- import { join as join2 } from "path";
5218
- import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
7848
+ import { homedir as homedir3 } from "os";
7849
+ import { join as join3 } from "path";
7850
+ import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
5219
7851
  var tabtab = null;
5220
7852
  async function getTabtab() {
5221
7853
  if (!tabtab) {
@@ -5280,15 +7912,15 @@ var CHAINS = [
5280
7912
  ];
5281
7913
  function getVaultNames() {
5282
7914
  try {
5283
- const vaultDir = join2(homedir2(), ".vultisig", "vaults");
5284
- if (!existsSync2(vaultDir)) return [];
7915
+ const vaultDir = join3(homedir3(), ".vultisig", "vaults");
7916
+ if (!existsSync3(vaultDir)) return [];
5285
7917
  const { readdirSync } = __require("fs");
5286
7918
  const files = readdirSync(vaultDir);
5287
7919
  const names = [];
5288
7920
  for (const file of files) {
5289
7921
  if (file.startsWith("vault:") && file.endsWith(".json")) {
5290
7922
  try {
5291
- const content = readFileSync2(join2(vaultDir, file), "utf-8");
7923
+ const content = readFileSync3(join3(vaultDir, file), "utf-8");
5292
7924
  const vault = JSON.parse(content);
5293
7925
  if (vault.name) names.push(vault.name);
5294
7926
  if (vault.id) names.push(vault.id);
@@ -5526,7 +8158,26 @@ complete -c vsig -n "__fish_seen_subcommand_from import export" -a "(__fish_comp
5526
8158
  `.trim();
5527
8159
  }
5528
8160
 
8161
+ // src/lib/user-agent.ts
8162
+ function setupUserAgent() {
8163
+ const userAgent = `vultisig-cli/${getVersion()}`;
8164
+ const originalFetch = globalThis.fetch;
8165
+ globalThis.fetch = (input, init2) => {
8166
+ const headers = new Headers(input instanceof Request ? input.headers : void 0);
8167
+ if (init2?.headers) {
8168
+ new Headers(init2.headers).forEach((value, key) => {
8169
+ headers.set(key, value);
8170
+ });
8171
+ }
8172
+ if (!headers.has("User-Agent")) {
8173
+ headers.set("User-Agent", userAgent);
8174
+ }
8175
+ return originalFetch(input, { ...init2, headers });
8176
+ };
8177
+ }
8178
+
5529
8179
  // src/index.ts
8180
+ setupUserAgent();
5530
8181
  (async () => {
5531
8182
  const handled = await handleCompletion();
5532
8183
  if (handled) process.exit(0);
@@ -5552,7 +8203,7 @@ async function init(vaultOverride, unlockPassword) {
5552
8203
  if (unlockPassword && vaultSelector) {
5553
8204
  cachePassword(vaultSelector, unlockPassword);
5554
8205
  }
5555
- const sdk = new Vultisig5({
8206
+ const sdk = new Vultisig7({
5556
8207
  onPasswordRequired: createPasswordCallback()
5557
8208
  });
5558
8209
  await sdk.initialize();
@@ -5574,10 +8225,10 @@ async function init(vaultOverride, unlockPassword) {
5574
8225
  return ctx;
5575
8226
  }
5576
8227
  var createCmd = program.command("create").description("Create a vault");
5577
- createCmd.command("fast").description("Create a fast vault (server-assisted 2-of-2)").requiredOption("--name <name>", "Vault name").requiredOption("--password <password>", "Vault password").requiredOption("--email <email>", "Email for verification").action(
8228
+ createCmd.command("fast").description("Create a fast vault (server-assisted 2-of-2)").requiredOption("--name <name>", "Vault name").requiredOption("--password <password>", "Vault password").requiredOption("--email <email>", "Email for verification").option("--two-step", 'Create vault without verifying OTP (verify later with "vultisig verify")').action(
5578
8229
  withExit(async (options) => {
5579
8230
  const context = await init(program.opts().vault);
5580
- await executeCreateFast(context, options);
8231
+ await executeCreateFast(context, { ...options, twoStep: options.twoStep });
5581
8232
  })
5582
8233
  );
5583
8234
  createCmd.command("secure").description("Create a secure vault (multi-device MPC)").requiredOption("--name <name>", "Vault name").option("--password <password>", "Vault password (optional)").option("--threshold <m>", "Signing threshold", "2").option("--shares <n>", "Total shares", "3").action(
@@ -5823,6 +8474,16 @@ program.command("broadcast").description("Broadcast a pre-signed raw transaction
5823
8474
  });
5824
8475
  })
5825
8476
  );
8477
+ program.command("tx-status").description("Check the status of a transaction (polls until confirmed)").requiredOption("--chain <chain>", "Target blockchain").requiredOption("--tx-hash <hash>", "Transaction hash to check").option("--no-wait", "Return immediately without waiting for confirmation").action(
8478
+ withExit(async (options) => {
8479
+ const context = await init(program.opts().vault);
8480
+ await executeTxStatus(context, {
8481
+ chain: findChainByName(options.chain) || options.chain,
8482
+ txHash: options.txHash,
8483
+ noWait: !options.wait
8484
+ });
8485
+ })
8486
+ );
5826
8487
  program.command("portfolio").description("Show total portfolio value").option("-c, --currency <currency>", "Fiat currency (usd, eur, gbp, etc.)", "usd").option("--raw", "Show raw values (wei/satoshis) for programmatic use").action(
5827
8488
  withExit(async (options) => {
5828
8489
  const context = await init(program.opts().vault);
@@ -6051,14 +8712,24 @@ rujiraCmd.command("withdraw <asset> <amount> <l1Address>").description("Withdraw
6051
8712
  }
6052
8713
  )
6053
8714
  );
8715
+ program.command("agent").description("AI-powered chat interface for wallet operations").option("--via-agent", "Use NDJSON pipe mode for agent-to-agent communication").option("--verbose", "Show detailed tool call parameters and debug output").option("--backend-url <url>", "Agent backend URL (default: http://localhost:9998)").option("--password <password>", "Vault password for signing operations").option("--conversation <id>", "Resume an existing conversation").action(async (options) => {
8716
+ const context = await init(program.opts().vault, options.password);
8717
+ await executeAgent(context, {
8718
+ viaAgent: options.viaAgent,
8719
+ verbose: options.verbose,
8720
+ backendUrl: options.backendUrl,
8721
+ password: options.password,
8722
+ conversationId: options.conversation
8723
+ });
8724
+ });
6054
8725
  program.command("version").description("Show detailed version information").action(
6055
8726
  withExit(async () => {
6056
8727
  printResult(formatVersionDetailed());
6057
8728
  const result = await checkForUpdates();
6058
8729
  if (result?.updateAvailable && result.latestVersion) {
6059
8730
  info("");
6060
- info(chalk13.yellow(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
6061
- info(chalk13.gray(`Run "${getUpdateCommand()}" to update`));
8731
+ info(chalk14.yellow(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
8732
+ info(chalk14.gray(`Run "${getUpdateCommand()}" to update`));
6062
8733
  }
6063
8734
  })
6064
8735
  );
@@ -6067,28 +8738,28 @@ program.command("update").description("Check for updates and show update command
6067
8738
  info("Checking for updates...");
6068
8739
  const result = await checkForUpdates();
6069
8740
  if (!result) {
6070
- printResult(chalk13.gray("Update checking is disabled"));
8741
+ printResult(chalk14.gray("Update checking is disabled"));
6071
8742
  return;
6072
8743
  }
6073
8744
  if (result.updateAvailable && result.latestVersion) {
6074
8745
  printResult("");
6075
- printResult(chalk13.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
8746
+ printResult(chalk14.green(`Update available: ${result.currentVersion} -> ${result.latestVersion}`));
6076
8747
  printResult("");
6077
8748
  if (options.check) {
6078
8749
  printResult(`Run "${getUpdateCommand()}" to update`);
6079
8750
  } else {
6080
8751
  const updateCmd = getUpdateCommand();
6081
8752
  printResult(`To update, run:`);
6082
- printResult(chalk13.cyan(` ${updateCmd}`));
8753
+ printResult(chalk14.cyan(` ${updateCmd}`));
6083
8754
  }
6084
8755
  } else {
6085
- printResult(chalk13.green(`You're on the latest version (${result.currentVersion})`));
8756
+ printResult(chalk14.green(`You're on the latest version (${result.currentVersion})`));
6086
8757
  }
6087
8758
  })
6088
8759
  );
6089
8760
  setupCompletionCommand(program);
6090
8761
  async function startInteractiveMode() {
6091
- const sdk = new Vultisig5({
8762
+ const sdk = new Vultisig7({
6092
8763
  onPasswordRequired: createPasswordCallback()
6093
8764
  });
6094
8765
  await sdk.initialize();
@@ -6110,3 +8781,8 @@ if (isInteractiveMode) {
6110
8781
  } else {
6111
8782
  program.parse();
6112
8783
  }
8784
+ /*! Bundled license information:
8785
+
8786
+ @noble/hashes/esm/utils.js:
8787
+ (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
8788
+ */