@rankcli/cli 0.0.17 → 0.0.19

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 (4) hide show
  1. package/README.md +15 -10
  2. package/dist/index.js +325 -131
  3. package/dist/index.mjs +325 -132
  4. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -284,7 +284,7 @@ async function analyze(options) {
284
284
  // src/commands/audit.ts
285
285
  var import_agent_runtime = require("@rankcli/agent-runtime");
286
286
  var import_ora = __toESM(require("ora"));
287
- var import_chalk = __toESM(require("chalk"));
287
+ var import_chalk2 = __toESM(require("chalk"));
288
288
 
289
289
  // src/lib/config.ts
290
290
  var import_conf = __toESM(require("conf"));
@@ -479,6 +479,174 @@ async function getAuthenticatedClient() {
479
479
  return supabase;
480
480
  }
481
481
 
482
+ // src/lib/upsell.ts
483
+ var import_chalk = __toESM(require("chalk"));
484
+ var WEB_APP_URL2 = "https://rankcli.dev";
485
+ var TIER_LIMITS = {
486
+ free_anon: {
487
+ checks: 50,
488
+ sites: 0,
489
+ domains_per_month: 1,
490
+ features: { geo: false, auto_fix: false, github: false, ai: false }
491
+ },
492
+ free: {
493
+ checks: 100,
494
+ sites: 1,
495
+ domains_per_month: 2,
496
+ features: { geo: "basic", auto_fix: false, github: false, ai: false }
497
+ },
498
+ solo: {
499
+ checks: 280,
500
+ sites: 3,
501
+ domains_per_month: 5,
502
+ features: { geo: true, auto_fix: true, github: false, ai: true }
503
+ },
504
+ pro: {
505
+ checks: 280,
506
+ sites: 10,
507
+ domains_per_month: 15,
508
+ features: { geo: true, auto_fix: true, github: true, ai: true }
509
+ },
510
+ agency: {
511
+ checks: 280,
512
+ sites: 50,
513
+ domains_per_month: null,
514
+ // unlimited
515
+ features: { geo: true, auto_fix: true, github: true, ai: true }
516
+ }
517
+ };
518
+ var UPSELL_MESSAGES = {
519
+ // After free audit completes
520
+ free_audit_complete: () => [
521
+ "",
522
+ import_chalk.default.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
523
+ import_chalk.default.yellow.bold(" \u{1F680} Get 2x more checks + audit history"),
524
+ import_chalk.default.dim(" Create a free account to unlock 100 checks and save your results."),
525
+ "",
526
+ import_chalk.default.cyan(` \u2192 rankcli login`),
527
+ import_chalk.default.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
528
+ ""
529
+ ],
530
+ // When checks limit is reached
531
+ checks_limit: (ctx) => [
532
+ "",
533
+ import_chalk.default.yellow(`\u26A0\uFE0F Running ${ctx.current || 50} of ${TIER_LIMITS.solo.checks} available checks`),
534
+ import_chalk.default.dim(` Unlock all ${TIER_LIMITS.solo.checks}+ checks with Solo ($9/mo)`),
535
+ import_chalk.default.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_checks`),
536
+ ""
537
+ ],
538
+ // When domain limit is reached
539
+ domain_limit: (ctx) => [
540
+ "",
541
+ import_chalk.default.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
542
+ import_chalk.default.red.bold(" \u2717 Domain limit reached"),
543
+ import_chalk.default.dim(` Your ${ctx.tier} plan allows ${ctx.max} domains per month.`),
544
+ "",
545
+ import_chalk.default.dim(" Upgrade to audit more sites:"),
546
+ import_chalk.default.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_domain_limit`),
547
+ import_chalk.default.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
548
+ ""
549
+ ],
550
+ // When GEO features are limited
551
+ geo_limited: (ctx) => [
552
+ "",
553
+ import_chalk.default.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
554
+ import_chalk.default.yellow.bold(" \u{1F916} Full GEO analysis requires Solo"),
555
+ import_chalk.default.dim(" You're seeing basic AI crawler access checks."),
556
+ import_chalk.default.dim(" Full analysis includes: LLM signals, citation readiness, content structure"),
557
+ "",
558
+ import_chalk.default.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_geo`),
559
+ import_chalk.default.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
560
+ ""
561
+ ],
562
+ // When auto-fix is attempted without access
563
+ auto_fix_blocked: () => [
564
+ "",
565
+ import_chalk.default.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
566
+ import_chalk.default.red.bold(" \u2717 Auto-fix requires Solo plan"),
567
+ import_chalk.default.dim(" Apply framework-specific SEO fixes directly to your codebase."),
568
+ "",
569
+ import_chalk.default.dim(" Solo ($9/mo) includes:"),
570
+ import_chalk.default.dim(" \u2022 Local auto-fix via CLI"),
571
+ import_chalk.default.dim(" \u2022 25+ framework support"),
572
+ import_chalk.default.dim(" \u2022 Full 280+ checks"),
573
+ "",
574
+ import_chalk.default.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_autofix`),
575
+ import_chalk.default.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
576
+ ""
577
+ ],
578
+ // When GitHub integration is attempted without access
579
+ github_blocked: () => [
580
+ "",
581
+ import_chalk.default.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
582
+ import_chalk.default.red.bold(" \u2717 GitHub auto-fix PRs require Pro"),
583
+ import_chalk.default.dim(" Automatically create fix PRs when SEO issues are detected."),
584
+ "",
585
+ import_chalk.default.dim(" Pro ($29/mo) includes:"),
586
+ import_chalk.default.dim(" \u2022 GitHub App integration"),
587
+ import_chalk.default.dim(" \u2022 Auto-fix PRs"),
588
+ import_chalk.default.dim(" \u2022 Daily scheduled audits"),
589
+ import_chalk.default.dim(" \u2022 Slack/Discord alerts"),
590
+ "",
591
+ import_chalk.default.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_github`),
592
+ import_chalk.default.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
593
+ ""
594
+ ],
595
+ // Rate limit exceeded
596
+ rate_limit: (ctx) => [
597
+ "",
598
+ import_chalk.default.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
599
+ import_chalk.default.red.bold(" \u2717 API rate limit exceeded"),
600
+ import_chalk.default.dim(` Your ${ctx.tier} plan allows ${ctx.max} requests/day.`),
601
+ "",
602
+ import_chalk.default.dim(" Need more? Upgrade for higher limits:"),
603
+ import_chalk.default.dim(" \u2022 Solo: 100 req/day"),
604
+ import_chalk.default.dim(" \u2022 Pro: 1,000 req/day"),
605
+ import_chalk.default.dim(" \u2022 Agency: 10,000 req/day"),
606
+ "",
607
+ import_chalk.default.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_rate_limit`),
608
+ import_chalk.default.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
609
+ ""
610
+ ],
611
+ // Schedule exceeded (for GitHub Action)
612
+ schedule_exceeded: (ctx) => [
613
+ "",
614
+ import_chalk.default.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
615
+ import_chalk.default.yellow.bold(" \u23F0 Scheduled audit skipped"),
616
+ import_chalk.default.dim(` Your ${ctx.tier} plan allows ${ctx.limit} scheduled audits.`),
617
+ "",
618
+ import_chalk.default.dim(" Upgrade for more frequent audits:"),
619
+ import_chalk.default.dim(" \u2022 Solo: Weekly"),
620
+ import_chalk.default.dim(" \u2022 Pro: Daily"),
621
+ import_chalk.default.dim(" \u2022 Agency: Hourly"),
622
+ "",
623
+ import_chalk.default.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=schedule_exceeded`),
624
+ import_chalk.default.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
625
+ ""
626
+ ],
627
+ // Login prompt (soft)
628
+ login_prompt: () => [
629
+ "",
630
+ import_chalk.default.dim(" \u{1F4A1} Tip: Log in to sync results and track progress over time:"),
631
+ import_chalk.default.dim(" rankcli login"),
632
+ ""
633
+ ]
634
+ };
635
+ function showUpsell(messageKey, context) {
636
+ const messageFn = UPSELL_MESSAGES[messageKey];
637
+ if (!messageFn) return;
638
+ const ctx = {
639
+ tier: "free",
640
+ ...context
641
+ };
642
+ const lines = messageFn(ctx);
643
+ lines.forEach((line) => console.log(line));
644
+ }
645
+ function hasFeature(tier, feature) {
646
+ const limits = TIER_LIMITS[tier];
647
+ return limits?.features?.[feature] ?? false;
648
+ }
649
+
482
650
  // src/commands/audit.ts
483
651
  var { detectFramework: detectFramework2, getFrameworkDisplayName, getFrameworkIssues } = import_agent_runtime.frameworks;
484
652
  async function fetchHtml(url, timeout = 1e4) {
@@ -546,7 +714,7 @@ Return JSON with:
546
714
  }
547
715
  const response = await fetch(apiUrl, { method: "POST", headers, body });
548
716
  if (!response.ok) {
549
- console.error(import_chalk.default.yellow(`AI analysis failed: ${response.status}`));
717
+ console.error(import_chalk2.default.yellow(`AI analysis failed: ${response.status}`));
550
718
  return null;
551
719
  }
552
720
  const data = await response.json();
@@ -556,7 +724,7 @@ Return JSON with:
556
724
  if (!jsonMatch) return null;
557
725
  return JSON.parse(jsonMatch[0]);
558
726
  } catch (error) {
559
- console.error(import_chalk.default.yellow(`AI analysis error: ${error}`));
727
+ console.error(import_chalk2.default.yellow(`AI analysis error: ${error}`));
560
728
  return null;
561
729
  }
562
730
  }
@@ -575,7 +743,7 @@ function mergeIssues(issueArrays) {
575
743
  }
576
744
  return Array.from(issueMap.values());
577
745
  }
578
- async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis) {
746
+ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis, durationMs, checksRun) {
579
747
  const supabase = await getAuthenticatedClient();
580
748
  if (!supabase) return null;
581
749
  const { userId, sitesLimit } = getUserInfo();
@@ -591,9 +759,9 @@ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis)
591
759
  const { count } = await supabase.from("projects").select("id", { count: "exact", head: true }).eq("user_id", userId).eq("is_active", true);
592
760
  const limit = sitesLimit ?? 1;
593
761
  if (count !== null && count >= limit) {
594
- console.log(import_chalk.default.yellow(`
762
+ console.log(import_chalk2.default.yellow(`
595
763
  \u26A0\uFE0F Project limit reached (${count}/${limit})`));
596
- console.log(import_chalk.default.dim(` Upgrade at: ${WEB_APP_URL}/account`));
764
+ console.log(import_chalk2.default.dim(` Upgrade at: ${WEB_APP_URL}/account`));
597
765
  return null;
598
766
  }
599
767
  const { data: newProject, error: projectError } = await supabase.from("projects").insert({
@@ -603,7 +771,7 @@ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis)
603
771
  domain
604
772
  }).select("id").single();
605
773
  if (projectError || !newProject) {
606
- console.error(import_chalk.default.dim("Failed to create project"));
774
+ console.error(import_chalk2.default.dim("Failed to create project"));
607
775
  return null;
608
776
  }
609
777
  projectId = newProject.id;
@@ -617,6 +785,7 @@ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis)
617
785
  performance: report.healthScore?.performance || 0,
618
786
  security: report.healthScore?.security || 0
619
787
  };
788
+ const now = (/* @__PURE__ */ new Date()).toISOString();
620
789
  const { data: audit2, error: auditError } = await supabase.from("audits").insert({
621
790
  project_id: projectId,
622
791
  triggered_by: "cli",
@@ -625,21 +794,23 @@ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis)
625
794
  score,
626
795
  pages_crawled: pagesAudited,
627
796
  issues_found: mergedIssues.length,
628
- checks_run: mergedIssues.length > 0 ? 200 : 0,
629
- // Approximate
797
+ checks_run: checksRun,
630
798
  health_scores: healthScores,
631
- completed_at: (/* @__PURE__ */ new Date()).toISOString(),
799
+ started_at: new Date(Date.now() - durationMs).toISOString(),
800
+ completed_at: now,
801
+ duration_ms: durationMs,
632
802
  summary: {
633
803
  errors: mergedIssues.filter((i) => i.severity === "error").length,
634
804
  warnings: mergedIssues.filter((i) => i.severity === "warning").length,
635
805
  notices: mergedIssues.filter((i) => i.severity === "notice").length,
636
806
  pagesAudited,
807
+ checksRun,
637
808
  source: "cli"
638
809
  },
639
810
  ai_analysis: aiAnalysis
640
811
  }).select("id").single();
641
812
  if (auditError || !audit2) {
642
- console.error(import_chalk.default.dim("Failed to save audit"));
813
+ console.error(import_chalk2.default.dim("Failed to save audit"));
643
814
  return null;
644
815
  }
645
816
  if (mergedIssues.length > 0) {
@@ -660,12 +831,14 @@ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis)
660
831
  await supabase.from("audit_issues").insert(issueRecords);
661
832
  }
662
833
  await supabase.from("projects").update({
663
- last_audit_at: (/* @__PURE__ */ new Date()).toISOString(),
664
- last_audit_score: score
834
+ last_audit_at: now,
835
+ last_audit_score: score,
836
+ last_scheduled_audit_at: now,
837
+ scheduled_audit_source: "cli"
665
838
  }).eq("id", projectId);
666
839
  return { projectId, auditId: audit2.id };
667
840
  } catch (error) {
668
- console.error(import_chalk.default.dim("Cloud sync failed"));
841
+ console.error(import_chalk2.default.dim("Cloud sync failed"));
669
842
  return null;
670
843
  }
671
844
  }
@@ -686,8 +859,8 @@ async function audit(options) {
686
859
  console.log(" --ai-key <key> API key (or set OPENAI_API_KEY/ANTHROPIC_API_KEY)");
687
860
  console.log(" --output json Output as JSON");
688
861
  if (!loggedIn) {
689
- console.log(import_chalk.default.dim("\nTip: Log in to sync results to the web dashboard:"));
690
- console.log(import_chalk.default.dim(" rankcli login"));
862
+ console.log(import_chalk2.default.dim("\nTip: Log in to sync results to the web dashboard:"));
863
+ console.log(import_chalk2.default.dim(" rankcli login"));
691
864
  }
692
865
  console.log("");
693
866
  return;
@@ -771,12 +944,17 @@ async function audit(options) {
771
944
  aiSpinner.warn("AI analysis failed or returned no results");
772
945
  }
773
946
  } else if (enableAI && !aiKey) {
774
- console.log(import_chalk.default.yellow("\n\u26A0\uFE0F AI analysis requested but no API key provided"));
775
- console.log(import_chalk.default.dim(" Set OPENAI_API_KEY or ANTHROPIC_API_KEY, or use --ai-key"));
947
+ console.log(import_chalk2.default.yellow("\n\u26A0\uFE0F AI analysis requested but no API key provided"));
948
+ console.log(import_chalk2.default.dim(" Set OPENAI_API_KEY or ANTHROPIC_API_KEY, or use --ai-key"));
776
949
  }
777
950
  const totalTime = Date.now() - startTime;
778
951
  const frameworkName = detectedFramework.framework !== "unknown" ? getFrameworkDisplayName(detectedFramework.framework) : null;
952
+ const actualChecksRun = (primaryReport.summary?.passed || 0) + (primaryReport.summary?.errors || 0) + (primaryReport.summary?.warnings || 0) || 200;
779
953
  if (options.output === "json") {
954
+ let syncResult = null;
955
+ if (loggedIn) {
956
+ syncResult = await syncToCloud(url, primaryReport, mergedIssues, successCount, aiAnalysis, totalTime, actualChecksRun);
957
+ }
780
958
  const jsonOutput = {
781
959
  url,
782
960
  score: primaryReport.healthScore?.overall || 0,
@@ -785,31 +963,37 @@ async function audit(options) {
785
963
  framework: frameworkName,
786
964
  issues: mergedIssues,
787
965
  healthScores: primaryReport.healthScore,
788
- aiAnalysis
966
+ aiAnalysis,
967
+ // Include cloud sync info if synced
968
+ ...syncResult && {
969
+ projectId: syncResult.projectId,
970
+ auditId: syncResult.auditId,
971
+ dashboardUrl: `${WEB_APP_URL}/dashboard/projects/${syncResult.projectId}`
972
+ }
789
973
  };
790
974
  console.log(JSON.stringify(jsonOutput, null, 2));
791
975
  } else {
792
- console.log("\n" + import_chalk.default.bold("\u2550".repeat(60)));
793
- console.log(import_chalk.default.bold.cyan(` SEO AUDIT REPORT: ${url}`));
794
- console.log(import_chalk.default.bold("\u2550".repeat(60)));
976
+ console.log("\n" + import_chalk2.default.bold("\u2550".repeat(60)));
977
+ console.log(import_chalk2.default.bold.cyan(` SEO AUDIT REPORT: ${url}`));
978
+ console.log(import_chalk2.default.bold("\u2550".repeat(60)));
795
979
  const score = primaryReport.healthScore?.overall || 0;
796
- const scoreColor = score >= 80 ? import_chalk.default.green : score >= 60 ? import_chalk.default.yellow : import_chalk.default.red;
980
+ const scoreColor = score >= 80 ? import_chalk2.default.green : score >= 60 ? import_chalk2.default.yellow : import_chalk2.default.red;
797
981
  console.log(`
798
- ${import_chalk.default.bold("Score:")} ${scoreColor.bold(score + "/100")}`);
799
- console.log(` ${import_chalk.default.bold("Pages audited:")} ${successCount}`);
800
- console.log(` ${import_chalk.default.bold("Issues found:")} ${mergedIssues.length}`);
801
- console.log(` ${import_chalk.default.bold("Time:")} ${totalTime}ms`);
982
+ ${import_chalk2.default.bold("Score:")} ${scoreColor.bold(score + "/100")}`);
983
+ console.log(` ${import_chalk2.default.bold("Pages audited:")} ${successCount}`);
984
+ console.log(` ${import_chalk2.default.bold("Issues found:")} ${mergedIssues.length}`);
985
+ console.log(` ${import_chalk2.default.bold("Time:")} ${totalTime}ms`);
802
986
  if (frameworkName) {
803
- console.log(` ${import_chalk.default.bold("Framework:")} ${import_chalk.default.cyan(frameworkName)}`);
987
+ console.log(` ${import_chalk2.default.bold("Framework:")} ${import_chalk2.default.cyan(frameworkName)}`);
804
988
  }
805
989
  if (primaryReport.healthScore) {
806
- console.log("\n" + import_chalk.default.bold(" Health Scores:"));
990
+ console.log("\n" + import_chalk2.default.bold(" Health Scores:"));
807
991
  const hs = primaryReport.healthScore;
808
992
  const bar = (score2) => {
809
993
  const filled = Math.round(score2 / 5);
810
994
  const empty = 20 - filled;
811
- const color = score2 >= 80 ? import_chalk.default.green : score2 >= 60 ? import_chalk.default.yellow : import_chalk.default.red;
812
- return color("\u2588".repeat(filled)) + import_chalk.default.gray("\u2591".repeat(empty));
995
+ const color = score2 >= 80 ? import_chalk2.default.green : score2 >= 60 ? import_chalk2.default.yellow : import_chalk2.default.red;
996
+ return color("\u2588".repeat(filled)) + import_chalk2.default.gray("\u2591".repeat(empty));
813
997
  };
814
998
  console.log(` Crawlability: ${bar(hs.crawlability || 0)} ${hs.crawlability || 0}%`);
815
999
  console.log(` On-Page: ${bar(hs.onPage || 0)} ${hs.onPage || 0}%`);
@@ -820,50 +1004,63 @@ async function audit(options) {
820
1004
  const errors = mergedIssues.filter((i) => i.severity === "error");
821
1005
  const warnings = mergedIssues.filter((i) => i.severity === "warning");
822
1006
  const notices = mergedIssues.filter((i) => i.severity === "notice");
823
- console.log("\n" + import_chalk.default.bold(" Issues by Severity:"));
824
- console.log(` ${import_chalk.default.red("\u25CF")} Errors: ${errors.length}`);
825
- console.log(` ${import_chalk.default.yellow("\u25CF")} Warnings: ${warnings.length}`);
826
- console.log(` ${import_chalk.default.blue("\u25CF")} Notices: ${notices.length}`);
827
- console.log("\n" + import_chalk.default.bold(" Top Issues:"));
1007
+ console.log("\n" + import_chalk2.default.bold(" Issues by Severity:"));
1008
+ console.log(` ${import_chalk2.default.red("\u25CF")} Errors: ${errors.length}`);
1009
+ console.log(` ${import_chalk2.default.yellow("\u25CF")} Warnings: ${warnings.length}`);
1010
+ console.log(` ${import_chalk2.default.blue("\u25CF")} Notices: ${notices.length}`);
1011
+ console.log("\n" + import_chalk2.default.bold(" Top Issues:"));
828
1012
  const topIssues = [...errors, ...warnings, ...notices].slice(0, 10);
829
1013
  topIssues.forEach((issue, i) => {
830
- const icon = issue.severity === "error" ? import_chalk.default.red("\u2717") : issue.severity === "warning" ? import_chalk.default.yellow("!") : import_chalk.default.blue("i");
1014
+ const icon = issue.severity === "error" ? import_chalk2.default.red("\u2717") : issue.severity === "warning" ? import_chalk2.default.yellow("!") : import_chalk2.default.blue("i");
831
1015
  console.log(` ${i + 1}. ${icon} ${issue.title}`);
832
1016
  if (issue.affectedUrls && issue.affectedUrls.length > 1) {
833
- console.log(import_chalk.default.dim(` Affects ${issue.affectedUrls.length} pages`));
1017
+ console.log(import_chalk2.default.dim(` Affects ${issue.affectedUrls.length} pages`));
834
1018
  }
835
1019
  });
836
1020
  if (aiAnalysis) {
837
- console.log("\n" + import_chalk.default.bold(" AI Analysis:"));
1021
+ console.log("\n" + import_chalk2.default.bold(" AI Analysis:"));
838
1022
  console.log(` Content Score: ${aiAnalysis.contentScore}/100`);
839
- console.log("\n " + import_chalk.default.bold("Suggestions:"));
1023
+ console.log("\n " + import_chalk2.default.bold("Suggestions:"));
840
1024
  aiAnalysis.suggestions.forEach((s, i) => {
841
1025
  console.log(` ${i + 1}. ${s}`);
842
1026
  });
843
1027
  if (aiAnalysis.prioritizedFixes?.length > 0) {
844
- console.log("\n " + import_chalk.default.bold("Priority Fixes:"));
1028
+ console.log("\n " + import_chalk2.default.bold("Priority Fixes:"));
845
1029
  aiAnalysis.prioritizedFixes.slice(0, 5).forEach((fix, i) => {
846
1030
  console.log(` ${i + 1}. [P${fix.priority}] ${fix.code}`);
847
- console.log(import_chalk.default.dim(` ${fix.rationale}`));
1031
+ console.log(import_chalk2.default.dim(` ${fix.rationale}`));
848
1032
  });
849
1033
  }
850
1034
  }
851
- console.log("\n" + import_chalk.default.bold("\u2550".repeat(60)));
1035
+ console.log("\n" + import_chalk2.default.bold("\u2550".repeat(60)));
1036
+ const userInfo = getUserInfo();
1037
+ const tier = userInfo.planName?.toLowerCase() || "free";
1038
+ const isFreeTier = !loggedIn || tier === "free";
1039
+ const isSoloOrBelow = isFreeTier || tier === "solo";
1040
+ if (isFreeTier) {
1041
+ const checksRun = mergedIssues.length > 0 ? loggedIn ? 100 : 50 : 0;
1042
+ showUpsell("checks_limit", {
1043
+ tier: loggedIn ? "free" : "free_anon",
1044
+ current: checksRun
1045
+ });
1046
+ }
1047
+ if (options.geo && isFreeTier) {
1048
+ showUpsell("geo_limited", { tier: loggedIn ? "free" : "free_anon" });
1049
+ }
852
1050
  if (loggedIn) {
853
1051
  const syncSpinner = (0, import_ora.default)("Syncing to cloud...").start();
854
- const syncResult = await syncToCloud(url, primaryReport, mergedIssues, successCount, aiAnalysis);
1052
+ const syncResult = await syncToCloud(url, primaryReport, mergedIssues, successCount, aiAnalysis, totalTime, actualChecksRun);
855
1053
  if (syncResult) {
856
1054
  syncSpinner.succeed("Results synced to cloud");
857
- console.log(import_chalk.default.cyan(`
1055
+ console.log(import_chalk2.default.cyan(`
858
1056
  View and manage issues in the web dashboard:`));
859
- console.log(import_chalk.default.bold(` ${WEB_APP_URL}/dashboard/projects/${syncResult.projectId}
1057
+ console.log(import_chalk2.default.bold(` ${WEB_APP_URL}/dashboard/projects/${syncResult.projectId}
860
1058
  `));
861
1059
  } else {
862
1060
  syncSpinner.warn("Could not sync to cloud");
863
1061
  }
864
1062
  } else {
865
- console.log(import_chalk.default.dim("\n Tip: Log in to sync results and manage issues in the web dashboard:"));
866
- console.log(import_chalk.default.dim(" rankcli login\n"));
1063
+ showUpsell("free_audit_complete");
867
1064
  }
868
1065
  }
869
1066
  } catch (error) {
@@ -930,40 +1127,37 @@ async function apply(options) {
930
1127
  }
931
1128
  console.log("\n\u{1F680} SEO Autopilot - Apply Fixes");
932
1129
  console.log("\u2550".repeat(50));
1130
+ const loggedIn = isLoggedIn();
1131
+ const userInfo = getUserInfo();
1132
+ const tier = userInfo.planName?.toLowerCase() || "free";
1133
+ const hasAutoFix = loggedIn && hasFeature(tier, "auto_fix");
1134
+ if (!hasAutoFix) {
1135
+ showUpsell("auto_fix_blocked");
1136
+ return;
1137
+ }
933
1138
  console.log("\n\u{1F50D} Detecting framework...");
934
1139
  const frameworkResult = await (0, import_agent_runtime2.detectFramework)({ cwd });
935
1140
  const framework = frameworkResult.success ? frameworkResult.data : { name: "Unknown", metaPattern: "html-head" };
936
1141
  console.log(` Framework: ${framework.name}`);
937
- const isPaidUser = isLoggedIn();
938
1142
  console.log(`
939
1143
  \u{1F50D} Analyzing ${url}...`);
940
- if (isPaidUser) {
941
- console.log(" Running full audit (200+ checks)...");
942
- } else {
943
- console.log(" Running basic analysis (login for full 200+ checks)");
944
- }
1144
+ console.log(" Running full audit (200+ checks)...");
945
1145
  let issues;
946
1146
  let score;
947
1147
  let auditResult = null;
948
1148
  try {
949
- if (isPaidUser) {
950
- auditResult = await (0, import_agent_runtime2.runAuditWithFixes)({
951
- url,
952
- generateFixes: true
953
- });
954
- issues = auditResult.report.issues.map((issue) => ({
955
- severity: issue.severity,
956
- category: issue.category,
957
- code: issue.code,
958
- message: issue.title,
959
- impact: issue.description || void 0
960
- }));
961
- score = auditResult.score;
962
- } else {
963
- const analysis = await (0, import_agent_runtime2.runDirectAnalysis)(url);
964
- issues = analysis.issues;
965
- score = analysis.score;
966
- }
1149
+ auditResult = await (0, import_agent_runtime2.runAuditWithFixes)({
1150
+ url,
1151
+ generateFixes: true
1152
+ });
1153
+ issues = auditResult.report.issues.map((issue) => ({
1154
+ severity: issue.severity,
1155
+ category: issue.category,
1156
+ code: issue.code,
1157
+ message: issue.title,
1158
+ impact: issue.description || void 0
1159
+ }));
1160
+ score = auditResult.score;
967
1161
  } catch (error) {
968
1162
  console.log(`
969
1163
  \u274C Analysis failed: ${error instanceof Error ? error.message : "Unknown error"}
@@ -1054,7 +1248,7 @@ async function apply(options) {
1054
1248
  // src/commands/geo.ts
1055
1249
  var import_agent_runtime3 = require("@rankcli/agent-runtime");
1056
1250
  var import_ora2 = __toESM(require("ora"));
1057
- var import_chalk2 = __toESM(require("chalk"));
1251
+ var import_chalk3 = __toESM(require("chalk"));
1058
1252
  async function fetchResources(url) {
1059
1253
  const headers = {};
1060
1254
  let html = null;
@@ -1126,55 +1320,55 @@ async function geo(options) {
1126
1320
  console.log(JSON.stringify(jsonOutput, null, 2));
1127
1321
  return;
1128
1322
  }
1129
- console.log("\n" + import_chalk2.default.bold("\u2550".repeat(60)));
1130
- console.log(import_chalk2.default.bold.cyan(" GEO ANALYSIS: AI Search Optimization"));
1131
- console.log(import_chalk2.default.bold("\u2550".repeat(60)));
1323
+ console.log("\n" + import_chalk3.default.bold("\u2550".repeat(60)));
1324
+ console.log(import_chalk3.default.bold.cyan(" GEO ANALYSIS: AI Search Optimization"));
1325
+ console.log(import_chalk3.default.bold("\u2550".repeat(60)));
1132
1326
  const geoScore = geoResult.score;
1133
- const scoreColor = geoScore >= 80 ? import_chalk2.default.green : geoScore >= 60 ? import_chalk2.default.yellow : import_chalk2.default.red;
1327
+ const scoreColor = geoScore >= 80 ? import_chalk3.default.green : geoScore >= 60 ? import_chalk3.default.yellow : import_chalk3.default.red;
1134
1328
  console.log(`
1135
- ${import_chalk2.default.bold("GEO Score:")} ${scoreColor.bold(geoScore + "/100")}`);
1136
- console.log("\n" + import_chalk2.default.bold(" AI Crawler Access:"));
1329
+ ${import_chalk3.default.bold("GEO Score:")} ${scoreColor.bold(geoScore + "/100")}`);
1330
+ console.log("\n" + import_chalk3.default.bold(" AI Crawler Access:"));
1137
1331
  const { aiCrawlerAccess } = geoResult;
1138
1332
  if (aiCrawlerAccess.blockedCrawlers.length > 0) {
1139
- console.log(import_chalk2.default.red(` \u274C Blocked: ${aiCrawlerAccess.blockedCrawlers.join(", ")}`));
1333
+ console.log(import_chalk3.default.red(` \u274C Blocked: ${aiCrawlerAccess.blockedCrawlers.join(", ")}`));
1140
1334
  }
1141
1335
  if (aiCrawlerAccess.allowedCrawlers.length > 0) {
1142
1336
  const topAllowed = aiCrawlerAccess.allowedCrawlers.slice(0, 5);
1143
- console.log(import_chalk2.default.green(` \u2705 Allowed: ${topAllowed.join(", ")}${aiCrawlerAccess.allowedCrawlers.length > 5 ? "..." : ""}`));
1337
+ console.log(import_chalk3.default.green(` \u2705 Allowed: ${topAllowed.join(", ")}${aiCrawlerAccess.allowedCrawlers.length > 5 ? "..." : ""}`));
1144
1338
  }
1145
- console.log("\n" + import_chalk2.default.bold(" Rendering:"));
1339
+ console.log("\n" + import_chalk3.default.bold(" Rendering:"));
1146
1340
  if (aiCrawlerAccess.serverSideRendered) {
1147
- console.log(import_chalk2.default.green(" \u2705 Server-side rendered (AI crawlers can see content)"));
1341
+ console.log(import_chalk3.default.green(" \u2705 Server-side rendered (AI crawlers can see content)"));
1148
1342
  } else if (aiCrawlerAccess.jsRenderingRequired) {
1149
- console.log(import_chalk2.default.red(" \u274C JavaScript required (AI crawlers may see blank page)"));
1343
+ console.log(import_chalk3.default.red(" \u274C JavaScript required (AI crawlers may see blank page)"));
1150
1344
  }
1151
- console.log("\n" + import_chalk2.default.bold(" LLM Friendliness:"));
1345
+ console.log("\n" + import_chalk3.default.bold(" LLM Friendliness:"));
1152
1346
  const { llmSignals } = geoResult;
1153
1347
  const bar = (score) => {
1154
1348
  const filled = Math.round(score / 5);
1155
1349
  const empty = 20 - filled;
1156
- const color = score >= 70 ? import_chalk2.default.green : score >= 40 ? import_chalk2.default.yellow : import_chalk2.default.red;
1157
- return color("\u2588".repeat(filled)) + import_chalk2.default.gray("\u2591".repeat(empty));
1350
+ const color = score >= 70 ? import_chalk3.default.green : score >= 40 ? import_chalk3.default.yellow : import_chalk3.default.red;
1351
+ return color("\u2588".repeat(filled)) + import_chalk3.default.gray("\u2591".repeat(empty));
1158
1352
  };
1159
1353
  console.log(` Content Clarity: ${bar(llmSignals.contentClarity)} ${llmSignals.contentClarity}%`);
1160
1354
  console.log(` Fact Density: ${bar(llmSignals.factDensity)} ${llmSignals.factDensity}%`);
1161
1355
  console.log(` Structure: ${bar(llmSignals.structureQuality)} ${llmSignals.structureQuality}%`);
1162
1356
  console.log(` Citations: ${bar(llmSignals.citationQuality)} ${llmSignals.citationQuality}%`);
1163
- console.log("\n" + import_chalk2.default.bold(" Content Structure:"));
1357
+ console.log("\n" + import_chalk3.default.bold(" Content Structure:"));
1164
1358
  const { contentStructure } = geoResult;
1165
- console.log(` ${contentStructure.hasStructuredData ? import_chalk2.default.green("\u2705") : import_chalk2.default.red("\u274C")} JSON-LD Structured Data`);
1166
- console.log(` ${contentStructure.hasFAQSchema ? import_chalk2.default.green("\u2705") : import_chalk2.default.yellow("\u25CB")} FAQ Schema`);
1167
- console.log(` ${contentStructure.hasArticleSchema ? import_chalk2.default.green("\u2705") : import_chalk2.default.yellow("\u25CB")} Article Schema`);
1168
- console.log(` ${contentStructure.hasBreadcrumbs ? import_chalk2.default.green("\u2705") : import_chalk2.default.yellow("\u25CB")} Breadcrumbs`);
1169
- console.log(` Heading Hierarchy: ${contentStructure.headingHierarchy === "good" ? import_chalk2.default.green("Good") : contentStructure.headingHierarchy === "needs-work" ? import_chalk2.default.yellow("Needs Work") : import_chalk2.default.red("Poor")}`);
1170
- console.log("\n" + import_chalk2.default.bold(" Citation Readiness:"));
1359
+ console.log(` ${contentStructure.hasStructuredData ? import_chalk3.default.green("\u2705") : import_chalk3.default.red("\u274C")} JSON-LD Structured Data`);
1360
+ console.log(` ${contentStructure.hasFAQSchema ? import_chalk3.default.green("\u2705") : import_chalk3.default.yellow("\u25CB")} FAQ Schema`);
1361
+ console.log(` ${contentStructure.hasArticleSchema ? import_chalk3.default.green("\u2705") : import_chalk3.default.yellow("\u25CB")} Article Schema`);
1362
+ console.log(` ${contentStructure.hasBreadcrumbs ? import_chalk3.default.green("\u2705") : import_chalk3.default.yellow("\u25CB")} Breadcrumbs`);
1363
+ console.log(` Heading Hierarchy: ${contentStructure.headingHierarchy === "good" ? import_chalk3.default.green("Good") : contentStructure.headingHierarchy === "needs-work" ? import_chalk3.default.yellow("Needs Work") : import_chalk3.default.red("Poor")}`);
1364
+ console.log("\n" + import_chalk3.default.bold(" Citation Readiness:"));
1171
1365
  const { citationReadiness } = geoResult;
1172
1366
  if (citationReadiness.trustSignals.length > 0) {
1173
- console.log(import_chalk2.default.green(` \u2705 ${citationReadiness.trustSignals.join(", ")}`));
1367
+ console.log(import_chalk3.default.green(` \u2705 ${citationReadiness.trustSignals.join(", ")}`));
1174
1368
  } else {
1175
- console.log(import_chalk2.default.yellow(" \u25CB No trust signals detected"));
1369
+ console.log(import_chalk3.default.yellow(" \u25CB No trust signals detected"));
1176
1370
  }
1177
- console.log("\n" + import_chalk2.default.bold(" Other Scores:"));
1371
+ console.log("\n" + import_chalk3.default.bold(" Other Scores:"));
1178
1372
  console.log(` Core Web Vitals: ${bar(cwvResult.overallScore)} ${cwvResult.overallScore}%`);
1179
1373
  console.log(` Security Headers: ${securityResult.grade} (${securityResult.score}%)`);
1180
1374
  console.log(` Structured Data: ${bar(schemaResult.score)} ${schemaResult.score}%`);
@@ -1188,18 +1382,18 @@ async function geo(options) {
1188
1382
  ];
1189
1383
  const criticalIssues = allIssues.filter((i) => i.severity === "critical" || i.severity === "error");
1190
1384
  if (criticalIssues.length > 0) {
1191
- console.log("\n" + import_chalk2.default.bold.red(" Critical Issues:"));
1385
+ console.log("\n" + import_chalk3.default.bold.red(" Critical Issues:"));
1192
1386
  criticalIssues.slice(0, 5).forEach((issue, i) => {
1193
- console.log(import_chalk2.default.red(` ${i + 1}. ${issue.title}`));
1387
+ console.log(import_chalk3.default.red(` ${i + 1}. ${issue.title}`));
1194
1388
  });
1195
1389
  }
1196
1390
  if (geoResult.recommendations.length > 0) {
1197
- console.log("\n" + import_chalk2.default.bold(" Recommendations:"));
1391
+ console.log("\n" + import_chalk3.default.bold(" Recommendations:"));
1198
1392
  geoResult.recommendations.slice(0, 5).forEach((rec, i) => {
1199
1393
  console.log(` ${i + 1}. ${rec}`);
1200
1394
  });
1201
1395
  }
1202
- console.log("\n" + import_chalk2.default.bold("\u2550".repeat(60)));
1396
+ console.log("\n" + import_chalk3.default.bold("\u2550".repeat(60)));
1203
1397
  console.log("");
1204
1398
  } catch (error) {
1205
1399
  spinner.fail(`Analysis failed: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -1849,15 +2043,15 @@ function showGSCInstructions() {
1849
2043
  }
1850
2044
 
1851
2045
  // src/commands/auth.ts
1852
- var import_chalk3 = __toESM(require("chalk"));
2046
+ var import_chalk4 = __toESM(require("chalk"));
1853
2047
  var import_ora3 = __toESM(require("ora"));
1854
2048
  var import_inquirer = __toESM(require("inquirer"));
1855
2049
  var import_open = __toESM(require("open"));
1856
2050
  async function login(options) {
1857
2051
  if (isLoggedIn()) {
1858
2052
  const { email } = getUserInfo();
1859
- console.log(import_chalk3.default.yellow(`Already logged in as ${email}`));
1860
- console.log(import_chalk3.default.dim('Run "rankcli logout" to sign out first.'));
2053
+ console.log(import_chalk4.default.yellow(`Already logged in as ${email}`));
2054
+ console.log(import_chalk4.default.dim('Run "rankcli logout" to sign out first.'));
1861
2055
  return;
1862
2056
  }
1863
2057
  const spinner = (0, import_ora3.default)();
@@ -1865,14 +2059,14 @@ async function login(options) {
1865
2059
  if (options.token) {
1866
2060
  const apiKey = options.token;
1867
2061
  if (!apiKey.startsWith("rankcli_")) {
1868
- console.log(import_chalk3.default.red('Invalid API key format. Keys should start with "rankcli_"'));
2062
+ console.log(import_chalk4.default.red('Invalid API key format. Keys should start with "rankcli_"'));
1869
2063
  return;
1870
2064
  }
1871
2065
  spinner.start("Validating API key...");
1872
2066
  const result = await validateApiKey(apiKey);
1873
2067
  if (!result.valid) {
1874
2068
  spinner.fail("Invalid API key");
1875
- console.log(import_chalk3.default.red(`
2069
+ console.log(import_chalk4.default.red(`
1876
2070
  ${result.error}`));
1877
2071
  return;
1878
2072
  }
@@ -1885,14 +2079,14 @@ ${result.error}`));
1885
2079
  });
1886
2080
  }
1887
2081
  spinner.succeed(`Authenticated via API key`);
1888
- console.log(import_chalk3.default.dim(`
2082
+ console.log(import_chalk4.default.dim(`
1889
2083
  Email: ${result.user?.email}`));
1890
- console.log(import_chalk3.default.dim(`Plan: ${result.subscription?.planName || "Free"}`));
2084
+ console.log(import_chalk4.default.dim(`Plan: ${result.subscription?.planName || "Free"}`));
1891
2085
  return;
1892
2086
  }
1893
2087
  if (options.browser) {
1894
- console.log(import_chalk3.default.cyan("\nOpening browser for login..."));
1895
- console.log(import_chalk3.default.dim("Complete the login in your browser, then return here.\n"));
2088
+ console.log(import_chalk4.default.cyan("\nOpening browser for login..."));
2089
+ console.log(import_chalk4.default.dim("Complete the login in your browser, then return here.\n"));
1896
2090
  const loginUrl = `${WEB_APP_URL}/login?cli=true`;
1897
2091
  await (0, import_open.default)(loginUrl);
1898
2092
  const { token } = await import_inquirer.default.prompt([
@@ -1908,7 +2102,7 @@ Email: ${result.user?.email}`));
1908
2102
  const { data, error } = await supabase.auth.getUser(token);
1909
2103
  if (error || !data.user) {
1910
2104
  spinner.fail("Invalid token");
1911
- console.log(import_chalk3.default.red("The token is invalid or expired. Please try again."));
2105
+ console.log(import_chalk4.default.red("The token is invalid or expired. Please try again."));
1912
2106
  return;
1913
2107
  }
1914
2108
  const { data: sessionData, error: sessionError } = await supabase.auth.setSession({
@@ -1922,7 +2116,7 @@ Email: ${result.user?.email}`));
1922
2116
  }
1923
2117
  saveSession(sessionData.session);
1924
2118
  await fetchAndSaveSubscription();
1925
- spinner.succeed(`Logged in as ${import_chalk3.default.green(data.user.email)}`);
2119
+ spinner.succeed(`Logged in as ${import_chalk4.default.green(data.user.email)}`);
1926
2120
  } else {
1927
2121
  let email = options.email;
1928
2122
  if (!email) {
@@ -1955,11 +2149,11 @@ Email: ${result.user?.email}`));
1955
2149
  });
1956
2150
  if (error) {
1957
2151
  spinner.fail("Login failed");
1958
- console.log(import_chalk3.default.red(`
2152
+ console.log(import_chalk4.default.red(`
1959
2153
  ${error.message}`));
1960
2154
  if (error.message.includes("Invalid login")) {
1961
- console.log(import_chalk3.default.dim("\nDon't have an account? Sign up at:"));
1962
- console.log(import_chalk3.default.cyan(` ${WEB_APP_URL}/signup`));
2155
+ console.log(import_chalk4.default.dim("\nDon't have an account? Sign up at:"));
2156
+ console.log(import_chalk4.default.cyan(` ${WEB_APP_URL}/signup`));
1963
2157
  }
1964
2158
  return;
1965
2159
  }
@@ -1969,19 +2163,19 @@ ${error.message}`));
1969
2163
  }
1970
2164
  saveSession(data.session);
1971
2165
  await fetchAndSaveSubscription();
1972
- spinner.succeed(`Logged in as ${import_chalk3.default.green(data.user?.email)}`);
2166
+ spinner.succeed(`Logged in as ${import_chalk4.default.green(data.user?.email)}`);
1973
2167
  const { planName, sitesLimit } = getUserInfo();
1974
- console.log(import_chalk3.default.dim(`
2168
+ console.log(import_chalk4.default.dim(`
1975
2169
  Plan: ${planName} (${sitesLimit} site${sitesLimit !== 1 ? "s" : ""})`));
1976
2170
  }
1977
2171
  } catch (err) {
1978
2172
  spinner.fail("Login failed");
1979
- console.error(import_chalk3.default.red(err instanceof Error ? err.message : "Unknown error"));
2173
+ console.error(import_chalk4.default.red(err instanceof Error ? err.message : "Unknown error"));
1980
2174
  }
1981
2175
  }
1982
2176
  async function logout() {
1983
2177
  if (!isLoggedIn()) {
1984
- console.log(import_chalk3.default.yellow("Not logged in"));
2178
+ console.log(import_chalk4.default.yellow("Not logged in"));
1985
2179
  return;
1986
2180
  }
1987
2181
  const { email } = getUserInfo();
@@ -1990,17 +2184,17 @@ async function logout() {
1990
2184
  const supabase = getSupabaseClient();
1991
2185
  await supabase.auth.signOut();
1992
2186
  clearCredentials();
1993
- spinner.succeed(`Logged out from ${import_chalk3.default.dim(email)}`);
2187
+ spinner.succeed(`Logged out from ${import_chalk4.default.dim(email)}`);
1994
2188
  } catch (err) {
1995
2189
  spinner.fail("Logout failed");
1996
- console.error(import_chalk3.default.red(err instanceof Error ? err.message : "Unknown error"));
2190
+ console.error(import_chalk4.default.red(err instanceof Error ? err.message : "Unknown error"));
1997
2191
  }
1998
2192
  }
1999
2193
  async function whoami() {
2000
2194
  if (!isLoggedIn()) {
2001
- console.log(import_chalk3.default.yellow("Not logged in"));
2002
- console.log(import_chalk3.default.dim('\nRun "rankcli login" to sign in.'));
2003
- console.log(import_chalk3.default.dim(`Or sign up at: ${WEB_APP_URL}/signup`));
2195
+ console.log(import_chalk4.default.yellow("Not logged in"));
2196
+ console.log(import_chalk4.default.dim('\nRun "rankcli login" to sign in.'));
2197
+ console.log(import_chalk4.default.dim(`Or sign up at: ${WEB_APP_URL}/signup`));
2004
2198
  return;
2005
2199
  }
2006
2200
  const spinner = (0, import_ora3.default)("Checking account...").start();
@@ -2023,18 +2217,18 @@ async function whoami() {
2023
2217
  const { email, planName, sitesLimit } = getUserInfo();
2024
2218
  const authMethod = isApiKeyAuth() ? "API Key" : "Session";
2025
2219
  spinner.stop();
2026
- console.log(import_chalk3.default.bold("\nRankCLI Account"));
2220
+ console.log(import_chalk4.default.bold("\nRankCLI Account"));
2027
2221
  console.log("\u2500".repeat(40));
2028
- console.log(` Email: ${import_chalk3.default.cyan(email || "N/A")}`);
2029
- console.log(` Plan: ${import_chalk3.default.green(planName)}`);
2222
+ console.log(` Email: ${import_chalk4.default.cyan(email || "N/A")}`);
2223
+ console.log(` Plan: ${import_chalk4.default.green(planName)}`);
2030
2224
  console.log(` Sites: ${sitesLimit} allowed`);
2031
- console.log(` Auth: ${import_chalk3.default.dim(authMethod)}`);
2225
+ console.log(` Auth: ${import_chalk4.default.dim(authMethod)}`);
2032
2226
  console.log("\u2500".repeat(40));
2033
- console.log(import_chalk3.default.dim(`
2227
+ console.log(import_chalk4.default.dim(`
2034
2228
  Manage at: ${WEB_APP_URL}/account`));
2035
2229
  } catch (err) {
2036
2230
  spinner.fail("Failed to fetch account info");
2037
- console.error(import_chalk3.default.red(err instanceof Error ? err.message : "Unknown error"));
2231
+ console.error(import_chalk4.default.red(err instanceof Error ? err.message : "Unknown error"));
2038
2232
  }
2039
2233
  }
2040
2234
  async function fetchAndSaveSubscription() {
@@ -2066,7 +2260,7 @@ async function fetchAndSaveSubscription() {
2066
2260
  }
2067
2261
 
2068
2262
  // src/version.ts
2069
- var VERSION = "0.0.17";
2263
+ var VERSION = "0.0.19";
2070
2264
 
2071
2265
  // src/index.ts
2072
2266
  var program = new import_commander.Command();