terminalmarket 0.4.1 → 0.5.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.
Files changed (3) hide show
  1. package/README.md +28 -0
  2. package/bin/tm.js +225 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -71,6 +71,30 @@ tm reviews <store-id> # View store reviews
71
71
  tm review <store-id> <rating> [comment] # Leave a review (1-5 stars)
72
72
  ```
73
73
 
74
+ ### AI Services
75
+
76
+ Run AI models directly from the terminal using credits.
77
+
78
+ ```bash
79
+ tm ai list # List available AI models
80
+ tm ai run <model> <input> # Run an AI model
81
+ tm ai credits # Check your credit balance
82
+ tm ai topup <amount> # Add credits ($5 minimum)
83
+ tm ai history # View usage history
84
+
85
+ # Shortcuts
86
+ tm credits # Check credits (shortcut)
87
+ tm topup <amount> # Add credits (shortcut)
88
+ ```
89
+
90
+ Example:
91
+ ```bash
92
+ tm ai list # See available models
93
+ tm ai topup 10 # Add $10 credits
94
+ tm ai run text-rewrite "Fix this text" # Run AI model
95
+ tm ai credits # Check remaining balance
96
+ ```
97
+
74
98
  ### Service Types
75
99
 
76
100
  Products have different service types:
@@ -165,6 +189,10 @@ tm orders
165
189
  - `POST /api/cart/clear` — Clear cart
166
190
  - `GET /api/orders` — Get orders
167
191
  - `POST /api/stores/:id/reviews` — Leave review
192
+ - `GET /api/credits` — Get AI credits balance
193
+ - `POST /api/credits/topup` — Create Stripe checkout for credits
194
+ - `POST /api/ai/run/:model` — Run AI model
195
+ - `GET /api/ai/history` — Get AI usage history
168
196
  - `POST /api/clicks` — Track clicks
169
197
  - `POST /api/intents` — Create purchase intent
170
198
 
package/bin/tm.js CHANGED
@@ -13,7 +13,7 @@ const program = new Command();
13
13
  program
14
14
  .name("tm")
15
15
  .description("TerminalMarket CLI — marketplace for developers")
16
- .version("0.4.0");
16
+ .version("0.5.0");
17
17
 
18
18
  // -----------------
19
19
  // config
@@ -461,6 +461,230 @@ program
461
461
  }
462
462
  });
463
463
 
464
+ // -----------------
465
+ // AI commands
466
+ // -----------------
467
+ const ai = program
468
+ .command("ai")
469
+ .description("AI services - run AI models with credits");
470
+
471
+ ai
472
+ .command("list")
473
+ .alias("ls")
474
+ .description("List available AI models")
475
+ .action(async () => {
476
+ try {
477
+ const data = await apiGet("/ai/models");
478
+ const { models, categories } = data;
479
+
480
+ if (!models || models.length === 0) {
481
+ console.log(chalk.yellow("No AI models available yet."));
482
+ return;
483
+ }
484
+
485
+ console.log(chalk.bold("Available AI Models"));
486
+ console.log("");
487
+
488
+ const catMap = new Map((categories || []).map(c => [c.id, c]));
489
+
490
+ // Group by category
491
+ const grouped = {};
492
+ for (const model of models) {
493
+ const cat = catMap.get(model.categoryId);
494
+ const catName = cat ? cat.name : "Other";
495
+ if (!grouped[catName]) grouped[catName] = [];
496
+ grouped[catName].push(model);
497
+ }
498
+
499
+ for (const [catName, catModels] of Object.entries(grouped)) {
500
+ console.log(chalk.cyan.bold(`${catName}:`));
501
+ for (const model of catModels) {
502
+ const price = parseFloat(model.pricePerRun).toFixed(4);
503
+ console.log(` ${chalk.white(model.slug.padEnd(25))} $${price} ${chalk.dim(model.outputType)}`);
504
+ if (model.description) {
505
+ console.log(` ${chalk.dim(model.description)}`);
506
+ }
507
+ }
508
+ console.log("");
509
+ }
510
+
511
+ console.log(chalk.dim("Run: tm ai run <model> <input>"));
512
+ } catch (e) {
513
+ console.error(chalk.red(e?.message || String(e)));
514
+ process.exitCode = 1;
515
+ }
516
+ });
517
+
518
+ ai
519
+ .command("run <model> [input...]")
520
+ .description("Run an AI model")
521
+ .action(async (model, input) => {
522
+ try {
523
+ const inputText = input.join(" ");
524
+ if (!inputText) {
525
+ console.error(chalk.red("Input is required. Usage: tm ai run <model> <input>"));
526
+ return;
527
+ }
528
+
529
+ const result = await apiPost(`/ai/run/${model}`, { input: inputText });
530
+
531
+ console.log(chalk.bold("AI Result"));
532
+ console.log("");
533
+
534
+ if (result.output?.message) {
535
+ console.log(result.output.message);
536
+ }
537
+ if (result.output?.note) {
538
+ console.log(chalk.dim(result.output.note));
539
+ }
540
+
541
+ console.log("");
542
+ console.log(`${chalk.dim("credits used:")} $${result.creditsUsed.toFixed(4)}`);
543
+ console.log(`${chalk.dim("new balance:")} $${result.newBalance}`);
544
+ } catch (e) {
545
+ if (e?.message?.includes("402") || e?.message?.includes("Insufficient")) {
546
+ console.error(chalk.red("Insufficient credits. Use 'tm ai topup <amount>' to add credits."));
547
+ } else {
548
+ console.error(chalk.red(e?.message || String(e)));
549
+ }
550
+ process.exitCode = 1;
551
+ }
552
+ });
553
+
554
+ ai
555
+ .command("credits")
556
+ .alias("balance")
557
+ .description("Check your AI credit balance")
558
+ .action(async () => {
559
+ try {
560
+ const credits = await apiGet("/credits");
561
+
562
+ console.log(chalk.bold("AI Credits"));
563
+ console.log("");
564
+ console.log(`${chalk.dim("balance:")} $${parseFloat(credits.balance).toFixed(4)}`);
565
+ console.log(`${chalk.dim("purchased:")} $${parseFloat(credits.totalPurchased).toFixed(2)}`);
566
+ console.log(`${chalk.dim("spent:")} $${parseFloat(credits.totalSpent).toFixed(4)}`);
567
+ console.log("");
568
+ console.log(chalk.dim("Top up: tm ai topup <amount>"));
569
+ } catch (e) {
570
+ if (e?.message?.includes("401") || e?.message?.includes("Login")) {
571
+ console.log(chalk.yellow("Please login first: tm login <email> <password>"));
572
+ } else {
573
+ console.error(chalk.red(e?.message || String(e)));
574
+ process.exitCode = 1;
575
+ }
576
+ }
577
+ });
578
+
579
+ ai
580
+ .command("topup <amount>")
581
+ .alias("add")
582
+ .description("Add credits to your account ($5 minimum)")
583
+ .action(async (amount) => {
584
+ try {
585
+ const amountNum = parseFloat(amount);
586
+ if (isNaN(amountNum) || amountNum < 5) {
587
+ console.error(chalk.red("Minimum top-up is $5"));
588
+ return;
589
+ }
590
+
591
+ const result = await apiPost("/credits/topup", { amount: amountNum });
592
+
593
+ console.log(chalk.green("Payment link created!"));
594
+ console.log("");
595
+ console.log("Open this link to complete payment:");
596
+ console.log(chalk.cyan(result.url));
597
+ console.log("");
598
+ console.log(chalk.dim("Credits will be added after payment."));
599
+
600
+ // Try to open in browser
601
+ try {
602
+ await open(result.url);
603
+ console.log(chalk.dim("(Opening in browser...)"));
604
+ } catch {}
605
+ } catch (e) {
606
+ if (e?.message?.includes("401") || e?.message?.includes("Login")) {
607
+ console.log(chalk.yellow("Please login first: tm login <email> <password>"));
608
+ } else {
609
+ console.error(chalk.red(e?.message || String(e)));
610
+ process.exitCode = 1;
611
+ }
612
+ }
613
+ });
614
+
615
+ ai
616
+ .command("history")
617
+ .description("View your AI usage history")
618
+ .option("-l, --limit <n>", "Limit results", "20")
619
+ .action(async (opts) => {
620
+ try {
621
+ const logs = await apiGet("/ai/history");
622
+ const limit = parseInt(opts.limit) || 20;
623
+
624
+ if (!logs || logs.length === 0) {
625
+ console.log(chalk.yellow("No AI usage history yet."));
626
+ console.log(chalk.dim("Try: tm ai run <model> <input>"));
627
+ return;
628
+ }
629
+
630
+ console.log(chalk.bold("AI Usage History"));
631
+ console.log("");
632
+
633
+ logs.slice(0, limit).forEach(log => {
634
+ const date = new Date(log.createdAt).toLocaleDateString();
635
+ const credits = parseFloat(log.creditsCharged).toFixed(4);
636
+ const statusColor = log.status === "completed" ? chalk.green :
637
+ log.status === "failed" ? chalk.red : chalk.yellow;
638
+
639
+ console.log(`${date} Model #${log.modelId} $${credits} ${statusColor(log.status)}`);
640
+ });
641
+ } catch (e) {
642
+ if (e?.message?.includes("401") || e?.message?.includes("Login")) {
643
+ console.log(chalk.yellow("Please login first: tm login <email> <password>"));
644
+ } else {
645
+ console.error(chalk.red(e?.message || String(e)));
646
+ process.exitCode = 1;
647
+ }
648
+ }
649
+ });
650
+
651
+ // Shortcuts for AI commands
652
+ program
653
+ .command("credits")
654
+ .description("Check AI credits (shortcut)")
655
+ .action(async () => {
656
+ try {
657
+ const credits = await apiGet("/credits");
658
+ console.log(chalk.bold("AI Credits"));
659
+ console.log(`${chalk.dim("balance:")} $${parseFloat(credits.balance).toFixed(4)}`);
660
+ console.log(chalk.dim("Top up: tm ai topup <amount>"));
661
+ } catch (e) {
662
+ if (e?.message?.includes("401")) {
663
+ console.log(chalk.yellow("Please login first."));
664
+ } else {
665
+ console.error(chalk.red(e?.message || String(e)));
666
+ }
667
+ }
668
+ });
669
+
670
+ program
671
+ .command("topup <amount>")
672
+ .description("Add AI credits (shortcut)")
673
+ .action(async (amount) => {
674
+ const amountNum = parseFloat(amount);
675
+ if (isNaN(amountNum) || amountNum < 5) {
676
+ console.error(chalk.red("Minimum top-up is $5"));
677
+ return;
678
+ }
679
+ try {
680
+ const result = await apiPost("/credits/topup", { amount: amountNum });
681
+ console.log(chalk.green("Payment link: ") + chalk.cyan(result.url));
682
+ try { await open(result.url); } catch {}
683
+ } catch (e) {
684
+ console.error(chalk.red(e?.message || String(e)));
685
+ }
686
+ });
687
+
464
688
  // -----------------
465
689
  // categories
466
690
  // -----------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalmarket",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "TerminalMarket CLI — marketplace for developers (client for terminalmarket.app)",
5
5
  "bin": {
6
6
  "tm": "./bin/tm.js"