minara 0.4.0 → 0.4.1

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.
@@ -455,11 +455,17 @@ const positionsCmd = new Command('positions')
455
455
  console.log(chalk.dim(` Total positions: ${totalPositions}`));
456
456
  console.log('');
457
457
  }));
458
- // ─── order ───────────────────────────────────────────────────────────────
459
458
  const orderCmd = new Command('order')
460
459
  .description('Place a perps order')
461
460
  .option(WALLET_OPT[0], WALLET_OPT[1])
462
461
  .option('-y, --yes', 'Skip confirmation')
462
+ .option('-S, --side <side>', 'Order side: long/buy or short/sell')
463
+ .option('-s, --symbol <symbol>', 'Asset symbol (e.g. BTC, ETH)')
464
+ .option('-T, --type <type>', 'Order type: market or limit', 'market')
465
+ .option('-p, --price <price>', 'Limit price (required for limit orders)')
466
+ .option('-z, --size <size>', 'Position size in contracts')
467
+ .option('-r, --reduce-only', 'Reduce-only order')
468
+ .option('-g, --grouping <grouping>', 'TP/SL grouping: na, normalTpsl, positionTpsl', 'na')
463
469
  .action(wrapAction(async (opts) => {
464
470
  const creds = requireAuth();
465
471
  const resolved = await resolveWallet(creds.accessToken, opts.wallet, 'Place order on which wallet?');
@@ -481,6 +487,8 @@ const orderCmd = new Command('order')
481
487
  console.log('');
482
488
  return;
483
489
  }
490
+ // Determine if running in non-interactive mode
491
+ const nonInteractive = opts.side && opts.symbol && opts.size;
484
492
  info('Building a Hyperliquid perps order…');
485
493
  const dataSpin = spinner('Fetching market data…');
486
494
  const address = await perpsApi.getPerpsAddress(creds.accessToken);
@@ -493,15 +501,36 @@ const orderCmd = new Command('order')
493
501
  for (const l of leverages) {
494
502
  leverageMap.set(l.coin.toUpperCase(), { value: l.leverageValue, type: l.leverageType });
495
503
  }
496
- const isBuy = await select({
497
- message: 'Side:',
498
- choices: [
499
- { name: 'Long (buy)', value: true },
500
- { name: 'Short (sell)', value: false },
501
- ],
502
- });
504
+ // ── Side ─────────────────────────────────────────────────────────────
505
+ let isBuy;
506
+ if (opts.side) {
507
+ const sideLower = opts.side.toLowerCase();
508
+ if (sideLower === 'long' || sideLower === 'buy') {
509
+ isBuy = true;
510
+ }
511
+ else if (sideLower === 'short' || sideLower === 'sell') {
512
+ isBuy = false;
513
+ }
514
+ else {
515
+ console.error(chalk.red('✖'), `Invalid side: ${opts.side}. Use 'long', 'buy', 'short', or 'sell'.`);
516
+ process.exit(1);
517
+ }
518
+ }
519
+ else {
520
+ isBuy = await select({
521
+ message: 'Side:',
522
+ choices: [
523
+ { name: 'Long (buy)', value: true },
524
+ { name: 'Short (sell)', value: false },
525
+ ],
526
+ });
527
+ }
528
+ // ── Asset ────────────────────────────────────────────────────────────
503
529
  let asset;
504
- if (assets.length > 0) {
530
+ if (opts.symbol) {
531
+ asset = opts.symbol.toUpperCase();
532
+ }
533
+ else if (assets.length > 0) {
505
534
  asset = await select({
506
535
  message: 'Asset:',
507
536
  choices: assets.map((a) => {
@@ -525,42 +554,124 @@ const orderCmd = new Command('order')
525
554
  else {
526
555
  info(`No leverage set for ${asset} — use 'minara perps leverage' to configure`);
527
556
  }
528
- const orderType = await select({
529
- message: 'Order type:',
530
- choices: [
531
- { name: 'Market', value: 'market' },
532
- { name: 'Limit', value: 'limit' },
533
- ],
534
- });
557
+ // ── Order Type ───────────────────────────────────────────────────────
558
+ let orderType;
559
+ if (opts.type) {
560
+ const typeLower = opts.type.toLowerCase();
561
+ if (typeLower === 'market') {
562
+ orderType = 'market';
563
+ }
564
+ else if (typeLower === 'limit') {
565
+ orderType = 'limit';
566
+ }
567
+ else {
568
+ console.error(chalk.red('✖'), `Invalid order type: ${opts.type}. Use 'market' or 'limit'.`);
569
+ process.exit(1);
570
+ }
571
+ }
572
+ else {
573
+ orderType = await select({
574
+ message: 'Order type:',
575
+ choices: [
576
+ { name: 'Market', value: 'market' },
577
+ { name: 'Limit', value: 'limit' },
578
+ ],
579
+ });
580
+ }
581
+ // ── Price ────────────────────────────────────────────────────────────
535
582
  const assetMeta = assets.find((a) => a.name.toUpperCase() === asset.toUpperCase());
536
583
  let limitPx;
537
584
  let marketPx;
538
585
  if (orderType === 'limit') {
539
- limitPx = await input({ message: 'Limit price:' });
586
+ if (opts.price) {
587
+ limitPx = opts.price;
588
+ }
589
+ else if (nonInteractive) {
590
+ console.error(chalk.red('✖'), 'Limit orders require --price');
591
+ process.exit(1);
592
+ }
593
+ else {
594
+ limitPx = await input({ message: 'Limit price:' });
595
+ }
540
596
  }
541
597
  else {
598
+ // Market order
542
599
  marketPx = assetMeta?.markPx;
543
- if (marketPx && marketPx > 0) {
600
+ if (opts.price) {
601
+ // User specified price for market order (use as trigger)
602
+ limitPx = opts.price;
603
+ marketPx = Number(opts.price);
604
+ info(`Market order at ~$${marketPx}`);
605
+ }
606
+ else if (marketPx && marketPx > 0) {
544
607
  const slippagePx = isBuy ? marketPx * 1.01 : marketPx * 0.99;
545
608
  limitPx = slippagePx.toPrecision(5);
546
609
  info(`Market order at ~$${marketPx}`);
547
610
  }
611
+ else if (nonInteractive) {
612
+ console.error(chalk.red('✖'), `Could not fetch current price for ${asset}. Use --price to specify.`);
613
+ process.exit(1);
614
+ }
548
615
  else {
549
616
  warn(`Could not fetch current price for ${asset}. Enter the approximate market price.`);
550
617
  limitPx = await input({ message: 'Price:' });
551
618
  marketPx = Number(limitPx);
552
619
  }
553
620
  }
554
- const sz = await input({ message: 'Size (in contracts):' });
555
- const reduceOnly = await confirm({ message: 'Reduce only?', default: false });
556
- const grouping = await select({
557
- message: 'Grouping (TP/SL):',
558
- choices: [
559
- { name: 'None', value: 'na' },
560
- { name: 'Normal TP/SL', value: 'normalTpsl' },
561
- { name: 'Position TP/SL', value: 'positionTpsl' },
562
- ],
563
- });
621
+ // ── Size ─────────────────────────────────────────────────────────────
622
+ let sz;
623
+ if (opts.size) {
624
+ sz = opts.size;
625
+ }
626
+ else if (nonInteractive) {
627
+ console.error(chalk.red(''), 'Size is required. Use --size');
628
+ process.exit(1);
629
+ }
630
+ else {
631
+ sz = await input({ message: 'Size (in contracts):' });
632
+ }
633
+ // ── Reduce Only ──────────────────────────────────────────────────────
634
+ let reduceOnly;
635
+ if (opts.reduceOnly !== undefined) {
636
+ reduceOnly = opts.reduceOnly;
637
+ }
638
+ else if (nonInteractive) {
639
+ reduceOnly = false;
640
+ }
641
+ else {
642
+ reduceOnly = await confirm({ message: 'Reduce only?', default: false });
643
+ }
644
+ // ── Grouping ─────────────────────────────────────────────────────────
645
+ let grouping;
646
+ if (opts.grouping) {
647
+ const groupingLower = opts.grouping.toLowerCase();
648
+ if (groupingLower === 'na' || groupingLower === 'none') {
649
+ grouping = 'na';
650
+ }
651
+ else if (groupingLower === 'normaltpsl' || groupingLower === 'normal_tpsl') {
652
+ grouping = 'normalTpsl';
653
+ }
654
+ else if (groupingLower === 'positiontpsl' || groupingLower === 'position_tpsl') {
655
+ grouping = 'positionTpsl';
656
+ }
657
+ else {
658
+ console.error(chalk.red('✖'), `Invalid grouping: ${opts.grouping}. Use 'na', 'normalTpsl', or 'positionTpsl'.`);
659
+ process.exit(1);
660
+ }
661
+ }
662
+ else if (nonInteractive) {
663
+ grouping = 'na';
664
+ }
665
+ else {
666
+ grouping = await select({
667
+ message: 'Grouping (TP/SL):',
668
+ choices: [
669
+ { name: 'None', value: 'na' },
670
+ { name: 'Normal TP/SL', value: 'normalTpsl' },
671
+ { name: 'Position TP/SL', value: 'positionTpsl' },
672
+ ],
673
+ });
674
+ }
564
675
  const order = {
565
676
  a: asset,
566
677
  b: isBuy,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minara",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "CLI client for Minara.ai — login, trade, deposit/withdraw, chat and more from your terminal.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",