@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,21 @@
1
+ import { WalletVault } from '../vault/index.js';
2
+ import '../vault/accounts.js';
3
+ import '../vault/config.js';
4
+ import '../vault/mnemonic.js';
5
+ import '../vault/keystore.js';
6
+
7
+ declare function isUnlocked(): boolean;
8
+ declare function cmdInit(): Promise<void>;
9
+ declare function cmdUnlock(): Promise<void>;
10
+ declare function cmdLock(): void;
11
+ declare function cmdAccounts(): Promise<void>;
12
+ declare function cmdNewAccount(name: string): Promise<void>;
13
+ declare function cmdUseAccount(nameOrIndex: string): Promise<void>;
14
+ declare function cmdAirdrop(sol?: number): Promise<void>;
15
+ declare function cmdConnect(wcUri: string): Promise<void>;
16
+ declare function cmdSessions(): void;
17
+ declare function cmdDisconnect(dappName: string): Promise<void>;
18
+ declare function cmdSend(to: string, sol: number, vault: WalletVault): Promise<void>;
19
+ declare function cmdSetCluster(cluster: string): void;
20
+
21
+ export { cmdAccounts, cmdAirdrop, cmdConnect, cmdDisconnect, cmdInit, cmdLock, cmdNewAccount, cmdSend, cmdSessions, cmdSetCluster, cmdUnlock, cmdUseAccount, isUnlocked };
@@ -0,0 +1,686 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/commands.ts
4
+ import chalk2 from "chalk";
5
+
6
+ // src/vault/mnemonic.ts
7
+ import * as bip39 from "bip39";
8
+ function generateMnemonic2(strength = 12) {
9
+ const mnemonic = bip39.generateMnemonic(strength == 12 ? 128 : 256);
10
+ const wordCount = strength;
11
+ return { mnemonic, wordCount };
12
+ }
13
+ function validateMnemonic2(mnemonic) {
14
+ const cleaned = cleanMnemonic(mnemonic);
15
+ return bip39.validateMnemonic(cleaned);
16
+ }
17
+ async function mnemonicToSeed2(mnemonic, passphrase = "") {
18
+ const cleaned = cleanMnemonic(mnemonic);
19
+ if (!validateMnemonic2(cleaned)) {
20
+ throw new Error("Invalid mnemonic: failed wordlist or checksum validation");
21
+ }
22
+ return bip39.mnemonicToSeed(cleaned, passphrase);
23
+ }
24
+ function cleanMnemonic(mnemonic) {
25
+ return mnemonic.trim().toLowerCase().replace(/\s+/g, " ");
26
+ }
27
+
28
+ // src/vault/keystore.ts
29
+ import crypto from "crypto";
30
+ import fs from "fs";
31
+ import os from "os";
32
+ import path from "path";
33
+ var VAULT_VERSION = 1;
34
+ var PBKDF2_ITERATIONS = 21e4;
35
+ var PBKDF2_DIGEST = "sha512";
36
+ var KEY_LENGTH = 32;
37
+ var SALT_LENGTH = 32;
38
+ var IV_LENGTH = 16;
39
+ var CIPHER = "aes-256-gcm";
40
+ function getWalletDir() {
41
+ return path.join(os.homedir(), ".wallet");
42
+ }
43
+ function getVaultPath() {
44
+ return path.join(getWalletDir(), "vault.enc");
45
+ }
46
+ function getConfigPath() {
47
+ return path.join(getWalletDir(), "config.json");
48
+ }
49
+ function getSessionsPath() {
50
+ return path.join(getWalletDir(), "sessions.json");
51
+ }
52
+ var SESSIONS_FILE = getSessionsPath();
53
+ function ensureWalletDir() {
54
+ const dir = getWalletDir();
55
+ if (!fs.existsSync(dir)) {
56
+ fs.mkdirSync(dir, { recursive: true, mode: 448 });
57
+ }
58
+ }
59
+ function vaultExists() {
60
+ return fs.existsSync(getVaultPath());
61
+ }
62
+ async function saveVault(data, password) {
63
+ ensureWalletDir();
64
+ const salt = crypto.randomBytes(SALT_LENGTH);
65
+ const iv = crypto.randomBytes(IV_LENGTH);
66
+ const key = await deriveKey(password, salt);
67
+ const plaintext = JSON.stringify(data);
68
+ const cipher = crypto.createCipheriv(CIPHER, key, iv);
69
+ const encrypted = Buffer.concat([
70
+ cipher.update(plaintext, "utf8"),
71
+ cipher.final()
72
+ ]);
73
+ const authTag = cipher.getAuthTag();
74
+ const vault = {
75
+ version: VAULT_VERSION,
76
+ salt: salt.toString("hex"),
77
+ iv: iv.toString("hex"),
78
+ authTag: authTag.toString("hex"),
79
+ ciphertext: encrypted.toString("hex")
80
+ };
81
+ fs.writeFileSync(getVaultPath(), JSON.stringify(vault, null, 2), {
82
+ mode: 384
83
+ // owner read/write only
84
+ });
85
+ }
86
+ async function loadVault(password) {
87
+ if (!vaultExists()) {
88
+ throw new Error("No vault found. Run `wallet init` to create one.");
89
+ }
90
+ const raw = fs.readFileSync(getVaultPath(), "utf8");
91
+ const vault = JSON.parse(raw);
92
+ if (vault.version !== VAULT_VERSION) {
93
+ throw new Error(`Unsupported vault version: ${vault.version}`);
94
+ }
95
+ const salt = Buffer.from(vault.salt, "hex");
96
+ const iv = Buffer.from(vault.iv, "hex");
97
+ const authTag = Buffer.from(vault.authTag, "hex");
98
+ const ciphertext = Buffer.from(vault.ciphertext, "hex");
99
+ const key = await deriveKey(password, salt);
100
+ try {
101
+ const decipher = crypto.createDecipheriv(CIPHER, key, iv);
102
+ decipher.setAuthTag(authTag);
103
+ const decrypted = Buffer.concat([
104
+ decipher.update(ciphertext),
105
+ decipher.final()
106
+ ]);
107
+ return JSON.parse(decrypted.toString("utf8"));
108
+ } catch {
109
+ throw new Error("Decryption failed: wrong password or vault is corrupted.");
110
+ }
111
+ }
112
+ function deriveKey(password, salt) {
113
+ return new Promise((resolve, reject) => {
114
+ crypto.pbkdf2(
115
+ password,
116
+ salt,
117
+ PBKDF2_ITERATIONS,
118
+ KEY_LENGTH,
119
+ PBKDF2_DIGEST,
120
+ (err, key) => {
121
+ if (err) reject(err);
122
+ else resolve(key);
123
+ }
124
+ );
125
+ });
126
+ }
127
+
128
+ // src/vault/accounts.ts
129
+ import { derivePath } from "ed25519-hd-key";
130
+ import nacl from "tweetnacl";
131
+ import bs58 from "bs58";
132
+ function deriveAccount(seed, index, name = `Account ${index + 1}`) {
133
+ const path2 = derivationPath(index);
134
+ const { key: privateKeyBytes } = derivePath(path2, seed.toString("hex"));
135
+ const keypair = nacl.sign.keyPair.fromSeed(privateKeyBytes);
136
+ const publicKey = bs58.encode(Buffer.from(keypair.publicKey));
137
+ return {
138
+ index,
139
+ name,
140
+ publicKey,
141
+ secretKey: keypair.secretKey,
142
+ // 64 bytes: seed + public key
143
+ derivationPath: path2,
144
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
145
+ };
146
+ }
147
+ function createAccountStore(firstAccountName = "Account 1") {
148
+ return {
149
+ accounts: [
150
+ {
151
+ index: 0,
152
+ name: firstAccountName,
153
+ publicKey: "",
154
+ // filled in after derivation
155
+ derivationPath: derivationPath(0),
156
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
157
+ }
158
+ ],
159
+ activeIndex: 0
160
+ };
161
+ }
162
+ function derivationPath(index) {
163
+ return `m/44'/501'/${index}'/0'`;
164
+ }
165
+
166
+ // src/solana/rpc.ts
167
+ import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
168
+
169
+ // src/vault/config.ts
170
+ import fs2 from "fs";
171
+ var HELIUS_API_KEY = process.env.HELIUS_API_KEY;
172
+ var RPC_URLS = {
173
+ "mainnet-beta": HELIUS_API_KEY ? `https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}` : "https://api.mainnet-beta.solana.com",
174
+ devnet: HELIUS_API_KEY ? `https://devnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}` : "https://api.devnet.solana.com",
175
+ testnet: "https://api.testnet.solana.com",
176
+ localnet: "http://localhost:8899"
177
+ };
178
+ var CONFIG_VERSION = 1;
179
+ function configExists() {
180
+ return fs2.existsSync(getConfigPath());
181
+ }
182
+ function loadConfig() {
183
+ if (!configExists()) {
184
+ throw new Error("No config found. Run `wallet init` first.");
185
+ }
186
+ const raw = fs2.readFileSync(getConfigPath(), "utf8");
187
+ return JSON.parse(raw);
188
+ }
189
+ function saveConfig(config) {
190
+ ensureWalletDir();
191
+ const updated = { ...config, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
192
+ fs2.writeFileSync(getConfigPath(), JSON.stringify(updated, null, 2), {
193
+ mode: 384
194
+ });
195
+ }
196
+ function createConfig(walletConnectProjectId = "", cluster = "devnet") {
197
+ const now = (/* @__PURE__ */ new Date()).toISOString();
198
+ return {
199
+ version: CONFIG_VERSION,
200
+ cluster,
201
+ rpcUrl: RPC_URLS[cluster],
202
+ walletConnectProjectId,
203
+ accountStore: createAccountStore("Account 1"),
204
+ createdAt: now,
205
+ updatedAt: now
206
+ };
207
+ }
208
+ function updateCluster(cluster) {
209
+ const config = loadConfig();
210
+ config.cluster = cluster;
211
+ config.rpcUrl = RPC_URLS[cluster];
212
+ saveConfig(config);
213
+ }
214
+ function updateAccountStore(accountStore) {
215
+ const config = loadConfig();
216
+ config.accountStore = accountStore;
217
+ saveConfig(config);
218
+ }
219
+ function getRpcUrl() {
220
+ return loadConfig().rpcUrl;
221
+ }
222
+ function getCluster() {
223
+ return loadConfig().cluster;
224
+ }
225
+
226
+ // src/solana/rpc.ts
227
+ var _connection = null;
228
+ function getConnection(forceNew = false) {
229
+ if (!_connection || forceNew) {
230
+ const rpcUrl = getRpcUrl();
231
+ _connection = new Connection(rpcUrl, "confirmed");
232
+ }
233
+ return _connection;
234
+ }
235
+ async function getBalance(publicKeyStr) {
236
+ const connection = getConnection();
237
+ const pubkey = new PublicKey(publicKeyStr);
238
+ const lamports = await connection.getBalance(pubkey);
239
+ return {
240
+ lamports,
241
+ sol: lamports / LAMPORTS_PER_SOL
242
+ };
243
+ }
244
+ async function requestAirdrop(publicKeyStr, solAmount = 1) {
245
+ const cluster = getCluster();
246
+ if (cluster === "mainnet-beta") {
247
+ throw new Error("Airdrops are not available on mainnet.");
248
+ }
249
+ const connection = getConnection();
250
+ const pubkey = new PublicKey(publicKeyStr);
251
+ const lamports = solAmount * LAMPORTS_PER_SOL;
252
+ const signature = await connection.requestAirdrop(pubkey, lamports);
253
+ await connection.confirmTransaction(signature, "confirmed");
254
+ return signature;
255
+ }
256
+
257
+ // src/handler/walletConnect.ts
258
+ import SignClientPkg from "@walletconnect/sign-client";
259
+ import { getSdkError } from "@walletconnect/utils";
260
+ import chalk from "chalk";
261
+
262
+ // src/solana/simulate.ts
263
+ import {
264
+ Transaction,
265
+ VersionedTransaction,
266
+ LAMPORTS_PER_SOL as LAMPORTS_PER_SOL2
267
+ } from "@solana/web3.js";
268
+
269
+ // src/solana/tx.ts
270
+ import {
271
+ Transaction as Transaction2,
272
+ VersionedTransaction as VersionedTransaction2,
273
+ Keypair
274
+ } from "@solana/web3.js";
275
+ import nacl2 from "tweetnacl";
276
+ import bs582 from "bs58";
277
+
278
+ // src/handler/index.ts
279
+ import bs583 from "bs58";
280
+
281
+ // src/session/index.ts
282
+ import * as fs3 from "fs";
283
+ function read() {
284
+ ensureWalletDir();
285
+ if (!fs3.existsSync(SESSIONS_FILE)) return { sessions: [] };
286
+ return JSON.parse(fs3.readFileSync(SESSIONS_FILE, "utf8"));
287
+ }
288
+ function write(store) {
289
+ ensureWalletDir();
290
+ fs3.writeFileSync(SESSIONS_FILE, JSON.stringify(store, null, 2));
291
+ }
292
+ function removeSession(topic) {
293
+ const store = read();
294
+ store.sessions = store.sessions.filter((s) => s.topic !== topic);
295
+ write(store);
296
+ }
297
+ function getAllSessions() {
298
+ return read().sessions.filter((s) => s.active);
299
+ }
300
+
301
+ // src/handler/walletConnect.ts
302
+ var SignClient = SignClientPkg.default ?? SignClientPkg;
303
+ var signClient = null;
304
+ async function pairWithDapp(uri) {
305
+ if (!signClient) throw new Error("WalletConnect not initialised");
306
+ console.log(chalk.dim("[WC] Connecting to dApp..."));
307
+ await new Promise((resolve, reject) => {
308
+ const timeout = setTimeout(() => {
309
+ signClient.off("session_proposal", onProposal);
310
+ reject(new Error("WalletConnect pairing timed out after 30s"));
311
+ }, 3e4);
312
+ const onProposal = ({ params }) => {
313
+ clearTimeout(timeout);
314
+ signClient.off("session_proposal", onProposal);
315
+ const meta = params.proposer.metadata;
316
+ console.log(
317
+ chalk.cyan(
318
+ `[WC] Request from: ${chalk.bold(meta.name)} (${meta.url})`,
319
+ params
320
+ )
321
+ );
322
+ resolve();
323
+ };
324
+ signClient.on("session_proposal", onProposal);
325
+ signClient.core.pairing.pair({ uri }).catch((err) => {
326
+ clearTimeout(timeout);
327
+ signClient.off("session_proposal", onProposal);
328
+ reject(err);
329
+ });
330
+ });
331
+ }
332
+ async function disconnectDapp(topic) {
333
+ if (!signClient) throw new Error("WalletConnect not initialised");
334
+ await signClient.disconnect({
335
+ topic,
336
+ reason: getSdkError("USER_DISCONNECTED")
337
+ });
338
+ removeSession(topic);
339
+ console.log(
340
+ chalk.dim(`[WC] Disconnected from topic ${topic.slice(0, 8)}...`)
341
+ );
342
+ }
343
+
344
+ // src/cli/prompts.tsx
345
+ import React, { useState, useRef } from "react";
346
+ import { render, Box, Text, useInput, useApp } from "ink";
347
+ function inkPassword(question) {
348
+ return new Promise((resolve) => {
349
+ let result = "";
350
+ function PasswordPrompt() {
351
+ const { exit } = useApp();
352
+ const valueRef = useRef("");
353
+ const [maskLen, setMaskLen] = useState(0);
354
+ useInput((input, key) => {
355
+ if (key.return) {
356
+ result = valueRef.current;
357
+ exit();
358
+ return;
359
+ }
360
+ if (key.backspace || key.delete) {
361
+ if (valueRef.current.length > 0) {
362
+ valueRef.current = valueRef.current.slice(0, -1);
363
+ setMaskLen(valueRef.current.length);
364
+ }
365
+ return;
366
+ }
367
+ if (input && !key.ctrl && !key.meta) {
368
+ valueRef.current += input;
369
+ setMaskLen(valueRef.current.length);
370
+ }
371
+ });
372
+ return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, question, " "), /* @__PURE__ */ React.createElement(Text, { color: "gray" }, "*".repeat(maskLen)));
373
+ }
374
+ const { waitUntilExit } = render(/* @__PURE__ */ React.createElement(PasswordPrompt, null));
375
+ waitUntilExit().then(() => resolve(result));
376
+ });
377
+ }
378
+ function inkSelect(message, choices) {
379
+ return new Promise((resolve) => {
380
+ let result = choices[0].value;
381
+ function SelectPrompt() {
382
+ const { exit } = useApp();
383
+ const cursorRef = useRef(0);
384
+ const [cursor, setCursor] = useState(0);
385
+ useInput((_, key) => {
386
+ if (key.upArrow) {
387
+ const n = Math.max(0, cursorRef.current - 1);
388
+ cursorRef.current = n;
389
+ setCursor(n);
390
+ }
391
+ if (key.downArrow) {
392
+ const n = Math.min(choices.length - 1, cursorRef.current + 1);
393
+ cursorRef.current = n;
394
+ setCursor(n);
395
+ }
396
+ if (key.return) {
397
+ result = choices[cursorRef.current].value;
398
+ exit();
399
+ }
400
+ });
401
+ 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))));
402
+ }
403
+ const { waitUntilExit } = render(/* @__PURE__ */ React.createElement(SelectPrompt, null));
404
+ waitUntilExit().then(() => resolve(result));
405
+ });
406
+ }
407
+ function inkConfirm(message, defaultYes = false) {
408
+ return new Promise((resolve) => {
409
+ let result = defaultYes;
410
+ function ConfirmPrompt() {
411
+ const { exit } = useApp();
412
+ useInput((input, key) => {
413
+ if (key.return) {
414
+ result = defaultYes;
415
+ exit();
416
+ return;
417
+ }
418
+ const ch = input.toLowerCase();
419
+ if (ch === "y") {
420
+ result = true;
421
+ exit();
422
+ } else if (ch === "n") {
423
+ result = false;
424
+ exit();
425
+ }
426
+ });
427
+ const hint = defaultYes ? "Y/n" : "y/N";
428
+ return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, message, " "), /* @__PURE__ */ React.createElement(Text, { color: "gray" }, "[", hint, "] "));
429
+ }
430
+ const { waitUntilExit } = render(/* @__PURE__ */ React.createElement(ConfirmPrompt, null));
431
+ waitUntilExit().then(() => resolve(result));
432
+ });
433
+ }
434
+
435
+ // src/cli/commands.ts
436
+ var sessionPassword = null;
437
+ var sessionMnemonic = null;
438
+ function isUnlocked() {
439
+ return sessionMnemonic !== null;
440
+ }
441
+ async function requireUnlocked() {
442
+ if (!sessionPassword || !sessionMnemonic) {
443
+ throw new Error("Wallet is locked. Run: wallet unlock");
444
+ }
445
+ return { password: sessionPassword, mnemonic: sessionMnemonic };
446
+ }
447
+ async function cmdInit() {
448
+ if (vaultExists()) {
449
+ console.log(
450
+ chalk2.yellow("Vault already exists. Use `wallet unlock` to access it.")
451
+ );
452
+ return;
453
+ }
454
+ const wordCount = await inkSelect("Mnemonic length:", [
455
+ { name: "12 words (standard)", value: 12 },
456
+ { name: "24 words (extra secure)", value: 24 }
457
+ ]);
458
+ const { mnemonic } = generateMnemonic2(wordCount);
459
+ console.log(
460
+ chalk2.bold("\n\u26A0\uFE0F Write down your seed phrase. Store it safely.\n")
461
+ );
462
+ console.log(chalk2.yellow(mnemonic));
463
+ console.log();
464
+ const confirmed = await inkConfirm(
465
+ "I have written down my seed phrase",
466
+ false
467
+ );
468
+ if (!confirmed) {
469
+ console.log(chalk2.red("Aborted."));
470
+ return;
471
+ }
472
+ const password = await inkPassword("Set vault password:");
473
+ const confirm = await inkPassword("Confirm password:");
474
+ if (password !== confirm) {
475
+ console.log(chalk2.red("Passwords do not match."));
476
+ return;
477
+ }
478
+ const seed = await mnemonicToSeed2(mnemonic);
479
+ const firstKp = deriveAccount(seed, 0, "Main");
480
+ await saveVault(
481
+ { mnemonic, createdAt: (/* @__PURE__ */ new Date()).toISOString(), version: 1 },
482
+ password
483
+ );
484
+ const config = createConfig("");
485
+ config.accountStore.accounts[0] = {
486
+ index: 0,
487
+ name: "Main",
488
+ publicKey: firstKp.publicKey,
489
+ derivationPath: `m/44'/501'/0'/0'`,
490
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
491
+ };
492
+ saveConfig(config);
493
+ sessionPassword = password;
494
+ sessionMnemonic = mnemonic;
495
+ console.log(chalk2.green("\n\u2713 Wallet created"));
496
+ console.log(chalk2.dim(` Address: ${firstKp.publicKey}`));
497
+ }
498
+ async function cmdUnlock() {
499
+ if (!vaultExists()) {
500
+ console.log(chalk2.red("No vault found. Run: wallet init"));
501
+ return;
502
+ }
503
+ const password = await inkPassword("Vault password:");
504
+ try {
505
+ const vault = await loadVault(password);
506
+ sessionPassword = password;
507
+ sessionMnemonic = vault.mnemonic;
508
+ console.log(chalk2.green("\u2713 Wallet unlocked"));
509
+ } catch {
510
+ console.log(chalk2.red("Wrong password."));
511
+ }
512
+ }
513
+ function cmdLock() {
514
+ sessionPassword = null;
515
+ sessionMnemonic = null;
516
+ console.log(chalk2.green("\u2713 Wallet locked"));
517
+ }
518
+ async function cmdAccounts() {
519
+ await requireUnlocked();
520
+ const config = loadConfig();
521
+ 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"));
522
+ for (const entry of config.accountStore.accounts) {
523
+ const active = entry.index === config.accountStore.activeIndex;
524
+ const marker = active ? chalk2.green("\u25CF") : chalk2.dim("\u25CB");
525
+ let balance = "...";
526
+ try {
527
+ const { sol } = await getBalance(entry.publicKey);
528
+ balance = sol.toFixed(4) + " SOL";
529
+ } catch {
530
+ }
531
+ console.log(
532
+ ` ${marker} [${entry.index}] ${entry.name.padEnd(16)} ${entry.publicKey} ${balance}`
533
+ );
534
+ }
535
+ console.log(chalk2.dim(`
536
+ Network: ${config.cluster} (${config.rpcUrl})`));
537
+ 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");
538
+ }
539
+ async function cmdNewAccount(name) {
540
+ const { mnemonic } = await requireUnlocked();
541
+ const config = loadConfig();
542
+ const nextIndex = config.accountStore.accounts.length;
543
+ const accountName = name || `Account ${nextIndex + 1}`;
544
+ const seed = await mnemonicToSeed2(mnemonic);
545
+ const keypair = deriveAccount(seed, nextIndex, accountName);
546
+ updateAccountStore({
547
+ ...config.accountStore,
548
+ accounts: [
549
+ ...config.accountStore.accounts,
550
+ {
551
+ index: nextIndex,
552
+ name: accountName,
553
+ publicKey: keypair.publicKey,
554
+ derivationPath: `m/44'/501'/${nextIndex}'/0'`,
555
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
556
+ }
557
+ ]
558
+ });
559
+ console.log(chalk2.green(`\u2713 Account created: ${accountName}`));
560
+ console.log(chalk2.dim(` Address: ${keypair.publicKey}`));
561
+ }
562
+ async function cmdUseAccount(nameOrIndex) {
563
+ await requireUnlocked();
564
+ const config = loadConfig();
565
+ const found = config.accountStore.accounts.find(
566
+ (a) => a.name === nameOrIndex || a.index === parseInt(nameOrIndex)
567
+ );
568
+ if (!found) {
569
+ console.log(chalk2.red(`Account not found: ${nameOrIndex}`));
570
+ return;
571
+ }
572
+ updateAccountStore({ ...config.accountStore, activeIndex: found.index });
573
+ console.log(
574
+ chalk2.green(`\u2713 Active account: ${found.name} (${found.publicKey})`)
575
+ );
576
+ }
577
+ async function cmdAirdrop(sol = 1) {
578
+ await requireUnlocked();
579
+ const config = loadConfig();
580
+ const active = config.accountStore.accounts.find(
581
+ (a) => a.index === config.accountStore.activeIndex
582
+ );
583
+ if (!active) {
584
+ console.log(chalk2.red("No active account."));
585
+ return;
586
+ }
587
+ console.log(chalk2.dim(`Requesting ${sol} SOL airdrop...`));
588
+ try {
589
+ const sig = await requestAirdrop(active.publicKey, sol);
590
+ console.log(chalk2.green(`\u2713 Airdrop complete`));
591
+ console.log(chalk2.dim(` tx: ${sig}`));
592
+ } catch (err) {
593
+ console.log(chalk2.red(`Airdrop failed: ${err.message}`));
594
+ }
595
+ }
596
+ async function cmdConnect(wcUri) {
597
+ if (!wcUri || !wcUri.startsWith("wc:")) {
598
+ console.log(chalk2.red("Invalid WalletConnect URI. Must start with wc:"));
599
+ return;
600
+ }
601
+ console.log(chalk2.dim("Pairing with dApp..."));
602
+ await pairWithDapp(wcUri);
603
+ }
604
+ function cmdSessions() {
605
+ const sessions = getAllSessions();
606
+ if (sessions.length === 0) {
607
+ console.log(chalk2.dim("No active sessions."));
608
+ return;
609
+ }
610
+ 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"));
611
+ sessions.forEach((s) => {
612
+ console.log(
613
+ ` ${chalk2.cyan(s.dappName.padEnd(20))} ${s.connectedAccount.slice(0, 8)}... since ${s.connectedAt.slice(0, 10)}`
614
+ );
615
+ console.log(
616
+ chalk2.dim(` ${s.dappUrl} topic: ${s.topic.slice(0, 8)}...`)
617
+ );
618
+ });
619
+ 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");
620
+ }
621
+ async function cmdDisconnect(dappName) {
622
+ const sessions = getAllSessions();
623
+ const session = sessions.find(
624
+ (s) => s.dappName.toLowerCase().includes(dappName.toLowerCase())
625
+ );
626
+ if (!session) {
627
+ console.log(chalk2.red(`No session found for: ${dappName}`));
628
+ return;
629
+ }
630
+ await disconnectDapp(session.topic);
631
+ console.log(chalk2.green(`\u2713 Disconnected from ${session.dappName}`));
632
+ }
633
+ async function cmdSend(to, sol, vault) {
634
+ const { PublicKey: PublicKey2, SystemProgram, Transaction: Transaction3, LAMPORTS_PER_SOL: LAMPORTS_PER_SOL3 } = await import("@solana/web3.js");
635
+ const keypair = vault.getActiveKeypair();
636
+ const { Keypair: Keypair2 } = await import("@solana/web3.js");
637
+ const solanaKeypair = Keypair2.fromSecretKey(keypair.secretKey);
638
+ const connection = getConnection();
639
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
640
+ const tx = new Transaction3().add(
641
+ SystemProgram.transfer({
642
+ fromPubkey: solanaKeypair.publicKey,
643
+ toPubkey: new PublicKey2(to),
644
+ lamports: Math.round(sol * LAMPORTS_PER_SOL3)
645
+ })
646
+ );
647
+ tx.recentBlockhash = blockhash;
648
+ tx.feePayer = solanaKeypair.publicKey;
649
+ tx.sign(solanaKeypair);
650
+ const raw = tx.serialize();
651
+ const sig = await connection.sendRawTransaction(raw, {
652
+ skipPreflight: false,
653
+ preflightCommitment: "confirmed"
654
+ });
655
+ await connection.confirmTransaction(
656
+ { signature: sig, blockhash, lastValidBlockHeight },
657
+ "confirmed"
658
+ );
659
+ console.log(chalk2.green(`\u2713 Sent ${sol} SOL to ${to}`));
660
+ console.log(chalk2.dim(` tx: ${sig}`));
661
+ }
662
+ function cmdSetCluster(cluster) {
663
+ const valid = ["mainnet-beta", "devnet", "testnet"];
664
+ if (!valid.includes(cluster)) {
665
+ console.log(chalk2.red(`Valid clusters: ${valid.join(", ")}`));
666
+ return;
667
+ }
668
+ updateCluster(cluster);
669
+ console.log(chalk2.green(`\u2713 Cluster set to: ${cluster}`));
670
+ }
671
+ export {
672
+ cmdAccounts,
673
+ cmdAirdrop,
674
+ cmdConnect,
675
+ cmdDisconnect,
676
+ cmdInit,
677
+ cmdLock,
678
+ cmdNewAccount,
679
+ cmdSend,
680
+ cmdSessions,
681
+ cmdSetCluster,
682
+ cmdUnlock,
683
+ cmdUseAccount,
684
+ isUnlocked
685
+ };
686
+ //# sourceMappingURL=commands.js.map