@gscdump/cli 0.32.12 → 0.33.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +106 -51
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { err, ok, unwrapResult } from "gscdump/result";
|
|
|
10
10
|
import { decodeSiteId, normalizeSiteUrl } from "gscdump/tenant";
|
|
11
11
|
import os from "node:os";
|
|
12
12
|
import { cancel, confirm, isCancel, multiselect, select, text } from "@clack/prompts";
|
|
13
|
-
import { addSite, batchInspectUrls, batchRequestIndexing, createAuth, deleteSite, discoverSitemap, fetchSitemap, fetchSitemapUrls, fetchSitesWithSitemaps, formatErrorForCli, getIndexingMetadata, getVerificationToken, getVerifiedSite, googleSearchConsole, listVerifiedSites, requestIndexing, runSequentialBatch, siteUrlToVerificationSite, unverifySite, verificationMethodsFor, verifySite } from "gscdump/api";
|
|
13
|
+
import { addSite, batchInspectUrls, batchRequestIndexing, createAuth, deleteSite, discoverSitemap, fetchSitemap, fetchSitemapUrls, fetchSitesWithSitemaps, formatErrorForCli, getIndexingMetadata, getVerificationToken, getVerifiedSite, googleSearchConsole, hasGscWriteScope, hasIndexingScope, listVerifiedSites, requestIndexing, runSequentialBatch, siteUrlToVerificationSite, unverifySite, verificationMethodsFor, verifySite } from "gscdump/api";
|
|
14
14
|
import { createServer } from "node:http";
|
|
15
15
|
import { JWT, OAuth2Client } from "google-auth-library";
|
|
16
16
|
import { ofetch } from "ofetch";
|
|
@@ -136,7 +136,7 @@ function loadEnvFromCwd() {
|
|
|
136
136
|
}
|
|
137
137
|
return applied;
|
|
138
138
|
}
|
|
139
|
-
var version = "0.
|
|
139
|
+
var version = "0.33.3";
|
|
140
140
|
const ALL_SEARCH_TYPES$1 = Object.values(SearchTypes);
|
|
141
141
|
const VERSION = version;
|
|
142
142
|
function noSubcommandSelected(parent, subNames) {
|
|
@@ -1769,6 +1769,17 @@ const REQUIRED_SCOPES$2 = [
|
|
|
1769
1769
|
"https://www.googleapis.com/auth/indexing",
|
|
1770
1770
|
"https://www.googleapis.com/auth/siteverification"
|
|
1771
1771
|
];
|
|
1772
|
+
function hasGoogleScope$2(scopes, scope) {
|
|
1773
|
+
const suffix = scope.replace("https://www.googleapis.com/auth/", "");
|
|
1774
|
+
return scopes.includes(scope) || scopes.includes(suffix);
|
|
1775
|
+
}
|
|
1776
|
+
function missingRequiredScopes$2(scopes) {
|
|
1777
|
+
return REQUIRED_SCOPES$2.filter((scope) => {
|
|
1778
|
+
if (scope.endsWith("/webmasters")) return !hasGscWriteScope(scopes);
|
|
1779
|
+
if (scope.endsWith("/indexing")) return !hasIndexingScope(scopes);
|
|
1780
|
+
return !hasGoogleScope$2(scopes, scope);
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1772
1783
|
async function fetchTokenInfo(accessToken) {
|
|
1773
1784
|
return ofetch("https://oauth2.googleapis.com/tokeninfo", { query: { access_token: accessToken } }).catch(() => null);
|
|
1774
1785
|
}
|
|
@@ -1781,8 +1792,7 @@ async function resolveLiveAuthState() {
|
|
|
1781
1792
|
else if (tokens?.access_token) liveToken = tokens.access_token;
|
|
1782
1793
|
const tokenInfo = liveToken ? await fetchTokenInfo(liveToken) : null;
|
|
1783
1794
|
const scopes = tokenInfo?.scope ? tokenInfo.scope.split(/\s+/).filter(Boolean) : [];
|
|
1784
|
-
const
|
|
1785
|
-
const missing = REQUIRED_SCOPES$2.filter((s) => !has(s));
|
|
1795
|
+
const missing = missingRequiredScopes$2(scopes);
|
|
1786
1796
|
return {
|
|
1787
1797
|
byok,
|
|
1788
1798
|
tokens,
|
|
@@ -2277,6 +2287,17 @@ function redact(v) {
|
|
|
2277
2287
|
if (v.length <= 6) return "***";
|
|
2278
2288
|
return `***${v.slice(-6)}`;
|
|
2279
2289
|
}
|
|
2290
|
+
function hasGoogleScope$1(scopes, scope) {
|
|
2291
|
+
const suffix = scope.replace("https://www.googleapis.com/auth/", "");
|
|
2292
|
+
return scopes.includes(scope) || scopes.includes(suffix);
|
|
2293
|
+
}
|
|
2294
|
+
function missingRequiredScopes$1(scopes) {
|
|
2295
|
+
return REQUIRED_SCOPES$1.filter((scope) => {
|
|
2296
|
+
if (scope.endsWith("/webmasters")) return !hasGscWriteScope(scopes);
|
|
2297
|
+
if (scope.endsWith("/indexing")) return !hasIndexingScope(scopes);
|
|
2298
|
+
return !hasGoogleScope$1(scopes, scope);
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2280
2301
|
async function checkEnv() {
|
|
2281
2302
|
const envPath = path.join(process.cwd(), ".env");
|
|
2282
2303
|
const parsed = parseEnvFile(envPath);
|
|
@@ -2384,7 +2405,7 @@ async function checkAuth$1(envKeys) {
|
|
|
2384
2405
|
detail: info.email
|
|
2385
2406
|
});
|
|
2386
2407
|
const scopes = info.scope ? info.scope.split(/\s+/) : [];
|
|
2387
|
-
const missing =
|
|
2408
|
+
const missing = missingRequiredScopes$1(scopes);
|
|
2388
2409
|
if (missing.length > 0) checks.push({
|
|
2389
2410
|
name: "auth.scopes",
|
|
2390
2411
|
status: "warn",
|
|
@@ -3740,6 +3761,17 @@ const REQUIRED_SCOPES = [
|
|
|
3740
3761
|
];
|
|
3741
3762
|
const FETCH_TIMEOUT_MS = 5e3;
|
|
3742
3763
|
const TIME_SKEW_WARN_MS = 5 * 6e4;
|
|
3764
|
+
function hasGoogleScope(scopes, scope) {
|
|
3765
|
+
const suffix = scope.replace("https://www.googleapis.com/auth/", "");
|
|
3766
|
+
return scopes.includes(scope) || scopes.includes(suffix);
|
|
3767
|
+
}
|
|
3768
|
+
function missingRequiredScopes(scopes) {
|
|
3769
|
+
return REQUIRED_SCOPES.filter((scope) => {
|
|
3770
|
+
if (scope.endsWith("/webmasters")) return !hasGscWriteScope(scopes);
|
|
3771
|
+
if (scope.endsWith("/indexing")) return !hasIndexingScope(scopes);
|
|
3772
|
+
return !hasGoogleScope(scopes, scope);
|
|
3773
|
+
});
|
|
3774
|
+
}
|
|
3743
3775
|
async function diagnostics(_input, ctx) {
|
|
3744
3776
|
const checks = [];
|
|
3745
3777
|
const token = await resolveAccessToken(ctx.auth);
|
|
@@ -3773,7 +3805,7 @@ async function diagnostics(_input, ctx) {
|
|
|
3773
3805
|
detail: tokenInfo.email ?? "token valid"
|
|
3774
3806
|
});
|
|
3775
3807
|
const scopes = tokenInfo.scope ? tokenInfo.scope.split(/\s+/) : [];
|
|
3776
|
-
const missing =
|
|
3808
|
+
const missing = missingRequiredScopes(scopes);
|
|
3777
3809
|
checks.push(missing.length > 0 ? {
|
|
3778
3810
|
name: "auth.scopes",
|
|
3779
3811
|
status: "warn",
|
|
@@ -4114,6 +4146,50 @@ const runReportInput = z.object({
|
|
|
4114
4146
|
prevEnd: z.string().optional().describe("Override comparison-window end."),
|
|
4115
4147
|
maxFindings: z.number().optional().describe("Cap findings per section (per-report default ~5).")
|
|
4116
4148
|
});
|
|
4149
|
+
async function runMcpSearchAnalyticsQuery(client, args) {
|
|
4150
|
+
const totalLimit = Math.max(0, args.rowLimit ?? 25e3);
|
|
4151
|
+
const pageSize = Math.min(totalLimit, 25e3);
|
|
4152
|
+
const allRows = [];
|
|
4153
|
+
let startRow = 0;
|
|
4154
|
+
while (allRows.length < totalLimit) {
|
|
4155
|
+
const remaining = totalLimit - allRows.length;
|
|
4156
|
+
const currentLimit = Math.min(pageSize, remaining);
|
|
4157
|
+
if (currentLimit <= 0) break;
|
|
4158
|
+
const rows = ((await client._rawQuery(args.siteUrl, {
|
|
4159
|
+
startDate: args.startDate,
|
|
4160
|
+
endDate: args.endDate,
|
|
4161
|
+
dimensions: args.dimensions,
|
|
4162
|
+
rowLimit: currentLimit,
|
|
4163
|
+
startRow,
|
|
4164
|
+
...args.type ? { type: args.type } : {},
|
|
4165
|
+
...args.dataState ? { dataState: args.dataState } : {},
|
|
4166
|
+
...args.aggregationType ? { aggregationType: args.aggregationType } : {},
|
|
4167
|
+
...args.dimensionFilterGroups ? { dimensionFilterGroups: args.dimensionFilterGroups.map((g) => ({
|
|
4168
|
+
groupType: g.groupType ?? "and",
|
|
4169
|
+
filters: g.filters
|
|
4170
|
+
})) } : {}
|
|
4171
|
+
})).rows || []).map((row) => {
|
|
4172
|
+
const result = {
|
|
4173
|
+
clicks: row.clicks ?? 0,
|
|
4174
|
+
impressions: row.impressions ?? 0,
|
|
4175
|
+
ctr: row.ctr ?? 0,
|
|
4176
|
+
position: row.position ?? 0
|
|
4177
|
+
};
|
|
4178
|
+
args.dimensions.forEach((dim, i) => {
|
|
4179
|
+
result[dim] = row.keys?.[i];
|
|
4180
|
+
});
|
|
4181
|
+
return result;
|
|
4182
|
+
});
|
|
4183
|
+
if (rows.length === 0) break;
|
|
4184
|
+
allRows.push(...rows);
|
|
4185
|
+
startRow += rows.length;
|
|
4186
|
+
}
|
|
4187
|
+
return {
|
|
4188
|
+
siteUrl: args.siteUrl,
|
|
4189
|
+
rowCount: allRows.length,
|
|
4190
|
+
rows: allRows
|
|
4191
|
+
};
|
|
4192
|
+
}
|
|
4117
4193
|
function createGscMcpServer(options) {
|
|
4118
4194
|
const { name = "gscdump", version = "1.0.0", getAuth } = options;
|
|
4119
4195
|
const server = new McpServer({
|
|
@@ -4264,47 +4340,20 @@ function createGscMcpServer(options) {
|
|
|
4264
4340
|
dimensionFilterGroups: z.array(filterGroupSchema).optional().describe("Filter groups (each \"and\"-ed internally; multiple groups are OR-ed)")
|
|
4265
4341
|
}).shape
|
|
4266
4342
|
}, async ({ siteUrl, startDate, endDate, dimensions, rowLimit, type, dataState, aggregationType, dimensionFilterGroups }) => {
|
|
4267
|
-
const
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
...type ? { type } : {},
|
|
4279
|
-
...dataState ? { dataState } : {},
|
|
4280
|
-
...aggregationType ? { aggregationType } : {},
|
|
4281
|
-
...dimensionFilterGroups ? { dimensionFilterGroups: dimensionFilterGroups.map((g) => ({
|
|
4282
|
-
groupType: g.groupType ?? "and",
|
|
4283
|
-
filters: g.filters
|
|
4284
|
-
})) } : {}
|
|
4285
|
-
})).rows || []).map((row) => {
|
|
4286
|
-
const result = {
|
|
4287
|
-
clicks: row.clicks ?? 0,
|
|
4288
|
-
impressions: row.impressions ?? 0,
|
|
4289
|
-
ctr: row.ctr ?? 0,
|
|
4290
|
-
position: row.position ?? 0
|
|
4291
|
-
};
|
|
4292
|
-
dimensions.forEach((dim, i) => {
|
|
4293
|
-
result[dim] = row.keys?.[i];
|
|
4294
|
-
});
|
|
4295
|
-
return result;
|
|
4296
|
-
});
|
|
4297
|
-
allRows.push(...rows);
|
|
4298
|
-
if (rows.length < limit) break;
|
|
4299
|
-
startRow += rows.length;
|
|
4300
|
-
}
|
|
4343
|
+
const result = await runMcpSearchAnalyticsQuery(await getClient(), {
|
|
4344
|
+
siteUrl,
|
|
4345
|
+
startDate,
|
|
4346
|
+
endDate,
|
|
4347
|
+
dimensions,
|
|
4348
|
+
rowLimit,
|
|
4349
|
+
type,
|
|
4350
|
+
dataState,
|
|
4351
|
+
aggregationType,
|
|
4352
|
+
dimensionFilterGroups
|
|
4353
|
+
});
|
|
4301
4354
|
return { content: [{
|
|
4302
4355
|
type: "text",
|
|
4303
|
-
text: JSON.stringify(
|
|
4304
|
-
siteUrl,
|
|
4305
|
-
rowCount: allRows.length,
|
|
4306
|
-
rows: allRows
|
|
4307
|
-
}, null, 2)
|
|
4356
|
+
text: JSON.stringify(result, null, 2)
|
|
4308
4357
|
}] };
|
|
4309
4358
|
});
|
|
4310
4359
|
server.registerTool("inspect-url", {
|
|
@@ -4505,6 +4554,7 @@ function createGscMcpServer(options) {
|
|
|
4505
4554
|
return server;
|
|
4506
4555
|
}
|
|
4507
4556
|
async function checkAuth() {
|
|
4557
|
+
if (await resolveServiceAccount().then(Boolean).catch(() => false)) return { ok: true };
|
|
4508
4558
|
if (resolveBYOK()) return { ok: true };
|
|
4509
4559
|
const config = await loadConfig();
|
|
4510
4560
|
if (!config.clientId && !config.clientSecret) return {
|
|
@@ -4546,7 +4596,7 @@ const mcpCommand = defineCommand({
|
|
|
4546
4596
|
const server = createGscMcpServer({
|
|
4547
4597
|
name: "gscdump",
|
|
4548
4598
|
version: VERSION,
|
|
4549
|
-
getAuth: () =>
|
|
4599
|
+
getAuth: () => resolveAuth({ interactive: false })
|
|
4550
4600
|
});
|
|
4551
4601
|
const transport = new StdioServerTransport();
|
|
4552
4602
|
await server.connect(transport);
|
|
@@ -4616,12 +4666,13 @@ function makeLeaf(col, operator, value) {
|
|
|
4616
4666
|
}
|
|
4617
4667
|
async function runLiveQuery(client, siteUrl, opts) {
|
|
4618
4668
|
const allRows = [];
|
|
4669
|
+
const totalLimit = Math.max(0, opts.rowLimit);
|
|
4670
|
+
const pageSize = Math.min(totalLimit, 25e3);
|
|
4619
4671
|
let startRow = 0;
|
|
4620
4672
|
const baseBody = {
|
|
4621
4673
|
startDate: opts.startDate,
|
|
4622
4674
|
endDate: opts.endDate,
|
|
4623
|
-
dimensions: opts.dimensions
|
|
4624
|
-
rowLimit: opts.rowLimit
|
|
4675
|
+
dimensions: opts.dimensions
|
|
4625
4676
|
};
|
|
4626
4677
|
if (opts.searchType) baseBody.type = opts.searchType;
|
|
4627
4678
|
if (opts.dataState) baseBody.dataState = opts.dataState;
|
|
@@ -4630,9 +4681,13 @@ async function runLiveQuery(client, siteUrl, opts) {
|
|
|
4630
4681
|
const groups = filterToGroups(opts.dimensionFilter);
|
|
4631
4682
|
if (groups.length > 0) baseBody.dimensionFilterGroups = groups;
|
|
4632
4683
|
}
|
|
4633
|
-
while (
|
|
4684
|
+
while (allRows.length < totalLimit) {
|
|
4685
|
+
const remaining = totalLimit - allRows.length;
|
|
4686
|
+
const rowLimit = Math.min(pageSize, remaining);
|
|
4687
|
+
if (rowLimit <= 0) break;
|
|
4634
4688
|
const rows = ((await client._rawQuery(siteUrl, {
|
|
4635
4689
|
...baseBody,
|
|
4690
|
+
rowLimit,
|
|
4636
4691
|
startRow
|
|
4637
4692
|
})).rows || []).map((row) => {
|
|
4638
4693
|
const result = {
|
|
@@ -4646,8 +4701,8 @@ async function runLiveQuery(client, siteUrl, opts) {
|
|
|
4646
4701
|
});
|
|
4647
4702
|
return result;
|
|
4648
4703
|
});
|
|
4704
|
+
if (rows.length === 0) break;
|
|
4649
4705
|
allRows.push(...rows);
|
|
4650
|
-
if (rows.length < opts.rowLimit) break;
|
|
4651
4706
|
startRow += rows.length;
|
|
4652
4707
|
}
|
|
4653
4708
|
return { rows: allRows };
|
|
@@ -6829,7 +6884,7 @@ async function runOneDate(store, client, siteUrl, table, searchType, dims, date)
|
|
|
6829
6884
|
});
|
|
6830
6885
|
if (transformed) rows.push(transformed.row);
|
|
6831
6886
|
}
|
|
6832
|
-
if (batch.length
|
|
6887
|
+
if (batch.length === 0) break;
|
|
6833
6888
|
startRow += batch.length;
|
|
6834
6889
|
}
|
|
6835
6890
|
const writeCtx = {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gscdump/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.33.3",
|
|
5
5
|
"description": "CLI for Google Search Console - dump, query, and run MCP server",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
"ofetch": "^1.5.1",
|
|
44
44
|
"open": "^11.0.0",
|
|
45
45
|
"zod": "^4.4.3",
|
|
46
|
-
"@gscdump/
|
|
47
|
-
"@gscdump/engine-gsc-api": "0.
|
|
48
|
-
"gscdump": "0.
|
|
49
|
-
"@gscdump/
|
|
46
|
+
"@gscdump/analysis": "0.33.3",
|
|
47
|
+
"@gscdump/engine-gsc-api": "0.33.3",
|
|
48
|
+
"gscdump": "0.33.3",
|
|
49
|
+
"@gscdump/engine": "0.33.3"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@duckdb/node-api": "1.5.1-r.2",
|