@gscdump/cli 0.8.2 → 0.9.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/_chunks/libs/node-fetch-native.mjs +1 -1
- package/dist/index.mjs +317 -76
- package/package.json +7 -7
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __require, t as __exportAll } from "../rolldown-runtime.mjs";
|
|
2
|
-
import http from "node:http";
|
|
3
2
|
import { basename } from "node:path";
|
|
3
|
+
import http from "node:http";
|
|
4
4
|
import https from "node:https";
|
|
5
5
|
import st from "node:zlib";
|
|
6
6
|
import me, { PassThrough, pipeline } from "node:stream";
|
package/dist/index.mjs
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as __exportAll } from "./_chunks/rolldown-runtime.mjs";
|
|
3
|
-
import { t as ofetch } from "./_chunks/libs/ofetch.mjs";
|
|
4
3
|
import { a as loadConfig, c as setConfigDir, i as getConfigPath, n as defaultDataDir, o as resolveDataDir, r as getConfigDir, s as saveConfig } from "./_chunks/config.mjs";
|
|
4
|
+
import { t as ofetch } from "./_chunks/libs/ofetch.mjs";
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
import { defineCommand, runMain } from "citty";
|
|
7
7
|
import { defaultAnalyzerRegistry } from "@gscdump/analysis/registry";
|
|
8
|
+
import fs, { readFile, readdir, rm } from "node:fs/promises";
|
|
9
|
+
import path, { join } from "node:path";
|
|
8
10
|
import { AnalyzerCapabilityError, analyzeFromSource, createEngineQuerySource } from "@gscdump/analysis";
|
|
9
11
|
import { createGscApiQuerySource } from "@gscdump/engine-gsc-api";
|
|
12
|
+
import { decodeSiteId, normalizeSiteUrl } from "gscdump/tenant";
|
|
13
|
+
import os from "node:os";
|
|
10
14
|
import { cancel, confirm, isCancel, multiselect, select, text } from "@clack/prompts";
|
|
11
15
|
import { addSite, batchInspectUrls, batchRequestIndexing, createAuth, daysAgo, deleteSite, discoverSitemap, fetchSitemap, fetchSitemapUrls, fetchSitesWithSitemaps, formatErrorForCli, getDateRange, getIndexingMetadata, getVerificationToken, getVerifiedSite, googleSearchConsole, listVerifiedSites, progressBar, requestIndexing, runSequentialBatch, siteUrlToVerificationSite, unverifySite, verificationMethodsFor, verifySite } from "gscdump";
|
|
12
|
-
import fs, { readFile, rm } from "node:fs/promises";
|
|
13
16
|
import { createServer } from "node:http";
|
|
14
|
-
import path from "node:path";
|
|
15
17
|
import { JWT, OAuth2Client } from "google-auth-library";
|
|
16
18
|
import { Buffer } from "node:buffer";
|
|
17
19
|
import fs$1 from "node:fs";
|
|
18
|
-
import os from "node:os";
|
|
19
20
|
import { createConsola } from "consola";
|
|
20
21
|
import { createNodeHarness } from "@gscdump/engine-duckdb-node";
|
|
21
22
|
import { TABLE_DIMS, transformGscRow } from "@gscdump/engine/ingest";
|
|
@@ -26,48 +27,11 @@ import { createEmptyTypesStore, createIndexingMetadataStore, createInspectionSto
|
|
|
26
27
|
import { createGscMcpServer } from "@gscdump/mcp/server";
|
|
27
28
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
28
29
|
import { SearchTypes, and, between, contains, country, date, device, eq, gsc, notRegex, page, query, regex, searchAppearance } from "gscdump/query";
|
|
30
|
+
import { defaultReportRegistry, dryRunReport, formatReport, runReport } from "@gscdump/analysis/report";
|
|
31
|
+
import { resolveWindow } from "@gscdump/engine/period";
|
|
29
32
|
import { inferLegacyTier } from "@gscdump/engine";
|
|
30
33
|
import { DEFAULT_ROLLUPS, rebuildRollups } from "@gscdump/analysis/rollups";
|
|
31
34
|
import { filesystemStats } from "@gscdump/engine/filesystem";
|
|
32
|
-
var LocalStoreUnsupportedError = class extends Error {
|
|
33
|
-
constructor(tool) {
|
|
34
|
-
super(`analysis "${tool}" is not yet implemented against the local Parquet store`);
|
|
35
|
-
this.name = "LocalStoreUnsupportedError";
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
var LocalStoreEmptyError = class extends Error {
|
|
39
|
-
constructor(siteUrl) {
|
|
40
|
-
super(`no local data synced for ${siteUrl} (run \`gscdump sync\` first)`);
|
|
41
|
-
this.name = "LocalStoreEmptyError";
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
async function hasLocalData(store, siteUrl) {
|
|
45
|
-
return (await store.engine.listLive({
|
|
46
|
-
userId: store.userId,
|
|
47
|
-
siteId: store.siteIdFor(siteUrl)
|
|
48
|
-
})).length > 0;
|
|
49
|
-
}
|
|
50
|
-
async function runLocalAnalysis(store, siteUrl, params) {
|
|
51
|
-
return analyzeFromSource(createEngineQuerySource({
|
|
52
|
-
engine: store.engine,
|
|
53
|
-
ctx: {
|
|
54
|
-
userId: store.userId,
|
|
55
|
-
siteId: store.siteIdFor(siteUrl)
|
|
56
|
-
}
|
|
57
|
-
}), params, defaultAnalyzerRegistry).catch((e) => {
|
|
58
|
-
if (e instanceof AnalyzerCapabilityError) throw new LocalStoreUnsupportedError(params.type);
|
|
59
|
-
throw e;
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
async function runLiveAnalysis(client, siteUrl, params) {
|
|
63
|
-
return analyzeFromSource(createGscApiQuerySource({
|
|
64
|
-
client,
|
|
65
|
-
siteUrl
|
|
66
|
-
}), params, defaultAnalyzerRegistry).catch((e) => {
|
|
67
|
-
if (e instanceof AnalyzerCapabilityError) throw new LocalStoreUnsupportedError(params.type);
|
|
68
|
-
throw e;
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
35
|
const ENV_LINE_RE$1 = /^([^=]+)=(.*)$/;
|
|
72
36
|
function parseEnvFile(envPath) {
|
|
73
37
|
let content;
|
|
@@ -110,7 +74,7 @@ function loadEnvFromCwd() {
|
|
|
110
74
|
}
|
|
111
75
|
return applied;
|
|
112
76
|
}
|
|
113
|
-
const VERSION = "0.
|
|
77
|
+
const VERSION = "0.9.0";
|
|
114
78
|
const baseLogger = createConsola({
|
|
115
79
|
stdout: process.stderr,
|
|
116
80
|
stderr: process.stderr
|
|
@@ -735,6 +699,92 @@ async function gscErrorHandler(error) {
|
|
|
735
699
|
console.error();
|
|
736
700
|
process.exit(1);
|
|
737
701
|
}
|
|
702
|
+
var LocalStoreUnsupportedError = class extends Error {
|
|
703
|
+
constructor(tool) {
|
|
704
|
+
super(`analysis "${tool}" is not yet implemented against the local Parquet store`);
|
|
705
|
+
this.name = "LocalStoreUnsupportedError";
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
async function hasLocalData(store, siteUrl) {
|
|
709
|
+
return (await store.engine.listLive({
|
|
710
|
+
userId: store.userId,
|
|
711
|
+
siteId: store.siteIdFor(siteUrl)
|
|
712
|
+
})).length > 0;
|
|
713
|
+
}
|
|
714
|
+
async function listLocalSites(dataDir, userId = "local") {
|
|
715
|
+
return readdir(join(dataDir, `u_${userId}`), { withFileTypes: true }).then((entries) => entries.filter((e) => e.isDirectory() && (e.name.startsWith("d_") || e.name.startsWith("h_"))).map((e) => decodeSiteId(e.name))).catch(() => []);
|
|
716
|
+
}
|
|
717
|
+
function pickLocalSite(siteUrls, hint) {
|
|
718
|
+
if (siteUrls.length === 0) return null;
|
|
719
|
+
if (!hint) return siteUrls.length === 1 ? siteUrls[0] : null;
|
|
720
|
+
const normalized = normalizeSiteUrl(hint);
|
|
721
|
+
const exact = siteUrls.find((s) => s === normalized || s === hint);
|
|
722
|
+
if (exact) return exact;
|
|
723
|
+
return siteUrls.find((s) => s.includes(hint) || hint.includes(s)) ?? null;
|
|
724
|
+
}
|
|
725
|
+
async function resolveAnalysisSource(args) {
|
|
726
|
+
const isLive = !!args.live;
|
|
727
|
+
const format = args.json ? "json" : args.format ? String(args.format) : "table";
|
|
728
|
+
if (!isLive) {
|
|
729
|
+
const config = await loadConfig();
|
|
730
|
+
const dataDir = resolveDataDir(config);
|
|
731
|
+
const store = createLocalStore({ dataDir });
|
|
732
|
+
const siteHint = args.site ? String(args.site) : config.defaultSite;
|
|
733
|
+
const localSites = await listLocalSites(dataDir, store.userId);
|
|
734
|
+
const siteUrl = pickLocalSite(localSites, siteHint);
|
|
735
|
+
if (!siteUrl) {
|
|
736
|
+
if (localSites.length === 0) logger.error(`No local data found in ${dataDir}. Run \`gscdump sync\` first, or pass --live.`);
|
|
737
|
+
else logger.error(`Could not resolve site${siteHint ? ` from "${siteHint}"` : ""}. Local sites: ${localSites.join(", ")}`);
|
|
738
|
+
process.exit(1);
|
|
739
|
+
}
|
|
740
|
+
if (!await hasLocalData(store, siteUrl).catch(() => false)) {
|
|
741
|
+
logger.error(`No local data for ${siteUrl}. Run \`gscdump sync\` first, or pass --live.`);
|
|
742
|
+
process.exit(1);
|
|
743
|
+
}
|
|
744
|
+
const source = createEngineQuerySource({
|
|
745
|
+
engine: store.engine,
|
|
746
|
+
ctx: {
|
|
747
|
+
userId: store.userId,
|
|
748
|
+
siteId: store.siteIdFor(siteUrl)
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
const runAnalysis = (params) => analyzeFromSource(source, params, defaultAnalyzerRegistry).catch((e) => {
|
|
752
|
+
if (e instanceof AnalyzerCapabilityError) {
|
|
753
|
+
logger.error(`${new LocalStoreUnsupportedError(params.type).message}. Pass --live to run against the GSC API.`);
|
|
754
|
+
process.exit(1);
|
|
755
|
+
}
|
|
756
|
+
logger.error(`Local analysis failed: ${e.message}`);
|
|
757
|
+
process.exit(1);
|
|
758
|
+
});
|
|
759
|
+
return {
|
|
760
|
+
source,
|
|
761
|
+
siteUrl,
|
|
762
|
+
format,
|
|
763
|
+
isLive,
|
|
764
|
+
runAnalysis
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
const ctx = await createCommandContext({
|
|
768
|
+
needsAuth: true,
|
|
769
|
+
needsStore: false
|
|
770
|
+
});
|
|
771
|
+
const siteUrl = await ctx.resolveSite(args.site ? String(args.site) : void 0);
|
|
772
|
+
const source = createGscApiQuerySource({
|
|
773
|
+
client: ctx.client,
|
|
774
|
+
siteUrl
|
|
775
|
+
});
|
|
776
|
+
const runAnalysis = (params) => analyzeFromSource(source, params, defaultAnalyzerRegistry).catch((e) => {
|
|
777
|
+
if (e instanceof AnalyzerCapabilityError) throw new LocalStoreUnsupportedError(params.type);
|
|
778
|
+
return gscErrorHandler(e);
|
|
779
|
+
});
|
|
780
|
+
return {
|
|
781
|
+
source,
|
|
782
|
+
siteUrl,
|
|
783
|
+
format,
|
|
784
|
+
isLive,
|
|
785
|
+
runAnalysis
|
|
786
|
+
};
|
|
787
|
+
}
|
|
738
788
|
const ANALYSIS_TOOLS = defaultAnalyzerRegistry.listAnalyzerIds();
|
|
739
789
|
const TOOL_EXTRA_ARGS = {
|
|
740
790
|
brand: { "brand-terms": {
|
|
@@ -851,40 +901,14 @@ function makeToolCommand(tool) {
|
|
|
851
901
|
...extraArgs
|
|
852
902
|
},
|
|
853
903
|
async run({ args }) {
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
904
|
+
const { format, runAnalysis } = await resolveAnalysisSource({
|
|
905
|
+
site: args.site,
|
|
906
|
+
live: !!args.live,
|
|
907
|
+
json: !!args.json,
|
|
908
|
+
format: args.format
|
|
857
909
|
});
|
|
858
|
-
const siteUrl = await ctx.resolveSite(args.site);
|
|
859
910
|
logger.info(`Running ${tool} analysis...`);
|
|
860
|
-
const
|
|
861
|
-
const format = args.json ? "json" : String(args.format);
|
|
862
|
-
if (!args.live) {
|
|
863
|
-
const store = ctx.store;
|
|
864
|
-
if (!await hasLocalData(store, siteUrl).catch(() => false)) {
|
|
865
|
-
logger.error(`No local data for ${siteUrl}. Run \`gscdump sync\` first, or pass --live.`);
|
|
866
|
-
process.exit(1);
|
|
867
|
-
}
|
|
868
|
-
const localResult = await runLocalAnalysis(store, siteUrl, params).catch((e) => {
|
|
869
|
-
if (e instanceof LocalStoreUnsupportedError) {
|
|
870
|
-
logger.error(`${e.message}. Pass --live to run against the GSC API.`);
|
|
871
|
-
process.exit(1);
|
|
872
|
-
}
|
|
873
|
-
if (e instanceof LocalStoreEmptyError) {
|
|
874
|
-
logger.error(`${e.message}`);
|
|
875
|
-
process.exit(1);
|
|
876
|
-
}
|
|
877
|
-
logger.error(`Local analysis failed: ${e.message}`);
|
|
878
|
-
process.exit(1);
|
|
879
|
-
});
|
|
880
|
-
if (format === "json") {
|
|
881
|
-
console.log(JSON.stringify(localResult, null, 2));
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
renderResults(localResult.results, localResult.results.length, format);
|
|
885
|
-
return;
|
|
886
|
-
}
|
|
887
|
-
const result = await runLiveAnalysis(ctx.client, siteUrl, params).catch(gscErrorHandler);
|
|
911
|
+
const result = await runAnalysis(buildParams(tool, args));
|
|
888
912
|
if (format === "json") {
|
|
889
913
|
console.log(JSON.stringify(result, null, 2));
|
|
890
914
|
return;
|
|
@@ -3966,6 +3990,222 @@ async function writeOutput(opts) {
|
|
|
3966
3990
|
function isKnownTable$1(name) {
|
|
3967
3991
|
return allTables().includes(name);
|
|
3968
3992
|
}
|
|
3993
|
+
const REPORT_IDS = defaultReportRegistry.listReportIds();
|
|
3994
|
+
const PERIOD_ALIASES = {
|
|
3995
|
+
"7d": "last-7d",
|
|
3996
|
+
"28d": "last-28d",
|
|
3997
|
+
"30d": "last-30d",
|
|
3998
|
+
"90d": "last-90d",
|
|
3999
|
+
"180d": "last-180d",
|
|
4000
|
+
"365d": "last-365d",
|
|
4001
|
+
"last-7d": "last-7d",
|
|
4002
|
+
"last-28d": "last-28d",
|
|
4003
|
+
"last-30d": "last-30d",
|
|
4004
|
+
"last-90d": "last-90d",
|
|
4005
|
+
"last-180d": "last-180d",
|
|
4006
|
+
"last-365d": "last-365d",
|
|
4007
|
+
"mtd": "mtd",
|
|
4008
|
+
"ytd": "ytd",
|
|
4009
|
+
"custom": "custom"
|
|
4010
|
+
};
|
|
4011
|
+
const COMPARISON_ALIASES = {
|
|
4012
|
+
"none": "none",
|
|
4013
|
+
"prev": "prev-period",
|
|
4014
|
+
"prev-period": "prev-period",
|
|
4015
|
+
"prior": "prev-period",
|
|
4016
|
+
"prior-period": "prev-period",
|
|
4017
|
+
"yoy": "yoy"
|
|
4018
|
+
};
|
|
4019
|
+
function resolvePeriod(input, fallback) {
|
|
4020
|
+
if (!input) return fallback;
|
|
4021
|
+
const preset = PERIOD_ALIASES[input.toLowerCase()];
|
|
4022
|
+
if (!preset) throw new Error(`Unknown --period "${input}". Supported: 7d, 28d, 30d, 90d, 180d, 365d, mtd, ytd, custom.`);
|
|
4023
|
+
return preset;
|
|
4024
|
+
}
|
|
4025
|
+
function resolveComparison(input, fallback) {
|
|
4026
|
+
if (!input) return fallback;
|
|
4027
|
+
const mode = COMPARISON_ALIASES[input.toLowerCase()];
|
|
4028
|
+
if (!mode) throw new Error(`Unknown --vs "${input}". Supported: none, prev-period, yoy.`);
|
|
4029
|
+
return mode;
|
|
4030
|
+
}
|
|
4031
|
+
function reportArgsToCitty(spec) {
|
|
4032
|
+
const out = {};
|
|
4033
|
+
for (const [key, def] of Object.entries(spec)) out[key] = {
|
|
4034
|
+
type: def.type === "boolean" ? "boolean" : "string",
|
|
4035
|
+
description: def.description,
|
|
4036
|
+
default: def.default == null ? void 0 : String(def.default),
|
|
4037
|
+
alias: def.alias,
|
|
4038
|
+
required: def.required
|
|
4039
|
+
};
|
|
4040
|
+
return out;
|
|
4041
|
+
}
|
|
4042
|
+
function buildReportParams(report, args) {
|
|
4043
|
+
const params = {};
|
|
4044
|
+
for (const [key, def] of Object.entries(report.argsSpec)) {
|
|
4045
|
+
const raw = args[key];
|
|
4046
|
+
if (raw == null || raw === "") continue;
|
|
4047
|
+
if (def.type === "number") {
|
|
4048
|
+
const n = Number(raw);
|
|
4049
|
+
if (Number.isFinite(n)) params[toCamel(key)] = n;
|
|
4050
|
+
} else if (def.type === "boolean") params[toCamel(key)] = !!raw;
|
|
4051
|
+
else params[toCamel(key)] = raw;
|
|
4052
|
+
}
|
|
4053
|
+
return params;
|
|
4054
|
+
}
|
|
4055
|
+
function toCamel(kebab) {
|
|
4056
|
+
return kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
4057
|
+
}
|
|
4058
|
+
function makeReportCommand(report) {
|
|
4059
|
+
const reportArgs = reportArgsToCitty(report.argsSpec);
|
|
4060
|
+
return defineCommand({
|
|
4061
|
+
meta: {
|
|
4062
|
+
name: report.id,
|
|
4063
|
+
description: report.description
|
|
4064
|
+
},
|
|
4065
|
+
args: {
|
|
4066
|
+
"site": {
|
|
4067
|
+
type: "string",
|
|
4068
|
+
alias: "s",
|
|
4069
|
+
description: "Site URL"
|
|
4070
|
+
},
|
|
4071
|
+
"period": {
|
|
4072
|
+
type: "string",
|
|
4073
|
+
description: "Window: 7d|28d|90d|mtd|ytd|custom",
|
|
4074
|
+
default: presetToFlag(report.defaultPeriod)
|
|
4075
|
+
},
|
|
4076
|
+
"vs": {
|
|
4077
|
+
type: "string",
|
|
4078
|
+
description: "Comparison: none|prev-period|yoy",
|
|
4079
|
+
default: report.defaultComparison
|
|
4080
|
+
},
|
|
4081
|
+
"start": {
|
|
4082
|
+
type: "string",
|
|
4083
|
+
description: "Custom start date (YYYY-MM-DD)"
|
|
4084
|
+
},
|
|
4085
|
+
"end": {
|
|
4086
|
+
type: "string",
|
|
4087
|
+
description: "Custom end date (YYYY-MM-DD)"
|
|
4088
|
+
},
|
|
4089
|
+
"prev-start": {
|
|
4090
|
+
type: "string",
|
|
4091
|
+
description: "Override comparison start"
|
|
4092
|
+
},
|
|
4093
|
+
"prev-end": {
|
|
4094
|
+
type: "string",
|
|
4095
|
+
description: "Override comparison end"
|
|
4096
|
+
},
|
|
4097
|
+
"live": {
|
|
4098
|
+
type: "boolean",
|
|
4099
|
+
default: false,
|
|
4100
|
+
description: "Force live GSC API; bypass local store"
|
|
4101
|
+
},
|
|
4102
|
+
"json": {
|
|
4103
|
+
type: "boolean",
|
|
4104
|
+
default: false,
|
|
4105
|
+
description: "Emit full ReportResult JSON"
|
|
4106
|
+
},
|
|
4107
|
+
"explain": {
|
|
4108
|
+
type: "boolean",
|
|
4109
|
+
default: false,
|
|
4110
|
+
description: "Print plan steps + window without executing"
|
|
4111
|
+
},
|
|
4112
|
+
"dry-run": {
|
|
4113
|
+
type: "boolean",
|
|
4114
|
+
default: false,
|
|
4115
|
+
description: "Alias for --explain"
|
|
4116
|
+
},
|
|
4117
|
+
...reportArgs
|
|
4118
|
+
},
|
|
4119
|
+
async run({ args }) {
|
|
4120
|
+
const preset = resolvePeriod(args.period, report.defaultPeriod);
|
|
4121
|
+
const comparison = resolveComparison(args.vs, report.defaultComparison);
|
|
4122
|
+
const window = resolveWindow({
|
|
4123
|
+
preset,
|
|
4124
|
+
comparison,
|
|
4125
|
+
start: args.start,
|
|
4126
|
+
end: args.end
|
|
4127
|
+
});
|
|
4128
|
+
if (args["prev-start"] && args["prev-end"]) window.comparison = {
|
|
4129
|
+
start: String(args["prev-start"]),
|
|
4130
|
+
end: String(args["prev-end"])
|
|
4131
|
+
};
|
|
4132
|
+
const params = buildReportParams(report, args);
|
|
4133
|
+
if (args.explain || args["dry-run"]) {
|
|
4134
|
+
const dry = await dryRunReport(report, {
|
|
4135
|
+
site: args.site ? String(args.site) : "(unresolved)",
|
|
4136
|
+
window,
|
|
4137
|
+
params,
|
|
4138
|
+
registryVersion: defaultReportRegistry.version
|
|
4139
|
+
});
|
|
4140
|
+
console.log(JSON.stringify({
|
|
4141
|
+
id: report.id,
|
|
4142
|
+
window,
|
|
4143
|
+
comparison,
|
|
4144
|
+
plan: dry.steps
|
|
4145
|
+
}, null, 2));
|
|
4146
|
+
return;
|
|
4147
|
+
}
|
|
4148
|
+
const { source, siteUrl } = await resolveAnalysisSource({
|
|
4149
|
+
site: args.site,
|
|
4150
|
+
live: !!args.live,
|
|
4151
|
+
json: !!args.json
|
|
4152
|
+
});
|
|
4153
|
+
const result = await runReport(report, {
|
|
4154
|
+
source,
|
|
4155
|
+
analyzers: defaultAnalyzerRegistry,
|
|
4156
|
+
ctx: {
|
|
4157
|
+
site: siteUrl,
|
|
4158
|
+
window,
|
|
4159
|
+
params,
|
|
4160
|
+
registryVersion: defaultReportRegistry.version
|
|
4161
|
+
}
|
|
4162
|
+
});
|
|
4163
|
+
if (args.json) {
|
|
4164
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4165
|
+
return;
|
|
4166
|
+
}
|
|
4167
|
+
console.log(formatReport(result));
|
|
4168
|
+
if (result.meta.degraded) logger.warn(`degraded: ${result.meta.steps.filter((s) => s.status === "error").map((s) => `${s.key}(${s.error})`).join(", ")}`);
|
|
4169
|
+
}
|
|
4170
|
+
});
|
|
4171
|
+
}
|
|
4172
|
+
function presetToFlag(preset) {
|
|
4173
|
+
if (preset === "mtd" || preset === "ytd" || preset === "custom") return preset;
|
|
4174
|
+
return preset.replace(/^last-/, "");
|
|
4175
|
+
}
|
|
4176
|
+
const reportCommand = defineCommand({
|
|
4177
|
+
meta: {
|
|
4178
|
+
name: "report",
|
|
4179
|
+
description: "Run an intent-keyed report (composes analyzers into bounded sections)"
|
|
4180
|
+
},
|
|
4181
|
+
subCommands: {
|
|
4182
|
+
list: defineCommand({
|
|
4183
|
+
meta: {
|
|
4184
|
+
name: "list",
|
|
4185
|
+
description: "List available report ids"
|
|
4186
|
+
},
|
|
4187
|
+
args: { json: {
|
|
4188
|
+
type: "boolean",
|
|
4189
|
+
default: false,
|
|
4190
|
+
description: "Output as JSON"
|
|
4191
|
+
} },
|
|
4192
|
+
async run({ args }) {
|
|
4193
|
+
const reports = defaultReportRegistry.listReports().map((r) => ({
|
|
4194
|
+
id: r.id,
|
|
4195
|
+
description: r.description,
|
|
4196
|
+
defaultPeriod: r.defaultPeriod,
|
|
4197
|
+
defaultComparison: r.defaultComparison
|
|
4198
|
+
}));
|
|
4199
|
+
if (args.json) {
|
|
4200
|
+
console.log(JSON.stringify(reports, null, 2));
|
|
4201
|
+
return;
|
|
4202
|
+
}
|
|
4203
|
+
for (const r of reports) console.log(`${r.id.padEnd(16)} ${r.description}`);
|
|
4204
|
+
}
|
|
4205
|
+
}),
|
|
4206
|
+
...Object.fromEntries(REPORT_IDS.map((id) => [id, makeReportCommand(defaultReportRegistry.getReport(id))]))
|
|
4207
|
+
}
|
|
4208
|
+
});
|
|
3969
4209
|
const sitemapsCommand = defineCommand({
|
|
3970
4210
|
meta: {
|
|
3971
4211
|
name: "sitemaps",
|
|
@@ -5819,6 +6059,7 @@ runMain(defineCommand({
|
|
|
5819
6059
|
indexing: indexingCommand,
|
|
5820
6060
|
entities: entitiesCommand,
|
|
5821
6061
|
analyze: analyzeCommand,
|
|
6062
|
+
report: reportCommand,
|
|
5822
6063
|
auth: authCommand,
|
|
5823
6064
|
config: configCommand,
|
|
5824
6065
|
profile: profileCommand,
|
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.9.0",
|
|
5
5
|
"description": "CLI for Google Search Console - dump, query, and run MCP server",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
"consola": "^3.4.2",
|
|
42
42
|
"google-auth-library": "^10.6.2",
|
|
43
43
|
"open": "^11.0.0",
|
|
44
|
-
"@gscdump/engine
|
|
45
|
-
"@gscdump/engine": "0.
|
|
46
|
-
"@gscdump/
|
|
47
|
-
"@gscdump/
|
|
48
|
-
"@gscdump/
|
|
49
|
-
"gscdump": "0.
|
|
44
|
+
"@gscdump/engine": "0.9.0",
|
|
45
|
+
"@gscdump/engine-duckdb-node": "0.9.0",
|
|
46
|
+
"@gscdump/analysis": "0.9.0",
|
|
47
|
+
"@gscdump/engine-gsc-api": "0.9.0",
|
|
48
|
+
"@gscdump/mcp": "0.9.0",
|
|
49
|
+
"gscdump": "0.9.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@duckdb/node-api": "1.5.1-r.2",
|