terminalmarket 0.13.2 → 0.13.3

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/bin/tm.js +93 -108
  2. package/package.json +1 -1
  3. package/src/format.js +20 -0
package/bin/tm.js CHANGED
@@ -26,6 +26,18 @@ const __filename = fileURLToPath(import.meta.url);
26
26
  const __dirname = dirname(__filename);
27
27
  const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
28
28
  const VERSION = pkg.version;
29
+ const commandManifest = (() => {
30
+ try {
31
+ return JSON.parse(
32
+ readFileSync(
33
+ join(__dirname, "../../../shared/command-manifest.json"),
34
+ "utf-8",
35
+ ),
36
+ );
37
+ } catch {
38
+ return { cli: { quickStart: [], groups: [] } };
39
+ }
40
+ })();
29
41
 
30
42
  function getPublicBaseUrl() {
31
43
  const apiBase = getApiBase();
@@ -475,8 +487,10 @@ async function showProfile() {
475
487
  }
476
488
 
477
489
  console.log(chalk.dim(' Use: tm profile set <field> <value>'));
478
- console.log(chalk.dim(' Fields: name, phone, city, country, github, linkedin, skills, bio, available'));
490
+ console.log(chalk.dim(' Fields: name, email, phone, city, country, github, linkedin, skills, bio, available'));
479
491
  console.log(chalk.dim(' telegram, whatsapp, viber, discord, teams, contact'));
492
+ console.log(chalk.dim(' Verify email: tm profile verify email [code]'));
493
+ console.log(chalk.dim(' Verify phone: tm profile verify phone [code]'));
480
494
  console.log();
481
495
  }
482
496
 
@@ -490,6 +504,7 @@ async function setProfileField(field, value) {
490
504
  // Map CLI field names to API field names
491
505
  const fieldMapping = {
492
506
  name: "name",
507
+ email: "email",
493
508
  phone: "phone",
494
509
  address: "address",
495
510
  city: "city",
@@ -530,6 +545,28 @@ async function setProfileField(field, value) {
530
545
 
531
546
  await apiPatch("/profile", { [apiField]: processedValue });
532
547
  console.log(chalk.green(`✓ Updated ${field} to "${newValue}"`));
548
+ if (field === "email") {
549
+ console.log(chalk.dim(" A verification code was sent to your new email."));
550
+ console.log(chalk.dim(" Confirm with: tm profile verify email <code>"));
551
+ }
552
+ }
553
+
554
+ async function verifyProfileChannel(channel, code) {
555
+ const normalized = String(channel || "").trim().toLowerCase();
556
+ if (!["email", "phone"].includes(normalized)) {
557
+ console.error(chalk.red("✗ Invalid channel. Use: email or phone"));
558
+ return;
559
+ }
560
+
561
+ if (!code) {
562
+ await apiPost(`/verification/send-${normalized}`, {});
563
+ console.log(chalk.green(`✓ Verification code sent for ${normalized}.`));
564
+ console.log(chalk.dim(` Confirm with: tm profile verify ${normalized} <code>`));
565
+ return;
566
+ }
567
+
568
+ await apiPost(`/verification/verify-${normalized}`, { code: String(code).trim() });
569
+ console.log(chalk.green(`✓ ${normalized[0].toUpperCase() + normalized.slice(1)} verified.`));
533
570
  }
534
571
 
535
572
  profile
@@ -546,7 +583,7 @@ profile
546
583
 
547
584
  profile
548
585
  .command("set <field> [value...]")
549
- .description("Update profile field (name, phone, address, city, country)")
586
+ .description("Update profile field (name, email, phone, address, city, country)")
550
587
  .action(async (field, value) => {
551
588
  try {
552
589
  await setProfileField(field, value);
@@ -556,6 +593,18 @@ profile
556
593
  }
557
594
  });
558
595
 
596
+ profile
597
+ .command("verify <channel> [code]")
598
+ .description("Send or confirm verification code (email|phone)")
599
+ .action(async (channel, code) => {
600
+ try {
601
+ await verifyProfileChannel(channel, code);
602
+ } catch (e) {
603
+ console.error(chalk.red(e?.message || String(e)));
604
+ process.exitCode = 1;
605
+ }
606
+ });
607
+
559
608
  profile.action(async () => {
560
609
  try {
561
610
  await showProfile();
@@ -690,8 +739,8 @@ program
690
739
  console.log("");
691
740
  console.log(chalk.bold(`Total: $${total.toFixed(2)}`));
692
741
  console.log("");
693
- console.log(chalk.dim("To complete checkout, visit the web terminal or use:"));
694
- console.log(chalk.dim(" tm checkout --confirm"));
742
+ console.log(chalk.dim("To complete checkout, use the web terminal checkout flow."));
743
+ console.log(chalk.dim(" https://terminalmarket.app"));
695
744
  } catch (e) {
696
745
  console.error(chalk.red(e?.message || String(e)));
697
746
  process.exitCode = 1;
@@ -2854,39 +2903,13 @@ program
2854
2903
  // help
2855
2904
  // -----------------
2856
2905
 
2857
- // Command groups for organized help
2858
- const commandGroups = {
2859
- 'Authentication': ['login', 'logout', 'register', 'auth', 'whoami', 'profile'],
2860
- 'Shopping': ['featured', 'deals', 'products', 'search', 'view', 'buy', 'book', 'open', 'categories'],
2861
- 'Cart & Orders': ['cart', 'add', 'checkout', 'orders'],
2862
- 'Reverse Marketplace': ['request'],
2863
- 'Dev Bounties': ['live', 'need', 'ping', 'bounty', 'reputation'],
2864
- 'Automation': ['watch', 'telegram'],
2865
- 'Developer Jobs': ['jobs', 'job', 'apply', 'applications'],
2866
- 'Stores': ['sellers', 'store', 'reviews', 'where'],
2867
- 'AI Services': ['ai', 'credits', 'topup'],
2868
- 'On-Demand Tasks': ['tasks', 'task'],
2869
- 'Personalization': ['alias', 'reward'],
2870
- 'Integrations': ['apps'],
2871
- 'Info': ['about', 'stats', 'policy', 'privacy', 'faq', 'contact'],
2872
- 'System': ['start', 'doctor', 'config', 'help']
2873
- };
2874
-
2875
- // Command groups by level
2876
- const basicGroups = {
2877
- 'Get Started': ['start', 'where', 'doctor'],
2878
- 'Shop': ['featured', 'deals', 'products', 'search', 'buy', 'book', 'view', 'open'],
2879
- 'Account': ['login', 'register', 'whoami', 'profile']
2880
- };
2881
-
2882
- const advancedGroups = {
2883
- 'Cart & Orders': ['cart', 'add', 'checkout', 'orders'],
2884
- 'Reverse Marketplace': ['request'],
2885
- 'Dev Bounties': ['live', 'need', 'ping', 'bounty', 'reputation'],
2886
- 'AI Services': ['ai', 'credits', 'topup'],
2887
- 'Stores': ['sellers', 'store', 'reviews'],
2888
- 'Automation': ['watch', 'telegram', 'alias', 'reward', 'subscribe', 'wishlist', 'webhook']
2889
- };
2906
+ const cliHelpManifest = commandManifest?.cli || { quickStart: [], groups: [] };
2907
+
2908
+ function baseCommandName(commandSyntax = "") {
2909
+ if (!commandSyntax) return "";
2910
+ const clean = String(commandSyntax).trim().replace(/^tm\s+/, "");
2911
+ return clean.split(/\s+/)[0] || "";
2912
+ }
2890
2913
 
2891
2914
  // Custom help formatter
2892
2915
  function showHelp(commandName = null, mode = 'basic') {
@@ -2949,32 +2972,24 @@ function showHelp(commandName = null, mode = 'basic') {
2949
2972
  return;
2950
2973
  }
2951
2974
 
2952
- // Collect all commands
2953
- const allCommands = {};
2954
- program.commands.forEach(cmd => {
2955
- const args = (cmd.registeredArguments || []).map(a => a.required ? `<${a.name()}>` : `[${a.name()}]`).join(' ');
2956
- allCommands[cmd.name()] = {
2957
- name: cmd.name(),
2958
- args,
2959
- desc: cmd.description()
2960
- };
2961
- });
2975
+ // Collect declared commands for existence checks
2976
+ const available = new Set(program.commands.map((cmd) => cmd.name()));
2962
2977
 
2963
- const COL_WIDTH = 28;
2978
+ const COL_WIDTH = 34;
2964
2979
 
2965
- const printGroup = (groupName, cmdNames, icon, color) => {
2966
- const groupCmds = cmdNames
2967
- .filter(name => allCommands[name])
2968
- .map(name => allCommands[name]);
2980
+ const printGroup = (group, color) => {
2981
+ const groupCmds = (group.commands || []).filter((entry) =>
2982
+ available.has(baseCommandName(entry.command)),
2983
+ );
2969
2984
 
2970
2985
  if (groupCmds.length === 0) return;
2971
2986
 
2972
- console.log(color.bold(`${icon} ${groupName}`));
2973
- groupCmds.forEach(c => {
2974
- const rawCmd = c.name + (c.args ? ' ' + c.args : '');
2975
- console.log(' ' + chalk.cyan(c.name) + (c.args ? chalk.yellow(' ' + c.args) : '') +
2987
+ console.log(color.bold(`${group.icon || '•'} ${group.title}`));
2988
+ groupCmds.forEach((entry) => {
2989
+ const rawCmd = entry.command;
2990
+ console.log(' ' + chalk.cyan(entry.command) +
2976
2991
  ' '.repeat(Math.max(1, COL_WIDTH - rawCmd.length)) +
2977
- chalk.dim(c.desc));
2992
+ chalk.dim(entry.description || ""));
2978
2993
  });
2979
2994
  console.log();
2980
2995
  };
@@ -2987,17 +3002,17 @@ function showHelp(commandName = null, mode = 'basic') {
2987
3002
  if (mode === 'basic') {
2988
3003
  // Simple, selling help
2989
3004
  console.log(chalk.yellow.bold('Quick Start:'));
2990
- console.log(` ${chalk.green('tm start')} ${chalk.dim('interactive onboarding')}`);
2991
- console.log(` ${chalk.green('tm where <city>')} ${chalk.dim('set your location')}`);
2992
- console.log(` ${chalk.green('tm featured')} ${chalk.dim('top picks this week')}`);
2993
- console.log(` ${chalk.green('tm buy <id>')} ${chalk.dim('purchase a product')}`);
3005
+ for (const step of cliHelpManifest.quickStart || []) {
3006
+ const command = step.command || "";
3007
+ console.log(` ${chalk.green(command)}${' '.repeat(Math.max(1, 28 - command.length))}${chalk.dim(step.description || "")}`);
3008
+ }
2994
3009
  console.log();
2995
3010
 
2996
- printGroup('Shop', ['featured', 'deals', 'search', 'products'], '🛒', chalk.green);
2997
- printGroup('Dev Bounties', ['live', 'need', 'ping', 'profile'], '🔥', chalk.red);
2998
- printGroup('Account', ['login', 'register', 'profile'], '👤', chalk.blue);
2999
- printGroup('Integrations', ['apps'], '🧩', chalk.magenta);
3000
- printGroup('Help', ['doctor', 'help'], '💡', chalk.gray);
3011
+ for (const group of cliHelpManifest.groups || []) {
3012
+ if (Array.isArray(group.levels) && group.levels.includes("basic")) {
3013
+ printGroup(group, chalk.white);
3014
+ }
3015
+ }
3001
3016
 
3002
3017
  console.log(chalk.dim('─'.repeat(45)));
3003
3018
  console.log(chalk.dim(' tm help --advanced') + chalk.dim(' cart, AI, rewards'));
@@ -3009,12 +3024,11 @@ function showHelp(commandName = null, mode = 'basic') {
3009
3024
  console.log(chalk.yellow.bold('Advanced Features:'));
3010
3025
  console.log();
3011
3026
 
3012
- printGroup('Cart & Orders', ['cart', 'add', 'checkout', 'orders'], '📦', chalk.yellow);
3013
- printGroup('Reverse Marketplace', ['request'], '📋', chalk.magenta);
3014
- printGroup('Dev Bounties', ['live', 'need', 'ping', 'bounty', 'reputation'], '🔥', chalk.red);
3015
- printGroup('AI Services', ['ai', 'credits', 'topup'], '🤖', chalk.cyan);
3016
- printGroup('Stores', ['sellers', 'store', 'reviews'], '🏪', chalk.magenta);
3017
- printGroup('Automation', ['watch', 'telegram', 'alias', 'reward', 'subscribe', 'wishlist'], '👁', chalk.cyan);
3027
+ for (const group of cliHelpManifest.groups || []) {
3028
+ if (Array.isArray(group.levels) && group.levels.includes("advanced")) {
3029
+ printGroup(group, chalk.white);
3030
+ }
3031
+ }
3018
3032
 
3019
3033
  console.log(chalk.dim('─'.repeat(45)));
3020
3034
  console.log(chalk.dim(' tm help') + chalk.dim(' basic commands'));
@@ -3026,42 +3040,10 @@ function showHelp(commandName = null, mode = 'basic') {
3026
3040
  console.log(chalk.magenta.bold('Usage:'), chalk.green('tm'), chalk.cyan('<command>'), chalk.dim('[options]'));
3027
3041
  console.log();
3028
3042
 
3029
- const groupColors = {
3030
- 'Authentication': chalk.blue,
3031
- 'Shopping': chalk.green,
3032
- 'Cart & Orders': chalk.yellow,
3033
- 'Reverse Marketplace': chalk.magenta,
3034
- 'Dev Bounties': chalk.red,
3035
- 'Automation': chalk.cyan,
3036
- 'Developer Jobs': chalk.magenta,
3037
- 'Stores': chalk.cyan,
3038
- 'AI Services': chalk.cyan,
3039
- 'On-Demand Tasks': chalk.yellow,
3040
- 'Integrations': chalk.magenta,
3041
- 'Personalization': chalk.white,
3042
- 'Info': chalk.dim,
3043
- 'System': chalk.gray
3044
- };
3045
-
3046
- const groupIcons = {
3047
- 'Authentication': '🔐',
3048
- 'Shopping': '🛒',
3049
- 'Cart & Orders': '📦',
3050
- 'Reverse Marketplace': '📋',
3051
- 'Dev Bounties': '🔥',
3052
- 'Automation': '👁',
3053
- 'Developer Jobs': '💼',
3054
- 'Stores': '🏪',
3055
- 'AI Services': '🤖',
3056
- 'On-Demand Tasks': '⚡',
3057
- 'Integrations': '🧩',
3058
- 'Personalization': '⚙️',
3059
- 'Info': 'ℹ️',
3060
- 'System': '💻'
3061
- };
3062
-
3063
- for (const [group, cmdNames] of Object.entries(commandGroups)) {
3064
- printGroup(group, cmdNames, groupIcons[group] || '•', groupColors[group] || chalk.white);
3043
+ for (const group of cliHelpManifest.groups || []) {
3044
+ if (Array.isArray(group.levels) && group.levels.includes("all")) {
3045
+ printGroup(group, chalk.white);
3046
+ }
3065
3047
  }
3066
3048
 
3067
3049
  console.log(chalk.dim('─'.repeat(45)));
@@ -5028,6 +5010,7 @@ function formatNamespaceResponse(namespace, command, data) {
5028
5010
  program
5029
5011
  .command("help [command]")
5030
5012
  .description("Show help for a command")
5013
+ .option("-b, --basic", "Show basic commands")
5031
5014
  .option("-a, --advanced", "Show advanced commands (cart, AI, rewards)")
5032
5015
  .option("--all", "Show all commands")
5033
5016
  .action((commandName, opts) => {
@@ -5035,6 +5018,8 @@ program
5035
5018
  showHelp(null, 'all');
5036
5019
  } else if (opts.advanced) {
5037
5020
  showHelp(null, 'advanced');
5021
+ } else if (opts.basic) {
5022
+ showHelp(null, 'basic');
5038
5023
  } else {
5039
5024
  showHelp(commandName, 'basic');
5040
5025
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalmarket",
3
- "version": "0.13.2",
3
+ "version": "0.13.3",
4
4
  "description": "TerminalMarket CLI — a curated marketplace for developers & founders (client for terminalmarket.app)",
5
5
  "bin": {
6
6
  "tm": "bin/tm.js"
package/src/format.js CHANGED
@@ -297,6 +297,20 @@ export function printOrders(orders) {
297
297
  orders.forEach((order) => {
298
298
  const date = new Date(order.createdAt).toLocaleDateString();
299
299
  const status = order.status?.toLowerCase() || 'pending';
300
+ const storeNames = Array.isArray(order.storeNames)
301
+ ? order.storeNames.filter(Boolean)
302
+ : [];
303
+ const itemLines = Array.isArray(order.items)
304
+ ? order.items
305
+ .map((item) => {
306
+ const name = item?.product?.name || item?.productName || `Product #${item?.productId || '?'}`;
307
+ const qty = Number(item?.quantity || 1);
308
+ const unitPrice = Number(item?.priceAtPurchase ?? item?.price ?? item?.product?.price ?? 0);
309
+ const subtotal = qty * unitPrice;
310
+ return `${name} ×${qty} (${chalk.green('$' + subtotal.toFixed(2))})`;
311
+ })
312
+ .filter(Boolean)
313
+ : [];
300
314
 
301
315
  // Status with color and icon
302
316
  let statusDisplay;
@@ -317,6 +331,12 @@ export function printOrders(orders) {
317
331
  console.log(chalk.white.bold(` ${order.orderNumber || '#' + order.id}`));
318
332
  console.log(` ${chalk.dim('Date:')} ${date} ${chalk.dim('Total:')} ${chalk.green('$' + (order.total || 0))}`);
319
333
  console.log(` ${statusDisplay}`);
334
+ if (storeNames.length > 0) {
335
+ console.log(` ${chalk.dim('Store:')} ${chalk.cyan(storeNames.join(', '))}`);
336
+ }
337
+ if (itemLines.length > 0) {
338
+ console.log(` ${chalk.dim('Items:')} ${itemLines.join(chalk.dim(' · '))}`);
339
+ }
320
340
  if (order.deliveryMethod === 'digital') {
321
341
  console.log(` ${chalk.dim('Download or key in library')}`);
322
342
  }