@gscdump/cli 5.0.0 → 5.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.
Files changed (41) hide show
  1. package/dist/analysis-local.mjs +2 -2
  2. package/dist/auth-state.mjs +60 -7
  3. package/dist/bing-hosted.mjs +26 -5
  4. package/dist/cli.d.mts +6 -0
  5. package/dist/cli.mjs +47 -6
  6. package/dist/command-meta.mjs +2 -2
  7. package/dist/commands/analyze.mjs +2 -2
  8. package/dist/commands/auth.mjs +13 -14
  9. package/dist/commands/bing.mjs +7 -2
  10. package/dist/commands/config.mjs +5 -15
  11. package/dist/commands/doctor.mjs +2 -2
  12. package/dist/commands/dump.mjs +2 -4
  13. package/dist/commands/entities.mjs +3 -9
  14. package/dist/commands/indexing-urls.mjs +2 -2
  15. package/dist/commands/indexing-watch.mjs +10 -7
  16. package/dist/commands/indexing.mjs +6 -22
  17. package/dist/commands/init.mjs +3 -6
  18. package/dist/commands/mcp.mjs +1 -1
  19. package/dist/commands/papercut.mjs +2 -5
  20. package/dist/commands/profile.mjs +5 -14
  21. package/dist/commands/query.mjs +16 -40
  22. package/dist/commands/report.mjs +2 -2
  23. package/dist/commands/sitemaps.mjs +3 -10
  24. package/dist/commands/sites.mjs +7 -12
  25. package/dist/commands/skill.mjs +1 -4
  26. package/dist/commands/sync.mjs +2 -8
  27. package/dist/config.mjs +1 -1
  28. package/dist/context.mjs +3 -3
  29. package/dist/error-handler.mjs +72 -16
  30. package/dist/hosted-auth.mjs +9 -1
  31. package/dist/hosted-session.mjs +17 -0
  32. package/dist/hosted-site.mjs +41 -18
  33. package/dist/mcp/errors.mjs +1 -1
  34. package/dist/package.mjs +1 -1
  35. package/dist/route.mjs +5 -9
  36. package/dist/runtime.d.mts +2 -0
  37. package/dist/runtime.mjs +1 -0
  38. package/dist/stop.mjs +23 -0
  39. package/dist/utils.mjs +13 -14
  40. package/package.json +7 -7
  41. package/skills/gscdump/SKILL.md +43 -4
@@ -1,8 +1,7 @@
1
- import { getHostedAccount, hostedCredential, parseAuthentication, resolveAuthentication } from "./auth-state.mjs";
1
+ import { stopError } from "./stop.mjs";
2
+ import { connectSiteStep, getHostedAccount, hostedCredential, parseAuthentication, resolveAuthentication, siteManagerOf } from "./auth-state.mjs";
2
3
  import { loadConfig } from "./config.mjs";
3
4
  import { resolveCliEnvironment } from "./environment.mjs";
4
- import { logger } from "./utils.mjs";
5
- import process from "node:process";
6
5
  import { createGscdumpV1Client } from "@gscdump/sdk/v1";
7
6
  const HOSTED_ARGS = {
8
7
  "site": {
@@ -57,35 +56,59 @@ function matchHostedSite(sites, target) {
57
56
  sites: [...sites]
58
57
  };
59
58
  }
60
- function describeMatchFailure(match) {
61
- if (match.kind === "ambiguous") return `Multiple hosted Sites match "${match.target}": ${match.matches.map((site) => site.siteUrl).join(", ")}. Pass the exact Site URL with --site.`;
62
- if (match.sites.length === 0) return "Your hosted account has no Sites. Add a Site at https://gscdump.com.";
63
- const available = `Hosted Sites: ${match.sites.map((site) => site.siteUrl).join(", ")}.`;
64
- return match.target ? `No hosted Site matches "${match.target}". ${available}` : `Pass --site. ${available}`;
59
+ function noSitesStop(manager) {
60
+ return {
61
+ code: "NO_SITES",
62
+ message: `Your hosted record has no Sites. ${connectSiteStep(manager)} Then run the command again.`,
63
+ nextCommand: null
64
+ };
65
65
  }
66
- function fail(message) {
67
- logger.error(message);
68
- process.exit(1);
66
+ function siteMatchStop(match, manager) {
67
+ const nextCommand = "gscdump sites --json";
68
+ if (match.kind === "ambiguous") return {
69
+ code: "SITE_AMBIGUOUS",
70
+ message: `Multiple hosted Sites match "${match.target}": ${match.matches.map((site) => site.siteUrl).join(", ")}. Pass the exact Site URL with --site.`,
71
+ nextCommand
72
+ };
73
+ if (match.sites.length === 0) return noSitesStop(manager);
74
+ const available = `Hosted Sites: ${match.sites.map((site) => site.siteUrl).join(", ")}.`;
75
+ return match.target ? {
76
+ code: "SITE_NOT_FOUND",
77
+ message: `No hosted Site matches "${match.target}". ${available}`,
78
+ nextCommand
79
+ } : {
80
+ code: "SITE_REQUIRED",
81
+ message: `Pass --site. ${available}`,
82
+ nextCommand
83
+ };
69
84
  }
70
85
  async function resolveHostedSite(args, command) {
71
- if (args._?.length) fail(`\`gscdump ${command.name}\` does not accept a positional Site ID. Pass --site instead.`);
86
+ if (args._?.length) throw new Error(`\`gscdump ${command.name}\` does not accept a positional Site ID. Pass --site instead.`);
72
87
  const environment = resolveCliEnvironment().values;
73
88
  const authentication = args["api-key"] ? parseAuthentication({
74
89
  _tag: "Hosted",
75
90
  apiKey: args["api-key"],
76
91
  apiRoot: String(args["api-root"] || environment.GSCDUMP_API_ROOT || "https://gscdump.com/api")
77
92
  }) : await resolveAuthentication();
78
- if (authentication._tag !== "Hosted") fail(`\`gscdump ${command.name}\` reads the hosted record, so it needs Hosted mode. Run \`gscdump auth login --mode hosted\`, or pass --api-key. In Local mode, ${command.localAlternative}.`);
79
- if (args["api-root"] && String(args["api-root"]).replace(/\/+$/, "") !== authentication.apiRoot) fail("The API root changed. Pass --api-key for the new API root.");
93
+ if (authentication._tag !== "Hosted") throw stopError({
94
+ code: "HOSTED_MODE_REQUIRED",
95
+ message: `\`gscdump ${command.name}\` reads the hosted record, so it needs Hosted mode. Run \`gscdump auth login --mode hosted\`, or pass --api-key. In Local mode, ${command.localAlternative}.`,
96
+ nextCommand: "gscdump auth login --mode hosted"
97
+ });
98
+ if (args["api-root"] && String(args["api-root"]).replace(/\/+$/, "") !== authentication.apiRoot) throw new Error("The API root changed. Pass --api-key for the new API root.");
80
99
  const target = args.site ? String(args.site) : (await loadConfig()).defaultSite;
81
- const match = matchHostedSite((await getHostedAccount(authentication)).sites, target);
82
- if (match.kind !== "found") fail(describeMatchFailure(match));
100
+ const account = await getHostedAccount(authentication);
101
+ const siteManager = siteManagerOf(authentication, account.issuer);
102
+ const match = matchHostedSite(account.sites, target);
103
+ if (match.kind !== "found") throw stopError(siteMatchStop(match, siteManager));
83
104
  return {
84
105
  client: createGscdumpV1Client({
85
106
  apiRoot: authentication.apiRoot,
86
107
  credential: hostedCredential(authentication)
87
108
  }),
88
- site: match.site
109
+ site: match.site,
110
+ authentication,
111
+ siteManager
89
112
  };
90
113
  }
91
- export { HOSTED_ARGS, matchHostedSite, resolveHostedSite };
114
+ export { HOSTED_ARGS, matchHostedSite, noSitesStop, resolveHostedSite, siteMatchStop };
@@ -1,9 +1,9 @@
1
1
  import { resolveBYOK, resolveServiceAccount } from "../auth.mjs";
2
2
  import { COMPARISON_FLAGS, PERIOD_FLAGS } from "../window.mjs";
3
+ import { isQueryError } from "gscdump/query";
3
4
  import { isAnalysisError } from "@gscdump/analysis/errors";
4
5
  import { isEngineError } from "@gscdump/engine/errors";
5
6
  import { classifyError } from "gscdump/errors";
6
- import { isQueryError } from "gscdump/query";
7
7
  const PERIODS = PERIOD_FLAGS;
8
8
  const COMPARISONS = COMPARISON_FLAGS;
9
9
  const mcpHandlerErrors = {
package/dist/package.mjs CHANGED
@@ -1,2 +1,2 @@
1
- var version = "5.0.0";
1
+ var version = "5.2.0";
2
2
  export { version };
package/dist/route.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { stopError } from "./stop.mjs";
1
2
  import { probeAuth } from "./auth.mjs";
2
3
  import { listStoreSites } from "./store-sites.mjs";
3
4
  import { formatSiteResolution, siteArg } from "./context.mjs";
@@ -199,11 +200,8 @@ function stopCode(route) {
199
200
  case "partial": return "STORE_RANGE_NOT_COVERED";
200
201
  }
201
202
  }
202
- function routeStopError(route, message) {
203
- return Object.assign(new Error(message), {
204
- name: "RouteStopError",
205
- routeStop: route
206
- });
203
+ function routeStopError(route, details) {
204
+ return Object.assign(stopError(details, "RouteStopError"), { routeStop: route });
207
205
  }
208
206
  async function needCoverage(needs, states) {
209
207
  const reader = syncStateCoverageReader(states);
@@ -299,9 +297,7 @@ function describeStop(route, req, auth) {
299
297
  } } : {}
300
298
  };
301
299
  }
302
- function stopAtRoute(route, req, auth, opts) {
303
- const details = describeStop(route, req, auth);
304
- if (opts.json) console.log(JSON.stringify({ error: details }, null, 2));
305
- throw routeStopError(route, details.message);
300
+ function stopAtRoute(route, req, auth) {
301
+ throw routeStopError(route, describeStop(route, req, auth));
306
302
  }
307
303
  export { decideRoute, describeStop, liveCommand, liveNote, needCoverage, readRouteState, readSiteStates, resolveReadSite, routeMessage, routeStopError, stopAtRoute, stopCode, syncCommandFor };
@@ -7,6 +7,8 @@ export interface CliRuntime {
7
7
  configDir: string;
8
8
  configDirOverridden: boolean;
9
9
  environment: CliEnvironmentSource;
10
+ /** The command writes JSON to stdout. A stop then prints its JSON form there too. */
11
+ jsonOutput: boolean;
10
12
  logger: ConsolaInstance;
11
13
  quiet: boolean;
12
14
  rawArgs: string[];
package/dist/runtime.mjs CHANGED
@@ -21,6 +21,7 @@ function createCliRuntime(opts = {}) {
21
21
  configDir: opts.configDir ?? path.join(os.homedir(), ".config", "gscdump"),
22
22
  configDirOverridden: false,
23
23
  environment: opts.environment ?? process.env,
24
+ jsonOutput: false,
24
25
  logger: baseLogger.withTag("gscdump"),
25
26
  quiet: false,
26
27
  rawArgs: [...opts.rawArgs ?? process.argv.slice(2)]
package/dist/stop.mjs ADDED
@@ -0,0 +1,23 @@
1
+ function stopError(details, name = "StopError") {
2
+ return Object.assign(new Error(details.message), {
3
+ name,
4
+ stopDetails: details
5
+ });
6
+ }
7
+ function stopDetailsOf(error) {
8
+ return error instanceof Error ? error.stopDetails : void 0;
9
+ }
10
+ function userStopError(details) {
11
+ return Object.assign(stopError(details, "UserStopError"), { userStop: true });
12
+ }
13
+ function isUserStop(error) {
14
+ return error instanceof Error && error.userStop === true;
15
+ }
16
+ function commandLineError(message) {
17
+ return stopError({
18
+ code: "USAGE",
19
+ message,
20
+ nextCommand: null
21
+ });
22
+ }
23
+ export { commandLineError, isUserStop, stopDetailsOf, stopError, userStopError };
package/dist/utils.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  import { useCliRuntime } from "./runtime.mjs";
2
+ import { commandLineError } from "./stop.mjs";
2
3
  import { version } from "./package.mjs";
3
4
  import process from "node:process";
4
5
  import fs from "node:fs/promises";
5
- import { SearchTypes } from "gscdump/query";
6
6
  import os from "node:os";
7
+ import { SearchTypes } from "gscdump/query";
7
8
  import { Buffer } from "node:buffer";
8
9
  import { MAX_FETCH_BUDGET } from "@gscdump/engine/analysis-types";
9
10
  const ALL_SEARCH_TYPES = Object.values(SearchTypes);
@@ -40,10 +41,14 @@ const OUTPUT_ARGS = {
40
41
  description: "Suppress info/success output"
41
42
  }
42
43
  };
44
+ function setJsonOutput(json) {
45
+ useCliRuntime().jsonOutput = json;
46
+ }
43
47
  function applyOutputMode(args) {
44
48
  const json = Boolean(args.json);
45
49
  const quiet = json || Boolean(args.quiet);
46
50
  setQuiet(quiet);
51
+ setJsonOutput(json);
47
52
  return {
48
53
  json,
49
54
  quiet
@@ -52,29 +57,26 @@ function applyOutputMode(args) {
52
57
  function parseSearchType(value, flag = "--search-type") {
53
58
  if (!value) return void 0;
54
59
  const v = String(value);
55
- if (!ALL_SEARCH_TYPES.includes(v)) {
56
- logger.error(`Invalid ${flag}: ${v}. Allowed: ${ALL_SEARCH_TYPES.join(", ")}`);
57
- process.exit(1);
58
- }
60
+ if (!ALL_SEARCH_TYPES.includes(v)) throw commandLineError(`Invalid ${flag}: ${v}. Allowed: ${ALL_SEARCH_TYPES.join(", ")}`);
59
61
  return v;
60
62
  }
61
63
  function parseNameList(value, known, flag) {
62
64
  const names = String(value ?? "").split(",").map((name) => name.trim()).filter(Boolean);
63
65
  const unknown = names.filter((name) => !known.includes(name));
64
- if (unknown.length > 0) throw new Error(`Unknown ${flag} name: ${unknown.join(", ")}. Valid names: ${known.join(", ")}.`);
65
- if (names.length === 0) throw new Error(`${flag} needs at least one name. Valid names: ${known.join(", ")}.`);
66
+ if (unknown.length > 0) throw commandLineError(`Unknown ${flag} name: ${unknown.join(", ")}. Valid names: ${known.join(", ")}.`);
67
+ if (names.length === 0) throw commandLineError(`${flag} needs at least one name. Valid names: ${known.join(", ")}.`);
66
68
  return names;
67
69
  }
68
70
  function parseIntegerOption(value, flag, minimum = 1) {
69
71
  if (value === void 0 || value === null) return void 0;
70
72
  const text = typeof value === "number" || typeof value === "string" ? String(value).trim() : "";
71
73
  const parsed = Number(text);
72
- if (!/^\d+$/.test(text) || !Number.isSafeInteger(parsed) || parsed < minimum) throw new Error(`${flag} must be ${minimum === 0 ? "a non-negative integer" : "a positive integer"}.`);
74
+ if (!/^\d+$/.test(text) || !Number.isSafeInteger(parsed) || parsed < minimum) throw commandLineError(`${flag} must be ${minimum === 0 ? "a non-negative integer" : "a positive integer"}.`);
73
75
  return parsed;
74
76
  }
75
77
  function parseFetchBudget(value) {
76
78
  const budget = parseIntegerOption(value, "--fetch-budget");
77
- if (budget !== void 0 && budget > MAX_FETCH_BUDGET) throw new Error(`--fetch-budget must be at most ${MAX_FETCH_BUDGET}. Larger fetches use up the GSC quota.`);
79
+ if (budget !== void 0 && budget > MAX_FETCH_BUDGET) throw commandLineError(`--fetch-budget must be at most ${MAX_FETCH_BUDGET}. Larger fetches use up the GSC quota.`);
78
80
  return budget;
79
81
  }
80
82
  const ANSI_RE = /\x1B\[[0-9;]*m/g;
@@ -141,17 +143,14 @@ function applyGradient(text) {
141
143
  }
142
144
  function showSplash() {
143
145
  console.log();
144
- console.log(` ${applyGradient("GSC Dump")} v${VERSION}`);
146
+ console.log(` ${applyGradient("gscdump")} v${VERSION}`);
145
147
  console.log();
146
148
  }
147
149
  function clearLine() {
148
150
  process.stdout.write("\r\x1B[K");
149
151
  }
150
152
  function displayPath(absPath) {
151
- const cwd = process.cwd();
152
153
  const home = os.homedir();
153
- if (absPath === cwd) return ".";
154
- if (absPath.startsWith(`${cwd}/`)) return absPath.slice(cwd.length + 1);
155
154
  if (absPath === home) return "~";
156
155
  if (absPath.startsWith(`${home}/`)) return `~/${absPath.slice(home.length + 1)}`;
157
156
  return absPath;
@@ -196,4 +195,4 @@ async function readUrlList(input) {
196
195
  for await (const chunk of process.stdin) chunks.push(chunk);
197
196
  return parseUrlLines(Buffer.concat(chunks).toString("utf-8"));
198
197
  }
199
- export { ALL_SEARCH_TYPES, OUTPUT_ARGS, VERSION, applyOutputMode, clearLine, color, cyan, dim, displayPath, formatAge, isColorEnabled, logger, parseFetchBudget, parseIntegerOption, parseNameList, parseSearchType, plural, progressBar, readUrlList, red, runWithConcurrency, setNoColor, setQuiet, showSplash, toCSV, withConfiguredOutput };
198
+ export { ALL_SEARCH_TYPES, OUTPUT_ARGS, VERSION, applyOutputMode, clearLine, color, cyan, dim, displayPath, formatAge, isColorEnabled, logger, parseFetchBudget, parseIntegerOption, parseNameList, parseSearchType, plural, progressBar, readUrlList, red, runWithConcurrency, setJsonOutput, setNoColor, setQuiet, showSplash, toCSV, withConfiguredOutput };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/cli",
3
3
  "type": "module",
4
- "version": "5.0.0",
4
+ "version": "5.2.0",
5
5
  "description": "CLI for Google Search Console and Bing with hosted or local authentication, data exports, and an MCP server",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -44,16 +44,16 @@
44
44
  "dependencies": {
45
45
  "@clack/prompts": "^1.8.1",
46
46
  "@duckdb/node-api": "1.5.5-r.5",
47
- "@gscdump/analysis": "^5.0.0",
48
- "@gscdump/contracts": "^5.0.0",
49
- "@gscdump/engine": "^5.0.0",
50
- "@gscdump/engine-gsc-api": "^5.0.0",
51
- "@gscdump/sdk": "^5.0.0",
47
+ "@gscdump/analysis": "^5.2.0",
48
+ "@gscdump/contracts": "^5.2.0",
49
+ "@gscdump/engine": "^5.2.0",
50
+ "@gscdump/engine-gsc-api": "^5.2.0",
51
+ "@gscdump/sdk": "^5.2.0",
52
52
  "@modelcontextprotocol/sdk": "^1.30.1",
53
53
  "citty": "^0.2.2",
54
54
  "consola": "^3.4.2",
55
55
  "google-auth-library": "^11.1.0",
56
- "gscdump": "^5.0.0",
56
+ "gscdump": "^5.2.0",
57
57
  "ofetch": "^1.5.1",
58
58
  "open": "^11.0.4",
59
59
  "proper-lockfile": "^4.1.2",
@@ -11,7 +11,7 @@ For `query`, `-s` means `--site`, `-d` means `--dimensions`, and `-f` means `--f
11
11
  Use `--start` and `--end` for dates. `--site=SITE` also works.
12
12
  Use each option once, with either its short or long spelling.
13
13
  Put options after the subcommand name: `gscdump store stats --json`, not `gscdump store --json stats`.
14
- A failed command prints one `Error:` line to stderr and exits 1.
14
+ A failed command prints one `Error:` line to stderr and exits 1. With JSON output it also prints `{ "error" }` on stdout. See [Stops](#stops).
15
15
  Example: `gscdump query --site=SITE --start=DATE --end=DATE -d page -f json`.
16
16
 
17
17
  ## Start each task
@@ -43,6 +43,11 @@ gscdump has 2 access modes. Check `gscdump auth status --json` before queries. R
43
43
  In Local mode, `googleAuthenticated: true` means Google accepted the credentials. When it is false, `googleError` says why.
44
44
  In Hosted mode, `hostedSync` lists each hosted Site with `syncStatus` and `syncProgress`, and `commands` lists what Hosted mode can run.
45
45
  `gscdump sites` shows the same Sites and progress.
46
+ An empty `sites` list means the hosted record has no Sites. Commands that need a Site then stop with `NO_SITES`.
47
+ Tell the user to connect a Site in the place the `NO_SITES` message names.
48
+ A browser login connects Sites on gscdump.com.
49
+ An API key connects Sites in the app that issued the key. That app can be a partner app, not gscdump.com.
50
+ When gscdump.com names the app, the message names it too, for example `Connect a Site in Request Indexing.`
46
51
 
47
52
  `--mode local|hosted` overrides one invocation. `GSCDUMP_AUTH_MODE` also overrides the saved mode.
48
53
  A successful login saves the mode per profile.
@@ -128,8 +133,7 @@ mixes Store rows and live rows.
128
133
  - `--live` always asks Search Console. It needs Local mode with Google credentials.
129
134
  - `query --sql` reads the Store only. It stops when no table it names has data.
130
135
  - JSON output carries `meta.source`: `local`, `live`, or `hosted`.
131
- - A stop with `--format json` or `--json` prints `{ "error": { "code", "message", "nextCommand" } }` on stdout and exits 1.
132
- Codes: `NOT_CONNECTED`, `STORE_RANGE_NOT_COVERED` (with `missingDates`), `SYNC_RUNNING` (with `sync.done` and `sync.total`), `NO_SYNCED_DATA`, `STORE_ONLY`, `LIVE_ONLY`.
136
+ - A failure with JSON output prints `{ "error" }` on stdout and exits 1. See [Stops](#stops).
133
137
  Partial coverage and a running sync are normal progress. Run `nextCommand`, or tell the user to.
134
138
  - A sync is running only while its heartbeat is recent. A killed sync does not block reads.
135
139
  - Every Search Analytics, URL Inspection, and Indexing API call spends the shared quota ledger in the data dir.
@@ -153,7 +157,8 @@ gscdump skill install --agent claude # Codex: --agent codex
153
157
  ```
154
158
 
155
159
  After upgrading the CLI, run this command again to update the installed skill.
156
- The command prints where it wrote the skill. Clients without a skill
160
+ The command prints where it wrote the skill, as a path that works from any directory, such as `~/.claude/skills/gscdump`.
161
+ With `--json`, `destination` is the absolute path. Clients without a skill
157
162
  directory can read `gscdump --help` and `gscdump <command> --help` instead.
158
163
 
159
164
  ## Local mode setup
@@ -220,6 +225,40 @@ Parse JSON. Never scrape human output.
220
225
  If the user requests JSON, return the CLI JSON unchanged. Do not replace it with a table.
221
226
  Do not rewrite rows, estimate metrics, or add manually calculated totals.
222
227
 
228
+ ## Stops
229
+
230
+ A stop ends a command at a known condition and exits 1. stderr gets the message.
231
+ If the command writes JSON, every failure prints `{ "error": { "code", "message", "nextCommand" } }` on stdout. A stop has a code from the table below.
232
+ Any other failure has the code `FAILED`, such as a network or API error, or `UNEXPECTED`, a defect in the CLI.
233
+ A command writes JSON with `--json`, with `--format json`, and for `query` with its default format.
234
+ If a command printed its JSON result before it failed, such as `inspect` after a quota stop, stdout holds only that result.
235
+ Read `code`, not the message. If `nextCommand` is not null, run it, or tell the user to run it.
236
+ A `gscdump auth login` command needs the user. Tell the user to run it. If `nextCommand` is null, tell the user the `message`.
237
+ A routing stop also has `siteUrl`. `STORE_RANGE_NOT_COVERED` adds `missingDates`, and `SYNC_RUNNING` adds `sync.done` and `sync.total`.
238
+
239
+ | Code | Meaning |
240
+ | --- | --- |
241
+ | `USAGE` | The command line is wrong: an unknown command or option, or a value the option does not accept. Fix it with the command's `--help` |
242
+ | `NOT_CONNECTED` | The Store cannot answer, and Google is not connected |
243
+ | `STORE_RANGE_NOT_COVERED` | The Store does not have `missingDates` |
244
+ | `SYNC_RUNNING` | A sync is still filling the dates |
245
+ | `NO_SYNCED_DATA` | A Store-only read found no synced data |
246
+ | `STORE_ONLY` | The read needs the Store, so `--live` cannot run it |
247
+ | `LIVE_ONLY` | Only `--live` can answer the read |
248
+ | `LOGIN_CANCELLED` | The user selected Cancel on the gscdump.com login page. Ask before you run `nextCommand` |
249
+ | `LOCAL_MODE_REQUIRED` | The command calls Google, and Hosted mode is selected |
250
+ | `HOSTED_MODE_REQUIRED` | The command reads the hosted record, and Local mode is selected |
251
+ | `HOSTED_CREDENTIALS_MISSING` | Hosted mode has no CLI session and no API key |
252
+ | `HOSTED_CREDENTIALS_REJECTED` | gscdump.com rejected the CLI session or API key. For an API key, `nextCommand` is null: the user needs a new key from the app that issued it |
253
+ | `NO_SITES` | The hosted record has no Sites. The message says where the user connects one |
254
+ | `SITE_NOT_FOUND` | No hosted Site matches `--site` |
255
+ | `SITE_AMBIGUOUS` | More than one hosted Site matches `--site` |
256
+ | `SITE_REQUIRED` | The hosted record has more than one Site. Pass `--site` |
257
+ | `BING_NOT_CONNECTED` | The Site has no hosted Bing connection |
258
+ | `RECORD_NOT_READY` | gscdump has not prepared the Site's record for reads yet |
259
+ | `RANGE_NOT_SYNCED` | The hosted record does not hold the requested dates |
260
+ | `QUOTA_USED_UP` | A Google API quota is used up. The message says when it resets |
261
+
223
262
  ## Commands
224
263
 
225
264
  | Command | Use it for |