@gscdump/cli 3.0.0 → 3.2.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.
- package/dist/analysis-local.mjs +3 -2
- package/dist/auth-scopes.mjs +9 -0
- package/dist/auth.mjs +4 -4
- package/dist/cli.mjs +4 -24
- package/dist/command-meta.mjs +83 -0
- package/dist/command-registry.mjs +56 -0
- package/dist/commands/analyze.mjs +2 -4
- package/dist/commands/auth.mjs +7 -34
- package/dist/commands/config.mjs +2 -4
- package/dist/commands/doctor.mjs +6 -23
- package/dist/commands/dump.mjs +3 -5
- package/dist/commands/entities.mjs +2 -4
- package/dist/commands/indexing.mjs +3 -5
- package/dist/commands/init.mjs +4 -6
- package/dist/commands/inspect.mjs +3 -5
- package/dist/commands/mcp.mjs +2 -4
- package/dist/commands/profile-selection.mjs +36 -0
- package/dist/commands/profile.mjs +15 -47
- package/dist/commands/query.mjs +4 -6
- package/dist/commands/report.mjs +2 -4
- package/dist/commands/rollups.mjs +1 -1
- package/dist/commands/sitemaps.mjs +3 -5
- package/dist/commands/sites.mjs +3 -5
- package/dist/commands/store.mjs +2 -4
- package/dist/commands/sync.mjs +2 -4
- package/dist/config.mjs +1 -1
- package/dist/context.mjs +1 -1
- package/dist/error-handler.mjs +1 -1
- package/dist/mcp/handlers/diagnostics.mjs +1 -17
- package/dist/mcp/handlers/indexing.mjs +1 -1
- package/dist/mcp/handlers/sites.mjs +1 -1
- package/dist/mcp/server/index.mjs +7 -11
- package/dist/mcp/types.mjs +2 -8
- package/dist/package.mjs +1 -1
- package/dist/utils.mjs +1 -1
- package/package.json +6 -6
- package/dist/mcp/handlers/analytics.mjs +0 -1
- package/dist/mcp/handlers/index.mjs +0 -7
- package/dist/mcp/handlers/query.mjs +0 -5
package/dist/analysis-local.mjs
CHANGED
|
@@ -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
|
|
7
|
+
import fs from "node:fs/promises";
|
|
9
8
|
import { err, ok, unwrapResult } from "gscdump/result";
|
|
10
|
-
import {
|
|
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 {
|
|
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)]))
|
package/dist/commands/auth.mjs
CHANGED
|
@@ -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,
|
package/dist/commands/config.mjs
CHANGED
|
@@ -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,
|
package/dist/commands/doctor.mjs
CHANGED
|
@@ -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
|
|
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);
|
package/dist/commands/dump.mjs
CHANGED
|
@@ -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,
|
package/dist/commands/init.mjs
CHANGED
|
@@ -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: {
|
package/dist/commands/mcp.mjs
CHANGED
|
@@ -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
|
|
16
|
+
await fs.mkdir(ROOT_DIR, {
|
|
46
17
|
recursive: true,
|
|
47
18
|
mode: 448
|
|
48
19
|
});
|
|
49
20
|
if (name == null) {
|
|
50
|
-
await fs
|
|
21
|
+
await fs.rm(ACTIVE_MARKER, { force: true });
|
|
51
22
|
return;
|
|
52
23
|
}
|
|
53
|
-
await fs
|
|
24
|
+
await fs.writeFile(ACTIVE_MARKER, name, { mode: 384 });
|
|
54
25
|
}
|
|
55
26
|
async function listProfiles() {
|
|
56
|
-
return fs
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
254
|
+
export { adoptCurrentConfigAsProfile, createProfile, listProfiles, profileCommand, profileNameFromEmail, resolveActiveProfile, setActiveProfile };
|
package/dist/commands/query.mjs
CHANGED
|
@@ -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",
|
package/dist/commands/report.mjs
CHANGED
|
@@ -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,
|
package/dist/commands/sites.mjs
CHANGED
|
@@ -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,
|
package/dist/commands/store.mjs
CHANGED
|
@@ -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,
|
package/dist/commands/sync.mjs
CHANGED
|
@@ -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
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();
|
package/dist/error-handler.mjs
CHANGED
|
@@ -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 {
|
|
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,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 {
|
|
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)")
|
package/dist/mcp/types.mjs
CHANGED
|
@@ -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.
|
|
1
|
+
var version = "3.2.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.
|
|
4
|
+
"version": "3.2.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/
|
|
55
|
-
"@gscdump/
|
|
56
|
-
"@gscdump/engine-gsc-api": "^3.
|
|
57
|
-
"
|
|
58
|
-
"gscdump": "^3.
|
|
54
|
+
"@gscdump/analysis": "^3.2.0",
|
|
55
|
+
"@gscdump/engine": "^3.2.0",
|
|
56
|
+
"@gscdump/engine-gsc-api": "^3.2.0",
|
|
57
|
+
"gscdump": "^3.2.0",
|
|
58
|
+
"@gscdump/sdk": "^3.2.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 };
|