terminalmarket 0.6.5 → 0.7.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.
- package/bin/tm.js +160 -249
- package/package.json +1 -1
- package/src/format.js +454 -9
package/bin/tm.js
CHANGED
|
@@ -7,7 +7,11 @@ import readline from "readline";
|
|
|
7
7
|
|
|
8
8
|
import { apiGet, apiPost, apiDelete, apiPatch } from "../src/api.js";
|
|
9
9
|
import { getApiBase, setApiBase, getUser, setUser, clearUser, clearSession } from "../src/config.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
printTable, pickProductFields, pickSellerFields, pickOfferFields, containsQuery, formatStars,
|
|
12
|
+
printHeader, printDivider, printSuccess, printError, printWarning, printInfo, printField, printEmpty,
|
|
13
|
+
printProductCard, printCart, printOrders, printStoreCard, printSellers, printReviews, printAIModels, printCredits
|
|
14
|
+
} from "../src/format.js";
|
|
11
15
|
|
|
12
16
|
// Helper for hidden password input
|
|
13
17
|
function askPassword(prompt = "Password: ") {
|
|
@@ -59,7 +63,7 @@ const program = new Command();
|
|
|
59
63
|
program
|
|
60
64
|
.name("tm")
|
|
61
65
|
.description("TerminalMarket CLI — marketplace for developers")
|
|
62
|
-
.version("0.
|
|
66
|
+
.version("0.7.0");
|
|
63
67
|
|
|
64
68
|
// -----------------
|
|
65
69
|
// config
|
|
@@ -289,28 +293,16 @@ cart
|
|
|
289
293
|
try {
|
|
290
294
|
const cartData = await apiGet("/cart");
|
|
291
295
|
|
|
292
|
-
if (!cartData.items || cartData.items.length === 0) {
|
|
293
|
-
console.log(chalk.yellow("Your cart is empty."));
|
|
294
|
-
console.log(chalk.dim("Add items with: tm cart add <product-id>"));
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
console.log(chalk.bold("Your Cart"));
|
|
299
|
-
console.log("");
|
|
300
|
-
|
|
301
296
|
let total = 0;
|
|
302
|
-
cartData.items
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
});
|
|
297
|
+
if (cartData.items?.length) {
|
|
298
|
+
cartData.items.forEach((item) => {
|
|
299
|
+
total += (item.price || 0) * (item.quantity || 1);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
308
302
|
|
|
309
|
-
|
|
310
|
-
console.log(chalk.bold(`Total: $${total.toFixed(2)}`));
|
|
311
|
-
console.log(chalk.dim("Checkout with: tm checkout"));
|
|
303
|
+
printCart(cartData.items || [], total);
|
|
312
304
|
} catch (e) {
|
|
313
|
-
|
|
305
|
+
printError(e?.message || String(e));
|
|
314
306
|
process.exitCode = 1;
|
|
315
307
|
}
|
|
316
308
|
});
|
|
@@ -323,9 +315,9 @@ cart
|
|
|
323
315
|
try {
|
|
324
316
|
const quantity = parseInt(opts.quantity) || 1;
|
|
325
317
|
await apiPost("/cart/add", { productId: parseInt(productId), quantity });
|
|
326
|
-
|
|
318
|
+
printSuccess(`Added to cart (qty: ${quantity})`);
|
|
327
319
|
} catch (e) {
|
|
328
|
-
|
|
320
|
+
printError(e?.message || String(e));
|
|
329
321
|
process.exitCode = 1;
|
|
330
322
|
}
|
|
331
323
|
});
|
|
@@ -336,9 +328,9 @@ cart
|
|
|
336
328
|
.action(async (productId) => {
|
|
337
329
|
try {
|
|
338
330
|
await apiPost("/cart/remove", { productId: parseInt(productId) });
|
|
339
|
-
|
|
331
|
+
printSuccess("Removed from cart");
|
|
340
332
|
} catch (e) {
|
|
341
|
-
|
|
333
|
+
printError(e?.message || String(e));
|
|
342
334
|
process.exitCode = 1;
|
|
343
335
|
}
|
|
344
336
|
});
|
|
@@ -349,9 +341,9 @@ cart
|
|
|
349
341
|
.action(async () => {
|
|
350
342
|
try {
|
|
351
343
|
await apiPost("/cart/clear", {});
|
|
352
|
-
|
|
344
|
+
printSuccess("Cart cleared");
|
|
353
345
|
} catch (e) {
|
|
354
|
-
|
|
346
|
+
printError(e?.message || String(e));
|
|
355
347
|
process.exitCode = 1;
|
|
356
348
|
}
|
|
357
349
|
});
|
|
@@ -421,28 +413,9 @@ program
|
|
|
421
413
|
try {
|
|
422
414
|
const orders = await apiGet("/orders");
|
|
423
415
|
const limit = parseInt(opts.limit) || 10;
|
|
424
|
-
|
|
425
|
-
if (!orders || orders.length === 0) {
|
|
426
|
-
console.log(chalk.yellow("No orders yet."));
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
console.log(chalk.bold("Order History"));
|
|
431
|
-
console.log("");
|
|
432
|
-
|
|
433
|
-
orders.slice(0, limit).forEach((order) => {
|
|
434
|
-
const date = new Date(order.createdAt).toLocaleDateString();
|
|
435
|
-
const statusColor = order.status === "delivered" ? chalk.green :
|
|
436
|
-
order.status === "shipped" ? chalk.cyan :
|
|
437
|
-
order.status === "paid" ? chalk.blue :
|
|
438
|
-
chalk.yellow;
|
|
439
|
-
|
|
440
|
-
console.log(`${chalk.bold(order.orderNumber || `#${order.id}`)} - ${date}`);
|
|
441
|
-
console.log(` ${chalk.dim("status:")} ${statusColor(order.status)} ${chalk.dim("total:")} $${order.total || 0}`);
|
|
442
|
-
console.log("");
|
|
443
|
-
});
|
|
416
|
+
printOrders((orders || []).slice(0, limit));
|
|
444
417
|
} catch (e) {
|
|
445
|
-
|
|
418
|
+
printError(e?.message || String(e));
|
|
446
419
|
process.exitCode = 1;
|
|
447
420
|
}
|
|
448
421
|
});
|
|
@@ -486,27 +459,9 @@ program
|
|
|
486
459
|
.action(async (storeId) => {
|
|
487
460
|
try {
|
|
488
461
|
const data = await apiGet(`/stores/${storeId}/reviews`);
|
|
489
|
-
|
|
490
|
-
if (data.reviewCount === 0) {
|
|
491
|
-
console.log(chalk.yellow(`No reviews yet for store #${storeId}.`));
|
|
492
|
-
console.log(chalk.dim(`Be the first: tm review ${storeId} <rating> [comment]`));
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
console.log(chalk.bold(`Store Reviews`));
|
|
497
|
-
console.log(`${formatStars(Math.round(data.averageRating))} ${data.averageRating.toFixed(1)}/5 (${data.reviewCount} reviews)`);
|
|
498
|
-
console.log("");
|
|
499
|
-
|
|
500
|
-
data.reviews.forEach((review) => {
|
|
501
|
-
const date = new Date(review.createdAt).toLocaleDateString();
|
|
502
|
-
console.log(`${formatStars(review.rating)} by ${review.userName} (${date})`);
|
|
503
|
-
if (review.comment) {
|
|
504
|
-
console.log(` "${review.comment}"`);
|
|
505
|
-
}
|
|
506
|
-
console.log("");
|
|
507
|
-
});
|
|
462
|
+
printReviews(data.reviews || [], data.averageRating);
|
|
508
463
|
} catch (e) {
|
|
509
|
-
|
|
464
|
+
printError(e?.message || String(e));
|
|
510
465
|
process.exitCode = 1;
|
|
511
466
|
}
|
|
512
467
|
});
|
|
@@ -522,31 +477,21 @@ program
|
|
|
522
477
|
const store = await apiGet(`/stores/${storeId}`);
|
|
523
478
|
|
|
524
479
|
if (!store) {
|
|
525
|
-
|
|
480
|
+
printError("Store not found");
|
|
526
481
|
return;
|
|
527
482
|
}
|
|
528
483
|
|
|
529
|
-
|
|
530
|
-
if (store.verified) console.log(chalk.green("✓ Verified"));
|
|
531
|
-
console.log("");
|
|
532
|
-
|
|
533
|
-
if (store.description) console.log(store.description);
|
|
534
|
-
console.log("");
|
|
535
|
-
|
|
536
|
-
console.log(`${chalk.dim("id:")} ${store.id}`);
|
|
537
|
-
if (store.slug) console.log(`${chalk.dim("slug:")} ${store.slug}`);
|
|
538
|
-
|
|
484
|
+
// Get rating
|
|
539
485
|
try {
|
|
540
486
|
const rating = await apiGet(`/stores/${storeId}/rating`);
|
|
541
487
|
if (rating.count > 0) {
|
|
542
|
-
|
|
488
|
+
store.rating = rating.average;
|
|
543
489
|
}
|
|
544
490
|
} catch {}
|
|
545
491
|
|
|
546
|
-
|
|
547
|
-
if (store.supportEmail) console.log(`${chalk.dim("support:")} ${store.supportEmail}`);
|
|
492
|
+
printStoreCard(store);
|
|
548
493
|
} catch (e) {
|
|
549
|
-
|
|
494
|
+
printError(e?.message || String(e));
|
|
550
495
|
process.exitCode = 1;
|
|
551
496
|
}
|
|
552
497
|
});
|
|
@@ -567,40 +512,17 @@ ai
|
|
|
567
512
|
const data = await apiGet("/ai/models");
|
|
568
513
|
const { models, categories } = data;
|
|
569
514
|
|
|
570
|
-
|
|
571
|
-
console.log(chalk.yellow("No AI models available yet."));
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
console.log(chalk.bold("Available AI Models"));
|
|
576
|
-
console.log("");
|
|
577
|
-
|
|
515
|
+
// Add category names to models
|
|
578
516
|
const catMap = new Map((categories || []).map(c => [c.id, c]));
|
|
517
|
+
const modelsWithCat = (models || []).map(m => ({
|
|
518
|
+
...m,
|
|
519
|
+
categoryName: catMap.get(m.categoryId)?.name || 'Other',
|
|
520
|
+
creditsPerRun: '$' + parseFloat(m.pricePerRun).toFixed(4)
|
|
521
|
+
}));
|
|
579
522
|
|
|
580
|
-
|
|
581
|
-
const grouped = {};
|
|
582
|
-
for (const model of models) {
|
|
583
|
-
const cat = catMap.get(model.categoryId);
|
|
584
|
-
const catName = cat ? cat.name : "Other";
|
|
585
|
-
if (!grouped[catName]) grouped[catName] = [];
|
|
586
|
-
grouped[catName].push(model);
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
for (const [catName, catModels] of Object.entries(grouped)) {
|
|
590
|
-
console.log(chalk.cyan.bold(`${catName}:`));
|
|
591
|
-
for (const model of catModels) {
|
|
592
|
-
const price = parseFloat(model.pricePerRun).toFixed(4);
|
|
593
|
-
console.log(` ${chalk.white(model.slug.padEnd(25))} $${price} ${chalk.dim(model.outputType)}`);
|
|
594
|
-
if (model.description) {
|
|
595
|
-
console.log(` ${chalk.dim(model.description)}`);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
console.log("");
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
console.log(chalk.dim("Run: tm ai run <model> <input>"));
|
|
523
|
+
printAIModels(modelsWithCat, categories);
|
|
602
524
|
} catch (e) {
|
|
603
|
-
|
|
525
|
+
printError(e?.message || String(e));
|
|
604
526
|
process.exitCode = 1;
|
|
605
527
|
}
|
|
606
528
|
});
|
|
@@ -649,18 +571,20 @@ ai
|
|
|
649
571
|
try {
|
|
650
572
|
const credits = await apiGet("/credits");
|
|
651
573
|
|
|
652
|
-
console.log(
|
|
653
|
-
console.log(
|
|
654
|
-
console.log(
|
|
655
|
-
console.log(
|
|
656
|
-
console.log(
|
|
657
|
-
console.log(
|
|
658
|
-
console.log(
|
|
574
|
+
console.log();
|
|
575
|
+
console.log(chalk.cyan.bold(' 💳 AI Credits'));
|
|
576
|
+
console.log();
|
|
577
|
+
console.log(` ${chalk.white('Balance:')} ${chalk.green.bold('$' + parseFloat(credits.balance).toFixed(4))}`);
|
|
578
|
+
console.log(` ${chalk.dim('Purchased:')} $${parseFloat(credits.totalPurchased).toFixed(2)}`);
|
|
579
|
+
console.log(` ${chalk.dim('Spent:')} $${parseFloat(credits.totalSpent).toFixed(4)}`);
|
|
580
|
+
console.log();
|
|
581
|
+
console.log(chalk.dim(' 💡 tm ai topup <amount> — add more credits'));
|
|
582
|
+
console.log();
|
|
659
583
|
} catch (e) {
|
|
660
584
|
if (e?.message?.includes("401") || e?.message?.includes("Login")) {
|
|
661
|
-
|
|
585
|
+
printWarning("Please login first: tm login <email>");
|
|
662
586
|
} else {
|
|
663
|
-
|
|
587
|
+
printError(e?.message || String(e));
|
|
664
588
|
process.exitCode = 1;
|
|
665
589
|
}
|
|
666
590
|
}
|
|
@@ -1216,48 +1140,8 @@ program
|
|
|
1216
1140
|
return;
|
|
1217
1141
|
}
|
|
1218
1142
|
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
if (p.shortDescription) {
|
|
1223
|
-
console.log(chalk.italic(p.shortDescription));
|
|
1224
|
-
console.log("");
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
if (p.description) {
|
|
1228
|
-
console.log(p.description);
|
|
1229
|
-
console.log("");
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
console.log(`${chalk.dim("id:")} ${p.productId || p.id}`);
|
|
1233
|
-
if (p.slug) console.log(`${chalk.dim("slug:")} ${p.slug}`);
|
|
1234
|
-
if (p.category) console.log(`${chalk.dim("category:")} ${p.category}`);
|
|
1235
|
-
if (p.price) console.log(`${chalk.dim("price:")} $${p.price}`);
|
|
1236
|
-
|
|
1237
|
-
const serviceType = p.serviceType || "global";
|
|
1238
|
-
const typeLabel = serviceType === "global" ? "🌍 Global" :
|
|
1239
|
-
serviceType === "national" ? "🏳️ National" : "📍 Local";
|
|
1240
|
-
console.log(`${chalk.dim("serviceType:")} ${typeLabel}`);
|
|
1241
|
-
|
|
1242
|
-
if (serviceType === "local" && p.serviceCity) {
|
|
1243
|
-
console.log(`${chalk.dim("city:")} ${p.serviceCity}`);
|
|
1244
|
-
}
|
|
1245
|
-
if ((serviceType === "national" || serviceType === "local") && p.serviceCountry) {
|
|
1246
|
-
console.log(`${chalk.dim("country:")} ${p.serviceCountry}`);
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
// Show image URL if available
|
|
1250
|
-
const imageUrl = p.imageUrl || p.image;
|
|
1251
|
-
if (imageUrl) {
|
|
1252
|
-
console.log("");
|
|
1253
|
-
console.log(`${chalk.dim("image:")} ${imageUrl}`);
|
|
1254
|
-
console.log(chalk.dim(`Use: tm view ${p.slug || p.id} --image`));
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
if (p.buyUrl) console.log(`${chalk.dim("buyUrl:")} ${p.buyUrl}`);
|
|
1258
|
-
if (p.subscriptionAvailable) console.log(`${chalk.dim("subscription:")} ${chalk.green("available")}`);
|
|
1259
|
-
if (p.tags && p.tags.length > 0) console.log(`${chalk.dim("tags:")} ${p.tags.join(", ")}`);
|
|
1260
|
-
if (p.storeId) console.log(`${chalk.dim("storeId:")} ${p.storeId}`);
|
|
1143
|
+
// Print beautiful product card
|
|
1144
|
+
printProductCard(p);
|
|
1261
1145
|
|
|
1262
1146
|
try {
|
|
1263
1147
|
const offers = await apiGet(`/products/${encodeURIComponent(p.productId || p.id)}/offers`);
|
|
@@ -1433,15 +1317,9 @@ program
|
|
|
1433
1317
|
);
|
|
1434
1318
|
}
|
|
1435
1319
|
|
|
1436
|
-
|
|
1437
|
-
printTable(rows, [
|
|
1438
|
-
{ key: "slug", title: "slug" },
|
|
1439
|
-
{ key: "name", title: "name" },
|
|
1440
|
-
{ key: "serviceType", title: "type" },
|
|
1441
|
-
{ key: "verified", title: "verified" },
|
|
1442
|
-
]);
|
|
1320
|
+
printSellers((sellers || []).slice(0, limit));
|
|
1443
1321
|
} catch (e) {
|
|
1444
|
-
|
|
1322
|
+
printError(e?.message || String(e));
|
|
1445
1323
|
process.exitCode = 1;
|
|
1446
1324
|
}
|
|
1447
1325
|
});
|
|
@@ -1454,47 +1332,14 @@ program
|
|
|
1454
1332
|
const seller = await apiGet(`/sellers/${encodeURIComponent(slug)}`);
|
|
1455
1333
|
|
|
1456
1334
|
if (!seller) {
|
|
1457
|
-
|
|
1335
|
+
printError("Seller not found");
|
|
1458
1336
|
process.exitCode = 1;
|
|
1459
1337
|
return;
|
|
1460
1338
|
}
|
|
1461
1339
|
|
|
1462
|
-
|
|
1463
|
-
if (seller.verified) {
|
|
1464
|
-
console.log(chalk.green("✓ Verified Seller"));
|
|
1465
|
-
}
|
|
1466
|
-
console.log("");
|
|
1467
|
-
|
|
1468
|
-
if (seller.description) console.log(seller.description);
|
|
1469
|
-
console.log("");
|
|
1470
|
-
|
|
1471
|
-
console.log(`${chalk.dim("slug:")} ${seller.slug}`);
|
|
1472
|
-
|
|
1473
|
-
const serviceType = seller.serviceType || "global";
|
|
1474
|
-
const typeLabel = serviceType === "global" ? "🌍 Global (SaaS/Digital)" :
|
|
1475
|
-
serviceType === "national" ? "🏳️ National" : "📍 Local";
|
|
1476
|
-
console.log(`${chalk.dim("serviceType:")} ${typeLabel}`);
|
|
1477
|
-
|
|
1478
|
-
if (serviceType === "local" && seller.baseCity) {
|
|
1479
|
-
console.log(`${chalk.dim("city:")} ${seller.baseCity}`);
|
|
1480
|
-
}
|
|
1481
|
-
if ((serviceType === "national" || serviceType === "local") && seller.baseCountry) {
|
|
1482
|
-
console.log(`${chalk.dim("country:")} ${seller.baseCountry}`);
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
if (seller.website) console.log(`${chalk.dim("website:")} ${seller.website}`);
|
|
1486
|
-
if (seller.supportEmail) console.log(`${chalk.dim("support:")} ${seller.supportEmail}`);
|
|
1487
|
-
if (seller.badges && seller.badges.length > 0) {
|
|
1488
|
-
console.log(`${chalk.dim("badges:")} ${seller.badges.join(", ")}`);
|
|
1489
|
-
}
|
|
1490
|
-
if (seller.categories && seller.categories.length > 0) {
|
|
1491
|
-
console.log(`${chalk.dim("categories:")} ${seller.categories.join(", ")}`);
|
|
1492
|
-
}
|
|
1493
|
-
if (seller.shippingPolicy) console.log(`${chalk.dim("shippingPolicy:")} ${seller.shippingPolicy}`);
|
|
1494
|
-
if (seller.returnPolicy) console.log(`${chalk.dim("returnPolicy:")} ${seller.returnPolicy}`);
|
|
1495
|
-
|
|
1340
|
+
printStoreCard(seller);
|
|
1496
1341
|
} catch (e) {
|
|
1497
|
-
|
|
1342
|
+
printError(e?.message || String(e));
|
|
1498
1343
|
process.exitCode = 1;
|
|
1499
1344
|
}
|
|
1500
1345
|
});
|
|
@@ -1540,45 +1385,75 @@ program
|
|
|
1540
1385
|
.command("about")
|
|
1541
1386
|
.description("About TerminalMarket")
|
|
1542
1387
|
.action(() => {
|
|
1543
|
-
console.log(
|
|
1544
|
-
console.log(
|
|
1545
|
-
console.log(
|
|
1546
|
-
console.log(
|
|
1547
|
-
console.log(chalk.
|
|
1548
|
-
console.log(
|
|
1388
|
+
console.log();
|
|
1389
|
+
console.log(chalk.green.bold(' ╔══════════════════════════════════════════════╗'));
|
|
1390
|
+
console.log(chalk.green.bold(' ║') + chalk.white.bold(' TERMINAL MARKET') + chalk.green.bold(' ║'));
|
|
1391
|
+
console.log(chalk.green.bold(' ║') + chalk.dim(' The marketplace for developers') + chalk.green.bold(' ║'));
|
|
1392
|
+
console.log(chalk.green.bold(' ╚══════════════════════════════════════════════╝'));
|
|
1393
|
+
console.log();
|
|
1394
|
+
console.log(chalk.white(' We connect developers with premium services:'));
|
|
1395
|
+
console.log();
|
|
1396
|
+
console.log(chalk.cyan(' ☕') + chalk.white(' Coffee subscriptions'));
|
|
1397
|
+
console.log(chalk.cyan(' 🥗') + chalk.white(' Healthy snacks & lunch'));
|
|
1398
|
+
console.log(chalk.cyan(' 💆') + chalk.white(' Health & wellness services'));
|
|
1399
|
+
console.log(chalk.cyan(' 🏢') + chalk.white(' Coworking spaces'));
|
|
1400
|
+
console.log(chalk.cyan(' 🤖') + chalk.white(' AI services & tools'));
|
|
1401
|
+
console.log(chalk.cyan(' ⚡') + chalk.white(' Productivity tools'));
|
|
1402
|
+
console.log();
|
|
1403
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────'));
|
|
1404
|
+
console.log();
|
|
1405
|
+
console.log(` ${chalk.dim('Website:')} ${chalk.cyan('https://terminalmarket.app')}`);
|
|
1406
|
+
console.log(` ${chalk.dim('Install:')} ${chalk.green('npm i -g terminalmarket')}`);
|
|
1407
|
+
console.log(` ${chalk.dim('Version:')} ${chalk.white('0.7.0')}`);
|
|
1408
|
+
console.log();
|
|
1549
1409
|
});
|
|
1550
1410
|
|
|
1551
1411
|
// -----------------
|
|
1552
1412
|
// help
|
|
1553
1413
|
// -----------------
|
|
1554
1414
|
|
|
1415
|
+
// Command groups for organized help
|
|
1416
|
+
const commandGroups = {
|
|
1417
|
+
'Authentication': ['login', 'logout', 'register', 'auth', 'github', 'whoami', 'profile'],
|
|
1418
|
+
'Shopping': ['products', 'search', 'view', 'open', 'buy', 'categories', 'category'],
|
|
1419
|
+
'Cart & Orders': ['cart', 'add', 'checkout', 'orders'],
|
|
1420
|
+
'Stores': ['sellers', 'seller', 'store', 'reviews', 'review', 'where'],
|
|
1421
|
+
'AI Services': ['ai', 'credits', 'topup'],
|
|
1422
|
+
'Personalization': ['alias', 'aliases', 'reward', 'rewards'],
|
|
1423
|
+
'System': ['config', 'help', 'about', 'offers']
|
|
1424
|
+
};
|
|
1425
|
+
|
|
1555
1426
|
// Custom help formatter
|
|
1556
1427
|
function showHelp(commandName = null) {
|
|
1557
1428
|
if (commandName) {
|
|
1558
1429
|
// Show detailed help for specific command
|
|
1559
1430
|
const cmd = program.commands.find(c => c.name() === commandName);
|
|
1560
1431
|
if (!cmd) {
|
|
1561
|
-
console.log(chalk.red(
|
|
1432
|
+
console.log(chalk.red(`✗ Unknown command: ${commandName}`));
|
|
1562
1433
|
console.log(chalk.dim(`Run 'tm help' to see all commands.`));
|
|
1563
1434
|
return;
|
|
1564
1435
|
}
|
|
1565
1436
|
|
|
1566
1437
|
console.log();
|
|
1567
|
-
console.log(chalk.
|
|
1438
|
+
console.log(chalk.cyan('━'.repeat(50)));
|
|
1439
|
+
console.log(chalk.green.bold(` tm ${cmd.name()}`));
|
|
1440
|
+
console.log(chalk.white(` ${cmd.description()}`));
|
|
1441
|
+
console.log(chalk.cyan('━'.repeat(50)));
|
|
1568
1442
|
console.log();
|
|
1569
1443
|
|
|
1570
1444
|
// Show usage
|
|
1571
1445
|
const args = cmd.registeredArguments || [];
|
|
1572
|
-
const argsStr = args.map(a => a.required ? `<${a.name()}>` : `[${a.name()}]`).join(' ');
|
|
1573
|
-
console.log(chalk.
|
|
1574
|
-
console.log(` tm ${cmd.name()}${argsStr ? ' ' + argsStr : ''} [options]`);
|
|
1446
|
+
const argsStr = args.map(a => a.required ? chalk.yellow(`<${a.name()}>`) : chalk.dim(`[${a.name()}]`)).join(' ');
|
|
1447
|
+
console.log(chalk.magenta.bold('Usage:'));
|
|
1448
|
+
console.log(` ${chalk.green('tm')} ${chalk.cyan(cmd.name())}${argsStr ? ' ' + argsStr : ''} ${chalk.dim('[options]')}`);
|
|
1575
1449
|
console.log();
|
|
1576
1450
|
|
|
1577
1451
|
// Show arguments
|
|
1578
1452
|
if (args.length > 0) {
|
|
1579
|
-
console.log(chalk.
|
|
1453
|
+
console.log(chalk.magenta.bold('Arguments:'));
|
|
1580
1454
|
args.forEach(a => {
|
|
1581
|
-
|
|
1455
|
+
const req = a.required ? chalk.yellow('(required)') : chalk.dim('(optional)');
|
|
1456
|
+
console.log(` ${chalk.cyan(a.name().padEnd(15))} ${req}`);
|
|
1582
1457
|
});
|
|
1583
1458
|
console.log();
|
|
1584
1459
|
}
|
|
@@ -1586,61 +1461,97 @@ function showHelp(commandName = null) {
|
|
|
1586
1461
|
// Show options
|
|
1587
1462
|
const opts = cmd.options;
|
|
1588
1463
|
if (opts.length > 0) {
|
|
1589
|
-
console.log(chalk.
|
|
1464
|
+
console.log(chalk.magenta.bold('Options:'));
|
|
1590
1465
|
opts.forEach(o => {
|
|
1591
|
-
const flags = o.flags.padEnd(
|
|
1592
|
-
console.log(` ${flags} ${o.description}`);
|
|
1466
|
+
const flags = chalk.yellow(o.flags.padEnd(28));
|
|
1467
|
+
console.log(` ${flags} ${chalk.white(o.description)}`);
|
|
1593
1468
|
});
|
|
1594
1469
|
console.log();
|
|
1595
1470
|
}
|
|
1596
1471
|
|
|
1597
1472
|
// Show subcommands if any
|
|
1598
1473
|
if (cmd.commands && cmd.commands.length > 0) {
|
|
1599
|
-
console.log(chalk.
|
|
1474
|
+
console.log(chalk.magenta.bold('Subcommands:'));
|
|
1600
1475
|
cmd.commands.sort((a, b) => a.name().localeCompare(b.name())).forEach(sub => {
|
|
1601
|
-
const subArgs = (sub.registeredArguments || []).map(a => a.required ? `<${a.name()}>` : `[${a.name()}]`).join(' ');
|
|
1602
|
-
|
|
1476
|
+
const subArgs = (sub.registeredArguments || []).map(a => a.required ? chalk.yellow(`<${a.name()}>`) : chalk.dim(`[${a.name()}]`)).join(' ');
|
|
1477
|
+
const cmdPart = chalk.cyan(sub.name()) + (subArgs ? ' ' + subArgs : '');
|
|
1478
|
+
console.log(` ${cmdPart.padEnd(40)} ${chalk.white(sub.description())}`);
|
|
1603
1479
|
});
|
|
1604
1480
|
console.log();
|
|
1605
|
-
console.log(chalk.dim(`Run 'tm ${cmd.name()} <subcommand> help' for more details.`));
|
|
1481
|
+
console.log(chalk.dim(` 💡 Run 'tm ${cmd.name()} <subcommand> help' for more details.`));
|
|
1606
1482
|
}
|
|
1483
|
+
console.log();
|
|
1607
1484
|
return;
|
|
1608
1485
|
}
|
|
1609
1486
|
|
|
1610
|
-
// Show all commands
|
|
1487
|
+
// Show all commands grouped
|
|
1611
1488
|
console.log();
|
|
1612
|
-
console.log(chalk.green.bold('
|
|
1613
|
-
console.log(chalk.dim('
|
|
1489
|
+
console.log(chalk.green.bold(' ╔════════════════════════════════════════╗'));
|
|
1490
|
+
console.log(chalk.green.bold(' ║') + chalk.white.bold(' TerminalMarket CLI ') + chalk.dim('v0.7.0') + chalk.green.bold(' ║'));
|
|
1491
|
+
console.log(chalk.green.bold(' ║') + chalk.dim(' Marketplace for developers') + chalk.green.bold(' ║'));
|
|
1492
|
+
console.log(chalk.green.bold(' ╚════════════════════════════════════════╝'));
|
|
1614
1493
|
console.log();
|
|
1615
|
-
console.log(chalk.
|
|
1494
|
+
console.log(chalk.magenta.bold('Usage:'), chalk.green('tm'), chalk.cyan('<command>'), chalk.dim('[options]'));
|
|
1616
1495
|
console.log();
|
|
1617
|
-
console.log(chalk.yellow('Commands:'));
|
|
1618
1496
|
|
|
1619
|
-
// Collect all commands
|
|
1620
|
-
const
|
|
1497
|
+
// Collect all commands
|
|
1498
|
+
const allCommands = {};
|
|
1621
1499
|
program.commands.forEach(cmd => {
|
|
1622
1500
|
const args = (cmd.registeredArguments || []).map(a => a.required ? `<${a.name()}>` : `[${a.name()}]`).join(' ');
|
|
1623
|
-
|
|
1501
|
+
allCommands[cmd.name()] = {
|
|
1624
1502
|
name: cmd.name(),
|
|
1625
1503
|
args,
|
|
1626
1504
|
desc: cmd.description(),
|
|
1627
1505
|
hasSubcommands: cmd.commands && cmd.commands.length > 0
|
|
1628
|
-
}
|
|
1506
|
+
};
|
|
1629
1507
|
});
|
|
1630
1508
|
|
|
1631
|
-
//
|
|
1632
|
-
|
|
1509
|
+
// Display by groups
|
|
1510
|
+
const groupColors = {
|
|
1511
|
+
'Authentication': chalk.blue,
|
|
1512
|
+
'Shopping': chalk.green,
|
|
1513
|
+
'Cart & Orders': chalk.yellow,
|
|
1514
|
+
'Stores': chalk.magenta,
|
|
1515
|
+
'AI Services': chalk.cyan,
|
|
1516
|
+
'Personalization': chalk.white,
|
|
1517
|
+
'System': chalk.gray
|
|
1518
|
+
};
|
|
1633
1519
|
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1520
|
+
const groupIcons = {
|
|
1521
|
+
'Authentication': '🔐',
|
|
1522
|
+
'Shopping': '🛒',
|
|
1523
|
+
'Cart & Orders': '📦',
|
|
1524
|
+
'Stores': '🏪',
|
|
1525
|
+
'AI Services': '🤖',
|
|
1526
|
+
'Personalization': '⚙️',
|
|
1527
|
+
'System': '💻'
|
|
1528
|
+
};
|
|
1640
1529
|
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1530
|
+
for (const [group, cmdNames] of Object.entries(commandGroups)) {
|
|
1531
|
+
const color = groupColors[group] || chalk.white;
|
|
1532
|
+
const icon = groupIcons[group] || '•';
|
|
1533
|
+
|
|
1534
|
+
console.log(color.bold(`${icon} ${group}`));
|
|
1535
|
+
|
|
1536
|
+
// Sort commands in group
|
|
1537
|
+
const groupCmds = cmdNames
|
|
1538
|
+
.filter(name => allCommands[name])
|
|
1539
|
+
.map(name => allCommands[name])
|
|
1540
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
1541
|
+
|
|
1542
|
+
groupCmds.forEach(c => {
|
|
1543
|
+
const cmdName = chalk.cyan(c.name);
|
|
1544
|
+
const cmdArgs = c.args ? chalk.yellow(` ${c.args}`) : '';
|
|
1545
|
+
const cmdStr = (c.name + (c.args ? ' ' + c.args : '')).padEnd(28);
|
|
1546
|
+
const suffix = c.hasSubcommands ? chalk.dim(' ⊕') : '';
|
|
1547
|
+
console.log(` ${cmdName}${cmdArgs}${' '.repeat(Math.max(0, 28 - c.name.length - (c.args?.length || 0)))} ${chalk.dim(c.desc)}${suffix}`);
|
|
1548
|
+
});
|
|
1549
|
+
console.log();
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
console.log(chalk.cyan('━'.repeat(50)));
|
|
1553
|
+
console.log(chalk.dim(` 💡 Run '`) + chalk.cyan('tm help <command>') + chalk.dim(`' for detailed help`));
|
|
1554
|
+
console.log(chalk.dim(` ⊕ = has subcommands`));
|
|
1644
1555
|
console.log();
|
|
1645
1556
|
}
|
|
1646
1557
|
|
package/package.json
CHANGED
package/src/format.js
CHANGED
|
@@ -1,21 +1,466 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Box drawing characters
|
|
4
|
+
const BOX = {
|
|
5
|
+
topLeft: '┌',
|
|
6
|
+
topRight: '┐',
|
|
7
|
+
bottomLeft: '└',
|
|
8
|
+
bottomRight: '┘',
|
|
9
|
+
horizontal: '─',
|
|
10
|
+
vertical: '│',
|
|
11
|
+
leftT: '├',
|
|
12
|
+
rightT: '┤',
|
|
13
|
+
topT: '┬',
|
|
14
|
+
bottomT: '┴',
|
|
15
|
+
cross: '┼',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Print a beautiful box header
|
|
19
|
+
export function printHeader(title, subtitle = null) {
|
|
20
|
+
const width = 50;
|
|
21
|
+
const line = BOX.horizontal.repeat(width - 2);
|
|
22
|
+
|
|
23
|
+
console.log();
|
|
24
|
+
console.log(chalk.green(`${BOX.topLeft}${line}${BOX.topRight}`));
|
|
25
|
+
|
|
26
|
+
const titlePadded = title.padStart(Math.floor((width - 2 + title.length) / 2)).padEnd(width - 2);
|
|
27
|
+
console.log(chalk.green(BOX.vertical) + chalk.white.bold(titlePadded) + chalk.green(BOX.vertical));
|
|
28
|
+
|
|
29
|
+
if (subtitle) {
|
|
30
|
+
const subPadded = subtitle.padStart(Math.floor((width - 2 + subtitle.length) / 2)).padEnd(width - 2);
|
|
31
|
+
console.log(chalk.green(BOX.vertical) + chalk.dim(subPadded) + chalk.green(BOX.vertical));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log(chalk.green(`${BOX.bottomLeft}${line}${BOX.bottomRight}`));
|
|
35
|
+
console.log();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Print a divider line
|
|
39
|
+
export function printDivider(char = '─', color = chalk.dim) {
|
|
40
|
+
console.log(color(char.repeat(50)));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Print success message
|
|
44
|
+
export function printSuccess(message) {
|
|
45
|
+
console.log(chalk.green('✓ ') + chalk.white(message));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Print error message
|
|
49
|
+
export function printError(message) {
|
|
50
|
+
console.log(chalk.red('✗ ') + chalk.white(message));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Print warning message
|
|
54
|
+
export function printWarning(message) {
|
|
55
|
+
console.log(chalk.yellow('⚠ ') + chalk.white(message));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Print info message
|
|
59
|
+
export function printInfo(message) {
|
|
60
|
+
console.log(chalk.cyan('ℹ ') + chalk.white(message));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Print a key-value pair
|
|
64
|
+
export function printField(label, value, labelColor = chalk.dim) {
|
|
65
|
+
console.log(` ${labelColor(label + ':')} ${chalk.white(value)}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Print empty state
|
|
69
|
+
export function printEmpty(message, hint = null) {
|
|
70
|
+
console.log();
|
|
71
|
+
console.log(chalk.dim(' ' + message));
|
|
72
|
+
if (hint) {
|
|
73
|
+
console.log(chalk.dim(' 💡 ' + hint));
|
|
74
|
+
}
|
|
75
|
+
console.log();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Beautiful table with borders
|
|
79
|
+
export function printTable(rows, columns, options = {}) {
|
|
80
|
+
const { title, showIndex = false, compact = false } = options;
|
|
81
|
+
|
|
4
82
|
if (!rows?.length) {
|
|
5
|
-
|
|
83
|
+
printEmpty("No results found.", "Try a different search or filter.");
|
|
6
84
|
return;
|
|
7
85
|
}
|
|
86
|
+
|
|
87
|
+
// Calculate column widths
|
|
8
88
|
const widths = {};
|
|
9
89
|
for (const col of columns) {
|
|
10
|
-
widths[col.key] = Math.max(
|
|
90
|
+
widths[col.key] = Math.max(
|
|
91
|
+
col.title.length,
|
|
92
|
+
...rows.map(r => String(r[col.key] ?? "").length)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (showIndex) {
|
|
97
|
+
widths._index = Math.max(1, String(rows.length).length);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Build header
|
|
101
|
+
const headerParts = [];
|
|
102
|
+
if (showIndex) {
|
|
103
|
+
headerParts.push(chalk.dim('#'.padEnd(widths._index)));
|
|
104
|
+
}
|
|
105
|
+
for (const col of columns) {
|
|
106
|
+
headerParts.push(chalk.cyan.bold(col.title.padEnd(widths[col.key])));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Print title if provided
|
|
110
|
+
if (title) {
|
|
111
|
+
console.log();
|
|
112
|
+
console.log(chalk.green.bold(` ${title}`));
|
|
113
|
+
console.log();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Print header
|
|
117
|
+
console.log(' ' + headerParts.join(' '));
|
|
118
|
+
|
|
119
|
+
// Print separator
|
|
120
|
+
const sepParts = [];
|
|
121
|
+
if (showIndex) {
|
|
122
|
+
sepParts.push(chalk.dim('─'.repeat(widths._index)));
|
|
123
|
+
}
|
|
124
|
+
for (const col of columns) {
|
|
125
|
+
sepParts.push(chalk.dim('─'.repeat(widths[col.key])));
|
|
126
|
+
}
|
|
127
|
+
console.log(' ' + sepParts.join('──'));
|
|
128
|
+
|
|
129
|
+
// Print rows
|
|
130
|
+
rows.forEach((r, index) => {
|
|
131
|
+
const rowParts = [];
|
|
132
|
+
if (showIndex) {
|
|
133
|
+
rowParts.push(chalk.dim(String(index + 1).padEnd(widths._index)));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (const col of columns) {
|
|
137
|
+
let value = String(r[col.key] ?? "").padEnd(widths[col.key]);
|
|
138
|
+
|
|
139
|
+
// Apply color based on column type
|
|
140
|
+
if (col.key === 'price' || col.key === 'total') {
|
|
141
|
+
value = chalk.green(value);
|
|
142
|
+
} else if (col.key === 'status') {
|
|
143
|
+
const status = r[col.key]?.toLowerCase() || '';
|
|
144
|
+
if (status === 'delivered' || status === 'active' || status === 'paid') {
|
|
145
|
+
value = chalk.green(value);
|
|
146
|
+
} else if (status === 'shipped' || status === 'processing') {
|
|
147
|
+
value = chalk.cyan(value);
|
|
148
|
+
} else if (status === 'pending') {
|
|
149
|
+
value = chalk.yellow(value);
|
|
150
|
+
} else if (status === 'cancelled' || status === 'suspended') {
|
|
151
|
+
value = chalk.red(value);
|
|
152
|
+
} else {
|
|
153
|
+
value = chalk.white(value);
|
|
154
|
+
}
|
|
155
|
+
} else if (col.key === 'verified') {
|
|
156
|
+
value = r[col.key] === '✓' ? chalk.green(value) : chalk.dim(value);
|
|
157
|
+
} else if (col.key === 'name' || col.key === 'title') {
|
|
158
|
+
value = chalk.white.bold(value);
|
|
159
|
+
} else if (col.key === 'id' || col.key === 'slug') {
|
|
160
|
+
value = chalk.dim(value);
|
|
161
|
+
} else if (col.key === 'category') {
|
|
162
|
+
value = chalk.magenta(value);
|
|
163
|
+
} else {
|
|
164
|
+
value = chalk.white(value);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
rowParts.push(value);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log(' ' + rowParts.join(' '));
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Print footer with count
|
|
174
|
+
console.log();
|
|
175
|
+
console.log(chalk.dim(` Showing ${rows.length} result${rows.length !== 1 ? 's' : ''}`));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Print product card
|
|
179
|
+
export function printProductCard(p) {
|
|
180
|
+
const width = 50;
|
|
181
|
+
const line = '─'.repeat(width);
|
|
182
|
+
|
|
183
|
+
console.log();
|
|
184
|
+
console.log(chalk.cyan(line));
|
|
185
|
+
console.log();
|
|
186
|
+
|
|
187
|
+
// Name
|
|
188
|
+
console.log(chalk.white.bold(' ' + (p.name || 'Unknown Product')));
|
|
189
|
+
|
|
190
|
+
// Short description
|
|
191
|
+
if (p.shortDescription) {
|
|
192
|
+
console.log(chalk.dim(' ' + p.shortDescription));
|
|
193
|
+
}
|
|
194
|
+
console.log();
|
|
195
|
+
|
|
196
|
+
// Price (big and prominent)
|
|
197
|
+
if (p.price) {
|
|
198
|
+
console.log(chalk.green.bold(` $${p.price}`));
|
|
199
|
+
console.log();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Details
|
|
203
|
+
if (p.description) {
|
|
204
|
+
console.log(chalk.white(' ' + p.description));
|
|
205
|
+
console.log();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Meta info
|
|
209
|
+
console.log(chalk.dim(' ─────────────────────────────'));
|
|
210
|
+
|
|
211
|
+
printField('ID', p.productId || p.id || '-');
|
|
212
|
+
if (p.slug) printField('Slug', p.slug);
|
|
213
|
+
if (p.category) printField('Category', p.category);
|
|
214
|
+
|
|
215
|
+
const serviceType = p.serviceType || 'global';
|
|
216
|
+
const typeLabel = serviceType === 'global' ? '🌍 Global' :
|
|
217
|
+
serviceType === 'national' ? '🏳️ National' : '📍 Local';
|
|
218
|
+
printField('Type', typeLabel);
|
|
219
|
+
|
|
220
|
+
if (serviceType === 'local' && p.serviceCity) {
|
|
221
|
+
printField('City', p.serviceCity);
|
|
222
|
+
}
|
|
223
|
+
if ((serviceType === 'local' || serviceType === 'national') && p.serviceCountry) {
|
|
224
|
+
printField('Country', p.serviceCountry);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (p.storeId || p.sellerId) {
|
|
228
|
+
printField('Store', p.storeId || p.sellerId);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
console.log();
|
|
232
|
+
console.log(chalk.cyan(line));
|
|
233
|
+
|
|
234
|
+
// Actions hint
|
|
235
|
+
console.log();
|
|
236
|
+
console.log(chalk.dim(' 💡 Quick actions:'));
|
|
237
|
+
console.log(chalk.dim(' tm add ' + (p.productId || p.id)) + chalk.dim(' — add to cart'));
|
|
238
|
+
console.log(chalk.dim(' tm buy ' + (p.slug || p.productId || p.id)) + chalk.dim(' — buy directly'));
|
|
239
|
+
console.log();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Print cart with totals
|
|
243
|
+
export function printCart(items, total) {
|
|
244
|
+
if (!items?.length) {
|
|
245
|
+
printEmpty("Your cart is empty.", "Add items with: tm add <product-id>");
|
|
246
|
+
return;
|
|
11
247
|
}
|
|
12
|
-
|
|
13
|
-
console.log(
|
|
14
|
-
console.log(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
248
|
+
|
|
249
|
+
console.log();
|
|
250
|
+
console.log(chalk.green.bold(' 🛒 Your Cart'));
|
|
251
|
+
console.log();
|
|
252
|
+
|
|
253
|
+
items.forEach((item, i) => {
|
|
254
|
+
const subtotal = (item.price || 0) * (item.quantity || 1);
|
|
255
|
+
|
|
256
|
+
console.log(chalk.white.bold(` ${i + 1}. ${item.name || `Product #${item.productId}`}`));
|
|
257
|
+
console.log(` ${chalk.dim('Qty:')} ${chalk.cyan(item.quantity)} ${chalk.dim('×')} ${chalk.green('$' + item.price)} ${chalk.dim('=')} ${chalk.green.bold('$' + subtotal.toFixed(2))}`);
|
|
258
|
+
console.log();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
console.log(chalk.dim(' ─────────────────────────────────────────'));
|
|
262
|
+
console.log();
|
|
263
|
+
console.log(` ${chalk.white('Total:')} ${chalk.green.bold('$' + total.toFixed(2))}`);
|
|
264
|
+
console.log();
|
|
265
|
+
console.log(chalk.dim(' 💡 tm checkout — proceed to payment'));
|
|
266
|
+
console.log();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Print order history
|
|
270
|
+
export function printOrders(orders) {
|
|
271
|
+
if (!orders?.length) {
|
|
272
|
+
printEmpty("No orders yet.", "Start shopping with: tm products");
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
console.log();
|
|
277
|
+
console.log(chalk.green.bold(' 📦 Order History'));
|
|
278
|
+
console.log();
|
|
279
|
+
|
|
280
|
+
orders.forEach((order) => {
|
|
281
|
+
const date = new Date(order.createdAt).toLocaleDateString();
|
|
282
|
+
const status = order.status?.toLowerCase() || 'pending';
|
|
283
|
+
|
|
284
|
+
// Status with color and icon
|
|
285
|
+
let statusDisplay;
|
|
286
|
+
if (status === 'delivered') {
|
|
287
|
+
statusDisplay = chalk.green('✓ Delivered');
|
|
288
|
+
} else if (status === 'shipped') {
|
|
289
|
+
statusDisplay = chalk.cyan('📦 Shipped');
|
|
290
|
+
} else if (status === 'paid') {
|
|
291
|
+
statusDisplay = chalk.blue('💳 Paid');
|
|
292
|
+
} else if (status === 'processing') {
|
|
293
|
+
statusDisplay = chalk.yellow('⏳ Processing');
|
|
294
|
+
} else if (status === 'cancelled') {
|
|
295
|
+
statusDisplay = chalk.red('✗ Cancelled');
|
|
296
|
+
} else {
|
|
297
|
+
statusDisplay = chalk.dim('○ ' + status);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
console.log(chalk.white.bold(` ${order.orderNumber || '#' + order.id}`));
|
|
301
|
+
console.log(` ${chalk.dim('Date:')} ${date} ${chalk.dim('Total:')} ${chalk.green('$' + (order.total || 0))}`);
|
|
302
|
+
console.log(` ${statusDisplay}`);
|
|
303
|
+
console.log();
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Print store/seller card
|
|
308
|
+
export function printStoreCard(s) {
|
|
309
|
+
const width = 50;
|
|
310
|
+
const line = '─'.repeat(width);
|
|
311
|
+
|
|
312
|
+
console.log();
|
|
313
|
+
console.log(chalk.magenta(line));
|
|
314
|
+
console.log();
|
|
315
|
+
|
|
316
|
+
// Name with verified badge
|
|
317
|
+
const verified = s.verified ? chalk.green(' ✓') : '';
|
|
318
|
+
console.log(chalk.white.bold(' 🏪 ' + (s.name || s.storeName || 'Unknown Store')) + verified);
|
|
319
|
+
|
|
320
|
+
if (s.description || s.storeDescription) {
|
|
321
|
+
console.log(chalk.dim(' ' + (s.description || s.storeDescription)));
|
|
322
|
+
}
|
|
323
|
+
console.log();
|
|
324
|
+
|
|
325
|
+
// Rating if available
|
|
326
|
+
if (s.rating || s.averageRating) {
|
|
327
|
+
const rating = s.rating || s.averageRating;
|
|
328
|
+
const stars = formatStars(rating);
|
|
329
|
+
console.log(` ${chalk.yellow(stars)} ${chalk.dim(`(${rating.toFixed(1)})`)}`);
|
|
330
|
+
console.log();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Details
|
|
334
|
+
console.log(chalk.dim(' ─────────────────────────────'));
|
|
335
|
+
|
|
336
|
+
if (s.id) printField('ID', s.id);
|
|
337
|
+
if (s.slug) printField('Slug', s.slug);
|
|
338
|
+
|
|
339
|
+
const serviceType = s.serviceType || 'global';
|
|
340
|
+
const typeLabel = serviceType === 'global' ? '🌍 Global' :
|
|
341
|
+
serviceType === 'national' ? '🏳️ National' : '📍 Local';
|
|
342
|
+
printField('Type', typeLabel);
|
|
343
|
+
|
|
344
|
+
if (s.baseCity) printField('City', s.baseCity);
|
|
345
|
+
if (s.baseCountry) printField('Country', s.baseCountry);
|
|
346
|
+
|
|
347
|
+
if (s.categories?.length) {
|
|
348
|
+
printField('Categories', s.categories.join(', '));
|
|
18
349
|
}
|
|
350
|
+
|
|
351
|
+
console.log();
|
|
352
|
+
console.log(chalk.magenta(line));
|
|
353
|
+
|
|
354
|
+
// Actions hint
|
|
355
|
+
console.log();
|
|
356
|
+
console.log(chalk.dim(' 💡 tm products --store ' + s.id) + chalk.dim(' — view products'));
|
|
357
|
+
console.log(chalk.dim(' 💡 tm reviews ' + s.id) + chalk.dim(' — see reviews'));
|
|
358
|
+
console.log();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Print sellers list
|
|
362
|
+
export function printSellers(sellers) {
|
|
363
|
+
if (!sellers?.length) {
|
|
364
|
+
printEmpty("No stores found.", "Try adjusting your filters.");
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
console.log();
|
|
369
|
+
console.log(chalk.green.bold(' 🏪 Stores'));
|
|
370
|
+
console.log();
|
|
371
|
+
|
|
372
|
+
sellers.forEach((s) => {
|
|
373
|
+
const verified = s.verified ? chalk.green(' ✓') : '';
|
|
374
|
+
const serviceType = s.serviceType || 'global';
|
|
375
|
+
const typeIcon = serviceType === 'global' ? '🌍' :
|
|
376
|
+
serviceType === 'national' ? '🏳️' : '📍';
|
|
377
|
+
|
|
378
|
+
console.log(chalk.white.bold(' ' + (s.name || s.storeName)) + verified);
|
|
379
|
+
console.log(` ${chalk.dim('Slug:')} ${chalk.cyan(s.slug)} ${chalk.dim('Type:')} ${typeIcon} ${serviceType}`);
|
|
380
|
+
|
|
381
|
+
if (s.baseCity || s.baseCountry) {
|
|
382
|
+
const location = [s.baseCity, s.baseCountry].filter(Boolean).join(', ');
|
|
383
|
+
console.log(` ${chalk.dim('Location:')} ${location}`);
|
|
384
|
+
}
|
|
385
|
+
console.log();
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
console.log(chalk.dim(` Showing ${sellers.length} store${sellers.length !== 1 ? 's' : ''}`));
|
|
389
|
+
console.log();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Print reviews
|
|
393
|
+
export function printReviews(reviews, averageRating) {
|
|
394
|
+
console.log();
|
|
395
|
+
|
|
396
|
+
if (averageRating !== undefined) {
|
|
397
|
+
console.log(chalk.green.bold(' ⭐ Store Rating'));
|
|
398
|
+
console.log(` ${chalk.yellow(formatStars(averageRating))} ${chalk.dim(`(${averageRating.toFixed(1)} average)`)}`);
|
|
399
|
+
console.log();
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (!reviews?.length) {
|
|
403
|
+
printEmpty("No reviews yet.", "Be the first to review: tm review <store-id> <1-5>");
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
console.log(chalk.green.bold(' 📝 Reviews'));
|
|
408
|
+
console.log();
|
|
409
|
+
|
|
410
|
+
reviews.forEach((r) => {
|
|
411
|
+
const stars = formatStars(r.rating);
|
|
412
|
+
const date = new Date(r.createdAt).toLocaleDateString();
|
|
413
|
+
|
|
414
|
+
console.log(` ${chalk.yellow(stars)} ${chalk.dim('— ' + date)}`);
|
|
415
|
+
if (r.comment) {
|
|
416
|
+
console.log(chalk.white(' "' + r.comment + '"'));
|
|
417
|
+
}
|
|
418
|
+
console.log();
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Print AI models list
|
|
423
|
+
export function printAIModels(models, categories = []) {
|
|
424
|
+
if (!models?.length) {
|
|
425
|
+
printEmpty("No AI models available.", "Check back later for new models.");
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
console.log();
|
|
430
|
+
console.log(chalk.cyan.bold(' 🤖 AI Models'));
|
|
431
|
+
console.log();
|
|
432
|
+
|
|
433
|
+
// Group by category if categories provided
|
|
434
|
+
const grouped = {};
|
|
435
|
+
models.forEach(m => {
|
|
436
|
+
const cat = m.categoryName || 'Other';
|
|
437
|
+
if (!grouped[cat]) grouped[cat] = [];
|
|
438
|
+
grouped[cat].push(m);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
for (const [category, catModels] of Object.entries(grouped)) {
|
|
442
|
+
console.log(chalk.magenta.bold(` ${category}`));
|
|
443
|
+
console.log();
|
|
444
|
+
|
|
445
|
+
catModels.forEach(m => {
|
|
446
|
+
console.log(chalk.white.bold(` ${m.name}`));
|
|
447
|
+
console.log(` ${chalk.dim(m.description || 'No description')}`);
|
|
448
|
+
console.log(` ${chalk.dim('Provider:')} ${m.provider} ${chalk.dim('Cost:')} ${chalk.green(m.creditsPerRun + ' credits')}`);
|
|
449
|
+
console.log(` ${chalk.dim('Run:')} ${chalk.cyan('tm ai run ' + m.slug)}`);
|
|
450
|
+
console.log();
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Print credits balance
|
|
456
|
+
export function printCredits(balance) {
|
|
457
|
+
console.log();
|
|
458
|
+
console.log(chalk.cyan.bold(' 💳 AI Credits'));
|
|
459
|
+
console.log();
|
|
460
|
+
console.log(` ${chalk.white('Balance:')} ${chalk.green.bold(balance + ' credits')}`);
|
|
461
|
+
console.log();
|
|
462
|
+
console.log(chalk.dim(' 💡 tm ai topup <amount> — add more credits'));
|
|
463
|
+
console.log();
|
|
19
464
|
}
|
|
20
465
|
|
|
21
466
|
export function pickProductFields(p) {
|