gsc-cli 2.2.0 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/AUTH.md +48 -18
- data/README.md +186 -141
- data/bin/gsc +129 -19
- data/dist/gsc +129 -19
- data/lib/gsc/cli/audit.rb +49 -1
- data/lib/gsc/cli/keywords.rb +7 -7
- data/lib/gsc/cli_advanced.rb +49 -1
- data/lib/gsc/page_speed.rb +22 -8
- data/lib/gsc/version.rb +1 -1
- metadata +1 -1
data/lib/gsc/cli/keywords.rb
CHANGED
|
@@ -384,7 +384,7 @@ module GSC
|
|
|
384
384
|
key = input unless input.nil? || input.empty?
|
|
385
385
|
key = existing if key.empty?
|
|
386
386
|
else
|
|
387
|
-
puts "Get your Keywords Everywhere API key at: #{Color.c('https://keywordseverywhere.com
|
|
387
|
+
puts "Get your Keywords Everywhere API key at: #{Color.c('https://keywordseverywhere.com/?fpr=us25sg', Color::UNDERLINE)}"
|
|
388
388
|
print "Paste your Keywords Everywhere API Key: "
|
|
389
389
|
key = $stdin.gets&.strip
|
|
390
390
|
end
|
|
@@ -415,7 +415,7 @@ module GSC
|
|
|
415
415
|
puts " 2. #{Color.c('100% Free Live Google Trends (Breakout & Related terms):', Color::BLUE, Color::BOLD)}"
|
|
416
416
|
puts " • Run: #{Color.c('gsc trends "seo audit"', Color::BLUE, Color::BOLD)}"
|
|
417
417
|
puts " 3. #{Color.c('Top Up Credits for Automated Background CLI Lookups:', Color::GRAY, Color::BOLD)}"
|
|
418
|
-
puts " • Purchase credits at: https://keywordseverywhere.com/credit-packages.html"
|
|
418
|
+
puts " • Purchase credits at: https://keywordseverywhere.com/credit-packages.html?fpr=us25sg"
|
|
419
419
|
puts " • Your key is already saved; it will work immediately upon top-up.\n"
|
|
420
420
|
else
|
|
421
421
|
puts "\nTry searching keywords:"
|
|
@@ -440,7 +440,7 @@ module GSC
|
|
|
440
440
|
puts "\n" + Color.c("ℹ️ You have 0 paid REST API credits remaining.", Color::YELLOW)
|
|
441
441
|
puts "• Free Web Alternative: Check volume on keywordseverywhere.com, click 'Copy', then run: #{Color.c('gsc import clip', Color::CYAN)}"
|
|
442
442
|
puts "• Free Live Trends: #{Color.c('gsc trends "seo audit"', Color::BLUE)}"
|
|
443
|
-
puts "• Top up credits at: https://keywordseverywhere.com/credit-packages.html"
|
|
443
|
+
puts "• Top up credits at: https://keywordseverywhere.com/credit-packages.html?fpr=us25sg"
|
|
444
444
|
end
|
|
445
445
|
else
|
|
446
446
|
if acct[:error].to_s =~ /401|Unauthorized/i
|
|
@@ -477,7 +477,7 @@ module GSC
|
|
|
477
477
|
else
|
|
478
478
|
puts Color.c("\n❌ Keywords Everywhere API key not configured!\n", Color::RED, Color::BOLD)
|
|
479
479
|
puts "#{Color::BOLD}RECOMMENDATIONS:#{Color::RESET}"
|
|
480
|
-
puts " 1. Connect a key: #{Color.c('gsc connect ke', Color::CYAN, Color::BOLD)} (Get key at https://keywordseverywhere.com
|
|
480
|
+
puts " 1. Connect a key: #{Color.c('gsc connect ke', Color::CYAN, Color::BOLD)} (Get key at https://keywordseverywhere.com/?fpr=us25sg)"
|
|
481
481
|
puts " 2. Use 100% free Google Trends (No key needed!): #{Color.c("gsc trends \"#{target}\"", Color::BLUE, Color::BOLD)}"
|
|
482
482
|
puts " 3. Use 500 free daily web lookups: Copy web table -> #{Color.c('gsc import clip', Color::GREEN, Color::BOLD)}\n"
|
|
483
483
|
end
|
|
@@ -524,7 +524,7 @@ module GSC
|
|
|
524
524
|
{ action: 'import_clipboard', cmd: 'gsc import clip', desc: 'Use 500 free daily web lookups on keywordseverywhere.com and import via clipboard' },
|
|
525
525
|
{ action: 'google_trends', cmd: "gsc trends \"#{target}\"", desc: '100% Free real-time search interest & rising breakout queries' },
|
|
526
526
|
{ action: 'gsc_analytics', cmd: 'gsc top-queries', desc: 'Real impressions & clicks from your active Google Search Console domain' },
|
|
527
|
-
{ action: 'topup', url: 'https://keywordseverywhere.com/credit-packages.html', desc: 'Purchase API credits to enable automated background CLI lookups' }
|
|
527
|
+
{ action: 'topup', url: 'https://keywordseverywhere.com/credit-packages.html?fpr=us25sg', desc: 'Purchase API credits to enable automated background CLI lookups' }
|
|
528
528
|
]
|
|
529
529
|
})
|
|
530
530
|
return
|
|
@@ -547,13 +547,13 @@ module GSC
|
|
|
547
547
|
puts " • High-impression queries and Page 2 striking-distance keywords:"
|
|
548
548
|
puts " #{Color.c('gsc top-queries', Color::MAGENTA, Color::BOLD)} or #{Color.c('gsc opportunities', Color::MAGENTA, Color::BOLD)}\n"
|
|
549
549
|
puts " 4. #{Color.c('Top Up API Credits for Automated Background CLI Lookups', Color::GRAY, Color::BOLD)}"
|
|
550
|
-
puts " • Purchase credits at: #{Color.c('https://keywordseverywhere.com/credit-packages.html', Color::UNDERLINE)}"
|
|
550
|
+
puts " • Purchase credits at: #{Color.c('https://keywordseverywhere.com/credit-packages.html?fpr=us25sg', Color::UNDERLINE)}"
|
|
551
551
|
puts " • Your key is already saved; it will work immediately once credited.\n"
|
|
552
552
|
elsif err_str =~ /401|Unauthorized/i
|
|
553
553
|
puts Color.c("\n🔑 Invalid or Expired Keywords Everywhere API Key", Color::RED, Color::BOLD)
|
|
554
554
|
puts "Your API key was rejected by api.keywordseverywhere.com (HTTP 401).\n\n"
|
|
555
555
|
puts "#{Color::BOLD}RECOMMENDATIONS:#{Color::RESET}"
|
|
556
|
-
puts " • Check or generate your API key at: #{Color.c('https://keywordseverywhere.com/first-install-addon.html', Color::UNDERLINE)}"
|
|
556
|
+
puts " • Check or generate your API key at: #{Color.c('https://keywordseverywhere.com/first-install-addon.html?fpr=us25sg', Color::UNDERLINE)}"
|
|
557
557
|
puts " • Reconnect key: #{Color.c('gsc connect ke <KEY>', Color::CYAN, Color::BOLD)}"
|
|
558
558
|
puts " • Or use free Google Trends without any key: #{Color.c("gsc trends \"#{target}\"", Color::BLUE)}"
|
|
559
559
|
elsif err_str =~ /429|Rate Limit/i
|
data/lib/gsc/cli_advanced.rb
CHANGED
|
@@ -170,13 +170,61 @@ module GSC
|
|
|
170
170
|
puts "╚══════════════════════════════════════════════════════════════╝"
|
|
171
171
|
|
|
172
172
|
m = data[:metrics] || {}
|
|
173
|
-
puts "\n#{Color::BOLD}
|
|
173
|
+
puts "\n#{Color::BOLD}🔬 LIGHTHOUSE LAB METRICS (Simulated #{strategy.upcase}):#{Color::RESET}"
|
|
174
174
|
puts " • LCP (Largest Contentful Paint) : #{Color.c(m[:lcp] || 'N/A', Color::BOLD)}"
|
|
175
175
|
puts " • FCP (First Contentful Paint) : #{Color.c(m[:fcp] || 'N/A', Color::BOLD)}"
|
|
176
176
|
puts " • CLS (Cumulative Layout Shift) : #{Color.c(m[:cls] || 'N/A', Color::BOLD)}"
|
|
177
177
|
puts " • TBT (Total Blocking Time) : #{Color.c(m[:tbt] || 'N/A', Color::BOLD)}"
|
|
178
178
|
puts " • Speed Index : #{Color.c(m[:speed_index] || 'N/A', Color::BOLD)}"
|
|
179
179
|
|
|
180
|
+
fd = data[:field_data] || {}
|
|
181
|
+
if fd.any?
|
|
182
|
+
source_label = data[:field_source] == :origin ? "Domain-Wide Origin RUM" : "URL-Level RUM"
|
|
183
|
+
overall = data[:overall_category] || "UNKNOWN"
|
|
184
|
+
overall_col = overall == 'FAST' ? Color::GREEN : (overall == 'AVERAGE' ? Color::YELLOW : Color::RED)
|
|
185
|
+
puts "\n#{Color::BOLD}🌐 CrUX FIELD DATA (28-Day Real User Monitoring - #{source_label}):#{Color::RESET}"
|
|
186
|
+
puts " • Core Web Vitals Status : #{Color.c(overall, overall_col, Color::BOLD)}"
|
|
187
|
+
|
|
188
|
+
if fd['LARGEST_CONTENTFUL_PAINT_MS']
|
|
189
|
+
val = "#{(fd['LARGEST_CONTENTFUL_PAINT_MS'][:percentile].to_f / 1000).round(2)} s"
|
|
190
|
+
cat = fd['LARGEST_CONTENTFUL_PAINT_MS'][:category]
|
|
191
|
+
col = cat == 'FAST' ? Color::GREEN : (cat == 'AVERAGE' ? Color::YELLOW : Color::RED)
|
|
192
|
+
puts " • LCP (75th Percentile) : #{Color.c(val, Color::BOLD)} [#{Color.c(cat, col)}]"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
if fd['INTERACTION_TO_NEXT_PAINT']
|
|
196
|
+
val = "#{fd['INTERACTION_TO_NEXT_PAINT'][:percentile]} ms"
|
|
197
|
+
cat = fd['INTERACTION_TO_NEXT_PAINT'][:category]
|
|
198
|
+
col = cat == 'FAST' ? Color::GREEN : (cat == 'AVERAGE' ? Color::YELLOW : Color::RED)
|
|
199
|
+
puts " • INP (75th Percentile) : #{Color.c(val, Color::BOLD)} [#{Color.c(cat, col)}]"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
if fd['CUMULATIVE_LAYOUT_SHIFT_SCORE']
|
|
203
|
+
val = (fd['CUMULATIVE_LAYOUT_SHIFT_SCORE'][:percentile].to_f / 100).round(3).to_s
|
|
204
|
+
cat = fd['CUMULATIVE_LAYOUT_SHIFT_SCORE'][:category]
|
|
205
|
+
col = cat == 'FAST' ? Color::GREEN : (cat == 'AVERAGE' ? Color::YELLOW : Color::RED)
|
|
206
|
+
puts " • CLS (75th Percentile) : #{Color.c(val, Color::BOLD)} [#{Color.c(cat, col)}]"
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
if fd['FIRST_CONTENTFUL_PAINT_MS']
|
|
210
|
+
val = "#{(fd['FIRST_CONTENTFUL_PAINT_MS'][:percentile].to_f / 1000).round(2)} s"
|
|
211
|
+
cat = fd['FIRST_CONTENTFUL_PAINT_MS'][:category]
|
|
212
|
+
col = cat == 'FAST' ? Color::GREEN : (cat == 'AVERAGE' ? Color::YELLOW : Color::RED)
|
|
213
|
+
puts " • FCP (75th Percentile) : #{Color.c(val, Color::BOLD)} [#{Color.c(cat, col)}]"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
if fd['EXPERIMENTAL_TIME_TO_FIRST_BYTE']
|
|
217
|
+
val = "#{fd['EXPERIMENTAL_TIME_TO_FIRST_BYTE'][:percentile]} ms"
|
|
218
|
+
cat = fd['EXPERIMENTAL_TIME_TO_FIRST_BYTE'][:category]
|
|
219
|
+
col = cat == 'FAST' ? Color::GREEN : (cat == 'AVERAGE' ? Color::YELLOW : Color::RED)
|
|
220
|
+
puts " • TTFB (75th Percentile) : #{Color.c(val, Color::BOLD)} [#{Color.c(cat, col)}]"
|
|
221
|
+
end
|
|
222
|
+
else
|
|
223
|
+
puts "\n#{Color::BOLD}🌐 CrUX FIELD DATA (28-Day Real User Monitoring):#{Color::RESET}"
|
|
224
|
+
puts " #{Color.c('ℹ️ Insufficient real-user Chrome visits over the rolling 28-day window for Google CrUX telemetry.', Color::GRAY)}"
|
|
225
|
+
puts " #{Color.c(' (Google requires a minimum traffic threshold before publishing real-user CrUX field data).', Color::GRAY)}"
|
|
226
|
+
end
|
|
227
|
+
|
|
180
228
|
opps = data[:opportunities] || []
|
|
181
229
|
unless opps.empty?
|
|
182
230
|
puts "\n#{Color::BOLD}💡 TOP SPEED OPPORTUNITIES:#{Color::RESET}"
|
data/lib/gsc/page_speed.rb
CHANGED
|
@@ -28,9 +28,9 @@ module GSC
|
|
|
28
28
|
|
|
29
29
|
uri = URI("#{API_URL}?#{query_params.join('&')}")
|
|
30
30
|
req = Net::HTTP::Get.new(uri)
|
|
31
|
-
req['User-Agent'] =
|
|
31
|
+
req['User-Agent'] = "gsc-cli/#{GSC::VERSION}"
|
|
32
32
|
|
|
33
|
-
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, open_timeout:
|
|
33
|
+
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, open_timeout: 15, read_timeout: 60) do |http|
|
|
34
34
|
http.request(req)
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -64,16 +64,28 @@ module GSC
|
|
|
64
64
|
tbt = audits.dig('total-blocking-time', 'displayValue')
|
|
65
65
|
si = audits.dig('speed-index', 'displayValue')
|
|
66
66
|
|
|
67
|
-
# CrUX Field Data (if available)
|
|
67
|
+
# CrUX Field Data (URL or Origin fallback if available)
|
|
68
68
|
crux_metrics = {}
|
|
69
|
+
crux_source = nil
|
|
69
70
|
crux = data.dig('loadingExperience', 'metrics') || {}
|
|
70
|
-
crux
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
if crux && !crux.empty?
|
|
72
|
+
crux_source = :url
|
|
73
|
+
else
|
|
74
|
+
crux = data.dig('originLoadingExperience', 'metrics') || {}
|
|
75
|
+
crux_source = :origin unless crux.empty?
|
|
75
76
|
end
|
|
76
77
|
|
|
78
|
+
if crux && !crux.empty?
|
|
79
|
+
crux.each do |k, v|
|
|
80
|
+
crux_metrics[k] = {
|
|
81
|
+
percentile: v['percentile'],
|
|
82
|
+
category: v['category']
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
overall_category = data.dig('loadingExperience', 'overall_category') || data.dig('originLoadingExperience', 'overall_category')
|
|
88
|
+
|
|
77
89
|
# Opportunities
|
|
78
90
|
opportunities = []
|
|
79
91
|
audits.each do |k, v|
|
|
@@ -103,6 +115,8 @@ module GSC
|
|
|
103
115
|
speed_index: si
|
|
104
116
|
},
|
|
105
117
|
field_data: crux_metrics,
|
|
118
|
+
field_source: crux_source,
|
|
119
|
+
overall_category: overall_category,
|
|
106
120
|
opportunities: opportunities.first(5)
|
|
107
121
|
}
|
|
108
122
|
end
|
data/lib/gsc/version.rb
CHANGED