@execra/core 1.0.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 (52) hide show
  1. package/dist/cli/commands.d.ts +21 -0
  2. package/dist/cli/commands.js +686 -0
  3. package/dist/cli/commands.js.map +1 -0
  4. package/dist/cli/index.d.ts +8 -0
  5. package/dist/cli/index.js +1162 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/cli/prompts.d.ts +12 -0
  8. package/dist/cli/prompts.js +98 -0
  9. package/dist/cli/prompts.js.map +1 -0
  10. package/dist/cli/ui.d.ts +38 -0
  11. package/dist/cli/ui.js +990 -0
  12. package/dist/cli/ui.js.map +1 -0
  13. package/dist/handler/index.d.ts +93 -0
  14. package/dist/handler/index.js +628 -0
  15. package/dist/handler/index.js.map +1 -0
  16. package/dist/handler/walletConnect.d.ts +6 -0
  17. package/dist/handler/walletConnect.js +623 -0
  18. package/dist/handler/walletConnect.js.map +1 -0
  19. package/dist/index.d.ts +14 -0
  20. package/dist/index.js +2046 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/session/index.d.ts +20 -0
  23. package/dist/session/index.js +79 -0
  24. package/dist/session/index.js.map +1 -0
  25. package/dist/solana/index.d.ts +5 -0
  26. package/dist/solana/index.js +302 -0
  27. package/dist/solana/index.js.map +1 -0
  28. package/dist/solana/rpc.d.ts +45 -0
  29. package/dist/solana/rpc.js +120 -0
  30. package/dist/solana/rpc.js.map +1 -0
  31. package/dist/solana/simulate.d.ts +41 -0
  32. package/dist/solana/simulate.js +173 -0
  33. package/dist/solana/simulate.js.map +1 -0
  34. package/dist/solana/tx.d.ts +54 -0
  35. package/dist/solana/tx.js +141 -0
  36. package/dist/solana/tx.js.map +1 -0
  37. package/dist/vault/accounts.d.ts +88 -0
  38. package/dist/vault/accounts.js +126 -0
  39. package/dist/vault/accounts.js.map +1 -0
  40. package/dist/vault/config.d.ts +40 -0
  41. package/dist/vault/config.js +131 -0
  42. package/dist/vault/config.js.map +1 -0
  43. package/dist/vault/index.d.ts +122 -0
  44. package/dist/vault/index.js +580 -0
  45. package/dist/vault/index.js.map +1 -0
  46. package/dist/vault/keystore.d.ts +44 -0
  47. package/dist/vault/keystore.js +118 -0
  48. package/dist/vault/keystore.js.map +1 -0
  49. package/dist/vault/mnemonic.d.ts +43 -0
  50. package/dist/vault/mnemonic.js +37 -0
  51. package/dist/vault/mnemonic.js.map +1 -0
  52. package/package.json +49 -0
@@ -0,0 +1,1162 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/ui.tsx
4
+ import React2, { useState as useState2, useEffect, useRef as useRef2 } from "react";
5
+ import { render as render2, Box as Box2, Text as Text2, useInput as useInput2, useApp as useApp2 } from "ink";
6
+
7
+ // src/vault/mnemonic.ts
8
+ import * as bip39 from "bip39";
9
+ function generateMnemonic2(strength = 12) {
10
+ const mnemonic = bip39.generateMnemonic(strength == 12 ? 128 : 256);
11
+ const wordCount = strength;
12
+ return { mnemonic, wordCount };
13
+ }
14
+ function validateMnemonic2(mnemonic) {
15
+ const cleaned = cleanMnemonic(mnemonic);
16
+ return bip39.validateMnemonic(cleaned);
17
+ }
18
+ async function mnemonicToSeed2(mnemonic, passphrase = "") {
19
+ const cleaned = cleanMnemonic(mnemonic);
20
+ if (!validateMnemonic2(cleaned)) {
21
+ throw new Error("Invalid mnemonic: failed wordlist or checksum validation");
22
+ }
23
+ return bip39.mnemonicToSeed(cleaned, passphrase);
24
+ }
25
+ function cleanMnemonic(mnemonic) {
26
+ return mnemonic.trim().toLowerCase().replace(/\s+/g, " ");
27
+ }
28
+
29
+ // src/vault/keystore.ts
30
+ import crypto from "crypto";
31
+ import fs from "fs";
32
+ import os from "os";
33
+ import path from "path";
34
+ var VAULT_VERSION = 1;
35
+ var PBKDF2_ITERATIONS = 21e4;
36
+ var PBKDF2_DIGEST = "sha512";
37
+ var KEY_LENGTH = 32;
38
+ var SALT_LENGTH = 32;
39
+ var IV_LENGTH = 16;
40
+ var CIPHER = "aes-256-gcm";
41
+ function getWalletDir() {
42
+ return path.join(os.homedir(), ".wallet");
43
+ }
44
+ function getVaultPath() {
45
+ return path.join(getWalletDir(), "vault.enc");
46
+ }
47
+ function getConfigPath() {
48
+ return path.join(getWalletDir(), "config.json");
49
+ }
50
+ function getSessionsPath() {
51
+ return path.join(getWalletDir(), "sessions.json");
52
+ }
53
+ var SESSIONS_FILE = getSessionsPath();
54
+ function ensureWalletDir() {
55
+ const dir = getWalletDir();
56
+ if (!fs.existsSync(dir)) {
57
+ fs.mkdirSync(dir, { recursive: true, mode: 448 });
58
+ }
59
+ }
60
+ function vaultExists() {
61
+ return fs.existsSync(getVaultPath());
62
+ }
63
+ async function saveVault(data, password) {
64
+ ensureWalletDir();
65
+ const salt = crypto.randomBytes(SALT_LENGTH);
66
+ const iv = crypto.randomBytes(IV_LENGTH);
67
+ const key = await deriveKey(password, salt);
68
+ const plaintext = JSON.stringify(data);
69
+ const cipher = crypto.createCipheriv(CIPHER, key, iv);
70
+ const encrypted = Buffer.concat([
71
+ cipher.update(plaintext, "utf8"),
72
+ cipher.final()
73
+ ]);
74
+ const authTag = cipher.getAuthTag();
75
+ const vault = {
76
+ version: VAULT_VERSION,
77
+ salt: salt.toString("hex"),
78
+ iv: iv.toString("hex"),
79
+ authTag: authTag.toString("hex"),
80
+ ciphertext: encrypted.toString("hex")
81
+ };
82
+ fs.writeFileSync(getVaultPath(), JSON.stringify(vault, null, 2), {
83
+ mode: 384
84
+ // owner read/write only
85
+ });
86
+ }
87
+ async function loadVault(password) {
88
+ if (!vaultExists()) {
89
+ throw new Error("No vault found. Run `wallet init` to create one.");
90
+ }
91
+ const raw = fs.readFileSync(getVaultPath(), "utf8");
92
+ const vault = JSON.parse(raw);
93
+ if (vault.version !== VAULT_VERSION) {
94
+ throw new Error(`Unsupported vault version: ${vault.version}`);
95
+ }
96
+ const salt = Buffer.from(vault.salt, "hex");
97
+ const iv = Buffer.from(vault.iv, "hex");
98
+ const authTag = Buffer.from(vault.authTag, "hex");
99
+ const ciphertext = Buffer.from(vault.ciphertext, "hex");
100
+ const key = await deriveKey(password, salt);
101
+ try {
102
+ const decipher = crypto.createDecipheriv(CIPHER, key, iv);
103
+ decipher.setAuthTag(authTag);
104
+ const decrypted = Buffer.concat([
105
+ decipher.update(ciphertext),
106
+ decipher.final()
107
+ ]);
108
+ return JSON.parse(decrypted.toString("utf8"));
109
+ } catch {
110
+ throw new Error("Decryption failed: wrong password or vault is corrupted.");
111
+ }
112
+ }
113
+ function deriveKey(password, salt) {
114
+ return new Promise((resolve, reject) => {
115
+ crypto.pbkdf2(
116
+ password,
117
+ salt,
118
+ PBKDF2_ITERATIONS,
119
+ KEY_LENGTH,
120
+ PBKDF2_DIGEST,
121
+ (err, key) => {
122
+ if (err) reject(err);
123
+ else resolve(key);
124
+ }
125
+ );
126
+ });
127
+ }
128
+
129
+ // src/vault/accounts.ts
130
+ import { derivePath } from "ed25519-hd-key";
131
+ import nacl from "tweetnacl";
132
+ import bs58 from "bs58";
133
+ function deriveAccount(seed, index, name = `Account ${index + 1}`) {
134
+ const path2 = derivationPath(index);
135
+ const { key: privateKeyBytes } = derivePath(path2, seed.toString("hex"));
136
+ const keypair = nacl.sign.keyPair.fromSeed(privateKeyBytes);
137
+ const publicKey = bs58.encode(Buffer.from(keypair.publicKey));
138
+ return {
139
+ index,
140
+ name,
141
+ publicKey,
142
+ secretKey: keypair.secretKey,
143
+ // 64 bytes: seed + public key
144
+ derivationPath: path2,
145
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
146
+ };
147
+ }
148
+ function createAccountStore(firstAccountName = "Account 1") {
149
+ return {
150
+ accounts: [
151
+ {
152
+ index: 0,
153
+ name: firstAccountName,
154
+ publicKey: "",
155
+ // filled in after derivation
156
+ derivationPath: derivationPath(0),
157
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
158
+ }
159
+ ],
160
+ activeIndex: 0
161
+ };
162
+ }
163
+ function derivationPath(index) {
164
+ return `m/44'/501'/${index}'/0'`;
165
+ }
166
+
167
+ // src/vault/config.ts
168
+ import fs2 from "fs";
169
+ var HELIUS_API_KEY = process.env.HELIUS_API_KEY;
170
+ var RPC_URLS = {
171
+ "mainnet-beta": HELIUS_API_KEY ? `https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}` : "https://api.mainnet-beta.solana.com",
172
+ devnet: HELIUS_API_KEY ? `https://devnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}` : "https://api.devnet.solana.com",
173
+ testnet: "https://api.testnet.solana.com",
174
+ localnet: "http://localhost:8899"
175
+ };
176
+ var CONFIG_VERSION = 1;
177
+ function configExists() {
178
+ return fs2.existsSync(getConfigPath());
179
+ }
180
+ function loadConfig() {
181
+ if (!configExists()) {
182
+ throw new Error("No config found. Run `wallet init` first.");
183
+ }
184
+ const raw = fs2.readFileSync(getConfigPath(), "utf8");
185
+ return JSON.parse(raw);
186
+ }
187
+ function saveConfig(config) {
188
+ ensureWalletDir();
189
+ const updated = { ...config, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
190
+ fs2.writeFileSync(getConfigPath(), JSON.stringify(updated, null, 2), {
191
+ mode: 384
192
+ });
193
+ }
194
+ function createConfig(walletConnectProjectId = "", cluster = "devnet") {
195
+ const now = (/* @__PURE__ */ new Date()).toISOString();
196
+ return {
197
+ version: CONFIG_VERSION,
198
+ cluster,
199
+ rpcUrl: RPC_URLS[cluster],
200
+ walletConnectProjectId,
201
+ accountStore: createAccountStore("Account 1"),
202
+ createdAt: now,
203
+ updatedAt: now
204
+ };
205
+ }
206
+ function updateCluster(cluster) {
207
+ const config = loadConfig();
208
+ config.cluster = cluster;
209
+ config.rpcUrl = RPC_URLS[cluster];
210
+ saveConfig(config);
211
+ }
212
+ function updateAccountStore(accountStore) {
213
+ const config = loadConfig();
214
+ config.accountStore = accountStore;
215
+ saveConfig(config);
216
+ }
217
+ function getRpcUrl() {
218
+ return loadConfig().rpcUrl;
219
+ }
220
+ function getCluster() {
221
+ return loadConfig().cluster;
222
+ }
223
+
224
+ // src/solana/rpc.ts
225
+ import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
226
+ var _connection = null;
227
+ function getConnection(forceNew = false) {
228
+ if (!_connection || forceNew) {
229
+ const rpcUrl = getRpcUrl();
230
+ _connection = new Connection(rpcUrl, "confirmed");
231
+ }
232
+ return _connection;
233
+ }
234
+ async function getBalance(publicKeyStr) {
235
+ const connection = getConnection();
236
+ const pubkey = new PublicKey(publicKeyStr);
237
+ const lamports = await connection.getBalance(pubkey);
238
+ return {
239
+ lamports,
240
+ sol: lamports / LAMPORTS_PER_SOL
241
+ };
242
+ }
243
+ async function requestAirdrop(publicKeyStr, solAmount = 1) {
244
+ const cluster = getCluster();
245
+ if (cluster === "mainnet-beta") {
246
+ throw new Error("Airdrops are not available on mainnet.");
247
+ }
248
+ const connection = getConnection();
249
+ const pubkey = new PublicKey(publicKeyStr);
250
+ const lamports = solAmount * LAMPORTS_PER_SOL;
251
+ const signature = await connection.requestAirdrop(pubkey, lamports);
252
+ await connection.confirmTransaction(signature, "confirmed");
253
+ return signature;
254
+ }
255
+
256
+ // src/cli/commands.ts
257
+ import chalk2 from "chalk";
258
+
259
+ // src/handler/walletConnect.ts
260
+ import SignClientPkg from "@walletconnect/sign-client";
261
+ import { getSdkError } from "@walletconnect/utils";
262
+ import chalk from "chalk";
263
+
264
+ // src/solana/simulate.ts
265
+ import {
266
+ Transaction,
267
+ VersionedTransaction,
268
+ LAMPORTS_PER_SOL as LAMPORTS_PER_SOL2
269
+ } from "@solana/web3.js";
270
+
271
+ // src/solana/tx.ts
272
+ import {
273
+ Transaction as Transaction2,
274
+ VersionedTransaction as VersionedTransaction2,
275
+ Keypair
276
+ } from "@solana/web3.js";
277
+ import nacl2 from "tweetnacl";
278
+ import bs582 from "bs58";
279
+
280
+ // src/handler/index.ts
281
+ import bs583 from "bs58";
282
+
283
+ // src/session/index.ts
284
+ import * as fs3 from "fs";
285
+ function read() {
286
+ ensureWalletDir();
287
+ if (!fs3.existsSync(SESSIONS_FILE)) return { sessions: [] };
288
+ return JSON.parse(fs3.readFileSync(SESSIONS_FILE, "utf8"));
289
+ }
290
+ function write(store) {
291
+ ensureWalletDir();
292
+ fs3.writeFileSync(SESSIONS_FILE, JSON.stringify(store, null, 2));
293
+ }
294
+ function removeSession(topic) {
295
+ const store = read();
296
+ store.sessions = store.sessions.filter((s) => s.topic !== topic);
297
+ write(store);
298
+ }
299
+ function getAllSessions() {
300
+ return read().sessions.filter((s) => s.active);
301
+ }
302
+
303
+ // src/handler/walletConnect.ts
304
+ var SignClient = SignClientPkg.default ?? SignClientPkg;
305
+ var signClient = null;
306
+ async function pairWithDapp(uri) {
307
+ if (!signClient) throw new Error("WalletConnect not initialised");
308
+ console.log(chalk.dim("[WC] Connecting to dApp..."));
309
+ await new Promise((resolve, reject) => {
310
+ const timeout = setTimeout(() => {
311
+ signClient.off("session_proposal", onProposal);
312
+ reject(new Error("WalletConnect pairing timed out after 30s"));
313
+ }, 3e4);
314
+ const onProposal = ({ params }) => {
315
+ clearTimeout(timeout);
316
+ signClient.off("session_proposal", onProposal);
317
+ const meta = params.proposer.metadata;
318
+ console.log(
319
+ chalk.cyan(
320
+ `[WC] Request from: ${chalk.bold(meta.name)} (${meta.url})`,
321
+ params
322
+ )
323
+ );
324
+ resolve();
325
+ };
326
+ signClient.on("session_proposal", onProposal);
327
+ signClient.core.pairing.pair({ uri }).catch((err) => {
328
+ clearTimeout(timeout);
329
+ signClient.off("session_proposal", onProposal);
330
+ reject(err);
331
+ });
332
+ });
333
+ }
334
+ async function disconnectDapp(topic) {
335
+ if (!signClient) throw new Error("WalletConnect not initialised");
336
+ await signClient.disconnect({
337
+ topic,
338
+ reason: getSdkError("USER_DISCONNECTED")
339
+ });
340
+ removeSession(topic);
341
+ console.log(
342
+ chalk.dim(`[WC] Disconnected from topic ${topic.slice(0, 8)}...`)
343
+ );
344
+ }
345
+
346
+ // src/cli/prompts.tsx
347
+ import React, { useState, useRef } from "react";
348
+ import { render, Box, Text, useInput, useApp } from "ink";
349
+ function inkPassword(question) {
350
+ return new Promise((resolve) => {
351
+ let result = "";
352
+ function PasswordPrompt() {
353
+ const { exit } = useApp();
354
+ const valueRef = useRef("");
355
+ const [maskLen, setMaskLen] = useState(0);
356
+ useInput((input, key) => {
357
+ if (key.return) {
358
+ result = valueRef.current;
359
+ exit();
360
+ return;
361
+ }
362
+ if (key.backspace || key.delete) {
363
+ if (valueRef.current.length > 0) {
364
+ valueRef.current = valueRef.current.slice(0, -1);
365
+ setMaskLen(valueRef.current.length);
366
+ }
367
+ return;
368
+ }
369
+ if (input && !key.ctrl && !key.meta) {
370
+ valueRef.current += input;
371
+ setMaskLen(valueRef.current.length);
372
+ }
373
+ });
374
+ return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, question, " "), /* @__PURE__ */ React.createElement(Text, { color: "gray" }, "*".repeat(maskLen)));
375
+ }
376
+ const { waitUntilExit } = render(/* @__PURE__ */ React.createElement(PasswordPrompt, null));
377
+ waitUntilExit().then(() => resolve(result));
378
+ });
379
+ }
380
+ function inkSelect(message, choices) {
381
+ return new Promise((resolve) => {
382
+ let result = choices[0].value;
383
+ function SelectPrompt() {
384
+ const { exit } = useApp();
385
+ const cursorRef = useRef(0);
386
+ const [cursor, setCursor] = useState(0);
387
+ useInput((_, key) => {
388
+ if (key.upArrow) {
389
+ const n = Math.max(0, cursorRef.current - 1);
390
+ cursorRef.current = n;
391
+ setCursor(n);
392
+ }
393
+ if (key.downArrow) {
394
+ const n = Math.min(choices.length - 1, cursorRef.current + 1);
395
+ cursorRef.current = n;
396
+ setCursor(n);
397
+ }
398
+ if (key.return) {
399
+ result = choices[cursorRef.current].value;
400
+ exit();
401
+ }
402
+ });
403
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true }, message), choices.map((choice, i) => /* @__PURE__ */ React.createElement(Box, { key: i }, /* @__PURE__ */ React.createElement(Text, { color: i === cursor ? "cyan" : "gray" }, i === cursor ? "\u276F " : " ", choice.name))));
404
+ }
405
+ const { waitUntilExit } = render(/* @__PURE__ */ React.createElement(SelectPrompt, null));
406
+ waitUntilExit().then(() => resolve(result));
407
+ });
408
+ }
409
+ function inkConfirm(message, defaultYes = false) {
410
+ return new Promise((resolve) => {
411
+ let result = defaultYes;
412
+ function ConfirmPrompt() {
413
+ const { exit } = useApp();
414
+ useInput((input, key) => {
415
+ if (key.return) {
416
+ result = defaultYes;
417
+ exit();
418
+ return;
419
+ }
420
+ const ch = input.toLowerCase();
421
+ if (ch === "y") {
422
+ result = true;
423
+ exit();
424
+ } else if (ch === "n") {
425
+ result = false;
426
+ exit();
427
+ }
428
+ });
429
+ const hint = defaultYes ? "Y/n" : "y/N";
430
+ return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, message, " "), /* @__PURE__ */ React.createElement(Text, { color: "gray" }, "[", hint, "] "));
431
+ }
432
+ const { waitUntilExit } = render(/* @__PURE__ */ React.createElement(ConfirmPrompt, null));
433
+ waitUntilExit().then(() => resolve(result));
434
+ });
435
+ }
436
+
437
+ // src/cli/commands.ts
438
+ var sessionPassword = null;
439
+ var sessionMnemonic = null;
440
+ function isUnlocked() {
441
+ return sessionMnemonic !== null;
442
+ }
443
+ async function requireUnlocked() {
444
+ if (!sessionPassword || !sessionMnemonic) {
445
+ throw new Error("Wallet is locked. Run: wallet unlock");
446
+ }
447
+ return { password: sessionPassword, mnemonic: sessionMnemonic };
448
+ }
449
+ async function cmdInit() {
450
+ if (vaultExists()) {
451
+ console.log(
452
+ chalk2.yellow("Vault already exists. Use `wallet unlock` to access it.")
453
+ );
454
+ return;
455
+ }
456
+ const wordCount = await inkSelect("Mnemonic length:", [
457
+ { name: "12 words (standard)", value: 12 },
458
+ { name: "24 words (extra secure)", value: 24 }
459
+ ]);
460
+ const { mnemonic } = generateMnemonic2(wordCount);
461
+ console.log(
462
+ chalk2.bold("\n\u26A0\uFE0F Write down your seed phrase. Store it safely.\n")
463
+ );
464
+ console.log(chalk2.yellow(mnemonic));
465
+ console.log();
466
+ const confirmed = await inkConfirm(
467
+ "I have written down my seed phrase",
468
+ false
469
+ );
470
+ if (!confirmed) {
471
+ console.log(chalk2.red("Aborted."));
472
+ return;
473
+ }
474
+ const password = await inkPassword("Set vault password:");
475
+ const confirm = await inkPassword("Confirm password:");
476
+ if (password !== confirm) {
477
+ console.log(chalk2.red("Passwords do not match."));
478
+ return;
479
+ }
480
+ const seed = await mnemonicToSeed2(mnemonic);
481
+ const firstKp = deriveAccount(seed, 0, "Main");
482
+ await saveVault(
483
+ { mnemonic, createdAt: (/* @__PURE__ */ new Date()).toISOString(), version: 1 },
484
+ password
485
+ );
486
+ const config = createConfig("");
487
+ config.accountStore.accounts[0] = {
488
+ index: 0,
489
+ name: "Main",
490
+ publicKey: firstKp.publicKey,
491
+ derivationPath: `m/44'/501'/0'/0'`,
492
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
493
+ };
494
+ saveConfig(config);
495
+ sessionPassword = password;
496
+ sessionMnemonic = mnemonic;
497
+ console.log(chalk2.green("\n\u2713 Wallet created"));
498
+ console.log(chalk2.dim(` Address: ${firstKp.publicKey}`));
499
+ }
500
+ async function cmdUnlock() {
501
+ if (!vaultExists()) {
502
+ console.log(chalk2.red("No vault found. Run: wallet init"));
503
+ return;
504
+ }
505
+ const password = await inkPassword("Vault password:");
506
+ try {
507
+ const vault = await loadVault(password);
508
+ sessionPassword = password;
509
+ sessionMnemonic = vault.mnemonic;
510
+ console.log(chalk2.green("\u2713 Wallet unlocked"));
511
+ } catch {
512
+ console.log(chalk2.red("Wrong password."));
513
+ }
514
+ }
515
+ function cmdLock() {
516
+ sessionPassword = null;
517
+ sessionMnemonic = null;
518
+ console.log(chalk2.green("\u2713 Wallet locked"));
519
+ }
520
+ async function cmdAccounts() {
521
+ await requireUnlocked();
522
+ const config = loadConfig();
523
+ console.log(chalk2.bold("\n\u2500\u2500 Accounts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
524
+ for (const entry of config.accountStore.accounts) {
525
+ const active = entry.index === config.accountStore.activeIndex;
526
+ const marker = active ? chalk2.green("\u25CF") : chalk2.dim("\u25CB");
527
+ let balance = "...";
528
+ try {
529
+ const { sol } = await getBalance(entry.publicKey);
530
+ balance = sol.toFixed(4) + " SOL";
531
+ } catch {
532
+ }
533
+ console.log(
534
+ ` ${marker} [${entry.index}] ${entry.name.padEnd(16)} ${entry.publicKey} ${balance}`
535
+ );
536
+ }
537
+ console.log(chalk2.dim(`
538
+ Network: ${config.cluster} (${config.rpcUrl})`));
539
+ console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
540
+ }
541
+ async function cmdNewAccount(name) {
542
+ const { mnemonic } = await requireUnlocked();
543
+ const config = loadConfig();
544
+ const nextIndex = config.accountStore.accounts.length;
545
+ const accountName = name || `Account ${nextIndex + 1}`;
546
+ const seed = await mnemonicToSeed2(mnemonic);
547
+ const keypair = deriveAccount(seed, nextIndex, accountName);
548
+ updateAccountStore({
549
+ ...config.accountStore,
550
+ accounts: [
551
+ ...config.accountStore.accounts,
552
+ {
553
+ index: nextIndex,
554
+ name: accountName,
555
+ publicKey: keypair.publicKey,
556
+ derivationPath: `m/44'/501'/${nextIndex}'/0'`,
557
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
558
+ }
559
+ ]
560
+ });
561
+ console.log(chalk2.green(`\u2713 Account created: ${accountName}`));
562
+ console.log(chalk2.dim(` Address: ${keypair.publicKey}`));
563
+ }
564
+ async function cmdUseAccount(nameOrIndex) {
565
+ await requireUnlocked();
566
+ const config = loadConfig();
567
+ const found = config.accountStore.accounts.find(
568
+ (a) => a.name === nameOrIndex || a.index === parseInt(nameOrIndex)
569
+ );
570
+ if (!found) {
571
+ console.log(chalk2.red(`Account not found: ${nameOrIndex}`));
572
+ return;
573
+ }
574
+ updateAccountStore({ ...config.accountStore, activeIndex: found.index });
575
+ console.log(
576
+ chalk2.green(`\u2713 Active account: ${found.name} (${found.publicKey})`)
577
+ );
578
+ }
579
+ async function cmdAirdrop(sol = 1) {
580
+ await requireUnlocked();
581
+ const config = loadConfig();
582
+ const active = config.accountStore.accounts.find(
583
+ (a) => a.index === config.accountStore.activeIndex
584
+ );
585
+ if (!active) {
586
+ console.log(chalk2.red("No active account."));
587
+ return;
588
+ }
589
+ console.log(chalk2.dim(`Requesting ${sol} SOL airdrop...`));
590
+ try {
591
+ const sig = await requestAirdrop(active.publicKey, sol);
592
+ console.log(chalk2.green(`\u2713 Airdrop complete`));
593
+ console.log(chalk2.dim(` tx: ${sig}`));
594
+ } catch (err) {
595
+ console.log(chalk2.red(`Airdrop failed: ${err.message}`));
596
+ }
597
+ }
598
+ async function cmdConnect(wcUri) {
599
+ if (!wcUri || !wcUri.startsWith("wc:")) {
600
+ console.log(chalk2.red("Invalid WalletConnect URI. Must start with wc:"));
601
+ return;
602
+ }
603
+ console.log(chalk2.dim("Pairing with dApp..."));
604
+ await pairWithDapp(wcUri);
605
+ }
606
+ function cmdSessions() {
607
+ const sessions = getAllSessions();
608
+ if (sessions.length === 0) {
609
+ console.log(chalk2.dim("No active sessions."));
610
+ return;
611
+ }
612
+ console.log(chalk2.bold("\n\u2500\u2500 Connected dApps \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
613
+ sessions.forEach((s) => {
614
+ console.log(
615
+ ` ${chalk2.cyan(s.dappName.padEnd(20))} ${s.connectedAccount.slice(0, 8)}... since ${s.connectedAt.slice(0, 10)}`
616
+ );
617
+ console.log(
618
+ chalk2.dim(` ${s.dappUrl} topic: ${s.topic.slice(0, 8)}...`)
619
+ );
620
+ });
621
+ console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
622
+ }
623
+ async function cmdDisconnect(dappName) {
624
+ const sessions = getAllSessions();
625
+ const session = sessions.find(
626
+ (s) => s.dappName.toLowerCase().includes(dappName.toLowerCase())
627
+ );
628
+ if (!session) {
629
+ console.log(chalk2.red(`No session found for: ${dappName}`));
630
+ return;
631
+ }
632
+ await disconnectDapp(session.topic);
633
+ console.log(chalk2.green(`\u2713 Disconnected from ${session.dappName}`));
634
+ }
635
+ async function cmdSend(to, sol, vault) {
636
+ const { PublicKey: PublicKey2, SystemProgram, Transaction: Transaction3, LAMPORTS_PER_SOL: LAMPORTS_PER_SOL3 } = await import("@solana/web3.js");
637
+ const keypair = vault.getActiveKeypair();
638
+ const { Keypair: Keypair2 } = await import("@solana/web3.js");
639
+ const solanaKeypair = Keypair2.fromSecretKey(keypair.secretKey);
640
+ const connection = getConnection();
641
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
642
+ const tx = new Transaction3().add(
643
+ SystemProgram.transfer({
644
+ fromPubkey: solanaKeypair.publicKey,
645
+ toPubkey: new PublicKey2(to),
646
+ lamports: Math.round(sol * LAMPORTS_PER_SOL3)
647
+ })
648
+ );
649
+ tx.recentBlockhash = blockhash;
650
+ tx.feePayer = solanaKeypair.publicKey;
651
+ tx.sign(solanaKeypair);
652
+ const raw = tx.serialize();
653
+ const sig = await connection.sendRawTransaction(raw, {
654
+ skipPreflight: false,
655
+ preflightCommitment: "confirmed"
656
+ });
657
+ await connection.confirmTransaction(
658
+ { signature: sig, blockhash, lastValidBlockHeight },
659
+ "confirmed"
660
+ );
661
+ console.log(chalk2.green(`\u2713 Sent ${sol} SOL to ${to}`));
662
+ console.log(chalk2.dim(` tx: ${sig}`));
663
+ }
664
+ function cmdSetCluster(cluster) {
665
+ const valid = ["mainnet-beta", "devnet", "testnet"];
666
+ if (!valid.includes(cluster)) {
667
+ console.log(chalk2.red(`Valid clusters: ${valid.join(", ")}`));
668
+ return;
669
+ }
670
+ updateCluster(cluster);
671
+ console.log(chalk2.green(`\u2713 Cluster set to: ${cluster}`));
672
+ }
673
+
674
+ // src/cli/ui.tsx
675
+ function stripAnsi(str) {
676
+ return str.replace(/\x1b\[[0-9;]*m/g, "");
677
+ }
678
+ function logColor(type) {
679
+ switch (type) {
680
+ case "success":
681
+ return "green";
682
+ case "error":
683
+ return "red";
684
+ case "warn":
685
+ return "yellow";
686
+ default:
687
+ return "gray";
688
+ }
689
+ }
690
+ function Header({ cluster }) {
691
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "\u2B21 Agentic Wallet"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " ", cluster)), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "\u2500".repeat(60)));
692
+ }
693
+ function AccountRow({
694
+ index,
695
+ name,
696
+ publicKey,
697
+ balance,
698
+ isActive
699
+ }) {
700
+ return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: isActive ? "green" : "gray" }, isActive ? "\u25CF" : "\u25CB", " "), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "[", index, "] "), /* @__PURE__ */ React2.createElement(Text2, { bold: isActive }, name.padEnd(18)), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, publicKey.slice(0, 6), "...", publicKey.slice(-4), " "), /* @__PURE__ */ React2.createElement(Text2, { color: isActive ? "cyan" : "gray" }, balance));
701
+ }
702
+ function SessionRow({
703
+ name,
704
+ url,
705
+ account
706
+ }) {
707
+ return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u25C9 "), /* @__PURE__ */ React2.createElement(Text2, { bold: true }, name.padEnd(20)), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, url.slice(0, 30).padEnd(32)), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, account.slice(0, 6), "...", account.slice(-4)));
708
+ }
709
+ function AgentRow({ agent }) {
710
+ return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: agent.running ? "green" : "gray" }, agent.running ? "\u25B6" : "\u25A0", " "), /* @__PURE__ */ React2.createElement(Text2, { bold: agent.running }, agent.id.padEnd(22)), /* @__PURE__ */ React2.createElement(Text2, { color: agent.running ? "cyan" : "gray" }, agent.running ? "RUNNING" : "STOPPED", " "), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "runs: ", agent.runCount), agent.error && /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, " \u26A0 ", agent.error.slice(0, 30)));
711
+ }
712
+ function LogRow({ entry }) {
713
+ return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, entry.time, " "), /* @__PURE__ */ React2.createElement(Text2, { color: "cyan" }, entry.source.padEnd(20)), /* @__PURE__ */ React2.createElement(Text2, { color: logColor(entry.type) }, entry.message));
714
+ }
715
+ function PromptRow({
716
+ prompt,
717
+ maskLen,
718
+ cursor
719
+ }) {
720
+ if (prompt.type === "password") {
721
+ return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, prompt.question, " "), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "*".repeat(maskLen)), /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2588"));
722
+ }
723
+ if (prompt.type === "select") {
724
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true }, prompt.message), prompt.choices.map((c, i) => /* @__PURE__ */ React2.createElement(Box2, { key: i }, /* @__PURE__ */ React2.createElement(Text2, { color: i === cursor ? "cyan" : "gray" }, i === cursor ? "\u276F " : " ", c.name))));
725
+ }
726
+ if (prompt.type === "confirm") {
727
+ const hint = prompt.defaultYes ? "Y/n" : "y/N";
728
+ return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { bold: true }, prompt.message, " "), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "[", hint, "]"));
729
+ }
730
+ return null;
731
+ }
732
+ function CommandBar({ buffer, running }) {
733
+ return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green", bold: true }, "wallet"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " \u203A "), running ? /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", dimColor: true }, "running\u2026") : /* @__PURE__ */ React2.createElement(Text2, null, buffer, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2588")));
734
+ }
735
+ function Dashboard({ vault, agentStatus, onExit }) {
736
+ const { exit } = useApp2();
737
+ const [view, setView] = useState2("dashboard");
738
+ const [accounts, setAccounts] = useState2([]);
739
+ const [balances, setBalances] = useState2({});
740
+ const [sessions, setSessions] = useState2([]);
741
+ const [cluster, setCluster] = useState2("devnet");
742
+ const [cmdBuffer, setCmdBuffer] = useState2("");
743
+ const [cmdRunning, setCmdRunning] = useState2(false);
744
+ const [promptState, setPromptState] = useState2(null);
745
+ const promptResolverRef = useRef2(null);
746
+ const passwordValueRef = useRef2("");
747
+ const [passwordMaskLen, setPasswordMaskLen] = useState2(0);
748
+ const selectCursorRef = useRef2(0);
749
+ const [selectCursor, setSelectCursor] = useState2(0);
750
+ const [uiLogs, setUiLogs] = useState2([...logBuffer]);
751
+ useEffect(() => {
752
+ const interval = setInterval(() => {
753
+ try {
754
+ const keypairs = vault.getAllKeypairs();
755
+ setAccounts(keypairs);
756
+ setCluster(getCluster());
757
+ setSessions(getAllSessions());
758
+ keypairs.forEach(async (kp) => {
759
+ try {
760
+ const { sol } = await getBalance(kp.publicKey);
761
+ setBalances((prev) => ({
762
+ ...prev,
763
+ [kp.publicKey]: sol.toFixed(4) + " SOL"
764
+ }));
765
+ } catch {
766
+ setBalances((prev) => ({ ...prev, [kp.publicKey]: "..." }));
767
+ }
768
+ });
769
+ } catch {
770
+ }
771
+ }, 2e3);
772
+ return () => clearInterval(interval);
773
+ }, []);
774
+ useEffect(() => {
775
+ const interval = setInterval(() => setSessions(getAllSessions()), 5e3);
776
+ return () => clearInterval(interval);
777
+ }, []);
778
+ useEffect(() => {
779
+ const fn = (updated) => setUiLogs(updated);
780
+ logListeners.push(fn);
781
+ return () => {
782
+ const idx = logListeners.indexOf(fn);
783
+ if (idx >= 0) logListeners.splice(idx, 1);
784
+ };
785
+ }, []);
786
+ const requestPrompt = (state) => new Promise((resolve) => {
787
+ promptResolverRef.current = resolve;
788
+ if (state.type === "password") {
789
+ passwordValueRef.current = "";
790
+ setPasswordMaskLen(0);
791
+ }
792
+ if (state.type === "select") {
793
+ selectCursorRef.current = 0;
794
+ setSelectCursor(0);
795
+ }
796
+ setPromptState(state);
797
+ });
798
+ const resolvePrompt = (val) => {
799
+ promptResolverRef.current?.(val);
800
+ promptResolverRef.current = null;
801
+ passwordValueRef.current = "";
802
+ setPasswordMaskLen(0);
803
+ selectCursorRef.current = 0;
804
+ setSelectCursor(0);
805
+ setPromptState(null);
806
+ };
807
+ const withCapture = async (fn) => {
808
+ const origLog = console.log;
809
+ const origError = console.error;
810
+ console.log = (...args) => {
811
+ const msg = stripAnsi(args.map(String).join(" ")).trim();
812
+ if (msg) addLog("wallet", msg, "info");
813
+ };
814
+ console.error = (...args) => {
815
+ const msg = stripAnsi(args.map(String).join(" ")).trim();
816
+ if (msg) addLog("wallet", msg, "error");
817
+ };
818
+ try {
819
+ await fn();
820
+ } finally {
821
+ console.log = origLog;
822
+ console.error = origError;
823
+ }
824
+ };
825
+ const handleInit = async () => {
826
+ if (vaultExists()) {
827
+ addLog(
828
+ "wallet",
829
+ "Vault already exists. Use `unlock` to access it.",
830
+ "warn"
831
+ );
832
+ return;
833
+ }
834
+ const wordCount = await requestPrompt({
835
+ type: "select",
836
+ message: "Mnemonic length:",
837
+ choices: [
838
+ { name: "12 words (standard)", value: 12 },
839
+ { name: "24 words (extra secure)", value: 24 }
840
+ ]
841
+ });
842
+ const { mnemonic } = generateMnemonic2(wordCount);
843
+ addLog(
844
+ "wallet",
845
+ "\u26A0 Write down your seed phrase and store it safely.",
846
+ "warn"
847
+ );
848
+ addLog("wallet", mnemonic, "warn");
849
+ const confirmed = await requestPrompt({
850
+ type: "confirm",
851
+ message: "I have written down my seed phrase",
852
+ defaultYes: false
853
+ });
854
+ if (!confirmed) {
855
+ addLog("wallet", "Aborted.", "error");
856
+ return;
857
+ }
858
+ const password = await requestPrompt({
859
+ type: "password",
860
+ question: "Set vault password:"
861
+ });
862
+ const confirmPw = await requestPrompt({
863
+ type: "password",
864
+ question: "Confirm password:"
865
+ });
866
+ if (password !== confirmPw) {
867
+ addLog("wallet", "Passwords do not match.", "error");
868
+ return;
869
+ }
870
+ const seed = await mnemonicToSeed2(mnemonic);
871
+ const firstKp = deriveAccount(seed, 0, "Main");
872
+ await saveVault(
873
+ { mnemonic, createdAt: (/* @__PURE__ */ new Date()).toISOString(), version: 1 },
874
+ password
875
+ );
876
+ const cfg = createConfig("");
877
+ cfg.accountStore.accounts[0] = {
878
+ index: 0,
879
+ name: "Main",
880
+ publicKey: firstKp.publicKey,
881
+ derivationPath: `m/44'/501'/0'/0'`,
882
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
883
+ };
884
+ saveConfig(cfg);
885
+ addLog("wallet", `\u2713 Wallet created \u2014 ${firstKp.publicKey}`, "success");
886
+ };
887
+ const handleUnlock = async () => {
888
+ if (!vaultExists()) {
889
+ addLog("wallet", "No vault found. Run: init", "error");
890
+ return;
891
+ }
892
+ const password = await requestPrompt({
893
+ type: "password",
894
+ question: "Vault password:"
895
+ });
896
+ try {
897
+ await loadVault(password);
898
+ addLog("wallet", "\u2713 Wallet unlocked", "success");
899
+ } catch {
900
+ addLog("wallet", "Wrong password.", "error");
901
+ }
902
+ };
903
+ const handleSend = async (to, amount) => {
904
+ if (!to || !amount) {
905
+ addLog("wallet", "Usage: send <address> <sol>", "warn");
906
+ return;
907
+ }
908
+ const confirmed = await requestPrompt({
909
+ type: "confirm",
910
+ message: `Send ${amount} SOL to ${to.slice(0, 8)}...${to.slice(-4)}?`,
911
+ defaultYes: false
912
+ });
913
+ if (!confirmed) {
914
+ addLog("wallet", "Cancelled.", "warn");
915
+ return;
916
+ }
917
+ await withCapture(() => cmdSend(to, amount, vault));
918
+ };
919
+ const executeCommand = async (line) => {
920
+ const parts = line.trim().split(/\s+/);
921
+ const cmd = parts[0];
922
+ setCmdRunning(true);
923
+ addLog("wallet", `> ${line}`, "info");
924
+ try {
925
+ switch (cmd) {
926
+ case "":
927
+ break;
928
+ case "help":
929
+ addLog(
930
+ "wallet",
931
+ "init unlock lock accounts [new|use] send <addr> <sol> airdrop [sol] connect <wc:uri> sessions disconnect <dapp> cluster <name> exit",
932
+ "info"
933
+ );
934
+ break;
935
+ case "exit":
936
+ case "quit":
937
+ onExit();
938
+ exit();
939
+ break;
940
+ case "init":
941
+ await handleInit();
942
+ break;
943
+ case "unlock":
944
+ await handleUnlock();
945
+ break;
946
+ case "lock":
947
+ await withCapture(async () => cmdLock());
948
+ break;
949
+ case "send":
950
+ await handleSend(parts[1], Number(parts[2]));
951
+ break;
952
+ case "airdrop":
953
+ await withCapture(() => cmdAirdrop(Number(parts[1]) || 1));
954
+ break;
955
+ case "connect":
956
+ await withCapture(() => cmdConnect(parts[1]));
957
+ break;
958
+ case "sessions":
959
+ await withCapture(async () => cmdSessions());
960
+ break;
961
+ case "disconnect":
962
+ await withCapture(() => cmdDisconnect(parts.slice(1).join(" ")));
963
+ break;
964
+ case "cluster":
965
+ await withCapture(async () => cmdSetCluster(parts[1]));
966
+ break;
967
+ case "accounts":
968
+ if (parts[1] === "new") {
969
+ await withCapture(() => cmdNewAccount(parts.slice(2).join(" ")));
970
+ } else if (parts[1] === "use") {
971
+ await withCapture(() => cmdUseAccount(parts[2]));
972
+ } else {
973
+ await withCapture(() => cmdAccounts());
974
+ }
975
+ break;
976
+ default:
977
+ addLog("wallet", `Unknown: ${cmd}. Type "help".`, "warn");
978
+ }
979
+ } catch (err) {
980
+ addLog("wallet", `Error: ${err.message}`, "error");
981
+ } finally {
982
+ setCmdRunning(false);
983
+ }
984
+ };
985
+ useInput2((input, key) => {
986
+ if (key.ctrl && input === "c") {
987
+ onExit();
988
+ exit();
989
+ return;
990
+ }
991
+ if (promptState) {
992
+ if (promptState.type === "password") {
993
+ if (key.return) {
994
+ resolvePrompt(passwordValueRef.current);
995
+ } else if (key.backspace || key.delete) {
996
+ if (passwordValueRef.current.length > 0) {
997
+ passwordValueRef.current = passwordValueRef.current.slice(0, -1);
998
+ setPasswordMaskLen(passwordValueRef.current.length);
999
+ }
1000
+ } else if (input && !key.ctrl && !key.meta) {
1001
+ passwordValueRef.current += input;
1002
+ setPasswordMaskLen(passwordValueRef.current.length);
1003
+ }
1004
+ return;
1005
+ }
1006
+ if (promptState.type === "select") {
1007
+ if (key.upArrow) {
1008
+ const n = Math.max(0, selectCursorRef.current - 1);
1009
+ selectCursorRef.current = n;
1010
+ setSelectCursor(n);
1011
+ } else if (key.downArrow) {
1012
+ const n = Math.min(
1013
+ promptState.choices.length - 1,
1014
+ selectCursorRef.current + 1
1015
+ );
1016
+ selectCursorRef.current = n;
1017
+ setSelectCursor(n);
1018
+ } else if (key.return) {
1019
+ resolvePrompt(promptState.choices[selectCursorRef.current].value);
1020
+ }
1021
+ return;
1022
+ }
1023
+ if (promptState.type === "confirm") {
1024
+ if (key.return) {
1025
+ resolvePrompt(promptState.defaultYes);
1026
+ } else if (input === "y" || input === "Y") {
1027
+ resolvePrompt(true);
1028
+ } else if (input === "n" || input === "N") {
1029
+ resolvePrompt(false);
1030
+ }
1031
+ return;
1032
+ }
1033
+ }
1034
+ if (!cmdBuffer && !cmdRunning) {
1035
+ if (input === "1") {
1036
+ setView("dashboard");
1037
+ return;
1038
+ }
1039
+ if (input === "2") {
1040
+ setView("accounts");
1041
+ return;
1042
+ }
1043
+ if (input === "q") {
1044
+ onExit();
1045
+ exit();
1046
+ return;
1047
+ }
1048
+ }
1049
+ if (cmdRunning) return;
1050
+ if (key.return) {
1051
+ const cmd = cmdBuffer.trim();
1052
+ setCmdBuffer("");
1053
+ if (cmd) executeCommand(cmd);
1054
+ return;
1055
+ }
1056
+ if (key.backspace || key.delete) {
1057
+ setCmdBuffer((b) => b.slice(0, -1));
1058
+ return;
1059
+ }
1060
+ if (input && !key.ctrl && !key.meta && !key.escape) {
1061
+ setCmdBuffer((b) => b + input);
1062
+ }
1063
+ });
1064
+ const activeKeypair = (() => {
1065
+ try {
1066
+ return vault.getActiveKeypair();
1067
+ } catch {
1068
+ return null;
1069
+ }
1070
+ })();
1071
+ const recentLogs = uiLogs.slice(-12);
1072
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React2.createElement(Header, { cluster }), /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React2.createElement(
1073
+ Text2,
1074
+ {
1075
+ color: view === "dashboard" ? "cyan" : "gray",
1076
+ bold: view === "dashboard"
1077
+ },
1078
+ "[1] Dashboard",
1079
+ " "
1080
+ ), /* @__PURE__ */ React2.createElement(
1081
+ Text2,
1082
+ {
1083
+ color: view === "accounts" ? "cyan" : "gray",
1084
+ bold: view === "accounts"
1085
+ },
1086
+ "[2] Accounts",
1087
+ " "
1088
+ ), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " q: quit")), view === "dashboard" && /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "Active Account"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "\u2500".repeat(40)), activeKeypair ? /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u25CF "), /* @__PURE__ */ React2.createElement(Text2, { bold: true }, activeKeypair.name.padEnd(18)), /* @__PURE__ */ React2.createElement(Text2, { color: "cyan" }, activeKeypair.publicKey), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " ", balances[activeKeypair.publicKey] ?? "...")) : /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "No active account")), /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "Connected dApps (", sessions.length, ")"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "\u2500".repeat(40)), sessions.length === 0 ? /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "None \u2014 paste a wc: URI to connect") : sessions.map((s) => /* @__PURE__ */ React2.createElement(
1089
+ SessionRow,
1090
+ {
1091
+ key: s.topic,
1092
+ name: s.dappName,
1093
+ url: s.dappUrl,
1094
+ account: s.connectedAccount
1095
+ }
1096
+ ))), agentStatus.length > 0 && /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "Agents (", agentStatus.filter((a) => a.running).length, " running)"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "\u2500".repeat(40)), agentStatus.map((agent) => /* @__PURE__ */ React2.createElement(AgentRow, { key: agent.id, agent }))), /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "Activity"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "\u2500".repeat(40)), recentLogs.length === 0 ? /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "Waiting for activity\u2026") : recentLogs.map((entry, i) => /* @__PURE__ */ React2.createElement(LogRow, { key: i, entry })))), view === "accounts" && /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "All Accounts"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "\u2500".repeat(60)), accounts.length === 0 ? /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "No accounts found") : accounts.map((acc) => /* @__PURE__ */ React2.createElement(
1097
+ AccountRow,
1098
+ {
1099
+ key: acc.publicKey,
1100
+ index: acc.index,
1101
+ name: acc.name,
1102
+ publicKey: acc.publicKey,
1103
+ balance: balances[acc.publicKey] ?? "...",
1104
+ isActive: acc.publicKey === activeKeypair?.publicKey
1105
+ }
1106
+ ))), /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "\u2500".repeat(60))), /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "WebSocket: "), /* @__PURE__ */ React2.createElement(Text2, { color: "cyan" }, "ws://localhost:3000"), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " \u2502 [1] Dashboard [2] Accounts [q] Quit")), /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, "\u2500".repeat(60)), promptState ? /* @__PURE__ */ React2.createElement(
1107
+ PromptRow,
1108
+ {
1109
+ prompt: promptState,
1110
+ maskLen: passwordMaskLen,
1111
+ cursor: selectCursor
1112
+ }
1113
+ ) : /* @__PURE__ */ React2.createElement(CommandBar, { buffer: cmdBuffer, running: cmdRunning })));
1114
+ }
1115
+ var logBuffer = [];
1116
+ var logListeners = [];
1117
+ function addLog(source, message, type = "info") {
1118
+ const entry = {
1119
+ time: (/* @__PURE__ */ new Date()).toTimeString().slice(0, 8),
1120
+ source,
1121
+ message,
1122
+ type
1123
+ };
1124
+ logBuffer.push(entry);
1125
+ if (logBuffer.length > 200) logBuffer.shift();
1126
+ logListeners.forEach((fn) => fn([...logBuffer]));
1127
+ }
1128
+ function startInkUI(vault, getAgentStatus, onExit) {
1129
+ function App() {
1130
+ const [agentStatus, setAgentStatus] = useState2(getAgentStatus());
1131
+ useEffect(() => {
1132
+ const interval = setInterval(
1133
+ () => setAgentStatus(getAgentStatus()),
1134
+ 2e3
1135
+ );
1136
+ return () => clearInterval(interval);
1137
+ }, []);
1138
+ return /* @__PURE__ */ React2.createElement(Dashboard, { vault, agentStatus, onExit });
1139
+ }
1140
+ render2(/* @__PURE__ */ React2.createElement(App, null));
1141
+ }
1142
+ export {
1143
+ addLog,
1144
+ cmdAccounts,
1145
+ cmdAirdrop,
1146
+ cmdConnect,
1147
+ cmdDisconnect,
1148
+ cmdInit,
1149
+ cmdLock,
1150
+ cmdNewAccount,
1151
+ cmdSend,
1152
+ cmdSessions,
1153
+ cmdSetCluster,
1154
+ cmdUnlock,
1155
+ cmdUseAccount,
1156
+ inkConfirm,
1157
+ inkPassword,
1158
+ inkSelect,
1159
+ isUnlocked,
1160
+ startInkUI
1161
+ };
1162
+ //# sourceMappingURL=index.js.map