@gscdump/cli 0.40.2 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@gscdump/cli?color=yellow)](https://npm.chart.dev/@gscdump/cli)
5
5
  [![license](https://img.shields.io/github/license/harlan-zw/gscdump?color=yellow)](https://github.com/harlan-zw/gscdump/blob/main/LICENSE)
6
6
 
7
- > CLI for Google Search Console — sync to a local DuckDB/Parquet store, run typed queries, execute 21 SEO analyzers, and serve an MCP endpoint for AI assistants.
7
+ > CLI for Google Search Console — sync to a local DuckDB/Parquet store, run typed queries, execute 29 SEO analyzers, and serve an MCP endpoint for AI assistants.
8
8
 
9
9
  ## Install
10
10
 
@@ -7,7 +7,7 @@ import fs from "node:fs/promises";
7
7
  import path from "node:path";
8
8
  import { isCancel, text } from "@clack/prompts";
9
9
  import { err, ok, unwrapResult } from "gscdump/result";
10
- import { createAuth } from "gscdump/api";
10
+ import { createAuth } from "gscdump";
11
11
  import { createServer } from "node:http";
12
12
  import { JWT, OAuth2Client } from "google-auth-library";
13
13
  import { ofetch } from "ofetch";
@@ -6,7 +6,7 @@ import { runSmokeTest } from "./init.mjs";
6
6
  import process from "node:process";
7
7
  import { defineCommand } from "citty";
8
8
  import path from "node:path";
9
- import { hasGscWriteScope, hasIndexingScope } from "gscdump/api";
9
+ import { hasGscWriteScope, hasIndexingScope } from "gscdump";
10
10
  import { ofetch } from "ofetch";
11
11
  const AUTH_SUBCOMMANDS = [
12
12
  "status",
@@ -3,7 +3,7 @@ import { logger } from "./utils.mjs";
3
3
  import { resolveAuth } from "./auth.mjs";
4
4
  import process from "node:process";
5
5
  import { cancel, isCancel, select } from "@clack/prompts";
6
- import { googleSearchConsole } from "gscdump/api";
6
+ import { googleSearchConsole } from "gscdump";
7
7
  import { createNodeHarness } from "@gscdump/engine/node";
8
8
  import { TABLE_DIMS, transformGscRow } from "@gscdump/engine/ingest";
9
9
  import { allTables, inferTable } from "@gscdump/engine/schema";
@@ -8,7 +8,7 @@ import process from "node:process";
8
8
  import { defineCommand } from "citty";
9
9
  import fs from "node:fs/promises";
10
10
  import path from "node:path";
11
- import { googleSearchConsole, hasGscWriteScope, hasIndexingScope } from "gscdump/api";
11
+ import { googleSearchConsole, hasGscWriteScope, hasIndexingScope } from "gscdump";
12
12
  import { ofetch } from "ofetch";
13
13
  const REQUIRED_SCOPES = [
14
14
  "https://www.googleapis.com/auth/webmasters",
@@ -1,11 +1,10 @@
1
- import { OUTPUT_ARGS, applyOutputMode, logger, runWithConcurrency } from "./utils.mjs";
1
+ import { OUTPUT_ARGS, applyOutputMode, logger, progressBar, runWithConcurrency } from "./utils.mjs";
2
2
  import { createCommandContext } from "./context.mjs";
3
3
  import process from "node:process";
4
4
  import { defineCommand } from "citty";
5
5
  import { readFile } from "node:fs/promises";
6
6
  import { Buffer } from "node:buffer";
7
7
  import { createIndexingMetadataStore, createInspectionStore, createSitemapStore } from "@gscdump/engine/entities";
8
- import { progressBar } from "gscdump";
9
8
  const INSPECTION_QPD_PER_PROPERTY = 2e3;
10
9
  const INDEXING_NOT_FOUND_RE = /\b404\b|NOT_FOUND/i;
11
10
  const INSPECTION_HISTORY_LOOKBACK_MONTHS = 24;
@@ -1,9 +1,28 @@
1
1
  import { formatAuthProvenance, isAuthError } from "./auth.mjs";
2
2
  import process from "node:process";
3
3
  import { isQueryError } from "gscdump/query";
4
- import { formatErrorForCli } from "gscdump/api";
4
+ import { classifyError } from "gscdump";
5
5
  import { isAnalysisError } from "@gscdump/analysis/errors";
6
6
  import { isEngineError } from "@gscdump/engine/errors";
7
+ const QUOTA_MESSAGE_RE = /quota|rate\s*limit/i;
8
+ function formatErrorForCli(cause) {
9
+ const error = classifyError(cause);
10
+ const lines = [`\x1B[31m${error.message}\x1B[0m`];
11
+ let suggestion = "";
12
+ switch (error.kind) {
13
+ case "auth-expired":
14
+ suggestion = "Run `gscdump auth` to re-authenticate.";
15
+ break;
16
+ case "rate-limited": {
17
+ const retryIn = error.retryAfter ? `${error.retryAfter}s` : "a few minutes";
18
+ if (QUOTA_MESSAGE_RE.test(error.message)) suggestion = error.message.includes("Indexing API") ? "Indexing API quota exhausted (~200/day). Try again tomorrow." : `Quota or rate limit hit (Search Analytics ~25000/day). Try again in ${retryIn}.`;
19
+ else suggestion = `Rate limited. Slow down requests. Try again in ${retryIn}.`;
20
+ break;
21
+ }
22
+ }
23
+ if (suggestion) lines.push("", suggestion);
24
+ return lines.join("\n");
25
+ }
7
26
  function extractQueryError(error) {
8
27
  if (isQueryError(error)) return error;
9
28
  const tagged = error.queryError;
@@ -3,7 +3,7 @@ import { createCommandContext } from "./context.mjs";
3
3
  import { gscErrorHandler } from "./error-handler.mjs";
4
4
  import process from "node:process";
5
5
  import { defineCommand } from "citty";
6
- import { batchRequestIndexing, fetchSitemapUrls, getIndexingMetadata, requestIndexing, runSequentialBatch } from "gscdump/api";
6
+ import { batchRequestIndexing, fetchSitemapUrls, getIndexingMetadata, requestIndexing, runSequentialBatch } from "gscdump";
7
7
  const RETRIES_ARG = { retries: {
8
8
  type: "string",
9
9
  description: "Override per-call retry count (default: 3)"
@@ -7,7 +7,7 @@ import { defineCommand } from "citty";
7
7
  import fs from "node:fs/promises";
8
8
  import path from "node:path";
9
9
  import { confirm, isCancel, text } from "@clack/prompts";
10
- import { googleSearchConsole } from "gscdump/api";
10
+ import { googleSearchConsole } from "gscdump";
11
11
  const ENV_LINE_RE = /^([^=]+)=(.*)$/;
12
12
  async function promptDataDir(existing) {
13
13
  const fallback = existing ?? defaultDataDir();
@@ -3,7 +3,7 @@ import { createCommandContext } from "./context.mjs";
3
3
  import { gscErrorHandler } from "./error-handler.mjs";
4
4
  import process from "node:process";
5
5
  import { defineCommand } from "citty";
6
- import { batchInspectUrls, fetchSitemapUrls } from "gscdump/api";
6
+ import { batchInspectUrls, fetchSitemapUrls } from "gscdump";
7
7
  function verdictTone(verdict) {
8
8
  if (verdict === "PASS") return "\x1B[32m";
9
9
  if (verdict === "NEUTRAL" || verdict === "PARTIAL") return "\x1B[33m";
@@ -7,7 +7,7 @@ import { isQueryError } from "gscdump/query";
7
7
  import { defaultAnalyzerRegistry } from "@gscdump/analysis/registry";
8
8
  import { createGscApiQuerySource } from "@gscdump/engine-gsc-api";
9
9
  import { err, ok, unwrapResult } from "gscdump/result";
10
- import { addSite, batchInspectUrls, batchRequestIndexing, deleteSite, discoverSitemap, fetchSitemap, fetchSitesWithSitemaps, getIndexingMetadata, getVerificationToken, getVerifiedSite, googleSearchConsole, hasGscWriteScope, hasIndexingScope, listVerifiedSites, requestIndexing, runSequentialBatch, unverifySite, verifySite } from "gscdump/api";
10
+ import { addSite, batchInspectUrls, batchRequestIndexing, deleteSite, discoverSitemap, fetchSitemap, fetchSitesWithSitemaps, getIndexingMetadata, getVerificationToken, getVerifiedSite, googleSearchConsole, hasGscWriteScope, hasIndexingScope, listVerifiedSites, requestIndexing, runSequentialBatch, unverifySite, verifySite } from "gscdump";
11
11
  import { ofetch } from "ofetch";
12
12
  import { isAnalysisError } from "@gscdump/analysis/errors";
13
13
  import { isEngineError } from "@gscdump/engine/errors";
@@ -417,7 +417,7 @@ async function runMcpSearchAnalyticsQuery(client, args) {
417
417
  const remaining = totalLimit - allRows.length;
418
418
  const currentLimit = Math.min(pageSize, remaining);
419
419
  if (currentLimit <= 0) break;
420
- const rows = ((await client._rawQuery(args.siteUrl, {
420
+ const rows = ((await client.searchAnalytics.query(args.siteUrl, {
421
421
  startDate: args.startDate,
422
422
  endDate: args.endDate,
423
423
  dimensions: args.dimensions,
@@ -92,7 +92,7 @@ async function runLiveQuery(client, siteUrl, opts) {
92
92
  const remaining = totalLimit - allRows.length;
93
93
  const rowLimit = Math.min(pageSize, remaining);
94
94
  if (rowLimit <= 0) break;
95
- const rows = ((await client._rawQuery(siteUrl, {
95
+ const rows = ((await client.searchAnalytics.query(siteUrl, {
96
96
  ...baseBody,
97
97
  rowLimit,
98
98
  startRow
@@ -3,7 +3,7 @@ import { createCommandContext } from "./context.mjs";
3
3
  import { gscErrorHandler } from "./error-handler.mjs";
4
4
  import process from "node:process";
5
5
  import { defineCommand } from "citty";
6
- import { discoverSitemap, fetchSitemap, fetchSitemapUrls } from "gscdump/api";
6
+ import { discoverSitemap, fetchSitemap, fetchSitemapUrls } from "gscdump";
7
7
  const listCommand = defineCommand({
8
8
  meta: {
9
9
  name: "list",
@@ -4,7 +4,7 @@ import { gscErrorHandler } from "./error-handler.mjs";
4
4
  import process from "node:process";
5
5
  import { defineCommand } from "citty";
6
6
  import { confirm, isCancel } from "@clack/prompts";
7
- import { addSite, deleteSite, fetchSitesWithSitemaps, getVerificationToken, getVerifiedSite, listVerifiedSites, siteUrlToVerificationSite, unverifySite, verificationMethodsFor, verifySite } from "gscdump/api";
7
+ import { addSite, deleteSite, fetchSitesWithSitemaps, getVerificationToken, getVerifiedSite, listVerifiedSites, siteUrlToVerificationSite, unverifySite, verificationMethodsFor, verifySite } from "gscdump";
8
8
  const ALL_METHODS = [
9
9
  "META",
10
10
  "FILE",
@@ -1,10 +1,9 @@
1
- import { OUTPUT_ARGS, applyOutputMode, clearLine, displayPath, formatAge, logger, runWithConcurrency } from "./utils.mjs";
1
+ import { OUTPUT_ARGS, applyOutputMode, clearLine, displayPath, formatAge, logger, progressBar, runWithConcurrency } from "./utils.mjs";
2
2
  import { TABLE_DIMS, allTables, createCommandContext, createLocalStore, transformGscRow } from "./context.mjs";
3
3
  import process from "node:process";
4
4
  import { defineCommand } from "citty";
5
5
  import { SearchTypes } from "gscdump/query";
6
6
  import { createEmptyTypesStore } from "@gscdump/engine/entities";
7
- import { progressBar } from "gscdump";
8
7
  import { daysAgoUtc, getDateRange } from "gscdump/dates";
9
8
  import { DEFAULT_ROLLUPS, rebuildRollups } from "@gscdump/engine/rollups";
10
9
  const DEFAULT_TABLES = [
@@ -100,7 +99,7 @@ async function runOneDate(store, client, siteUrl, table, searchType, dims, date)
100
99
  const rows = [];
101
100
  let startRow = 0;
102
101
  while (true) {
103
- const batch = (await client._rawQuery(siteUrl, {
102
+ const batch = (await client.searchAnalytics.query(siteUrl, {
104
103
  startDate: date,
105
104
  endDate: date,
106
105
  dimensions: dims,
@@ -4,7 +4,7 @@ import fs from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import { Buffer } from "node:buffer";
6
6
  import { SearchTypes } from "gscdump/query";
7
- var version = "0.40.2";
7
+ var version = "1.0.1";
8
8
  const ALL_SEARCH_TYPES = Object.values(SearchTypes);
9
9
  const VERSION = version;
10
10
  function noSubcommandSelected(parent, subNames) {
@@ -90,6 +90,30 @@ async function withConfiguredOutput(run) {
90
90
  process.stdout.write = original;
91
91
  }
92
92
  }
93
+ const ANSI = {
94
+ reset: "\x1B[0m",
95
+ bold: "\x1B[1m",
96
+ dim: "\x1B[90m",
97
+ red: "\x1B[31m",
98
+ green: "\x1B[32m",
99
+ yellow: "\x1B[33m",
100
+ cyan: "\x1B[36m"
101
+ };
102
+ function color(c, s) {
103
+ if (!isColorEnabled()) return String(s);
104
+ return `${ANSI[c]}${s}${ANSI.reset}`;
105
+ }
106
+ function dim(s) {
107
+ return color("dim", s);
108
+ }
109
+ function cyan(s) {
110
+ return color("cyan", s);
111
+ }
112
+ function progressBar(current, total, label, width = 30) {
113
+ const percent = total > 0 ? Math.min(current / total, 1) : 0;
114
+ const filled = Math.round(width * percent);
115
+ return ` ${`${cyan("█".repeat(filled))}${dim("░".repeat(width - filled))}`} ${dim(`${current}/${total}`)} ${label}`;
116
+ }
93
117
  const gradientColors = [
94
118
  (s) => `\x1B[38;2;52;211;153m${s}\x1B[0m`,
95
119
  (s) => `\x1B[38;2;45;212;191m${s}\x1B[0m`,
@@ -188,4 +212,4 @@ function exportToCSV(output) {
188
212
  ])}`);
189
213
  return sections.join("\n\n");
190
214
  }
191
- export { ALL_SEARCH_TYPES, OUTPUT_ARGS, VERSION, applyOutputMode, clearLine, configureColor, displayPath, exportToCSV, formatAge, logger, noSubcommandSelected, parseSearchType, readUrlList, runWithConcurrency, showSplash, toCSV, withConfiguredOutput };
215
+ export { ALL_SEARCH_TYPES, OUTPUT_ARGS, VERSION, applyOutputMode, clearLine, configureColor, displayPath, exportToCSV, formatAge, logger, noSubcommandSelected, parseSearchType, progressBar, readUrlList, runWithConcurrency, showSplash, toCSV, withConfiguredOutput };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/cli",
3
3
  "type": "module",
4
- "version": "0.40.2",
4
+ "version": "1.0.1",
5
5
  "description": "CLI for Google Search Console - dump, query, and run MCP server",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -44,7 +44,7 @@
44
44
  "dist"
45
45
  ],
46
46
  "engines": {
47
- "node": ">=18"
47
+ "node": ">=22"
48
48
  },
49
49
  "dependencies": {
50
50
  "@clack/prompts": "^1.7.0",
@@ -56,10 +56,10 @@
56
56
  "ofetch": "^1.5.1",
57
57
  "open": "^11.0.0",
58
58
  "zod": "^4.4.3",
59
- "@gscdump/analysis": "0.40.2",
60
- "@gscdump/engine": "0.40.2",
61
- "gscdump": "0.40.2",
62
- "@gscdump/engine-gsc-api": "0.40.2"
59
+ "@gscdump/analysis": "^1.0.1",
60
+ "@gscdump/engine": "^1.0.1",
61
+ "@gscdump/engine-gsc-api": "^1.0.1",
62
+ "gscdump": "^1.0.1"
63
63
  },
64
64
  "devDependencies": {
65
65
  "vitest": "^4.1.10"