@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.
- package/README.md +15 -10
- package/dist/index.js +325 -131
- package/dist/index.mjs +325 -132
- 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
|
|
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(
|
|
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(
|
|
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(
|
|
762
|
+
console.log(import_chalk2.default.yellow(`
|
|
595
763
|
\u26A0\uFE0F Project limit reached (${count}/${limit})`));
|
|
596
|
-
console.log(
|
|
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(
|
|
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:
|
|
629
|
-
// Approximate
|
|
797
|
+
checks_run: checksRun,
|
|
630
798
|
health_scores: healthScores,
|
|
631
|
-
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
690
|
-
console.log(
|
|
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(
|
|
775
|
-
console.log(
|
|
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" +
|
|
793
|
-
console.log(
|
|
794
|
-
console.log(
|
|
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 ?
|
|
980
|
+
const scoreColor = score >= 80 ? import_chalk2.default.green : score >= 60 ? import_chalk2.default.yellow : import_chalk2.default.red;
|
|
797
981
|
console.log(`
|
|
798
|
-
${
|
|
799
|
-
console.log(` ${
|
|
800
|
-
console.log(` ${
|
|
801
|
-
console.log(` ${
|
|
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(` ${
|
|
987
|
+
console.log(` ${import_chalk2.default.bold("Framework:")} ${import_chalk2.default.cyan(frameworkName)}`);
|
|
804
988
|
}
|
|
805
989
|
if (primaryReport.healthScore) {
|
|
806
|
-
console.log("\n" +
|
|
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 ?
|
|
812
|
-
return color("\u2588".repeat(filled)) +
|
|
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" +
|
|
824
|
-
console.log(` ${
|
|
825
|
-
console.log(` ${
|
|
826
|
-
console.log(` ${
|
|
827
|
-
console.log("\n" +
|
|
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" ?
|
|
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(
|
|
1017
|
+
console.log(import_chalk2.default.dim(` Affects ${issue.affectedUrls.length} pages`));
|
|
834
1018
|
}
|
|
835
1019
|
});
|
|
836
1020
|
if (aiAnalysis) {
|
|
837
|
-
console.log("\n" +
|
|
1021
|
+
console.log("\n" + import_chalk2.default.bold(" AI Analysis:"));
|
|
838
1022
|
console.log(` Content Score: ${aiAnalysis.contentScore}/100`);
|
|
839
|
-
console.log("\n " +
|
|
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 " +
|
|
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(
|
|
1031
|
+
console.log(import_chalk2.default.dim(` ${fix.rationale}`));
|
|
848
1032
|
});
|
|
849
1033
|
}
|
|
850
1034
|
}
|
|
851
|
-
console.log("\n" +
|
|
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(
|
|
1055
|
+
console.log(import_chalk2.default.cyan(`
|
|
858
1056
|
View and manage issues in the web dashboard:`));
|
|
859
|
-
console.log(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
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
|
|
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" +
|
|
1130
|
-
console.log(
|
|
1131
|
-
console.log(
|
|
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 ?
|
|
1327
|
+
const scoreColor = geoScore >= 80 ? import_chalk3.default.green : geoScore >= 60 ? import_chalk3.default.yellow : import_chalk3.default.red;
|
|
1134
1328
|
console.log(`
|
|
1135
|
-
${
|
|
1136
|
-
console.log("\n" +
|
|
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(
|
|
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(
|
|
1337
|
+
console.log(import_chalk3.default.green(` \u2705 Allowed: ${topAllowed.join(", ")}${aiCrawlerAccess.allowedCrawlers.length > 5 ? "..." : ""}`));
|
|
1144
1338
|
}
|
|
1145
|
-
console.log("\n" +
|
|
1339
|
+
console.log("\n" + import_chalk3.default.bold(" Rendering:"));
|
|
1146
1340
|
if (aiCrawlerAccess.serverSideRendered) {
|
|
1147
|
-
console.log(
|
|
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(
|
|
1343
|
+
console.log(import_chalk3.default.red(" \u274C JavaScript required (AI crawlers may see blank page)"));
|
|
1150
1344
|
}
|
|
1151
|
-
console.log("\n" +
|
|
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 ?
|
|
1157
|
-
return color("\u2588".repeat(filled)) +
|
|
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" +
|
|
1357
|
+
console.log("\n" + import_chalk3.default.bold(" Content Structure:"));
|
|
1164
1358
|
const { contentStructure } = geoResult;
|
|
1165
|
-
console.log(` ${contentStructure.hasStructuredData ?
|
|
1166
|
-
console.log(` ${contentStructure.hasFAQSchema ?
|
|
1167
|
-
console.log(` ${contentStructure.hasArticleSchema ?
|
|
1168
|
-
console.log(` ${contentStructure.hasBreadcrumbs ?
|
|
1169
|
-
console.log(` Heading Hierarchy: ${contentStructure.headingHierarchy === "good" ?
|
|
1170
|
-
console.log("\n" +
|
|
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(
|
|
1367
|
+
console.log(import_chalk3.default.green(` \u2705 ${citationReadiness.trustSignals.join(", ")}`));
|
|
1174
1368
|
} else {
|
|
1175
|
-
console.log(
|
|
1369
|
+
console.log(import_chalk3.default.yellow(" \u25CB No trust signals detected"));
|
|
1176
1370
|
}
|
|
1177
|
-
console.log("\n" +
|
|
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" +
|
|
1385
|
+
console.log("\n" + import_chalk3.default.bold.red(" Critical Issues:"));
|
|
1192
1386
|
criticalIssues.slice(0, 5).forEach((issue, i) => {
|
|
1193
|
-
console.log(
|
|
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" +
|
|
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" +
|
|
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
|
|
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(
|
|
1860
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
2082
|
+
console.log(import_chalk4.default.dim(`
|
|
1889
2083
|
Email: ${result.user?.email}`));
|
|
1890
|
-
console.log(
|
|
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(
|
|
1895
|
-
console.log(
|
|
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(
|
|
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 ${
|
|
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(
|
|
2152
|
+
console.log(import_chalk4.default.red(`
|
|
1959
2153
|
${error.message}`));
|
|
1960
2154
|
if (error.message.includes("Invalid login")) {
|
|
1961
|
-
console.log(
|
|
1962
|
-
console.log(
|
|
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 ${
|
|
2166
|
+
spinner.succeed(`Logged in as ${import_chalk4.default.green(data.user?.email)}`);
|
|
1973
2167
|
const { planName, sitesLimit } = getUserInfo();
|
|
1974
|
-
console.log(
|
|
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(
|
|
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(
|
|
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 ${
|
|
2187
|
+
spinner.succeed(`Logged out from ${import_chalk4.default.dim(email)}`);
|
|
1994
2188
|
} catch (err) {
|
|
1995
2189
|
spinner.fail("Logout failed");
|
|
1996
|
-
console.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(
|
|
2002
|
-
console.log(
|
|
2003
|
-
console.log(
|
|
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(
|
|
2220
|
+
console.log(import_chalk4.default.bold("\nRankCLI Account"));
|
|
2027
2221
|
console.log("\u2500".repeat(40));
|
|
2028
|
-
console.log(` Email: ${
|
|
2029
|
-
console.log(` Plan: ${
|
|
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: ${
|
|
2225
|
+
console.log(` Auth: ${import_chalk4.default.dim(authMethod)}`);
|
|
2032
2226
|
console.log("\u2500".repeat(40));
|
|
2033
|
-
console.log(
|
|
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(
|
|
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.
|
|
2263
|
+
var VERSION = "0.0.19";
|
|
2070
2264
|
|
|
2071
2265
|
// src/index.ts
|
|
2072
2266
|
var program = new import_commander.Command();
|