@gscdump/cli 3.0.0 → 3.1.0

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 (39) hide show
  1. package/dist/analysis-local.mjs +3 -2
  2. package/dist/auth-scopes.mjs +9 -0
  3. package/dist/auth.mjs +4 -4
  4. package/dist/cli.mjs +4 -24
  5. package/dist/command-meta.mjs +83 -0
  6. package/dist/command-registry.mjs +56 -0
  7. package/dist/commands/analyze.mjs +2 -4
  8. package/dist/commands/auth.mjs +7 -34
  9. package/dist/commands/config.mjs +2 -4
  10. package/dist/commands/doctor.mjs +6 -23
  11. package/dist/commands/dump.mjs +3 -5
  12. package/dist/commands/entities.mjs +2 -4
  13. package/dist/commands/indexing.mjs +3 -5
  14. package/dist/commands/init.mjs +4 -6
  15. package/dist/commands/inspect.mjs +3 -5
  16. package/dist/commands/mcp.mjs +2 -4
  17. package/dist/commands/profile-selection.mjs +36 -0
  18. package/dist/commands/profile.mjs +15 -47
  19. package/dist/commands/query.mjs +4 -6
  20. package/dist/commands/report.mjs +2 -4
  21. package/dist/commands/rollups.mjs +1 -1
  22. package/dist/commands/sitemaps.mjs +3 -5
  23. package/dist/commands/sites.mjs +3 -5
  24. package/dist/commands/store.mjs +2 -4
  25. package/dist/commands/sync.mjs +2 -4
  26. package/dist/config.mjs +1 -1
  27. package/dist/context.mjs +1 -1
  28. package/dist/error-handler.mjs +1 -1
  29. package/dist/mcp/handlers/diagnostics.mjs +1 -17
  30. package/dist/mcp/handlers/indexing.mjs +1 -1
  31. package/dist/mcp/handlers/sites.mjs +1 -1
  32. package/dist/mcp/server/index.mjs +7 -11
  33. package/dist/mcp/types.mjs +2 -8
  34. package/dist/package.mjs +1 -1
  35. package/dist/utils.mjs +1 -1
  36. package/package.json +6 -6
  37. package/dist/mcp/handlers/analytics.mjs +0 -1
  38. package/dist/mcp/handlers/index.mjs +0 -7
  39. package/dist/mcp/handlers/query.mjs +0 -5
@@ -3,11 +3,12 @@ import { createLocalStore } from "./local-store.mjs";
3
3
  import { createCommandContext } from "./context.mjs";
4
4
  import { LocalStoreUnsupportedError } from "./error-handler.mjs";
5
5
  import process from "node:process";
6
- import { readdir } from "node:fs/promises";
7
6
  import { join } from "node:path";
7
+ import { readdir } from "node:fs/promises";
8
8
  import { defaultAnalyzerRegistry } from "@gscdump/analysis/registry";
9
- import { AnalyzerCapabilityError, createEngineQuerySource, runAnalyzerFromSource } from "@gscdump/analysis";
10
9
  import { createGscApiQuerySource } from "@gscdump/engine-gsc-api";
10
+ import { AnalyzerCapabilityError, runAnalyzerFromSource } from "@gscdump/engine/analyzer";
11
+ import { createEngineQuerySource } from "@gscdump/engine/source";
11
12
  import { err, ok, unwrapResult } from "gscdump/result";
12
13
  import { decodeSiteId, normalizeSiteUrl } from "gscdump/tenant";
13
14
  async function hasLocalData(store, siteUrl) {
@@ -0,0 +1,9 @@
1
+ import { GSC_INDEXING_SCOPE, GSC_SITE_VERIFICATION_SCOPE, GSC_WRITE_SCOPE, hasGoogleScope } from "gscdump/client";
2
+ function missingRequiredScopes(scopes) {
3
+ return [
4
+ GSC_WRITE_SCOPE,
5
+ GSC_INDEXING_SCOPE,
6
+ GSC_SITE_VERIFICATION_SCOPE
7
+ ].filter((scope) => !hasGoogleScope(scopes, scope));
8
+ }
9
+ export { missingRequiredScopes };
package/dist/auth.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  import { getConfigDir, loadConfig } from "./config.mjs";
2
2
  import { pickCliEnvironmentValue, resolveCliEnvironment } from "./environment.mjs";
3
- import { displayPath, logger } from "./utils.mjs";
4
3
  import { getAppliedEnvKeys, getLoadedEnvPath } from "./env-file.mjs";
4
+ import { displayPath, logger } from "./utils.mjs";
5
5
  import process from "node:process";
6
- import fs from "node:fs/promises";
7
6
  import path from "node:path";
8
- import { isCancel, text } from "@clack/prompts";
7
+ import fs from "node:fs/promises";
9
8
  import { err, ok, unwrapResult } from "gscdump/result";
10
- import { createAuth } from "gscdump";
9
+ import { isCancel, text } from "@clack/prompts";
10
+ import { createAuth } from "gscdump/client";
11
11
  import { createServer } from "node:http";
12
12
  import { JWT, OAuth2Client } from "google-auth-library";
13
13
  import { ofetch } from "ofetch";
package/dist/cli.mjs CHANGED
@@ -1,8 +1,9 @@
1
+ import { CLI_SUBCOMMANDS } from "./command-registry.mjs";
1
2
  import { createCliRuntime, runWithCliRuntime } from "./runtime.mjs";
2
3
  import { resolveCliEnvironment } from "./environment.mjs";
3
- import { VERSION, configureColor, showSplash, withConfiguredOutput } from "./utils.mjs";
4
- import { applyProfileFromCli } from "./commands/profile.mjs";
4
+ import { applyProfileFromCli } from "./commands/profile-selection.mjs";
5
5
  import { loadEnvFromCwd } from "./env-file.mjs";
6
+ import { VERSION, configureColor, showSplash, withConfiguredOutput } from "./utils.mjs";
6
7
  import process from "node:process";
7
8
  import { defineCommand, runMain } from "citty";
8
9
  function shouldShowSplash(rawArgs) {
@@ -62,28 +63,7 @@ const main = defineCommand({
62
63
  version: VERSION,
63
64
  description: "Google Search Console Data Extractor"
64
65
  },
65
- subCommands: {
66
- init: () => import("./commands/init.mjs").then((m) => m.initCommand),
67
- dump: () => import("./commands/dump.mjs").then((m) => m.dumpCommand),
68
- query: () => import("./commands/query.mjs").then((m) => m.queryCommand),
69
- sites: () => import("./commands/sites.mjs").then((m) => m.sitesCommand),
70
- sitemaps: () => import("./commands/sitemaps.mjs").then((m) => m.sitemapsCommand),
71
- sync: () => import("./commands/sync.mjs").then((m) => m.syncCommand),
72
- store: () => import("./commands/store.mjs").then((m) => m.storeCommand),
73
- inspect: () => import("./commands/inspect.mjs").then((m) => m.inspectCommand),
74
- indexing: () => import("./commands/indexing.mjs").then((m) => m.indexingCommand),
75
- entities: () => import("./commands/entities.mjs").then((m) => m.entitiesCommand),
76
- analyze: () => import("./commands/analyze.mjs").then((m) => m.analyzeCommand),
77
- report: () => import("./commands/report.mjs").then((m) => m.reportCommand),
78
- auth: () => import("./commands/auth.mjs").then((m) => m.authCommand),
79
- login: () => import("./commands/auth.mjs").then((m) => m.loginCommand),
80
- logout: () => import("./commands/auth.mjs").then((m) => m.logoutCommand),
81
- status: () => import("./commands/auth.mjs").then((m) => m.statusCommand),
82
- config: () => import("./commands/config.mjs").then((m) => m.configCommand),
83
- profile: () => import("./commands/profile.mjs").then((m) => m.profileCommand),
84
- doctor: () => import("./commands/doctor.mjs").then((m) => m.doctorCommand),
85
- mcp: () => import("./commands/mcp.mjs").then((m) => m.mcpCommand)
86
- },
66
+ subCommands: CLI_SUBCOMMANDS,
87
67
  setup({ rawArgs }) {
88
68
  if (shouldShowSplash(rawArgs)) showSplash();
89
69
  }
@@ -0,0 +1,83 @@
1
+ const initCommandMeta = {
2
+ name: "init",
3
+ description: "Set up GSCDump authentication"
4
+ };
5
+ const dumpCommandMeta = {
6
+ name: "dump",
7
+ description: "Export live Parquet files from the local store to a directory"
8
+ };
9
+ const queryCommandMeta = {
10
+ name: "query",
11
+ description: "Run a search analytics query (local Parquet by default, --live hits GSC API)"
12
+ };
13
+ const sitesCommandMeta = {
14
+ name: "sites",
15
+ description: "List GSC sites; manage properties (add/delete) and verify ownership"
16
+ };
17
+ const sitemapsCommandMeta = {
18
+ name: "sitemaps",
19
+ description: "Manage sitemaps"
20
+ };
21
+ const syncCommandMeta = {
22
+ name: "sync",
23
+ description: "Sync GSC data to local Parquet store"
24
+ };
25
+ const storeCommandMeta = {
26
+ name: "store",
27
+ description: "Manage the local DuckDB/Parquet store"
28
+ };
29
+ const inspectCommandMeta = {
30
+ name: "inspect",
31
+ description: "Inspect URL indexing status (single URL; use `inspect batch` for many)"
32
+ };
33
+ const indexingCommandMeta = {
34
+ name: "indexing",
35
+ description: "Notify Google about URL updates/removals (Indexing API)"
36
+ };
37
+ const entitiesCommandMeta = {
38
+ name: "entities",
39
+ description: "Manage local entity snapshots (URL inspections and indexing metadata)"
40
+ };
41
+ const analyzeCommandMeta = {
42
+ name: "analyze",
43
+ description: "SEO analysis tools"
44
+ };
45
+ const reportCommandMeta = {
46
+ name: "report",
47
+ description: "Run an intent-keyed report (composes analyzers into bounded sections)"
48
+ };
49
+ const authCommandMeta = {
50
+ status: {
51
+ name: "status",
52
+ description: "Show current authentication status"
53
+ },
54
+ login: {
55
+ name: "login",
56
+ description: "Run OAuth flow and persist tokens (skip if BYOK env vars set)"
57
+ },
58
+ logout: {
59
+ name: "logout",
60
+ description: "Clear stored OAuth tokens"
61
+ },
62
+ auth: {
63
+ name: "auth",
64
+ description: "Manage authentication"
65
+ }
66
+ };
67
+ const configCommandMeta = {
68
+ name: "config",
69
+ description: "Manage configuration"
70
+ };
71
+ const profileCommandMeta = {
72
+ name: "profile",
73
+ description: "Manage gscdump profiles (per-account token + config dirs)"
74
+ };
75
+ const doctorCommandMeta = {
76
+ name: "doctor",
77
+ description: "Run health checks (env, auth, scopes, time, dataDir, store, GSC reachability + ping, defaultSite)"
78
+ };
79
+ const mcpCommandMeta = {
80
+ name: "mcp",
81
+ description: "Start MCP server for AI assistants"
82
+ };
83
+ export { analyzeCommandMeta, authCommandMeta, configCommandMeta, doctorCommandMeta, dumpCommandMeta, entitiesCommandMeta, indexingCommandMeta, initCommandMeta, inspectCommandMeta, mcpCommandMeta, profileCommandMeta, queryCommandMeta, reportCommandMeta, sitemapsCommandMeta, sitesCommandMeta, storeCommandMeta, syncCommandMeta };
@@ -0,0 +1,56 @@
1
+ import { analyzeCommandMeta, authCommandMeta, configCommandMeta, doctorCommandMeta, dumpCommandMeta, entitiesCommandMeta, indexingCommandMeta, initCommandMeta, inspectCommandMeta, mcpCommandMeta, profileCommandMeta, queryCommandMeta, reportCommandMeta, sitemapsCommandMeta, sitesCommandMeta, storeCommandMeta, syncCommandMeta } from "./command-meta.mjs";
2
+ function resolveValue(value) {
3
+ return typeof value === "function" ? value() : value;
4
+ }
5
+ function shallowCommand(meta, load) {
6
+ return {
7
+ meta,
8
+ args: async () => await resolveValue((await load()).args) ?? {},
9
+ default: async () => await resolveValue((await load()).default) ?? "",
10
+ subCommands: async () => await resolveValue((await load()).subCommands) ?? {},
11
+ async setup(context) {
12
+ const command = await load();
13
+ await command.setup?.({
14
+ ...context,
15
+ cmd: command
16
+ });
17
+ },
18
+ async cleanup(context) {
19
+ const command = await load();
20
+ await command.cleanup?.({
21
+ ...context,
22
+ cmd: command
23
+ });
24
+ },
25
+ async run(context) {
26
+ const command = await load();
27
+ return command.run?.({
28
+ ...context,
29
+ cmd: command
30
+ });
31
+ }
32
+ };
33
+ }
34
+ const CLI_SUBCOMMANDS = {
35
+ init: shallowCommand(initCommandMeta, () => import("./commands/init.mjs").then((module) => module.initCommand)),
36
+ dump: shallowCommand(dumpCommandMeta, () => import("./commands/dump.mjs").then((module) => module.dumpCommand)),
37
+ query: shallowCommand(queryCommandMeta, () => import("./commands/query.mjs").then((module) => module.queryCommand)),
38
+ sites: shallowCommand(sitesCommandMeta, () => import("./commands/sites.mjs").then((module) => module.sitesCommand)),
39
+ sitemaps: shallowCommand(sitemapsCommandMeta, () => import("./commands/sitemaps.mjs").then((module) => module.sitemapsCommand)),
40
+ sync: shallowCommand(syncCommandMeta, () => import("./commands/sync.mjs").then((module) => module.syncCommand)),
41
+ store: shallowCommand(storeCommandMeta, () => import("./commands/store.mjs").then((module) => module.storeCommand)),
42
+ inspect: shallowCommand(inspectCommandMeta, () => import("./commands/inspect.mjs").then((module) => module.inspectCommand)),
43
+ indexing: shallowCommand(indexingCommandMeta, () => import("./commands/indexing.mjs").then((module) => module.indexingCommand)),
44
+ entities: shallowCommand(entitiesCommandMeta, () => import("./commands/entities.mjs").then((module) => module.entitiesCommand)),
45
+ analyze: shallowCommand(analyzeCommandMeta, () => import("./commands/analyze.mjs").then((module) => module.analyzeCommand)),
46
+ report: shallowCommand(reportCommandMeta, () => import("./commands/report.mjs").then((module) => module.reportCommand)),
47
+ auth: shallowCommand(authCommandMeta.auth, () => import("./commands/auth.mjs").then((module) => module.authCommand)),
48
+ login: shallowCommand(authCommandMeta.login, () => import("./commands/auth.mjs").then((module) => module.loginCommand)),
49
+ logout: shallowCommand(authCommandMeta.logout, () => import("./commands/auth.mjs").then((module) => module.logoutCommand)),
50
+ status: shallowCommand(authCommandMeta.status, () => import("./commands/auth.mjs").then((module) => module.statusCommand)),
51
+ config: shallowCommand(configCommandMeta, () => import("./commands/config.mjs").then((module) => module.configCommand)),
52
+ profile: shallowCommand(profileCommandMeta, () => import("./commands/profile.mjs").then((module) => module.profileCommand)),
53
+ doctor: shallowCommand(doctorCommandMeta, () => import("./commands/doctor.mjs").then((module) => module.doctorCommand)),
54
+ mcp: shallowCommand(mcpCommandMeta, () => import("./commands/mcp.mjs").then((module) => module.mcpCommand))
55
+ };
56
+ export { CLI_SUBCOMMANDS };
@@ -1,3 +1,4 @@
1
+ import { analyzeCommandMeta } from "../command-meta.mjs";
1
2
  import { logger, toCSV } from "../utils.mjs";
2
3
  import { gscErrorHandler } from "../error-handler.mjs";
3
4
  import { resolveAnalysisSource } from "../analysis-local.mjs";
@@ -323,10 +324,7 @@ const listCommand = defineCommand({
323
324
  }
324
325
  });
325
326
  const analyzeCommand = defineCommand({
326
- meta: {
327
- name: "analyze",
328
- description: "SEO analysis tools"
329
- },
327
+ meta: analyzeCommandMeta,
330
328
  subCommands: {
331
329
  list: listCommand,
332
330
  ...Object.fromEntries(ANALYSIS_TOOLS.map((tool) => [tool, makeToolCommand(tool)]))
@@ -1,12 +1,13 @@
1
+ import { authCommandMeta } from "../command-meta.mjs";
1
2
  import { loadConfig, saveConfig } from "../config.mjs";
2
3
  import { OUTPUT_ARGS, applyOutputMode, logger, noSubcommandSelected } from "../utils.mjs";
3
- import { adoptCurrentConfigAsProfile, profileNameFromEmail, resolveActiveProfile } from "./profile.mjs";
4
4
  import { authenticate, clearTokens, formatAuthProvenance, getAuth, getAuthCredentials, loadServiceAccount, loadTokens, resolveBYOK, saveTokens } from "../auth.mjs";
5
+ import { missingRequiredScopes } from "../auth-scopes.mjs";
5
6
  import { runSmokeTest } from "./init.mjs";
7
+ import { adoptCurrentConfigAsProfile, profileNameFromEmail, resolveActiveProfile } from "./profile.mjs";
6
8
  import process from "node:process";
7
9
  import { defineCommand } from "citty";
8
10
  import path from "node:path";
9
- import { hasGscWriteScope, hasIndexingScope } from "gscdump";
10
11
  import { ofetch } from "ofetch";
11
12
  const AUTH_SUBCOMMANDS = [
12
13
  "status",
@@ -15,22 +16,6 @@ const AUTH_SUBCOMMANDS = [
15
16
  "refresh",
16
17
  "scopes"
17
18
  ];
18
- const REQUIRED_SCOPES = [
19
- "https://www.googleapis.com/auth/webmasters",
20
- "https://www.googleapis.com/auth/indexing",
21
- "https://www.googleapis.com/auth/siteverification"
22
- ];
23
- function hasGoogleScope(scopes, scope) {
24
- const suffix = scope.replace("https://www.googleapis.com/auth/", "");
25
- return scopes.includes(scope) || scopes.includes(suffix);
26
- }
27
- function missingRequiredScopes(scopes) {
28
- return REQUIRED_SCOPES.filter((scope) => {
29
- if (scope.endsWith("/webmasters")) return !hasGscWriteScope(scopes);
30
- if (scope.endsWith("/indexing")) return !hasIndexingScope(scopes);
31
- return !hasGoogleScope(scopes, scope);
32
- });
33
- }
34
19
  async function fetchTokenInfo(accessToken) {
35
20
  return ofetch("https://oauth2.googleapis.com/tokeninfo", { query: { access_token: accessToken } }).catch(() => null);
36
21
  }
@@ -114,10 +99,7 @@ async function runStatus(args) {
114
99
  if (isExpired && !hasRefresh) process.exit(1);
115
100
  }
116
101
  const statusCommand = defineCommand({
117
- meta: {
118
- name: "status",
119
- description: "Show current authentication status"
120
- },
102
+ meta: authCommandMeta.status,
121
103
  args: { ...OUTPUT_ARGS },
122
104
  async run({ args }) {
123
105
  await runStatus(args);
@@ -156,10 +138,7 @@ const refreshCommand = defineCommand({
156
138
  }
157
139
  });
158
140
  const loginCommand = defineCommand({
159
- meta: {
160
- name: "login",
161
- description: "Run OAuth flow and persist tokens (skip if BYOK env vars set)"
162
- },
141
+ meta: authCommandMeta.login,
163
142
  args: {
164
143
  ...OUTPUT_ARGS,
165
144
  "force": {
@@ -231,10 +210,7 @@ const loginCommand = defineCommand({
231
210
  }
232
211
  });
233
212
  const logoutCommand = defineCommand({
234
- meta: {
235
- name: "logout",
236
- description: "Clear stored OAuth tokens"
237
- },
213
+ meta: authCommandMeta.logout,
238
214
  args: { ...OUTPUT_ARGS },
239
215
  async run({ args }) {
240
216
  applyOutputMode(args);
@@ -273,10 +249,7 @@ const scopesCommand = defineCommand({
273
249
  }
274
250
  });
275
251
  const authCommand = defineCommand({
276
- meta: {
277
- name: "auth",
278
- description: "Manage authentication"
279
- },
252
+ meta: authCommandMeta.auth,
280
253
  args: { ...OUTPUT_ARGS },
281
254
  subCommands: {
282
255
  status: statusCommand,
@@ -1,3 +1,4 @@
1
+ import { configCommandMeta } from "../command-meta.mjs";
1
2
  import { getConfigPath, loadConfig, saveConfig } from "../config.mjs";
2
3
  import { OUTPUT_ARGS, applyOutputMode, displayPath, logger, noSubcommandSelected } from "../utils.mjs";
3
4
  import process from "node:process";
@@ -209,10 +210,7 @@ const validateCommand = defineCommand({
209
210
  }
210
211
  });
211
212
  const configCommand = defineCommand({
212
- meta: {
213
- name: "config",
214
- description: "Manage configuration"
215
- },
213
+ meta: configCommandMeta,
216
214
  subCommands: {
217
215
  show: showCommand,
218
216
  set: setCommand,
@@ -1,21 +1,18 @@
1
+ import { doctorCommandMeta } from "../command-meta.mjs";
1
2
  import { loadConfig } from "../config.mjs";
2
3
  import { resolveCliEnvironment } from "../environment.mjs";
3
- import { OUTPUT_ARGS, applyOutputMode, displayPath, logger } from "../utils.mjs";
4
4
  import { parseEnvFile } from "../env-file.mjs";
5
+ import { OUTPUT_ARGS, applyOutputMode, displayPath, logger } from "../utils.mjs";
5
6
  import { loadTokens, resolveAuth, resolveBYOK } from "../auth.mjs";
6
7
  import { createLocalStore } from "../local-store.mjs";
7
8
  import { createCommandContext } from "../context.mjs";
9
+ import { missingRequiredScopes } from "../auth-scopes.mjs";
8
10
  import process from "node:process";
9
11
  import { defineCommand } from "citty";
10
- import fs from "node:fs/promises";
11
12
  import path from "node:path";
12
- import { googleSearchConsole, hasGscWriteScope, hasIndexingScope } from "gscdump";
13
+ import fs from "node:fs/promises";
14
+ import { googleSearchConsole } from "gscdump/client";
13
15
  import { ofetch } from "ofetch";
14
- const REQUIRED_SCOPES = [
15
- "https://www.googleapis.com/auth/webmasters",
16
- "https://www.googleapis.com/auth/indexing",
17
- "https://www.googleapis.com/auth/siteverification"
18
- ];
19
16
  const FETCH_TIMEOUT_MS = 5e3;
20
17
  const TIME_SKEW_WARN_MS = 3e5;
21
18
  const WATERMARK_STALE_DAYS_WARN = 7;
@@ -36,17 +33,6 @@ function redact(v) {
36
33
  if (v.length <= 6) return "***";
37
34
  return `***${v.slice(-6)}`;
38
35
  }
39
- function hasGoogleScope(scopes, scope) {
40
- const suffix = scope.replace("https://www.googleapis.com/auth/", "");
41
- return scopes.includes(scope) || scopes.includes(suffix);
42
- }
43
- function missingRequiredScopes(scopes) {
44
- return REQUIRED_SCOPES.filter((scope) => {
45
- if (scope.endsWith("/webmasters")) return !hasGscWriteScope(scopes);
46
- if (scope.endsWith("/indexing")) return !hasIndexingScope(scopes);
47
- return !hasGoogleScope(scopes, scope);
48
- });
49
- }
50
36
  async function checkEnv() {
51
37
  const envPath = path.join(process.cwd(), ".env");
52
38
  const parsed = parseEnvFile(envPath);
@@ -335,10 +321,7 @@ async function checkGscSites() {
335
321
  return checks;
336
322
  }
337
323
  const doctorCommand = defineCommand({
338
- meta: {
339
- name: "doctor",
340
- description: "Run health checks (env, auth, scopes, time, dataDir, store, GSC reachability + ping, defaultSite)"
341
- },
324
+ meta: doctorCommandMeta,
342
325
  args: { ...OUTPUT_ARGS },
343
326
  async run({ args }) {
344
327
  const { json } = applyOutputMode(args);
@@ -1,11 +1,12 @@
1
+ import { dumpCommandMeta } from "../command-meta.mjs";
1
2
  import { ALL_SEARCH_TYPES, OUTPUT_ARGS, applyOutputMode, displayPath, logger, parseSearchType, runWithConcurrency, toCSV } from "../utils.mjs";
2
3
  import { allTables } from "../local-store.mjs";
3
4
  import { createCommandContext } from "../context.mjs";
4
5
  import { readParquetRows } from "../native-duckdb.mjs";
5
6
  import process from "node:process";
6
7
  import { defineCommand } from "citty";
7
- import fs from "node:fs/promises";
8
8
  import path from "node:path";
9
+ import fs from "node:fs/promises";
9
10
  const DEFAULT_OUT = "./gscdump-export";
10
11
  const FORMATS = [
11
12
  "parquet",
@@ -14,10 +15,7 @@ const FORMATS = [
14
15
  "csv"
15
16
  ];
16
17
  const dumpCommand = defineCommand({
17
- meta: {
18
- name: "dump",
19
- description: "Export live Parquet files from the local store to a directory"
20
- },
18
+ meta: dumpCommandMeta,
21
19
  args: {
22
20
  "site": {
23
21
  type: "string",
@@ -1,3 +1,4 @@
1
+ import { entitiesCommandMeta } from "../command-meta.mjs";
1
2
  import { OUTPUT_ARGS, applyOutputMode, logger, progressBar, runWithConcurrency } from "../utils.mjs";
2
3
  import { createCommandContext } from "../context.mjs";
3
4
  import process from "node:process";
@@ -267,10 +268,7 @@ const indexingSubCommand = defineCommand({
267
268
  subCommands: { snapshot: indexingSnapshotSubCommand }
268
269
  });
269
270
  const entitiesCommand = defineCommand({
270
- meta: {
271
- name: "entities",
272
- description: "Manage local entity snapshots (URL inspections and indexing metadata)"
273
- },
271
+ meta: entitiesCommandMeta,
274
272
  subCommands: {
275
273
  inspect: inspectSubCommand,
276
274
  show: showSubCommand,
@@ -1,10 +1,11 @@
1
+ import { indexingCommandMeta } from "../command-meta.mjs";
1
2
  import { OUTPUT_ARGS, applyOutputMode, logger, readUrlList } from "../utils.mjs";
2
3
  import { createCommandContext } from "../context.mjs";
3
4
  import { gscErrorHandler } from "../error-handler.mjs";
4
5
  import { loadSitemapUrls } from "../sitemap.mjs";
5
6
  import process from "node:process";
6
7
  import { defineCommand } from "citty";
7
- import { batchRequestIndexing, getIndexingMetadata, requestIndexing, runSequentialBatch } from "gscdump";
8
+ import { batchRequestIndexing, getIndexingMetadata, requestIndexing, runSequentialBatch } from "gscdump/indexing";
8
9
  const RETRIES_ARG = { retries: {
9
10
  type: "string",
10
11
  description: "Override per-call retry count (default: 3)"
@@ -309,10 +310,7 @@ const batchStatusCommand = defineCommand({
309
310
  }
310
311
  });
311
312
  const indexingCommand = defineCommand({
312
- meta: {
313
- name: "indexing",
314
- description: "Notify Google about URL updates/removals (Indexing API)"
315
- },
313
+ meta: indexingCommandMeta,
316
314
  subCommands: {
317
315
  "submit": submitCommand,
318
316
  "remove": removeCommand,
@@ -1,13 +1,14 @@
1
+ import { initCommandMeta } from "../command-meta.mjs";
1
2
  import { defaultDataDir, loadConfig, saveConfig } from "../config.mjs";
2
3
  import { applyCliEnvironment } from "../environment.mjs";
3
4
  import { OUTPUT_ARGS, applyOutputMode, displayPath, logger } from "../utils.mjs";
4
5
  import { authenticate, getAuthCredentials, loadTokens, resolveBYOK, saveTokens } from "../auth.mjs";
5
6
  import process from "node:process";
6
7
  import { defineCommand } from "citty";
7
- import fs from "node:fs/promises";
8
8
  import path from "node:path";
9
+ import fs from "node:fs/promises";
9
10
  import { confirm, isCancel, text } from "@clack/prompts";
10
- import { googleSearchConsole } from "gscdump";
11
+ import { googleSearchConsole } from "gscdump/client";
11
12
  const ENV_LINE_RE = /^([^=]+)=(.*)$/;
12
13
  async function promptDataDir(existing) {
13
14
  const fallback = existing ?? defaultDataDir();
@@ -38,10 +39,7 @@ async function loadEnvFile() {
38
39
  return env;
39
40
  }
40
41
  const initCommand = defineCommand({
41
- meta: {
42
- name: "init",
43
- description: "Set up GSCDump authentication"
44
- },
42
+ meta: initCommandMeta,
45
43
  args: {
46
44
  "force": {
47
45
  type: "boolean",
@@ -1,10 +1,11 @@
1
+ import { inspectCommandMeta } from "../command-meta.mjs";
1
2
  import { OUTPUT_ARGS, applyOutputMode, logger, readUrlList } from "../utils.mjs";
2
3
  import { createCommandContext } from "../context.mjs";
3
4
  import { gscErrorHandler } from "../error-handler.mjs";
4
5
  import { loadSitemapUrls } from "../sitemap.mjs";
5
6
  import process from "node:process";
6
7
  import { defineCommand } from "citty";
7
- import { batchInspectUrls } from "gscdump";
8
+ import { batchInspectUrls } from "gscdump/indexing";
8
9
  function verdictTone(verdict) {
9
10
  if (verdict === "PASS") return "\x1B[32m";
10
11
  if (verdict === "NEUTRAL" || verdict === "PARTIAL") return "\x1B[33m";
@@ -160,10 +161,7 @@ const batchCommand = defineCommand({
160
161
  }
161
162
  });
162
163
  const inspectCommand = defineCommand({
163
- meta: {
164
- name: "inspect",
165
- description: "Inspect URL indexing status (single URL; use `inspect batch` for many)"
166
- },
164
+ meta: inspectCommandMeta,
167
165
  args: {
168
166
  ...OUTPUT_ARGS,
169
167
  site: {
@@ -1,3 +1,4 @@
1
+ import { mcpCommandMeta } from "../command-meta.mjs";
1
2
  import { loadConfig } from "../config.mjs";
2
3
  import { VERSION } from "../utils.mjs";
3
4
  import { loadTokens, resolveAuth, resolveBYOK, resolveServiceAccount } from "../auth.mjs";
@@ -35,10 +36,7 @@ Then restart your MCP client.`
35
36
  return { ok: true };
36
37
  }
37
38
  const mcpCommand = defineCommand({
38
- meta: {
39
- name: "mcp",
40
- description: "Start MCP server for AI assistants"
41
- },
39
+ meta: mcpCommandMeta,
42
40
  async run() {
43
41
  const authCheck = await checkAuth();
44
42
  if (!authCheck.ok) {
@@ -0,0 +1,36 @@
1
+ import { useCliRuntime } from "../runtime.mjs";
2
+ import { setConfigDir } from "../config.mjs";
3
+ import { resolveCliEnvironment } from "../environment.mjs";
4
+ import fs from "node:fs";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ const ROOT_DIR = path.join(os.homedir(), ".config", "gscdump");
8
+ const PROFILES_DIR = path.join(ROOT_DIR, "profiles");
9
+ const ACTIVE_MARKER = path.join(ROOT_DIR, "active-profile");
10
+ function getProfileDir(name) {
11
+ return path.join(PROFILES_DIR, name);
12
+ }
13
+ function readActiveMarkerSync() {
14
+ if (!fs.existsSync(ACTIVE_MARKER)) return null;
15
+ return fs.readFileSync(ACTIVE_MARKER, "utf-8").trim() || null;
16
+ }
17
+ function applyProfileFromCli(opts) {
18
+ if (opts.configDir) {
19
+ setConfigDir(opts.configDir);
20
+ useCliRuntime().configDirOverridden = true;
21
+ return;
22
+ }
23
+ if (opts.profile) {
24
+ useCliRuntime().activeProfileOverride = opts.profile;
25
+ setConfigDir(getProfileDir(opts.profile));
26
+ return;
27
+ }
28
+ const envProfile = opts.envProfile ?? resolveCliEnvironment().profile;
29
+ if (envProfile) {
30
+ setConfigDir(getProfileDir(envProfile));
31
+ return;
32
+ }
33
+ const marker = readActiveMarkerSync();
34
+ if (marker) setConfigDir(getProfileDir(marker));
35
+ }
36
+ export { ACTIVE_MARKER, PROFILES_DIR, ROOT_DIR, applyProfileFromCli, getProfileDir, readActiveMarkerSync };
@@ -1,63 +1,34 @@
1
+ import { profileCommandMeta } from "../command-meta.mjs";
1
2
  import { useCliRuntime } from "../runtime.mjs";
2
3
  import { getConfigDir, setConfigDir } from "../config.mjs";
3
4
  import { resolveCliEnvironment } from "../environment.mjs";
5
+ import { ACTIVE_MARKER, PROFILES_DIR, ROOT_DIR, getProfileDir, readActiveMarkerSync } from "./profile-selection.mjs";
4
6
  import { OUTPUT_ARGS, applyOutputMode, displayPath, logger, noSubcommandSelected } from "../utils.mjs";
5
7
  import process from "node:process";
6
8
  import { defineCommand } from "citty";
7
- import fs from "node:fs";
8
- import fs$1 from "node:fs/promises";
9
- import os from "node:os";
10
9
  import path from "node:path";
10
+ import fs from "node:fs/promises";
11
11
  import { confirm, isCancel } from "@clack/prompts";
12
- const ROOT_DIR = path.join(os.homedir(), ".config", "gscdump");
13
- const PROFILES_DIR = path.join(ROOT_DIR, "profiles");
14
- const ACTIVE_MARKER = path.join(ROOT_DIR, "active-profile");
15
- function getProfileDir(name) {
16
- return path.join(PROFILES_DIR, name);
17
- }
18
- function readActiveMarkerSync() {
19
- if (!fs.existsSync(ACTIVE_MARKER)) return null;
20
- return fs.readFileSync(ACTIVE_MARKER, "utf-8").trim() || null;
21
- }
22
12
  function resolveActiveProfile() {
23
13
  return useCliRuntime().activeProfileOverride ?? resolveCliEnvironment().profile ?? readActiveMarkerSync();
24
14
  }
25
- function applyProfileFromCli(opts) {
26
- if (opts.configDir) {
27
- setConfigDir(opts.configDir);
28
- useCliRuntime().configDirOverridden = true;
29
- return;
30
- }
31
- if (opts.profile) {
32
- useCliRuntime().activeProfileOverride = opts.profile;
33
- setConfigDir(getProfileDir(opts.profile));
34
- return;
35
- }
36
- const envProfile = opts.envProfile ?? resolveCliEnvironment().profile;
37
- if (envProfile) {
38
- setConfigDir(getProfileDir(envProfile));
39
- return;
40
- }
41
- const marker = readActiveMarkerSync();
42
- if (marker) setConfigDir(getProfileDir(marker));
43
- }
44
15
  async function setActiveProfile(name) {
45
- await fs$1.mkdir(ROOT_DIR, {
16
+ await fs.mkdir(ROOT_DIR, {
46
17
  recursive: true,
47
18
  mode: 448
48
19
  });
49
20
  if (name == null) {
50
- await fs$1.rm(ACTIVE_MARKER, { force: true });
21
+ await fs.rm(ACTIVE_MARKER, { force: true });
51
22
  return;
52
23
  }
53
- await fs$1.writeFile(ACTIVE_MARKER, name, { mode: 384 });
24
+ await fs.writeFile(ACTIVE_MARKER, name, { mode: 384 });
54
25
  }
55
26
  async function listProfiles() {
56
- return fs$1.readdir(PROFILES_DIR).then((entries) => entries.filter((e) => !e.startsWith(".")).sort()).catch(() => []);
27
+ return fs.readdir(PROFILES_DIR).then((entries) => entries.filter((e) => !e.startsWith(".")).sort()).catch(() => []);
57
28
  }
58
29
  async function createProfile(name) {
59
30
  const dir = getProfileDir(name);
60
- await fs$1.mkdir(dir, {
31
+ await fs.mkdir(dir, {
61
32
  recursive: true,
62
33
  mode: 448
63
34
  });
@@ -73,14 +44,14 @@ async function adoptCurrentConfigAsProfile(name) {
73
44
  const currentDir = getConfigDir();
74
45
  const targetDir = getProfileDir(name);
75
46
  if (currentDir === targetDir) return targetDir;
76
- await fs$1.mkdir(targetDir, {
47
+ await fs.mkdir(targetDir, {
77
48
  recursive: true,
78
49
  mode: 448
79
50
  });
80
51
  for (const f of ["tokens.json", "config.json"]) {
81
52
  const src = path.join(currentDir, f);
82
53
  const dst = path.join(targetDir, f);
83
- if (await fs$1.stat(src).then(() => true).catch(() => false)) await fs$1.rename(src, dst);
54
+ if (await fs.stat(src).then(() => true).catch(() => false)) await fs.rename(src, dst);
84
55
  }
85
56
  await setActiveProfile(name);
86
57
  runtime.activeProfileOverride = name;
@@ -159,7 +130,7 @@ const useCmd = defineCommand({
159
130
  applyOutputMode(args);
160
131
  const name = String(args.name);
161
132
  const dir = getProfileDir(name);
162
- if (!await fs$1.stat(dir).then(() => true).catch(() => false)) {
133
+ if (!await fs.stat(dir).then(() => true).catch(() => false)) {
163
134
  logger.error(`Profile not found: ${name}`);
164
135
  logger.info(`Create it with: gscdump profile create ${name}`);
165
136
  process.exit(1);
@@ -230,7 +201,7 @@ const deleteCmd = defineCommand({
230
201
  applyOutputMode(args);
231
202
  const name = String(args.name);
232
203
  const dir = getProfileDir(name);
233
- if (!await fs$1.stat(dir).then(() => true).catch(() => false)) {
204
+ if (!await fs.stat(dir).then(() => true).catch(() => false)) {
234
205
  logger.error(`Profile not found: ${name}`);
235
206
  process.exit(1);
236
207
  }
@@ -244,7 +215,7 @@ const deleteCmd = defineCommand({
244
215
  process.exit(0);
245
216
  }
246
217
  }
247
- await fs$1.rm(dir, {
218
+ await fs.rm(dir, {
248
219
  recursive: true,
249
220
  force: true
250
221
  });
@@ -253,10 +224,7 @@ const deleteCmd = defineCommand({
253
224
  }
254
225
  });
255
226
  const profileCommand = defineCommand({
256
- meta: {
257
- name: "profile",
258
- description: "Manage gscdump profiles (per-account token + config dirs)"
259
- },
227
+ meta: profileCommandMeta,
260
228
  subCommands: {
261
229
  list: listCmd,
262
230
  path: pathCmd,
@@ -283,4 +251,4 @@ const profileCommand = defineCommand({
283
251
  });
284
252
  }
285
253
  });
286
- export { adoptCurrentConfigAsProfile, applyProfileFromCli, createProfile, getProfileDir, listProfiles, profileCommand, profileNameFromEmail, resolveActiveProfile, setActiveProfile };
254
+ export { adoptCurrentConfigAsProfile, createProfile, listProfiles, profileCommand, profileNameFromEmail, resolveActiveProfile, setActiveProfile };
@@ -1,3 +1,4 @@
1
+ import { queryCommandMeta } from "../command-meta.mjs";
1
2
  import { loadConfig } from "../config.mjs";
2
3
  import { ALL_SEARCH_TYPES, exportToCSV, logger, parseSearchType } from "../utils.mjs";
3
4
  import { allTables, inferTable } from "../local-store.mjs";
@@ -6,10 +7,10 @@ import { gscErrorHandler } from "../error-handler.mjs";
6
7
  import process from "node:process";
7
8
  import { defineCommand } from "citty";
8
9
  import fs from "node:fs/promises";
9
- import { cancel, isCancel, multiselect, text } from "@clack/prompts";
10
10
  import { and, between, contains, country, date, device, eq, gsc, hour, notRegex, page, query, regex, searchAppearance } from "gscdump/query";
11
+ import { cancel, isCancel, multiselect, text } from "@clack/prompts";
11
12
  import { daysAgoUtc } from "gscdump/dates";
12
- import { collectSpans } from "@gscdump/engine";
13
+ import { collectSpans } from "@gscdump/engine/profile";
13
14
  const DIMENSIONS = [
14
15
  "page",
15
16
  "query",
@@ -124,10 +125,7 @@ function filterToGroups(f) {
124
125
  })) }];
125
126
  }
126
127
  const queryCommand = defineCommand({
127
- meta: {
128
- name: "query",
129
- description: "Run a search analytics query (local Parquet by default, --live hits GSC API)"
130
- },
128
+ meta: queryCommandMeta,
131
129
  args: {
132
130
  "site": {
133
131
  type: "string",
@@ -1,3 +1,4 @@
1
+ import { reportCommandMeta } from "../command-meta.mjs";
1
2
  import { logger } from "../utils.mjs";
2
3
  import { resolveAnalysisSource } from "../analysis-local.mjs";
3
4
  import { defineCommand } from "citty";
@@ -234,10 +235,7 @@ const listCommand = defineCommand({
234
235
  }
235
236
  });
236
237
  const reportCommand = defineCommand({
237
- meta: {
238
- name: "report",
239
- description: "Run an intent-keyed report (composes analyzers into bounded sections)"
240
- },
238
+ meta: reportCommandMeta,
241
239
  subCommands: {
242
240
  list: listCommand,
243
241
  ...Object.fromEntries(REPORT_IDS.map((id) => [id, makeReportCommand(defaultReportRegistry.getReport(id))]))
@@ -1,8 +1,8 @@
1
1
  import { OUTPUT_ARGS, applyOutputMode, logger } from "../utils.mjs";
2
2
  import { createCommandContext } from "../context.mjs";
3
3
  import { defineCommand } from "citty";
4
- import { INTENT_CLASSIFIER_VERSION, NORMALIZER_VERSION, classifyQueryIntent, encodeIntent, normalizeQuery } from "@gscdump/analysis";
5
4
  import { buildQueryDimRecords, createQueryDimStore } from "@gscdump/engine/entities";
5
+ import { INTENT_CLASSIFIER_VERSION, NORMALIZER_VERSION, classifyQueryIntent, encodeIntent, normalizeQuery } from "@gscdump/analysis/query-dimension";
6
6
  import { CANONICAL_ROLLUPS, DEFAULT_ROLLUPS, rebuildRollups } from "@gscdump/engine/rollups";
7
7
  async function buildSiteQueryDim(store, siteId) {
8
8
  const ctx = {
@@ -1,3 +1,4 @@
1
+ import { sitemapsCommandMeta } from "../command-meta.mjs";
1
2
  import { resolveCliEnvironment } from "../environment.mjs";
2
3
  import { OUTPUT_ARGS, applyOutputMode, logger, noSubcommandSelected } from "../utils.mjs";
3
4
  import { createCommandContext } from "../context.mjs";
@@ -5,7 +6,7 @@ import { gscErrorHandler } from "../error-handler.mjs";
5
6
  import { discoverLiveSitemap, loadSitemapUrls } from "../sitemap.mjs";
6
7
  import process from "node:process";
7
8
  import { defineCommand } from "citty";
8
- import { fetchSitemap } from "gscdump";
9
+ import { fetchSitemap } from "gscdump/sites";
9
10
  import { createGscdumpV1Client } from "@gscdump/sdk/v1";
10
11
  const HOSTED_ARGS = {
11
12
  "api-root": {
@@ -467,10 +468,7 @@ const exportCommand = defineCommand({
467
468
  }
468
469
  });
469
470
  const sitemapsCommand = defineCommand({
470
- meta: {
471
- name: "sitemaps",
472
- description: "Manage sitemaps"
473
- },
471
+ meta: sitemapsCommandMeta,
474
472
  subCommands: {
475
473
  list: listCommand,
476
474
  get: getCommand,
@@ -1,10 +1,11 @@
1
+ import { sitesCommandMeta } from "../command-meta.mjs";
1
2
  import { OUTPUT_ARGS, applyOutputMode, logger } from "../utils.mjs";
2
3
  import { createCommandContext } from "../context.mjs";
3
4
  import { gscErrorHandler } from "../error-handler.mjs";
4
5
  import process from "node:process";
5
6
  import { defineCommand } from "citty";
6
7
  import { confirm, isCancel } from "@clack/prompts";
7
- import { addSite, deleteSite, fetchSitesWithSitemaps, getVerificationToken, getVerifiedSite, listVerifiedSites, siteUrlToVerificationSite, unverifySite, verificationMethodsFor, verifySite } from "gscdump";
8
+ import { addSite, deleteSite, fetchSitesWithSitemaps, getVerificationToken, getVerifiedSite, listVerifiedSites, siteUrlToVerificationSite, unverifySite, verificationMethodsFor, verifySite } from "gscdump/sites";
8
9
  const ALL_METHODS = [
9
10
  "META",
10
11
  "FILE",
@@ -483,10 +484,7 @@ const listCommand = defineCommand({
483
484
  }
484
485
  });
485
486
  const sitesCommand = defineCommand({
486
- meta: {
487
- name: "sites",
488
- description: "List GSC sites; manage properties (add/delete) and verify ownership"
489
- },
487
+ meta: sitesCommandMeta,
490
488
  args: LIST_ARGS,
491
489
  subCommands: {
492
490
  "list": listCommand,
@@ -1,3 +1,4 @@
1
+ import { storeCommandMeta } from "../command-meta.mjs";
1
2
  import { noSubcommandSelected } from "../utils.mjs";
2
3
  import { compactCommand } from "./compact.mjs";
3
4
  import { exportCommand } from "./export.mjs";
@@ -7,10 +8,7 @@ import { statsCommand } from "./stats.mjs";
7
8
  import { resetCommand, rmSiteCommand } from "./store-purge.mjs";
8
9
  import { defineCommand } from "citty";
9
10
  const storeCommand = defineCommand({
10
- meta: {
11
- name: "store",
12
- description: "Manage the local DuckDB/Parquet store"
13
- },
11
+ meta: storeCommandMeta,
14
12
  subCommands: {
15
13
  "stats": statsCommand,
16
14
  "compact": compactCommand,
@@ -1,3 +1,4 @@
1
+ import { syncCommandMeta } from "../command-meta.mjs";
1
2
  import { OUTPUT_ARGS, applyOutputMode, clearLine, displayPath, formatAge, logger, progressBar, runWithConcurrency } from "../utils.mjs";
2
3
  import { TABLE_DIMS, allTables, createLocalStore, transformGscRow } from "../local-store.mjs";
3
4
  import { createCommandContext } from "../context.mjs";
@@ -135,10 +136,7 @@ async function runOneDate(store, client, siteUrl, table, searchType, dims, date)
135
136
  };
136
137
  }
137
138
  const syncCommand = defineCommand({
138
- meta: {
139
- name: "sync",
140
- description: "Sync GSC data to local Parquet store"
141
- },
139
+ meta: syncCommandMeta,
142
140
  args: {
143
141
  "site": {
144
142
  type: "string",
package/dist/config.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useCliRuntime } from "./runtime.mjs";
2
- import fs from "node:fs/promises";
3
2
  import os from "node:os";
4
3
  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/context.mjs CHANGED
@@ -4,7 +4,7 @@ import { resolveAuth } from "./auth.mjs";
4
4
  import { createLocalStore } from "./local-store.mjs";
5
5
  import process from "node:process";
6
6
  import { cancel, isCancel, select } from "@clack/prompts";
7
- import { googleSearchConsole } from "gscdump";
7
+ import { googleSearchConsole } from "gscdump/client";
8
8
  async function createCommandContext(opts = {}) {
9
9
  const { needsAuth = false, needsStore = false, interactive = false, byok, fetchOptions } = opts;
10
10
  const { config, dataDir } = await loadResolvedConfig();
@@ -1,9 +1,9 @@
1
1
  import { formatAuthProvenance, isAuthError } from "./auth.mjs";
2
2
  import process from "node:process";
3
3
  import { isQueryError } from "gscdump/query";
4
- import { classifyError } from "gscdump";
5
4
  import { isAnalysisError } from "@gscdump/analysis/errors";
6
5
  import { isEngineError } from "@gscdump/engine/errors";
6
+ import { classifyError } from "gscdump/errors";
7
7
  const QUOTA_MESSAGE_RE = /quota|rate\s*limit/i;
8
8
  function formatErrorForCli(cause) {
9
9
  const error = classifyError(cause);
@@ -1,23 +1,7 @@
1
- import { hasGscWriteScope, hasIndexingScope } from "gscdump";
1
+ import { missingRequiredScopes } from "../../auth-scopes.mjs";
2
2
  import { ofetch } from "ofetch";
3
- const REQUIRED_SCOPES = [
4
- "https://www.googleapis.com/auth/webmasters",
5
- "https://www.googleapis.com/auth/indexing",
6
- "https://www.googleapis.com/auth/siteverification"
7
- ];
8
3
  const FETCH_TIMEOUT_MS = 5e3;
9
4
  const TIME_SKEW_WARN_MS = 3e5;
10
- function hasGoogleScope(scopes, scope) {
11
- const suffix = scope.replace("https://www.googleapis.com/auth/", "");
12
- return scopes.includes(scope) || scopes.includes(suffix);
13
- }
14
- function missingRequiredScopes(scopes) {
15
- return REQUIRED_SCOPES.filter((scope) => {
16
- if (scope.endsWith("/webmasters")) return !hasGscWriteScope(scopes);
17
- if (scope.endsWith("/indexing")) return !hasIndexingScope(scopes);
18
- return !hasGoogleScope(scopes, scope);
19
- });
20
- }
21
5
  async function diagnostics(_input, ctx) {
22
6
  const checks = [];
23
7
  const token = await resolveAccessToken(ctx.auth);
@@ -1,4 +1,4 @@
1
- import { batchInspectUrls, batchRequestIndexing, getIndexingMetadata, requestIndexing } from "gscdump";
1
+ import { batchInspectUrls, batchRequestIndexing, getIndexingMetadata, requestIndexing } from "gscdump/indexing";
2
2
  async function requestIndexing$1(input, ctx) {
3
3
  return requestIndexing(ctx.client, input.url, { type: input.type || "URL_UPDATED" });
4
4
  }
@@ -1,4 +1,4 @@
1
- import { fetchSitemap, fetchSitesWithSitemaps } from "gscdump";
1
+ import { fetchSitemap, fetchSitesWithSitemaps } from "gscdump/sites";
2
2
  async function listSitesWithSitemaps(_input, ctx) {
3
3
  return fetchSitesWithSitemaps(ctx.client);
4
4
  }
@@ -1,12 +1,13 @@
1
1
  import { discoverLiveSitemap } from "../../sitemap.mjs";
2
2
  import { diagnostics } from "../handlers/diagnostics.mjs";
3
3
  import { batchInspectUrls as batchInspectUrls$1, batchRequestIndexing as batchRequestIndexing$1, getIndexingStatus, requestIndexing as requestIndexing$1 } from "../handlers/indexing.mjs";
4
- import { listReports, runReportHandler } from "../handlers/reports.mjs";
5
4
  import { getSitemap, listSitesWithSitemaps } from "../handlers/sites.mjs";
6
- import "../handlers/index.mjs";
7
5
  import { batchInspectUrlsInput, batchRequestIndexingInput, getIndexingStatusInput, inspectUrlInput, listReportsInput, listSitemapsInput, listSitesInput, requestIndexingInput, runReportInput, sitemapInput } from "../types.mjs";
8
- import { addSite, deleteSite, getIndexingMetadata, getVerificationToken, getVerifiedSite, googleSearchConsole, listVerifiedSites, runSequentialBatch, unverifySite, verifySite } from "gscdump";
6
+ import { SearchTypes } from "gscdump/query";
7
+ import { googleSearchConsole } from "gscdump/client";
8
+ import { getIndexingMetadata, runSequentialBatch } from "gscdump/indexing";
9
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
+ import { addSite, deleteSite, getVerificationToken, getVerifiedSite, listVerifiedSites, unverifySite, verifySite } from "gscdump/sites";
10
11
  import { z } from "zod";
11
12
  async function runMcpSearchAnalyticsQuery(client, args) {
12
13
  const totalLimit = Math.max(0, args.rowLimit ?? 25e3);
@@ -134,6 +135,7 @@ function createGscMcpServer(options) {
134
135
  description: "List available reports (intent-keyed analyzer compositions). Returns id, description, default period/comparison, and per-report argsSpec.",
135
136
  inputSchema: listReportsInput.shape
136
137
  }, async () => {
138
+ const { listReports } = await import("../handlers/reports.mjs");
137
139
  const result = listReports();
138
140
  return { content: [{
139
141
  type: "text",
@@ -144,6 +146,7 @@ function createGscMcpServer(options) {
144
146
  description: "Run a report against the GSC API. Returns a structured ReportResult with bounded findings per section. See list-reports for ids.",
145
147
  inputSchema: runReportInput.shape
146
148
  }, async (args) => {
149
+ const { runReportHandler } = await import("../handlers/reports.mjs");
147
150
  const result = await runReportHandler(args, await getContext());
148
151
  return { content: [{
149
152
  type: "text",
@@ -189,14 +192,7 @@ function createGscMcpServer(options) {
189
192
  "searchAppearance"
190
193
  ])).describe("Dimensions to group by"),
191
194
  rowLimit: z.number().optional().describe("Max rows (default 25000)"),
192
- type: z.enum([
193
- "web",
194
- "image",
195
- "video",
196
- "news",
197
- "discover",
198
- "googleNews"
199
- ]).optional().describe("Search type"),
195
+ type: z.enum(SearchTypes).optional().describe("Search type"),
200
196
  dataState: z.enum(["final", "all"]).optional().describe("Data state: final (settled) or all (includes fresh)"),
201
197
  aggregationType: z.enum(["byPage", "byProperty"]).optional().describe("Aggregation type"),
202
198
  dimensionFilterGroups: z.array(filterGroupSchema).optional().describe("Filter groups (each \"and\"-ed internally; multiple groups are OR-ed)")
@@ -1,3 +1,4 @@
1
+ import { SearchTypes } from "gscdump/query";
1
2
  import { z } from "zod";
2
3
  const periodSchema = z.object({
3
4
  start: z.string().describe("Start date (YYYY-MM-DD)"),
@@ -5,14 +6,7 @@ const periodSchema = z.object({
5
6
  }).describe("Date range for the query");
6
7
  const siteUrlSchema = z.string().describe("GSC property URL (e.g., sc-domain:example.com or https://example.com/)");
7
8
  const queryOptionsSchema = z.object({
8
- type: z.enum([
9
- "web",
10
- "image",
11
- "video",
12
- "news",
13
- "discover",
14
- "googleNews"
15
- ]).optional().describe("Data type"),
9
+ type: z.enum(SearchTypes).optional().describe("Data type"),
16
10
  dataState: z.enum(["final", "all"]).optional().describe("Data state: final (settled) or all (includes fresh)"),
17
11
  aggregationType: z.enum(["byPage", "byProperty"]).optional().describe("Aggregation: byPage or byProperty")
18
12
  }).optional();
package/dist/package.mjs CHANGED
@@ -1,2 +1,2 @@
1
- var version = "3.0.0";
1
+ var version = "3.1.0";
2
2
  export { version };
package/dist/utils.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { useCliRuntime } from "./runtime.mjs";
2
2
  import { version } from "./package.mjs";
3
3
  import process from "node:process";
4
- import fs from "node:fs/promises";
5
4
  import os from "node:os";
5
+ import fs from "node:fs/promises";
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": "3.0.0",
4
+ "version": "3.1.0",
5
5
  "description": "CLI for Google Search Console - dump, query, and run MCP server",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -51,11 +51,11 @@
51
51
  "open": "^11.0.1",
52
52
  "sitemapd": "^0.2.2",
53
53
  "zod": "^4.4.3",
54
- "@gscdump/engine": "^3.0.0",
55
- "@gscdump/analysis": "^3.0.0",
56
- "@gscdump/engine-gsc-api": "^3.0.0",
57
- "@gscdump/sdk": "^3.0.0",
58
- "gscdump": "^3.0.0"
54
+ "@gscdump/analysis": "^3.1.0",
55
+ "@gscdump/engine": "^3.1.0",
56
+ "@gscdump/engine-gsc-api": "^3.1.0",
57
+ "@gscdump/sdk": "^3.1.0",
58
+ "gscdump": "^3.1.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "vitest": "^4.1.10"
@@ -1 +0,0 @@
1
- export {};
@@ -1,7 +0,0 @@
1
- import "./analytics.mjs";
2
- import { diagnostics } from "./diagnostics.mjs";
3
- import { batchInspectUrls, batchRequestIndexing, getIndexingStatus, requestIndexing } from "./indexing.mjs";
4
- import "./query.mjs";
5
- import { listReports, runReportHandler, runReportHandlerResult } from "./reports.mjs";
6
- import { getSitemap, listSitesWithSitemaps } from "./sites.mjs";
7
- export { batchInspectUrls, batchRequestIndexing, diagnostics, getIndexingStatus, getSitemap, listReports, listSitesWithSitemaps, requestIndexing, runReportHandler, runReportHandlerResult };
@@ -1,5 +0,0 @@
1
- import "./analytics.mjs";
2
- import "../errors.mjs";
3
- import "gscdump/query";
4
- import "gscdump/result";
5
- export {};