@gscdump/cli 1.4.11 → 1.5.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.
@@ -2,9 +2,9 @@ import { logger } from "./utils.mjs";
2
2
  import { createLocalStore } from "./local-store.mjs";
3
3
  import { createCommandContext } from "./context.mjs";
4
4
  import { LocalStoreUnsupportedError } from "./error-handler.mjs";
5
- import { join } from "node:path";
6
5
  import process from "node:process";
7
6
  import { readdir } from "node:fs/promises";
7
+ import { join } from "node:path";
8
8
  import { defaultAnalyzerRegistry } from "@gscdump/analysis/registry";
9
9
  import { AnalyzerCapabilityError, createEngineQuerySource, runAnalyzerFromSource } from "@gscdump/analysis";
10
10
  import { createGscApiQuerySource } from "@gscdump/engine-gsc-api";
package/dist/auth.mjs CHANGED
@@ -2,9 +2,9 @@ import { getConfigDir, loadConfig } from "./config.mjs";
2
2
  import { pickCliEnvironmentValue, resolveCliEnvironment } from "./environment.mjs";
3
3
  import { displayPath, logger } from "./utils.mjs";
4
4
  import { getAppliedEnvKeys, getLoadedEnvPath } from "./env-file.mjs";
5
- import path from "node:path";
6
5
  import process from "node:process";
7
6
  import fs from "node:fs/promises";
7
+ import path from "node:path";
8
8
  import { isCancel, text } from "@clack/prompts";
9
9
  import { err, ok, unwrapResult } from "gscdump/result";
10
10
  import { createAuth } from "gscdump";
@@ -3,9 +3,9 @@ import { OUTPUT_ARGS, applyOutputMode, logger, noSubcommandSelected } from "../u
3
3
  import { adoptCurrentConfigAsProfile, profileNameFromEmail, resolveActiveProfile } from "./profile.mjs";
4
4
  import { authenticate, clearTokens, formatAuthProvenance, getAuth, getAuthCredentials, loadServiceAccount, loadTokens, resolveBYOK, saveTokens } from "../auth.mjs";
5
5
  import { runSmokeTest } from "./init.mjs";
6
- import path from "node:path";
7
6
  import process from "node:process";
8
7
  import { defineCommand } from "citty";
8
+ import path from "node:path";
9
9
  import { hasGscWriteScope, hasIndexingScope } from "gscdump";
10
10
  import { ofetch } from "ofetch";
11
11
  const AUTH_SUBCOMMANDS = [
@@ -5,10 +5,10 @@ import { parseEnvFile } from "../env-file.mjs";
5
5
  import { loadTokens, resolveAuth, resolveBYOK } from "../auth.mjs";
6
6
  import { createLocalStore } from "../local-store.mjs";
7
7
  import { createCommandContext } from "../context.mjs";
8
- import path from "node:path";
9
8
  import process from "node:process";
10
9
  import { defineCommand } from "citty";
11
10
  import fs from "node:fs/promises";
11
+ import path from "node:path";
12
12
  import { googleSearchConsole, hasGscWriteScope, hasIndexingScope } from "gscdump";
13
13
  import { ofetch } from "ofetch";
14
14
  const REQUIRED_SCOPES = [
@@ -2,10 +2,10 @@ import { ALL_SEARCH_TYPES, OUTPUT_ARGS, applyOutputMode, displayPath, logger, pa
2
2
  import { allTables } from "../local-store.mjs";
3
3
  import { createCommandContext } from "../context.mjs";
4
4
  import { readParquetRows } from "../native-duckdb.mjs";
5
- import path from "node:path";
6
5
  import process from "node:process";
7
6
  import { defineCommand } from "citty";
8
7
  import fs from "node:fs/promises";
8
+ import path from "node:path";
9
9
  const DEFAULT_OUT = "./gscdump-export";
10
10
  const FORMATS = [
11
11
  "parquet",
@@ -2,8 +2,8 @@ import { OUTPUT_ARGS, applyOutputMode, displayPath } from "../utils.mjs";
2
2
  import { allTables } from "../local-store.mjs";
3
3
  import { createCommandContext } from "../context.mjs";
4
4
  import { materializeParquetTables } from "../native-duckdb.mjs";
5
- import path from "node:path";
6
5
  import { defineCommand } from "citty";
6
+ import path from "node:path";
7
7
  async function exportToDuckDB(opts) {
8
8
  const outPath = path.resolve(opts.outPath);
9
9
  const entries = await opts.engine.listLive({
@@ -1,9 +1,10 @@
1
1
  import { OUTPUT_ARGS, applyOutputMode, logger, readUrlList } from "../utils.mjs";
2
2
  import { createCommandContext } from "../context.mjs";
3
3
  import { gscErrorHandler } from "../error-handler.mjs";
4
+ import { loadSitemapUrls } from "../sitemap.mjs";
4
5
  import process from "node:process";
5
6
  import { defineCommand } from "citty";
6
- import { batchRequestIndexing, fetchSitemapUrls, getIndexingMetadata, requestIndexing, runSequentialBatch } from "gscdump";
7
+ import { batchRequestIndexing, getIndexingMetadata, requestIndexing, runSequentialBatch } from "gscdump";
7
8
  const RETRIES_ARG = { retries: {
8
9
  type: "string",
9
10
  description: "Override per-call retry count (default: 3)"
@@ -15,10 +16,15 @@ function parseRetries(v) {
15
16
  }
16
17
  async function resolveUrlSource(args) {
17
18
  const fromSitemap = args["from-sitemap"];
18
- if (fromSitemap) return fetchSitemapUrls(String(fromSitemap)).catch((e) => {
19
- logger.error(`Sitemap fetch failed: ${e.message}`);
20
- process.exit(1);
21
- });
19
+ if (fromSitemap) {
20
+ const result = await loadSitemapUrls(String(fromSitemap));
21
+ if (result._tag === "error") {
22
+ logger.error(`Sitemap fetch failed: ${result.message}`);
23
+ process.exit(1);
24
+ }
25
+ if (!result.value.complete) logger.warn("Sitemap walk was incomplete; indexing only the URLs that were read");
26
+ return result.value.urls;
27
+ }
22
28
  return readUrlList(args);
23
29
  }
24
30
  const submitCommand = defineCommand({
@@ -2,10 +2,10 @@ import { defaultDataDir, loadConfig, saveConfig } from "../config.mjs";
2
2
  import { applyCliEnvironment } from "../environment.mjs";
3
3
  import { OUTPUT_ARGS, applyOutputMode, displayPath, logger } from "../utils.mjs";
4
4
  import { authenticate, getAuthCredentials, loadTokens, resolveBYOK, saveTokens } from "../auth.mjs";
5
- import path from "node:path";
6
5
  import process from "node:process";
7
6
  import { defineCommand } from "citty";
8
7
  import fs from "node:fs/promises";
8
+ import path from "node:path";
9
9
  import { confirm, isCancel, text } from "@clack/prompts";
10
10
  import { googleSearchConsole } from "gscdump";
11
11
  const ENV_LINE_RE = /^([^=]+)=(.*)$/;
@@ -1,9 +1,10 @@
1
1
  import { OUTPUT_ARGS, applyOutputMode, logger, readUrlList } from "../utils.mjs";
2
2
  import { createCommandContext } from "../context.mjs";
3
3
  import { gscErrorHandler } from "../error-handler.mjs";
4
+ import { loadSitemapUrls } from "../sitemap.mjs";
4
5
  import process from "node:process";
5
6
  import { defineCommand } from "citty";
6
- import { batchInspectUrls, fetchSitemapUrls } from "gscdump";
7
+ import { batchInspectUrls } from "gscdump";
7
8
  function verdictTone(verdict) {
8
9
  if (verdict === "PASS") return "\x1B[32m";
9
10
  if (verdict === "NEUTRAL" || verdict === "PARTIAL") return "\x1B[33m";
@@ -121,10 +122,16 @@ const batchCommand = defineCommand({
121
122
  },
122
123
  async run({ args }) {
123
124
  const { json, quiet } = applyOutputMode(args);
124
- const urls = args["from-sitemap"] ? await fetchSitemapUrls(String(args["from-sitemap"])).catch((e) => {
125
- logger.error(`Sitemap fetch failed: ${e.message}`);
126
- process.exit(1);
127
- }) : await readUrlList(args);
125
+ let urls;
126
+ if (args["from-sitemap"]) {
127
+ const result = await loadSitemapUrls(String(args["from-sitemap"]));
128
+ if (result._tag === "error") {
129
+ logger.error(`Sitemap fetch failed: ${result.message}`);
130
+ process.exit(1);
131
+ }
132
+ if (!result.value.complete) logger.warn("Sitemap walk was incomplete; inspecting only the URLs that were read");
133
+ urls = result.value.urls;
134
+ } else urls = await readUrlList(args);
128
135
  if (urls.length === 0) {
129
136
  logger.error("No URLs provided. Pass URLs as args, --file, --from-sitemap, or stdin.");
130
137
  process.exit(1);
@@ -2,12 +2,12 @@ import { useCliRuntime } from "../runtime.mjs";
2
2
  import { getConfigDir, setConfigDir } from "../config.mjs";
3
3
  import { resolveCliEnvironment } from "../environment.mjs";
4
4
  import { OUTPUT_ARGS, applyOutputMode, displayPath, logger, noSubcommandSelected } from "../utils.mjs";
5
- import os from "node:os";
6
- import path from "node:path";
7
5
  import process from "node:process";
8
6
  import { defineCommand } from "citty";
9
7
  import fs from "node:fs";
10
8
  import fs$1 from "node:fs/promises";
9
+ import os from "node:os";
10
+ import path from "node:path";
11
11
  import { confirm, isCancel } from "@clack/prompts";
12
12
  const ROOT_DIR = path.join(os.homedir(), ".config", "gscdump");
13
13
  const PROFILES_DIR = path.join(ROOT_DIR, "profiles");
@@ -1,9 +1,11 @@
1
1
  import { OUTPUT_ARGS, applyOutputMode, logger, noSubcommandSelected } from "../utils.mjs";
2
2
  import { createCommandContext } from "../context.mjs";
3
3
  import { gscErrorHandler } from "../error-handler.mjs";
4
+ import { loadSitemapUrls } from "../sitemap.mjs";
4
5
  import process from "node:process";
5
6
  import { defineCommand } from "citty";
6
- import { discoverSitemap, fetchSitemap, fetchSitemapUrls } from "gscdump";
7
+ import { fetchSitemap } from "gscdump";
8
+ import { discoverSitemapResult } from "gscdump/sitemap";
7
9
  const listCommand = defineCommand({
8
10
  meta: {
9
11
  name: "list",
@@ -195,14 +197,20 @@ const sitemapsCommand = defineCommand({
195
197
  async run({ args }) {
196
198
  const { json } = applyOutputMode(args);
197
199
  const domain = String(args.domain).replace(/^https?:\/\//, "").replace(/\/.*$/, "");
198
- const url = await discoverSitemap(domain).catch(() => null);
200
+ const discovery = await discoverSitemapResult(domain);
201
+ const url = discovery._tag === "found" ? discovery.url : null;
199
202
  if (json) {
200
203
  console.log(JSON.stringify({
201
204
  domain,
202
- sitemap: url
205
+ sitemap: url,
206
+ status: discovery._tag
203
207
  }, null, 2));
204
208
  return;
205
209
  }
210
+ if (discovery._tag === "incomplete") {
211
+ logger.error(`Sitemap discovery was incomplete: ${discovery.failures[0]?.detail ?? "unknown failure"}`);
212
+ process.exit(1);
213
+ }
206
214
  if (!url) {
207
215
  logger.warn(`No sitemap discovered for ${domain}`);
208
216
  process.exit(1);
@@ -236,21 +244,26 @@ const sitemapsCommand = defineCommand({
236
244
  const { json } = applyOutputMode(args);
237
245
  const limit = args.limit ? Number.parseInt(String(args.limit), 10) : void 0;
238
246
  const maxDepth = args["max-depth"] ? Number.parseInt(String(args["max-depth"]), 10) : void 0;
239
- const urls = await fetchSitemapUrls(String(args.url), {
240
- limit,
247
+ const result = await loadSitemapUrls(String(args.url), {
248
+ maxUrls: limit,
241
249
  maxDepth
242
- }).catch((e) => {
243
- logger.error(`Sitemap fetch failed: ${e.message}`);
244
- process.exit(1);
245
250
  });
251
+ if (result._tag === "error") {
252
+ logger.error(`Sitemap fetch failed: ${result.message}`);
253
+ process.exit(1);
254
+ }
255
+ const { urls, complete, documentsRead } = result.value;
246
256
  if (json) {
247
257
  console.log(JSON.stringify({
248
258
  sitemap: args.url,
249
259
  count: urls.length,
260
+ complete,
261
+ documentsRead,
250
262
  urls
251
263
  }, null, 2));
252
264
  return;
253
265
  }
266
+ if (!complete) logger.warn("Sitemap walk was truncated or a child document failed");
254
267
  for (const u of urls) console.log(u);
255
268
  }
256
269
  })
package/dist/config.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useCliRuntime } from "./runtime.mjs";
2
+ import fs from "node:fs/promises";
2
3
  import os from "node:os";
3
4
  import path from "node:path";
4
- import fs from "node:fs/promises";
5
5
  function setConfigDir(dir) {
6
6
  useCliRuntime().configDir = dir;
7
7
  }
package/dist/env-file.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useCliRuntime } from "./runtime.mjs";
2
- import path from "node:path";
3
2
  import process from "node:process";
4
3
  import fs from "node:fs";
4
+ import path from "node:path";
5
5
  const ENV_LINE_RE = /^([^=]+)=(.*)$/;
6
6
  function parseEnvFile(envPath) {
7
7
  let content;
@@ -4,7 +4,8 @@ import { listReports, runReportHandler } from "../handlers/reports.mjs";
4
4
  import { getSitemap, listSitesWithSitemaps } from "../handlers/sites.mjs";
5
5
  import "../handlers/index.mjs";
6
6
  import { batchInspectUrlsInput, batchRequestIndexingInput, getIndexingStatusInput, inspectUrlInput, listReportsInput, listSitemapsInput, listSitesInput, requestIndexingInput, runReportInput, sitemapInput } from "../types.mjs";
7
- import { addSite, deleteSite, discoverSitemap, getIndexingMetadata, getVerificationToken, getVerifiedSite, googleSearchConsole, listVerifiedSites, runSequentialBatch, unverifySite, verifySite } from "gscdump";
7
+ import { addSite, deleteSite, getIndexingMetadata, getVerificationToken, getVerifiedSite, googleSearchConsole, listVerifiedSites, runSequentialBatch, unverifySite, verifySite } from "gscdump";
8
+ import { discoverSitemapResult } from "gscdump/sitemap";
8
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
10
  import { z } from "zod";
10
11
  async function runMcpSearchAnalyticsQuery(client, args) {
@@ -385,12 +386,14 @@ function createGscMcpServer(options) {
385
386
  inputSchema: z.object({ domain: z.string().describe("Domain (e.g., example.com)") }).shape
386
387
  }, async ({ domain }) => {
387
388
  const cleaned = String(domain).replace(/^https?:\/\//, "").replace(/\/.*$/, "");
388
- const url = await discoverSitemap(cleaned).catch(() => null);
389
+ const discovery = await discoverSitemapResult(cleaned);
389
390
  return { content: [{
390
391
  type: "text",
391
392
  text: JSON.stringify({
392
393
  domain: cleaned,
393
- sitemap: url
394
+ sitemap: discovery._tag === "found" ? discovery.url : null,
395
+ status: discovery._tag,
396
+ ...discovery._tag === "incomplete" ? { failures: discovery.failures } : {}
394
397
  }, null, 2)
395
398
  }] };
396
399
  });
package/dist/package.mjs CHANGED
@@ -1,2 +1,2 @@
1
- var version = "1.4.11";
1
+ var version = "1.5.1";
2
2
  export { version };
package/dist/runtime.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
1
+ import process from "node:process";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
- import process from "node:process";
4
+ import { AsyncLocalStorage } from "node:async_hooks";
5
5
  import { createConsola } from "consola";
6
6
  let runtimeStorage;
7
7
  let fallbackRuntime;
@@ -0,0 +1,27 @@
1
+ import { walkSitemaps } from "gscdump/sitemap";
2
+ function failureMessage(result) {
3
+ if (result._tag === "not_found") return "No sitemap document was found";
4
+ const first = result.failures[0];
5
+ if (!first) return "Sitemap walk failed before reading a document";
6
+ const error = first.error;
7
+ if (error._tag === "network_error") return `${first.url}: ${error.error}`;
8
+ if (error._tag === "http_error") return `${first.url}: HTTP ${error.status} ${error.statusText}`;
9
+ if (error._tag === "not_found") return `${first.url}: HTTP ${error.status}`;
10
+ return `${first.url}: ${error.error._tag}`;
11
+ }
12
+ async function loadSitemapUrls(url, options = {}) {
13
+ const result = await walkSitemaps(url, options);
14
+ if (result._tag !== "ok") return {
15
+ _tag: "error",
16
+ message: failureMessage(result)
17
+ };
18
+ return {
19
+ _tag: "ok",
20
+ value: {
21
+ urls: result.entries.map((entry) => entry.loc),
22
+ complete: result.complete,
23
+ documentsRead: result.documentsRead
24
+ }
25
+ };
26
+ }
27
+ export { loadSitemapUrls };
package/dist/utils.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { useCliRuntime } from "./runtime.mjs";
2
2
  import { version } from "./package.mjs";
3
- import os from "node:os";
4
3
  import process from "node:process";
5
4
  import fs from "node:fs/promises";
5
+ import os from "node:os";
6
6
  import { Buffer } from "node:buffer";
7
7
  import { SearchTypes } from "gscdump/query";
8
8
  const ALL_SEARCH_TYPES = Object.values(SearchTypes);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/cli",
3
3
  "type": "module",
4
- "version": "1.4.11",
4
+ "version": "1.5.1",
5
5
  "description": "CLI for Google Search Console - dump, query, and run MCP server",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -29,13 +29,7 @@
29
29
  "data-export"
30
30
  ],
31
31
  "sideEffects": false,
32
- "exports": {
33
- ".": {
34
- "types": "./dist/index.d.mts",
35
- "import": "./dist/index.mjs",
36
- "default": "./dist/index.mjs"
37
- }
38
- },
32
+ "exports": {},
39
33
  "bin": {
40
34
  "gscdump": "./bin/gscdump.mjs"
41
35
  },
@@ -52,14 +46,14 @@
52
46
  "@modelcontextprotocol/sdk": "^1.29.0",
53
47
  "citty": "^0.2.2",
54
48
  "consola": "^3.4.2",
55
- "google-auth-library": "^10.9.0",
49
+ "google-auth-library": "^10.9.1",
56
50
  "ofetch": "^1.5.1",
57
51
  "open": "^11.0.0",
58
52
  "zod": "^4.4.3",
59
- "@gscdump/analysis": "^1.4.11",
60
- "@gscdump/engine-gsc-api": "^1.4.11",
61
- "gscdump": "^1.4.11",
62
- "@gscdump/engine": "^1.4.11"
53
+ "@gscdump/analysis": "^1.5.1",
54
+ "@gscdump/engine-gsc-api": "^1.5.1",
55
+ "@gscdump/engine": "^1.5.1",
56
+ "gscdump": "^1.5.1"
63
57
  },
64
58
  "devDependencies": {
65
59
  "vitest": "^4.1.10"
package/dist/index.d.mts DELETED
@@ -1,2 +0,0 @@
1
- import { CliRuntime, CreateCliRuntimeOptions, createCliRuntime } from "./runtime.mjs";
2
- export { type CliRuntime, type CreateCliRuntimeOptions, createCliRuntime };
package/dist/index.mjs DELETED
@@ -1,2 +0,0 @@
1
- import { createCliRuntime } from "./runtime.mjs";
2
- export { createCliRuntime };