@clawdvault/cli 0.1.5 → 0.3.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 (3) hide show
  1. package/README.md +0 -48
  2. package/dist/index.js +338 -409
  3. package/package.json +4 -3
package/README.md CHANGED
@@ -160,54 +160,6 @@ Examples:
160
160
  clawdvault trade quote -m TOKEN_MINT -t buy -a 0.1
161
161
  ```
162
162
 
163
- ### `clawdvault stream`
164
-
165
- Real-time data streaming (live trades, prices, chat).
166
-
167
- ```bash
168
- # Stream live trades
169
- clawdvault stream trades [options]
170
- -m, --mint <address> Token mint address (required)
171
- --json Output as JSON (one object per line)
172
- --append Append mode (simple log format)
173
-
174
- # Stream token price updates
175
- clawdvault stream token [options]
176
- -m, --mint <address> Token mint address (required)
177
- --json Output as JSON
178
-
179
- # Stream chat messages
180
- clawdvault stream chat [options]
181
- -m, --mint <address> Token mint address (required)
182
- --json Output as JSON
183
-
184
- Examples:
185
- # Watch trades in real-time (table mode)
186
- clawdvault stream trades -m TOKEN_MINT
187
-
188
- # Watch trades for scripting (JSON output)
189
- clawdvault stream trades -m TOKEN_MINT --json
190
-
191
- # Log trades to file
192
- clawdvault stream trades -m TOKEN_MINT --append >> trades.log
193
-
194
- # Monitor price changes
195
- clawdvault stream token -m TOKEN_MINT
196
-
197
- # Watch chat
198
- clawdvault stream chat -m TOKEN_MINT
199
-
200
- # Pipe to jq for filtering
201
- clawdvault stream trades -m TOKEN_MINT --json | jq 'select(.type == "buy")'
202
- ```
203
-
204
- **Features:**
205
- - Auto-reconnect on connection loss
206
- - Graceful shutdown with Ctrl+C
207
- - Multiple output modes: table (default), append, JSON
208
- - Table mode clears and updates in-place
209
- - JSON mode outputs one object per line (great for piping)
210
-
211
163
  ### `clawdvault wallet`
212
164
 
213
165
  Wallet management and info.
package/dist/index.js CHANGED
@@ -43,6 +43,44 @@ var os = __toESM(require("os"));
43
43
  function getConfigDir() {
44
44
  return path.join(os.homedir(), ".clawdvault");
45
45
  }
46
+ function getAuthConfigPath() {
47
+ return path.join(getConfigDir(), "auth.json");
48
+ }
49
+ function loadAuthConfig() {
50
+ const configPath = getAuthConfigPath();
51
+ if (!fs.existsSync(configPath)) {
52
+ return null;
53
+ }
54
+ try {
55
+ const data = fs.readFileSync(configPath, "utf-8");
56
+ const config = JSON.parse(data);
57
+ if (config.expiresAt) {
58
+ const expiresAt = new Date(config.expiresAt);
59
+ if (expiresAt <= /* @__PURE__ */ new Date()) {
60
+ fs.unlinkSync(configPath);
61
+ return null;
62
+ }
63
+ }
64
+ return config;
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+ function saveAuthConfig(config) {
70
+ const configDir = getConfigDir();
71
+ const configPath = getAuthConfigPath();
72
+ if (!fs.existsSync(configDir)) {
73
+ fs.mkdirSync(configDir, { recursive: true });
74
+ }
75
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
76
+ fs.chmodSync(configPath, 384);
77
+ }
78
+ function clearAuthConfig() {
79
+ const configPath = getAuthConfigPath();
80
+ if (fs.existsSync(configPath)) {
81
+ fs.unlinkSync(configPath);
82
+ }
83
+ }
46
84
  function getWalletPath() {
47
85
  if (process.env.CLAWDVAULT_WALLET) {
48
86
  return process.env.CLAWDVAULT_WALLET;
@@ -81,13 +119,27 @@ function getBaseUrl() {
81
119
  function createClientWithWallet(walletPath) {
82
120
  const signer = loadSigner(walletPath);
83
121
  const baseUrl = getBaseUrl();
84
- const client = (0, import_sdk.createClient)({ signer: signer || void 0, baseUrl });
122
+ const authConfig = loadAuthConfig();
123
+ const sessionToken = authConfig?.sessionToken;
124
+ const client = (0, import_sdk.createClient)({
125
+ signer: signer || void 0,
126
+ baseUrl,
127
+ sessionToken
128
+ });
85
129
  return {
86
130
  client,
87
131
  signer,
88
132
  walletAddress: signer?.publicKey.toBase58() ?? null
89
133
  };
90
134
  }
135
+ function requireAuth() {
136
+ const authConfig = loadAuthConfig();
137
+ if (!authConfig?.sessionToken) {
138
+ error("Not logged in. Run: clawdvault wallet login");
139
+ process.exit(1);
140
+ }
141
+ return authConfig;
142
+ }
91
143
  function createReadOnlyClient() {
92
144
  const baseUrl = getBaseUrl();
93
145
  return (0, import_sdk.createClient)({ baseUrl });
@@ -178,7 +230,7 @@ tokensCommand.command("list").description("List all tokens").option("-s, --sort
178
230
  return;
179
231
  }
180
232
  console.log(import_chalk2.default.bold(`
181
- \u{1F4CA} Tokens (Page ${result.page}, ${result.tokens.length} of ${result.total})
233
+ \u{1F4CA} Tokens (Page ${result.page}, ${result.tokens?.length ?? 0} of ${result.total ?? 0})
182
234
  `));
183
235
  const table = new import_cli_table3.default({
184
236
  head: [
@@ -192,16 +244,16 @@ tokensCommand.command("list").description("List all tokens").option("-s, --sort
192
244
  ],
193
245
  style: { head: [], border: [] }
194
246
  });
195
- for (const token of result.tokens) {
247
+ for (const token of result.tokens ?? []) {
196
248
  const status = token.graduated ? import_chalk2.default.green("\u{1F393} Graduated") : import_chalk2.default.yellow("\u{1F4C8} Bonding");
197
249
  table.push([
198
- import_chalk2.default.bold(token.symbol),
199
- token.name,
200
- formatSol(token.price_sol),
201
- formatSol(token.market_cap_sol),
250
+ import_chalk2.default.bold(token.symbol ?? "???"),
251
+ token.name ?? "Unknown",
252
+ formatSol(token.price_sol ?? 0),
253
+ formatSol(token.market_cap_sol ?? 0),
202
254
  token.volume_24h ? formatSol(token.volume_24h) : "-",
203
255
  status,
204
- shortenAddress(token.mint)
256
+ shortenAddress(token.mint ?? "")
205
257
  ]);
206
258
  }
207
259
  console.log(table.toString());
@@ -223,7 +275,6 @@ var import_commander2 = require("commander");
223
275
  var import_chalk3 = __toESM(require("chalk"));
224
276
  var import_cli_table32 = __toESM(require("cli-table3"));
225
277
  var fs2 = __toESM(require("fs"));
226
- var import_sdk3 = require("@clawdvault/sdk");
227
278
  var tokenCommand = new import_commander2.Command("token").description("Token operations");
228
279
  tokenCommand.command("get <mint>").description("Get token details").option("--json", "Output as JSON").action(async (mint, options) => {
229
280
  const spin = spinner("Fetching token...").start();
@@ -235,18 +286,23 @@ tokenCommand.command("get <mint>").description("Get token details").option("--js
235
286
  console.log(JSON.stringify(result, null, 2));
236
287
  return;
237
288
  }
238
- const { token, trades } = result;
289
+ const token = result.token;
290
+ const trades = result.trades ?? [];
291
+ if (!token) {
292
+ console.log(import_chalk3.default.yellow("Token not found"));
293
+ return;
294
+ }
239
295
  console.log(import_chalk3.default.bold(`
240
- \u{1FA99} ${token.name} (${token.symbol})
296
+ \u{1FA99} ${token.name ?? "Unknown"} (${token.symbol ?? "???"})
241
297
  `));
242
298
  const table = new import_cli_table32.default({
243
299
  style: { head: [], border: [] }
244
300
  });
245
301
  table.push(
246
- { [import_chalk3.default.cyan("Mint")]: token.mint },
247
- { [import_chalk3.default.cyan("Creator")]: shortenAddress(token.creator) },
248
- { [import_chalk3.default.cyan("Price")]: formatSol(token.price_sol) },
249
- { [import_chalk3.default.cyan("Market Cap")]: formatSol(token.market_cap_sol) },
302
+ { [import_chalk3.default.cyan("Mint")]: token.mint ?? "" },
303
+ { [import_chalk3.default.cyan("Creator")]: shortenAddress(token.creator ?? "") },
304
+ { [import_chalk3.default.cyan("Price")]: formatSol(token.price_sol ?? 0) },
305
+ { [import_chalk3.default.cyan("Market Cap")]: formatSol(token.market_cap_sol ?? 0) },
250
306
  { [import_chalk3.default.cyan("Status")]: token.graduated ? "\u{1F393} Graduated" : "\u{1F4C8} Bonding Curve" }
251
307
  );
252
308
  if (token.description) {
@@ -280,10 +336,10 @@ tokenCommand.command("get <mint>").description("Get token details").option("--js
280
336
  const typeStr = trade.type === "buy" ? import_chalk3.default.green("BUY") : import_chalk3.default.red("SELL");
281
337
  tradesTable.push([
282
338
  typeStr,
283
- formatSol(trade.sol_amount),
284
- formatTokens(trade.token_amount),
285
- shortenAddress(trade.trader),
286
- new Date(trade.created_at).toLocaleString()
339
+ formatSol(trade.sol_amount ?? 0),
340
+ formatTokens(trade.token_amount ?? 0),
341
+ shortenAddress(trade.trader ?? ""),
342
+ new Date(trade.created_at ?? Date.now()).toLocaleString()
287
343
  ]);
288
344
  }
289
345
  console.log(tradesTable.toString());
@@ -327,13 +383,13 @@ tokenCommand.command("create").description("Create a new token").requiredOption(
327
383
  createSpin.stop();
328
384
  success(`Token created successfully!`);
329
385
  console.log();
330
- info(`Name: ${result.token.name}`);
331
- info(`Symbol: ${result.token.symbol}`);
332
- info(`Mint: ${result.mint}`);
333
- info(`Signature: ${result.signature}`);
386
+ info(`Name: ${result.token?.name ?? "Unknown"}`);
387
+ info(`Symbol: ${result.token?.symbol ?? "???"}`);
388
+ info(`Mint: ${result.mint ?? ""}`);
389
+ info(`Signature: ${result.signature ?? ""}`);
334
390
  console.log();
335
- info(`Explorer: ${link(result.explorer)}`);
336
- info(`View: ${link(`https://clawdvault.com/${result.mint}`)}`);
391
+ info(`Explorer: ${link(result.explorer ?? "")}`);
392
+ info(`View: ${link(`https://clawdvault.com/${result.mint ?? ""}`)}`);
337
393
  console.log();
338
394
  } catch (err) {
339
395
  createSpin.stop();
@@ -353,17 +409,21 @@ tokenCommand.command("stats <mint>").description("Get on-chain stats").option("-
353
409
  console.log(import_chalk3.default.bold(`
354
410
  \u{1F4C8} On-Chain Stats
355
411
  `));
356
- const { onChain } = result;
412
+ const onChain = result.onChain;
413
+ if (!onChain) {
414
+ console.log(import_chalk3.default.yellow("No on-chain data available"));
415
+ return;
416
+ }
357
417
  const table = new import_cli_table32.default({
358
418
  style: { head: [], border: [] }
359
419
  });
360
420
  table.push(
361
- { [import_chalk3.default.cyan("Price")]: formatSol(onChain.price) },
362
- { [import_chalk3.default.cyan("Market Cap")]: formatSol(onChain.marketCap) },
363
- { [import_chalk3.default.cyan("Total Supply")]: formatTokens(onChain.totalSupply) },
364
- { [import_chalk3.default.cyan("Circulating")]: formatTokens(onChain.circulatingSupply) },
365
- { [import_chalk3.default.cyan("Curve Balance")]: formatTokens(onChain.bondingCurveBalance) },
366
- { [import_chalk3.default.cyan("Curve SOL")]: formatSol(onChain.bondingCurveSol) },
421
+ { [import_chalk3.default.cyan("Price")]: formatSol(onChain.price ?? 0) },
422
+ { [import_chalk3.default.cyan("Market Cap")]: formatSol(onChain.marketCap ?? 0) },
423
+ { [import_chalk3.default.cyan("Total Supply")]: formatTokens(onChain.totalSupply ?? 0) },
424
+ { [import_chalk3.default.cyan("Circulating")]: formatTokens(onChain.circulatingSupply ?? 0) },
425
+ { [import_chalk3.default.cyan("Curve Balance")]: formatTokens(onChain.bondingCurveBalance ?? 0) },
426
+ { [import_chalk3.default.cyan("Curve SOL")]: formatSol(onChain.bondingCurveSol ?? 0) },
367
427
  { [import_chalk3.default.cyan("Status")]: onChain.graduated ? "\u{1F393} Graduated" : "\u{1F4C8} Bonding" }
368
428
  );
369
429
  console.log(table.toString());
@@ -396,12 +456,12 @@ tokenCommand.command("holders <mint>").description("Get top token holders").opti
396
456
  ],
397
457
  style: { head: [], border: [] }
398
458
  });
399
- result.holders.forEach((holder, i) => {
459
+ (result.holders ?? []).forEach((holder, i) => {
400
460
  table.push([
401
461
  i + 1,
402
- shortenAddress(holder.address),
403
- formatTokens(holder.balance),
404
- `${holder.percentage.toFixed(2)}%`,
462
+ shortenAddress(holder.address ?? ""),
463
+ formatTokens(holder.balance ?? 0),
464
+ `${(holder.percentage ?? 0).toFixed(2)}%`,
405
465
  holder.label || "-"
406
466
  ]);
407
467
  });
@@ -412,86 +472,11 @@ tokenCommand.command("holders <mint>").description("Get top token holders").opti
412
472
  handleError(err);
413
473
  }
414
474
  });
415
- tokenCommand.command("watch <mint>").description("Watch token price in real-time").option("--json", "Output as JSON").action(async (mint, options) => {
416
- const baseUrl = process.env.CLAWDVAULT_API_URL || "https://clawdvault.com/api";
417
- console.log(import_chalk3.default.bold(`
418
- \u{1F4C8} Watching ${shortenAddress(mint)}
419
- `));
420
- info(`Connecting to ${baseUrl}...`);
421
- console.log(import_chalk3.default.dim("Press Ctrl+C to stop\n"));
422
- const streaming = (0, import_sdk3.createStreaming)(baseUrl);
423
- const conn = streaming.streamToken(mint);
424
- const client = createReadOnlyClient();
425
- let solPrice = 0;
426
- try {
427
- const { price } = await client.getSolPrice();
428
- solPrice = price;
429
- } catch {
430
- }
431
- let tokenInfo = {};
432
- let lastUpdate = null;
433
- const displayUpdate = (update) => {
434
- if (options.json) {
435
- console.log(JSON.stringify({ ...update, ...tokenInfo }));
436
- return;
437
- }
438
- console.clear();
439
- console.log(import_chalk3.default.bold(`
440
- \u{1F4C8} ${tokenInfo.name || "Token"} (${tokenInfo.symbol || shortenAddress(mint)})
441
- `));
442
- const table = new import_cli_table32.default({
443
- style: { head: [], border: [] }
444
- });
445
- const priceUsd = solPrice > 0 ? formatUsd(update.price_sol * solPrice) : "-";
446
- const mcapUsd = solPrice > 0 ? formatUsd(update.market_cap_sol * solPrice) : "-";
447
- table.push(
448
- { [import_chalk3.default.cyan("Price (SOL)")]: formatSol(update.price_sol) },
449
- { [import_chalk3.default.cyan("Price (USD)")]: priceUsd },
450
- { [import_chalk3.default.cyan("Market Cap (SOL)")]: formatSol(update.market_cap_sol) },
451
- { [import_chalk3.default.cyan("Market Cap (USD)")]: mcapUsd },
452
- { [import_chalk3.default.cyan("Bonding Curve SOL")]: formatSol(update.real_sol_reserves) },
453
- { [import_chalk3.default.cyan("Status")]: update.graduated ? import_chalk3.default.green("\u2713 Graduated") : import_chalk3.default.yellow("Bonding Curve") }
454
- );
455
- console.log(table.toString());
456
- if (lastUpdate && lastUpdate.price_sol !== update.price_sol) {
457
- const change = (update.price_sol - lastUpdate.price_sol) / lastUpdate.price_sol * 100;
458
- const changeStr = change >= 0 ? import_chalk3.default.green(`+${change.toFixed(2)}%`) : import_chalk3.default.red(`${change.toFixed(2)}%`);
459
- console.log(`
460
- ${import_chalk3.default.dim("Last change:")} ${changeStr}`);
461
- }
462
- console.log(import_chalk3.default.dim("\nLast update: " + new Date(update.timestamp).toLocaleTimeString()));
463
- console.log(import_chalk3.default.dim("Press Ctrl+C to stop"));
464
- lastUpdate = update;
465
- };
466
- conn.onConnect(() => success("Connected to stream"));
467
- conn.onDisconnect(() => warn("Disconnected - reconnecting..."));
468
- conn.on("connected", (data) => {
469
- tokenInfo = { name: data.name, symbol: data.symbol };
470
- displayUpdate(data);
471
- });
472
- conn.on("update", displayUpdate);
473
- conn.on("trade", (trade) => {
474
- if (lastUpdate) {
475
- lastUpdate.price_sol = trade.price_sol;
476
- displayUpdate(lastUpdate);
477
- }
478
- });
479
- conn.connect();
480
- process.on("SIGINT", () => {
481
- console.log("\n");
482
- info("Disconnecting...");
483
- streaming.disconnectAll();
484
- process.exit(0);
485
- });
486
- await new Promise(() => {
487
- });
488
- });
489
475
 
490
476
  // src/commands/trade.ts
491
477
  var import_commander3 = require("commander");
492
478
  var import_chalk4 = __toESM(require("chalk"));
493
479
  var import_cli_table33 = __toESM(require("cli-table3"));
494
- var import_sdk4 = require("@clawdvault/sdk");
495
480
  var tradeCommand = new import_commander3.Command("trade").description("Trading operations");
496
481
  tradeCommand.command("buy").description("Buy tokens").requiredOption("-m, --mint <address>", "Token mint address").requiredOption("-a, --sol <amount>", "Amount of SOL to spend").option("-s, --slippage <percent>", "Slippage tolerance (default: 1%)", "1").option("-w, --wallet <path>", "Wallet file path").option("--simulate", "Only simulate, don't execute").action(async (options) => {
497
482
  const { client, signer, walletAddress } = createClientWithWallet(options.wallet);
@@ -510,12 +495,13 @@ tradeCommand.command("buy").description("Buy tokens").requiredOption("-m, --mint
510
495
  const table = new import_cli_table33.default({
511
496
  style: { head: [], border: [] }
512
497
  });
498
+ const priceImpact = quote.price_impact ?? 0;
513
499
  table.push(
514
500
  { [import_chalk4.default.cyan("Input")]: `${solAmount} SOL` },
515
- { [import_chalk4.default.cyan("Output")]: `~${formatTokens(quote.output)} tokens` },
516
- { [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price) },
517
- { [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee) },
518
- { [import_chalk4.default.cyan("Price Impact")]: formatPercent(quote.price_impact) },
501
+ { [import_chalk4.default.cyan("Output")]: `~${formatTokens(quote.output ?? 0)} tokens` },
502
+ { [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price ?? 0) },
503
+ { [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee ?? 0) },
504
+ { [import_chalk4.default.cyan("Price Impact")]: formatPercent(priceImpact) },
519
505
  { [import_chalk4.default.cyan("Slippage")]: `${(slippage * 100).toFixed(1)}%` }
520
506
  );
521
507
  console.log(table.toString());
@@ -523,8 +509,8 @@ tradeCommand.command("buy").description("Buy tokens").requiredOption("-m, --mint
523
509
  info("Simulation only - no transaction executed");
524
510
  return;
525
511
  }
526
- if (quote.price_impact > 0.05) {
527
- warn(`High price impact: ${(quote.price_impact * 100).toFixed(2)}%`);
512
+ if (priceImpact > 0.05) {
513
+ warn(`High price impact: ${(priceImpact * 100).toFixed(2)}%`);
528
514
  }
529
515
  console.log();
530
516
  const tradeSpin = spinner("Executing trade...").start();
@@ -540,10 +526,10 @@ tradeCommand.command("buy").description("Buy tokens").requiredOption("-m, --mint
540
526
  success("Trade executed successfully!");
541
527
  console.log();
542
528
  info(`Signature: ${result.signature}`);
543
- info(`SOL spent: ${formatSol(result.trade.solAmount)}`);
544
- info(`Tokens received: ${formatTokens(result.trade.tokenAmount)}`);
529
+ info(`SOL spent: ${formatSol(result.trade?.solAmount ?? 0)}`);
530
+ info(`Tokens received: ${formatTokens(result.trade?.tokenAmount ?? 0)}`);
545
531
  console.log();
546
- info(`Explorer: ${link(result.explorer)}`);
532
+ info(`Explorer: ${link(result.explorer ?? "")}`);
547
533
  console.log();
548
534
  } catch (err) {
549
535
  quoteSpin.stop();
@@ -559,7 +545,8 @@ tradeCommand.command("sell").description("Sell tokens").requiredOption("-m, --mi
559
545
  const slippage = parseFloat(options.slippage) / 100;
560
546
  const balanceSpin = spinner("Checking balance...").start();
561
547
  try {
562
- const { balance } = await client.getMyBalance(options.mint);
548
+ const { balance: rawBalance } = await client.getMyBalance(options.mint);
549
+ const balance = rawBalance ?? 0;
563
550
  balanceSpin.stop();
564
551
  if (balance <= 0) {
565
552
  throw new Error("No tokens to sell");
@@ -586,12 +573,13 @@ tradeCommand.command("sell").description("Sell tokens").requiredOption("-m, --mi
586
573
  const table = new import_cli_table33.default({
587
574
  style: { head: [], border: [] }
588
575
  });
576
+ const sellPriceImpact = quote.price_impact ?? 0;
589
577
  table.push(
590
578
  { [import_chalk4.default.cyan("Input")]: `${formatTokens(tokenAmount)} tokens` },
591
- { [import_chalk4.default.cyan("Output")]: `~${formatSol(quote.output)}` },
592
- { [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price) },
593
- { [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee) },
594
- { [import_chalk4.default.cyan("Price Impact")]: formatPercent(-quote.price_impact) },
579
+ { [import_chalk4.default.cyan("Output")]: `~${formatSol(quote.output ?? 0)}` },
580
+ { [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price ?? 0) },
581
+ { [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee ?? 0) },
582
+ { [import_chalk4.default.cyan("Price Impact")]: formatPercent(-sellPriceImpact) },
595
583
  { [import_chalk4.default.cyan("Slippage")]: `${(slippage * 100).toFixed(1)}%` }
596
584
  );
597
585
  console.log(table.toString());
@@ -599,8 +587,8 @@ tradeCommand.command("sell").description("Sell tokens").requiredOption("-m, --mi
599
587
  info("Simulation only - no transaction executed");
600
588
  return;
601
589
  }
602
- if (quote.price_impact > 0.05) {
603
- warn(`High price impact: ${(quote.price_impact * 100).toFixed(2)}%`);
590
+ if (sellPriceImpact > 0.05) {
591
+ warn(`High price impact: ${(sellPriceImpact * 100).toFixed(2)}%`);
604
592
  }
605
593
  console.log();
606
594
  const tradeSpin = spinner("Executing trade...").start();
@@ -616,10 +604,10 @@ tradeCommand.command("sell").description("Sell tokens").requiredOption("-m, --mi
616
604
  success("Trade executed successfully!");
617
605
  console.log();
618
606
  info(`Signature: ${result.signature}`);
619
- info(`Tokens sold: ${formatTokens(result.trade.tokenAmount)}`);
620
- info(`SOL received: ${formatSol(result.trade.solAmount)}`);
607
+ info(`Tokens sold: ${formatTokens(result.trade?.tokenAmount ?? 0)}`);
608
+ info(`SOL received: ${formatSol(result.trade?.solAmount ?? 0)}`);
621
609
  console.log();
622
- info(`Explorer: ${link(result.explorer)}`);
610
+ info(`Explorer: ${link(result.explorer ?? "")}`);
623
611
  console.log();
624
612
  } catch (err) {
625
613
  balanceSpin.stop();
@@ -647,12 +635,13 @@ tradeCommand.command("quote").description("Get a price quote without executing")
647
635
  const table = new import_cli_table33.default({
648
636
  style: { head: [], border: [] }
649
637
  });
638
+ const quotePriceImpact = quote.price_impact ?? 0;
650
639
  table.push(
651
640
  { [import_chalk4.default.cyan("Input")]: isBuy ? `${options.amount} SOL` : `${formatTokens(parseFloat(options.amount))} tokens` },
652
- { [import_chalk4.default.cyan("Output")]: isBuy ? `~${formatTokens(quote.output)} tokens` : `~${formatSol(quote.output)}` },
653
- { [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price) },
654
- { [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee) },
655
- { [import_chalk4.default.cyan("Price Impact")]: formatPercent(isBuy ? quote.price_impact : -quote.price_impact) }
641
+ { [import_chalk4.default.cyan("Output")]: isBuy ? `~${formatTokens(quote.output ?? 0)} tokens` : `~${formatSol(quote.output ?? 0)}` },
642
+ { [import_chalk4.default.cyan("Price")]: formatSol(quote.current_price ?? 0) },
643
+ { [import_chalk4.default.cyan("Fee")]: formatSol(quote.fee ?? 0) },
644
+ { [import_chalk4.default.cyan("Price Impact")]: formatPercent(isBuy ? quotePriceImpact : -quotePriceImpact) }
656
645
  );
657
646
  console.log(table.toString());
658
647
  console.log();
@@ -688,15 +677,15 @@ tradeCommand.command("history").description("Get trade history for a token").req
688
677
  ],
689
678
  style: { head: [], border: [] }
690
679
  });
691
- for (const trade of result.trades) {
680
+ for (const trade of result.trades ?? []) {
692
681
  const typeStr = trade.type === "buy" ? import_chalk4.default.green("BUY") : import_chalk4.default.red("SELL");
693
682
  table.push([
694
683
  typeStr,
695
- formatSol(trade.sol_amount),
696
- formatTokens(trade.token_amount),
697
- formatSol(trade.price_sol || trade.price || 0),
698
- shortenAddress(trade.trader),
699
- new Date(trade.created_at).toLocaleString()
684
+ formatSol(trade.sol_amount ?? 0),
685
+ formatTokens(trade.token_amount ?? 0),
686
+ formatSol(trade.price ?? 0),
687
+ shortenAddress(trade.trader ?? ""),
688
+ new Date(trade.created_at ?? Date.now()).toLocaleString()
700
689
  ]);
701
690
  }
702
691
  console.log(table.toString());
@@ -706,78 +695,6 @@ tradeCommand.command("history").description("Get trade history for a token").req
706
695
  handleError(err);
707
696
  }
708
697
  });
709
- tradeCommand.command("stream").description("Stream trades in real-time").requiredOption("-m, --mint <address>", "Token mint address").option("--json", "Output as JSON (one object per line)").option("--append", "Append mode (simple log format)").action(async (options) => {
710
- const baseUrl = process.env.CLAWDVAULT_API_URL || "https://clawdvault.com/api";
711
- console.log(import_chalk4.default.bold(`
712
- \u{1F4E1} Streaming trades for ${shortenAddress(options.mint)}
713
- `));
714
- info(`Connecting to ${baseUrl}...`);
715
- console.log(import_chalk4.default.dim("Press Ctrl+C to stop\n"));
716
- const streaming = (0, import_sdk4.createStreaming)(baseUrl);
717
- const conn = streaming.streamTrades(options.mint);
718
- const trades = [];
719
- const MAX_DISPLAY = 20;
720
- conn.onConnect(() => {
721
- success("Connected to stream");
722
- console.log();
723
- });
724
- conn.onDisconnect(() => warn("Disconnected - reconnecting..."));
725
- conn.on("trade", (trade) => {
726
- if (options.json) {
727
- console.log(JSON.stringify(trade));
728
- return;
729
- }
730
- if (options.append) {
731
- const typeStr = trade.type === "buy" ? import_chalk4.default.green("BUY ") : import_chalk4.default.red("SELL");
732
- const time = new Date(trade.created_at).toLocaleTimeString();
733
- console.log(
734
- `${import_chalk4.default.dim(time)} ${typeStr} ${formatSol(trade.sol_amount).padEnd(15)} ${formatTokens(trade.token_amount).padEnd(12)} @ ${formatSol(trade.price_sol).padEnd(18)} ${import_chalk4.default.dim(shortenAddress(trade.trader))}`
735
- );
736
- return;
737
- }
738
- trades.unshift(trade);
739
- if (trades.length > MAX_DISPLAY) {
740
- trades.pop();
741
- }
742
- console.clear();
743
- console.log(import_chalk4.default.bold(`
744
- \u{1F4E1} Live Trades - ${shortenAddress(options.mint)}
745
- `));
746
- const table = new import_cli_table33.default({
747
- head: [
748
- import_chalk4.default.cyan("Type"),
749
- import_chalk4.default.cyan("SOL"),
750
- import_chalk4.default.cyan("Tokens"),
751
- import_chalk4.default.cyan("Price"),
752
- import_chalk4.default.cyan("Trader"),
753
- import_chalk4.default.cyan("Time")
754
- ],
755
- style: { head: [], border: [] }
756
- });
757
- for (const t of trades) {
758
- const typeStr = t.type === "buy" ? import_chalk4.default.green("BUY") : import_chalk4.default.red("SELL");
759
- table.push([
760
- typeStr,
761
- formatSol(t.sol_amount),
762
- formatTokens(t.token_amount),
763
- formatSol(t.price_sol),
764
- shortenAddress(t.trader),
765
- new Date(t.created_at).toLocaleTimeString()
766
- ]);
767
- }
768
- console.log(table.toString());
769
- console.log(import_chalk4.default.dim("\nPress Ctrl+C to stop"));
770
- });
771
- conn.connect();
772
- process.on("SIGINT", () => {
773
- console.log("\n");
774
- info("Disconnecting...");
775
- streaming.disconnectAll();
776
- process.exit(0);
777
- });
778
- await new Promise(() => {
779
- });
780
- });
781
698
 
782
699
  // src/commands/wallet.ts
783
700
  var import_commander4 = require("commander");
@@ -811,7 +728,7 @@ walletCommand.command("info").description("Show wallet information").option("-w,
811
728
  handleError(err);
812
729
  }
813
730
  });
814
- walletCommand.command("init").description("Generate a new wallet").option("-o, --output <path>", "Output file path (default: ~/.clawdvault/wallet.json)").option("--force", "Overwrite existing wallet").action(async (options) => {
731
+ walletCommand.command("init").description("Generate a new wallet").option("-o, --output <path>", "Output file path (default: ~/.clawdvault/wallet.json)").option("--force", "Overwrite existing wallet").option("--mnemonic", "Generate wallet with recoverable seed phrase (can import into Phantom)").action(async (options) => {
815
732
  try {
816
733
  const configDir = getConfigDir();
817
734
  const outputPath = options.output || path2.join(configDir, "wallet.json");
@@ -823,16 +740,36 @@ walletCommand.command("init").description("Generate a new wallet").option("-o, -
823
740
  if (!fs3.existsSync(configDir)) {
824
741
  fs3.mkdirSync(configDir, { recursive: true });
825
742
  }
826
- const keypair = import_web3.Keypair.generate();
827
- const secretKey = Array.from(keypair.secretKey);
828
- fs3.writeFileSync(outputPath, JSON.stringify(secretKey));
743
+ let keypair;
744
+ let mnemonic;
745
+ if (options.mnemonic) {
746
+ const bip39 = await import("bip39");
747
+ mnemonic = bip39.generateMnemonic(256);
748
+ const seed = await bip39.mnemonicToSeed(mnemonic);
749
+ const secretKey = seed.slice(0, 32);
750
+ keypair = import_web3.Keypair.fromSeed(new Uint8Array(secretKey));
751
+ } else {
752
+ keypair = import_web3.Keypair.generate();
753
+ }
754
+ const secretKeyArray = Array.from(keypair.secretKey);
755
+ fs3.writeFileSync(outputPath, JSON.stringify(secretKeyArray));
829
756
  fs3.chmodSync(outputPath, 384);
830
757
  success("Wallet created successfully!");
831
758
  console.log();
832
759
  info(`Address: ${keypair.publicKey.toBase58()}`);
833
760
  info(`Saved to: ${outputPath}`);
834
- console.log();
835
- warn("\u26A0\uFE0F IMPORTANT: Back up your wallet file! Loss of this file means loss of funds.");
761
+ if (mnemonic) {
762
+ console.log();
763
+ console.log(import_chalk5.default.yellow("\u{1F4DD} SEED PHRASE (Write this down!):"));
764
+ console.log(import_chalk5.default.white(mnemonic));
765
+ console.log();
766
+ warn("\u26A0\uFE0F IMPORTANT: This seed phrase can recover your wallet in Phantom!");
767
+ warn(" Write it down and store it safely. Anyone with this phrase can steal your funds!");
768
+ } else {
769
+ console.log();
770
+ warn("\u26A0\uFE0F IMPORTANT: Back up your wallet file! Loss of this file means loss of funds.");
771
+ info(" Use --mnemonic if you want a recoverable seed phrase for Phantom import.");
772
+ }
836
773
  console.log();
837
774
  } catch (err) {
838
775
  handleError(err);
@@ -860,7 +797,7 @@ walletCommand.command("balance").description("Get wallet balance for a token").r
860
797
  table.push(
861
798
  { [import_chalk5.default.cyan("Wallet")]: result.wallet },
862
799
  { [import_chalk5.default.cyan("Token")]: result.mint },
863
- { [import_chalk5.default.cyan("Balance")]: formatTokens(result.balance) }
800
+ { [import_chalk5.default.cyan("Balance")]: formatTokens(result.balance ?? 0) }
864
801
  );
865
802
  console.log(table.toString());
866
803
  console.log();
@@ -925,7 +862,7 @@ walletCommand.command("sol-price").description("Get current SOL/USD price").opti
925
862
  return;
926
863
  }
927
864
  console.log(import_chalk5.default.bold(`
928
- \u{1F4B2} SOL Price: $${result.price.toFixed(2)}
865
+ \u{1F4B2} SOL Price: $${(result.price ?? 0).toFixed(2)}
929
866
  `));
930
867
  if (result.cached) {
931
868
  info(`Cached ${result.age}s ago from ${result.source}`);
@@ -955,7 +892,7 @@ walletCommand.command("sol-balance").description("Get SOL balance with USD value
955
892
  let usdValue = 0;
956
893
  try {
957
894
  const priceData = await client.getSolPrice();
958
- solPrice = priceData.price;
895
+ solPrice = priceData.price ?? 0;
959
896
  usdValue = balanceSol * solPrice;
960
897
  } catch {
961
898
  }
@@ -1154,214 +1091,206 @@ walletCommand.command("airdrop").description("Request SOL from devnet faucet (de
1154
1091
  handleError(err);
1155
1092
  }
1156
1093
  });
1157
-
1158
- // src/commands/stream.ts
1159
- var import_commander5 = require("commander");
1160
- var import_chalk6 = __toESM(require("chalk"));
1161
- var import_cli_table35 = __toESM(require("cli-table3"));
1162
- var import_sdk5 = require("@clawdvault/sdk");
1163
- function getBaseUrl2() {
1164
- return process.env.CLAWDVAULT_API_URL || "https://clawdvault.com/api";
1165
- }
1166
- var streamCommand = new import_commander5.Command("stream").description("Real-time streaming commands");
1167
- streamCommand.command("trades").description("Stream real-time trades for a token").requiredOption("-m, --mint <address>", "Token mint address").option("--json", "Output as JSON (one object per line)").option("--append", "Append mode instead of table (good for logging)").action(async (options) => {
1168
- const baseUrl = getBaseUrl2();
1169
- console.log(import_chalk6.default.bold(`
1170
- \u{1F4E1} Streaming trades for ${shortenAddress(options.mint)}
1171
- `));
1172
- info(`Connecting to ${baseUrl}...`);
1173
- console.log(import_chalk6.default.dim("Press Ctrl+C to stop\n"));
1174
- const streaming = (0, import_sdk5.createStreaming)(baseUrl, {
1175
- autoReconnect: true,
1176
- reconnectDelay: 3e3
1177
- });
1178
- const conn = streaming.streamTrades(options.mint);
1179
- const trades = [];
1180
- const MAX_DISPLAY = 20;
1181
- conn.onConnect(() => {
1182
- success("Connected to stream");
1183
- console.log();
1184
- });
1185
- conn.onDisconnect(() => {
1186
- warn("Disconnected - reconnecting...");
1187
- });
1188
- conn.onError((err) => {
1189
- if (!err.message.includes("Max reconnect")) {
1190
- } else {
1191
- console.error(import_chalk6.default.red(`Error: ${err.message}`));
1094
+ walletCommand.command("login").description("Login to ClawdVault (get session token)").option("-w, --wallet <path>", "Wallet file path").action(async (options) => {
1095
+ const spin = spinner("Authenticating...").start();
1096
+ try {
1097
+ const signer = loadSigner(options.wallet);
1098
+ requireWallet(signer);
1099
+ const walletAddress = signer.publicKey.toBase58();
1100
+ const { createClient: createClient3 } = require("@clawdvault/sdk");
1101
+ const baseUrl = process.env.CLAWDVAULT_API_URL;
1102
+ const client = createClient3({ signer, baseUrl });
1103
+ const session = await client.createSession();
1104
+ if (!session.token) {
1105
+ spin.stop();
1106
+ error("Failed to get session token");
1192
1107
  process.exit(1);
1193
1108
  }
1194
- });
1195
- conn.on("trade", (trade) => {
1196
- if (options.json) {
1197
- console.log(JSON.stringify(trade));
1109
+ const expiresInMs = (session.expiresIn || 7 * 24 * 60 * 60) * 1e3;
1110
+ const expiresAt = new Date(Date.now() + expiresInMs).toISOString();
1111
+ saveAuthConfig({
1112
+ sessionToken: session.token,
1113
+ wallet: walletAddress,
1114
+ expiresAt
1115
+ });
1116
+ spin.stop();
1117
+ success("Logged in successfully!");
1118
+ console.log();
1119
+ info(`Wallet: ${walletAddress}`);
1120
+ info(`Session expires: ${new Date(expiresAt).toLocaleString()}`);
1121
+ info(`Config saved to: ${getAuthConfigPath()}`);
1122
+ console.log();
1123
+ } catch (err) {
1124
+ spin.stop();
1125
+ handleError(err);
1126
+ }
1127
+ });
1128
+ walletCommand.command("logout").description("Logout from ClawdVault (clear session token)").action(async () => {
1129
+ try {
1130
+ const authConfig = loadAuthConfig();
1131
+ if (!authConfig) {
1132
+ warn("Not logged in");
1198
1133
  return;
1199
1134
  }
1200
- if (options.append) {
1201
- const typeStr = trade.type === "buy" ? import_chalk6.default.green("BUY ") : import_chalk6.default.red("SELL");
1202
- const time = new Date(trade.created_at).toLocaleTimeString();
1203
- console.log(
1204
- `${import_chalk6.default.dim(time)} ${typeStr} ${formatSol(trade.sol_amount).padEnd(15)} ${formatTokens(trade.token_amount).padEnd(12)} @ ${formatSol(trade.price_sol).padEnd(18)} ${import_chalk6.default.dim(shortenAddress(trade.trader))}`
1205
- );
1135
+ clearAuthConfig();
1136
+ success("Logged out successfully");
1137
+ console.log();
1138
+ } catch (err) {
1139
+ handleError(err);
1140
+ }
1141
+ });
1142
+ walletCommand.command("status").description("Show authentication status").action(async () => {
1143
+ try {
1144
+ const authConfig = loadAuthConfig();
1145
+ console.log(import_chalk5.default.bold("\n\u{1F510} Auth Status\n"));
1146
+ if (!authConfig?.sessionToken) {
1147
+ warn("Not logged in");
1148
+ info("Run: clawdvault wallet login");
1149
+ console.log();
1206
1150
  return;
1207
1151
  }
1208
- trades.unshift(trade);
1209
- if (trades.length > MAX_DISPLAY) {
1210
- trades.pop();
1211
- }
1212
- console.clear();
1213
- console.log(import_chalk6.default.bold(`
1214
- \u{1F4E1} Live Trades - ${shortenAddress(options.mint)}
1215
- `));
1216
- const table = new import_cli_table35.default({
1217
- head: [
1218
- import_chalk6.default.cyan("Type"),
1219
- import_chalk6.default.cyan("SOL"),
1220
- import_chalk6.default.cyan("Tokens"),
1221
- import_chalk6.default.cyan("Price"),
1222
- import_chalk6.default.cyan("Trader"),
1223
- import_chalk6.default.cyan("Time")
1224
- ],
1152
+ const table = new import_cli_table34.default({
1225
1153
  style: { head: [], border: [] }
1226
1154
  });
1227
- for (const t of trades) {
1228
- const typeStr = t.type === "buy" ? import_chalk6.default.green("BUY") : import_chalk6.default.red("SELL");
1229
- table.push([
1230
- typeStr,
1231
- formatSol(t.sol_amount),
1232
- formatTokens(t.token_amount),
1233
- formatSol(t.price_sol),
1234
- shortenAddress(t.trader),
1235
- new Date(t.created_at).toLocaleTimeString()
1236
- ]);
1237
- }
1155
+ table.push(
1156
+ { [import_chalk5.default.cyan("Status")]: import_chalk5.default.green("\u2713 Logged in") },
1157
+ { [import_chalk5.default.cyan("Wallet")]: authConfig.wallet || "Unknown" },
1158
+ { [import_chalk5.default.cyan("Expires")]: authConfig.expiresAt ? new Date(authConfig.expiresAt).toLocaleString() : "Unknown" }
1159
+ );
1238
1160
  console.log(table.toString());
1239
- console.log(import_chalk6.default.dim("\nPress Ctrl+C to stop"));
1240
- });
1241
- conn.connect();
1242
- process.on("SIGINT", () => {
1243
- console.log("\n");
1244
- info("Disconnecting...");
1245
- streaming.disconnectAll();
1246
- process.exit(0);
1247
- });
1248
- await new Promise(() => {
1249
- });
1161
+ console.log();
1162
+ const { client } = createClientWithWallet();
1163
+ const spin = spinner("Validating session...").start();
1164
+ try {
1165
+ const validation = await client.validateSession();
1166
+ spin.stop();
1167
+ if (validation.valid) {
1168
+ success("Session is valid");
1169
+ } else {
1170
+ warn("Session may be expired or invalid");
1171
+ info("Run: clawdvault wallet login");
1172
+ }
1173
+ } catch {
1174
+ spin.stop();
1175
+ warn("Could not validate session with server");
1176
+ }
1177
+ console.log();
1178
+ } catch (err) {
1179
+ handleError(err);
1180
+ }
1250
1181
  });
1251
- streamCommand.command("token").description("Stream real-time token updates (price, market cap)").requiredOption("-m, --mint <address>", "Token mint address").option("--json", "Output as JSON (one object per line)").action(async (options) => {
1252
- const baseUrl = getBaseUrl2();
1253
- console.log(import_chalk6.default.bold(`
1254
- \u{1F4C8} Watching token ${shortenAddress(options.mint)}
1255
- `));
1256
- info(`Connecting to ${baseUrl}...`);
1257
- console.log(import_chalk6.default.dim("Press Ctrl+C to stop\n"));
1258
- const streaming = (0, import_sdk5.createStreaming)(baseUrl);
1259
- const conn = streaming.streamToken(options.mint);
1260
- const client = createReadOnlyClient();
1261
- let solPrice = 0;
1182
+
1183
+ // src/commands/chat.ts
1184
+ var import_commander5 = require("commander");
1185
+ var import_chalk6 = __toESM(require("chalk"));
1186
+ var chatCommand = new import_commander5.Command("chat").description("Token chat operations");
1187
+ chatCommand.command("send").description("Send a chat message to a token").requiredOption("-m, --mint <address>", "Token mint address").argument("<message>", "Message to send").option("-w, --wallet <path>", "Wallet file path").option("--reply <id>", "Reply to message ID").option("--json", "Output as JSON").action(async (message, options) => {
1188
+ const spin = spinner("Sending message...").start();
1262
1189
  try {
1263
- const { price } = await client.getSolPrice();
1264
- solPrice = price;
1265
- } catch {
1190
+ const auth = requireAuth();
1191
+ const { client, signer, walletAddress } = createClientWithWallet(options.wallet);
1192
+ if (!signer) {
1193
+ spin.stop();
1194
+ warn("Wallet recommended for chat (using session wallet)");
1195
+ }
1196
+ const result = await client.sendChat({
1197
+ mint: options.mint,
1198
+ message,
1199
+ replyTo: options.reply
1200
+ });
1201
+ spin.stop();
1202
+ if (options.json) {
1203
+ console.log(JSON.stringify(result, null, 2));
1204
+ return;
1205
+ }
1206
+ if (result.success) {
1207
+ success("Message sent!");
1208
+ console.log();
1209
+ const msg = result.message;
1210
+ if (msg) {
1211
+ info(`ID: ${msg.id}`);
1212
+ if (msg.created_at) {
1213
+ info(`Time: ${new Date(msg.created_at).toLocaleString()}`);
1214
+ }
1215
+ }
1216
+ } else {
1217
+ error("Failed to send message");
1218
+ }
1219
+ console.log();
1220
+ } catch (err) {
1221
+ spin.stop();
1222
+ handleError(err);
1266
1223
  }
1267
- let tokenInfo = {};
1268
- let lastUpdate = null;
1269
- const displayUpdate = (update) => {
1224
+ });
1225
+ chatCommand.command("history").description("Get chat history for a token").requiredOption("-m, --mint <address>", "Token mint address").option("-l, --limit <number>", "Number of messages to fetch", "20").option("--before <id>", "Get messages before this ID").option("--json", "Output as JSON").action(async (options) => {
1226
+ const spin = spinner("Fetching messages...").start();
1227
+ try {
1228
+ const { client } = createClientWithWallet();
1229
+ const result = await client.getChat({
1230
+ mint: options.mint,
1231
+ limit: parseInt(options.limit, 10),
1232
+ before: options.before
1233
+ });
1234
+ spin.stop();
1270
1235
  if (options.json) {
1271
- console.log(JSON.stringify({ ...update, ...tokenInfo }));
1236
+ console.log(JSON.stringify(result, null, 2));
1237
+ return;
1238
+ }
1239
+ const messages = result.messages || [];
1240
+ if (messages.length === 0) {
1241
+ info("No messages found");
1272
1242
  return;
1273
1243
  }
1274
- console.clear();
1275
1244
  console.log(import_chalk6.default.bold(`
1276
- \u{1F4C8} ${tokenInfo.name || "Token"} (${tokenInfo.symbol || shortenAddress(options.mint)})
1245
+ \u{1F4AC} Chat History - ${shortenAddress(options.mint)}
1277
1246
  `));
1278
- const table = new import_cli_table35.default({
1279
- style: { head: [], border: [] }
1280
- });
1281
- const priceUsd = solPrice > 0 ? formatUsd(update.price_sol * solPrice) : "-";
1282
- const mcapUsd = solPrice > 0 ? formatUsd(update.market_cap_sol * solPrice) : "-";
1283
- table.push(
1284
- { [import_chalk6.default.cyan("Price (SOL)")]: formatSol(update.price_sol) },
1285
- { [import_chalk6.default.cyan("Price (USD)")]: priceUsd },
1286
- { [import_chalk6.default.cyan("Market Cap (SOL)")]: formatSol(update.market_cap_sol) },
1287
- { [import_chalk6.default.cyan("Market Cap (USD)")]: mcapUsd },
1288
- { [import_chalk6.default.cyan("Bonding Curve SOL")]: formatSol(update.real_sol_reserves) },
1289
- { [import_chalk6.default.cyan("Status")]: update.graduated ? import_chalk6.default.green("\u2713 Graduated") : import_chalk6.default.yellow("Bonding Curve") }
1290
- );
1291
- console.log(table.toString());
1292
- if (lastUpdate && lastUpdate.price_sol !== update.price_sol) {
1293
- const change = (update.price_sol - lastUpdate.price_sol) / lastUpdate.price_sol * 100;
1294
- const changeStr = change >= 0 ? import_chalk6.default.green(`+${change.toFixed(2)}%`) : import_chalk6.default.red(`${change.toFixed(2)}%`);
1295
- console.log(`
1296
- ${import_chalk6.default.dim("Last change:")} ${changeStr}`);
1247
+ const sorted = [...messages].reverse();
1248
+ for (const msg of sorted) {
1249
+ const time = msg.created_at ? new Date(msg.created_at).toLocaleTimeString() : "??:??";
1250
+ const sender = msg.username || (msg.wallet ? shortenAddress(msg.wallet) : "unknown");
1251
+ const replyPrefix = msg.reply_to ? import_chalk6.default.dim(`\u21A9 ${shortenAddress(msg.reply_to)} `) : "";
1252
+ console.log(
1253
+ `${import_chalk6.default.dim(time)} ${import_chalk6.default.cyan(sender)}: ${replyPrefix}${msg.message || ""}`
1254
+ );
1297
1255
  }
1298
- console.log(import_chalk6.default.dim("\nLast update: " + new Date(update.timestamp).toLocaleTimeString()));
1299
- console.log(import_chalk6.default.dim("Press Ctrl+C to stop"));
1300
- lastUpdate = update;
1301
- };
1302
- conn.onConnect(() => {
1303
- success("Connected to stream");
1304
- });
1305
- conn.onDisconnect(() => {
1306
- warn("Disconnected - reconnecting...");
1307
- });
1308
- conn.on("connected", (data) => {
1309
- tokenInfo = { name: data.name, symbol: data.symbol };
1310
- displayUpdate(data);
1311
- });
1312
- conn.on("update", displayUpdate);
1313
- conn.on("trade", (trade) => {
1314
- if (lastUpdate) {
1315
- lastUpdate.price_sol = trade.price_sol;
1316
- displayUpdate(lastUpdate);
1256
+ console.log();
1257
+ info(`Showing ${messages.length} messages`);
1258
+ if (messages.length > 0) {
1259
+ info(`Use --before ${messages[messages.length - 1]?.id} for older messages`);
1317
1260
  }
1318
- });
1319
- conn.connect();
1320
- process.on("SIGINT", () => {
1321
- console.log("\n");
1322
- info("Disconnecting...");
1323
- streaming.disconnectAll();
1324
- process.exit(0);
1325
- });
1326
- await new Promise(() => {
1327
- });
1261
+ console.log();
1262
+ } catch (err) {
1263
+ spin.stop();
1264
+ handleError(err);
1265
+ }
1328
1266
  });
1329
- streamCommand.command("chat").description("Stream real-time chat messages for a token").requiredOption("-m, --mint <address>", "Token mint address").option("--json", "Output as JSON (one object per line)").action(async (options) => {
1330
- const baseUrl = getBaseUrl2();
1331
- console.log(import_chalk6.default.bold(`
1332
- \u{1F4AC} Streaming chat for ${shortenAddress(options.mint)}
1333
- `));
1334
- info(`Connecting to ${baseUrl}...`);
1335
- console.log(import_chalk6.default.dim("Press Ctrl+C to stop\n"));
1336
- const streaming = (0, import_sdk5.createStreaming)(baseUrl);
1337
- const conn = streaming.streamChat(options.mint);
1338
- conn.onConnect(() => {
1339
- success("Connected to stream");
1267
+ chatCommand.command("react").description("Add reaction to a message").requiredOption("-i, --id <messageId>", "Message ID").requiredOption("-e, --emoji <emoji>", "Emoji to react with").option("-w, --wallet <path>", "Wallet file path").action(async (options) => {
1268
+ const spin = spinner("Adding reaction...").start();
1269
+ try {
1270
+ requireAuth();
1271
+ const { client } = createClientWithWallet(options.wallet);
1272
+ await client.addReaction(options.id, options.emoji);
1273
+ spin.stop();
1274
+ success(`Reacted with ${options.emoji}`);
1340
1275
  console.log();
1341
- });
1342
- conn.onDisconnect(() => {
1343
- warn("Disconnected - reconnecting...");
1344
- });
1345
- conn.on("message", (msg) => {
1346
- if (options.json) {
1347
- console.log(JSON.stringify(msg));
1348
- return;
1349
- }
1350
- const time = new Date(msg.created_at).toLocaleTimeString();
1351
- const sender = msg.username || shortenAddress(msg.wallet);
1352
- console.log(
1353
- `${import_chalk6.default.dim(time)} ${import_chalk6.default.cyan(sender)}: ${msg.message}`
1354
- );
1355
- });
1356
- conn.connect();
1357
- process.on("SIGINT", () => {
1358
- console.log("\n");
1359
- info("Disconnecting...");
1360
- streaming.disconnectAll();
1361
- process.exit(0);
1362
- });
1363
- await new Promise(() => {
1364
- });
1276
+ } catch (err) {
1277
+ spin.stop();
1278
+ handleError(err);
1279
+ }
1280
+ });
1281
+ chatCommand.command("unreact").description("Remove reaction from a message").requiredOption("-i, --id <messageId>", "Message ID").requiredOption("-e, --emoji <emoji>", "Emoji to remove").option("-w, --wallet <path>", "Wallet file path").action(async (options) => {
1282
+ const spin = spinner("Removing reaction...").start();
1283
+ try {
1284
+ requireAuth();
1285
+ const { client } = createClientWithWallet(options.wallet);
1286
+ await client.removeReaction(options.id, options.emoji);
1287
+ spin.stop();
1288
+ success(`Removed ${options.emoji} reaction`);
1289
+ console.log();
1290
+ } catch (err) {
1291
+ spin.stop();
1292
+ handleError(err);
1293
+ }
1365
1294
  });
1366
1295
 
1367
1296
  // src/index.ts
@@ -1371,7 +1300,7 @@ program.addCommand(tokensCommand);
1371
1300
  program.addCommand(tokenCommand);
1372
1301
  program.addCommand(tradeCommand);
1373
1302
  program.addCommand(walletCommand);
1374
- program.addCommand(streamCommand);
1303
+ program.addCommand(chatCommand);
1375
1304
  program.hook("preAction", () => {
1376
1305
  });
1377
1306
  program.configureOutput({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawdvault/cli",
3
- "version": "0.1.5",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for ClawdVault - Solana token launchpad",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -10,14 +10,15 @@
10
10
  "dist"
11
11
  ],
12
12
  "scripts": {
13
- "build": "tsup src/index.ts --format cjs --clean --shims",
13
+ "build": "tsc --noEmit && tsup src/index.ts --format cjs --clean --shims",
14
14
  "dev": "tsup src/index.ts --format cjs --watch --shims",
15
15
  "clean": "rm -rf dist"
16
16
  },
17
17
  "dependencies": {
18
- "@clawdvault/sdk": "^0.1.3",
18
+ "@clawdvault/sdk": "^0.3.0",
19
19
  "@solana/spl-token": "^0.4.0",
20
20
  "@solana/web3.js": "^1.91.0",
21
+ "bip39": "^3.1.0",
21
22
  "bs58": "^5.0.0",
22
23
  "chalk": "^4.1.2",
23
24
  "commander": "^12.0.0",