@gscdump/cli 4.2.3 → 4.4.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Google Search Console CLI and MCP server.
4
4
 
5
- Choose [cloud or local access](../../docs/gscdump-cli/guides/1.start/2.choose-access.md) before login. Cloud login opens gscdump.com and saves a CLI session. Local login calls Google directly and can use your own Google credentials.
5
+ Choose [Local or Hosted mode](../../docs/gscdump-cli/guides/1.start/2.choose-access.md) before login. Local mode calls Google with your own credentials: a service account or an OAuth client. Hosted mode opens gscdump.com, saves a CLI session, and reads your hosted record. It never calls Google.
6
6
 
7
7
  ```bash
8
8
  npm install -g @gscdump/cli
@@ -1,9 +1,9 @@
1
- import { GSC_INDEXING_SCOPE, GSC_READ_SCOPE, GSC_SITE_VERIFICATION_SCOPE, GSC_WRITE_SCOPE, hasGoogleScope } from "gscdump/client";
2
- function missingRequiredScopes(scopes, provider) {
3
- return (provider === "gscdump" ? [GSC_READ_SCOPE] : [
1
+ import { GSC_INDEXING_SCOPE, GSC_SITE_VERIFICATION_SCOPE, GSC_WRITE_SCOPE, hasGoogleScope } from "gscdump/client";
2
+ function missingRequiredScopes(scopes) {
3
+ return [
4
4
  GSC_WRITE_SCOPE,
5
5
  GSC_INDEXING_SCOPE,
6
6
  GSC_SITE_VERIFICATION_SCOPE
7
- ]).filter((scope) => !hasGoogleScope(scopes, scope));
7
+ ].filter((scope) => !hasGoogleScope(scopes, scope));
8
8
  }
9
9
  export { missingRequiredScopes };
@@ -3,9 +3,15 @@ import { useCliRuntime } from "./runtime.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import fs from "node:fs/promises";
5
5
  import path from "node:path";
6
- import { gscdumpAvailableSiteSchema } from "@gscdump/contracts";
7
6
  import { z } from "zod";
8
- const HOSTED_SESSION_REJECTED = "gscdump.com rejected the CLI session. Run `gscdump auth login --mode cloud` again.";
7
+ const HOSTED_SESSION_REJECTED = "gscdump.com rejected the CLI session. Run `gscdump auth login --mode hosted` again.";
8
+ const LOCAL_MODE_REQUIRED = [
9
+ "This command calls Google, so it needs Local mode.",
10
+ "Hosted mode reads your gscdump.com record and never calls Google.",
11
+ "If you want Local mode, run `gscdump auth login --mode local`."
12
+ ].join("\n");
13
+ const HOSTED_NOT_SET_UP = "Hosted credentials are missing. Run `gscdump auth login --mode hosted`, or set GSCDUMP_API_KEY.";
14
+ const SAVED_STATE_INVALID = "The saved access mode is invalid. Run `gscdump auth login --mode local` or `gscdump auth login --mode hosted`.";
9
15
  const apiRootSchema = z.url().transform((value) => value.replace(/\/+$/, "")).refine((value) => {
10
16
  const url = new URL(value);
11
17
  return !url.username && !url.password && !url.search && !url.hash && (url.protocol === "https:" || url.protocol === "http:" && [
@@ -17,24 +23,47 @@ const apiRootSchema = z.url().transform((value) => value.replace(/\/+$/, "")).re
17
23
  const stateSchema = z.union([
18
24
  z.object({ _tag: z.literal("Local") }),
19
25
  z.object({
20
- _tag: z.literal("Cloud"),
26
+ _tag: z.literal("Hosted"),
21
27
  apiRoot: apiRootSchema,
22
28
  apiKey: z.string().trim().regex(/^gsd_user_\S+$/)
23
29
  }),
24
30
  z.object({
25
- _tag: z.literal("Cloud"),
31
+ _tag: z.literal("Hosted"),
26
32
  apiRoot: apiRootSchema,
27
33
  sessionId: z.string().regex(/^[a-f0-9]{64}$/)
28
34
  })
29
35
  ]);
30
36
  function parseAuthMode(value) {
31
37
  if (value === void 0 || value === "") return void 0;
32
- if (value === "cloud" || value === "local") return value;
33
- throw new Error("Authentication mode must be cloud or local.");
38
+ if (value === "local" || value === "hosted") return value;
39
+ throw new Error("Access mode must be local or hosted.");
40
+ }
41
+ function resolveAccessMode(input) {
42
+ if (input.flag) return {
43
+ mode: input.flag,
44
+ source: "flag"
45
+ };
46
+ const env = parseAuthMode(input.env);
47
+ if (env) return {
48
+ mode: env,
49
+ source: "env"
50
+ };
51
+ if (input.saved) return {
52
+ mode: input.saved,
53
+ source: "saved"
54
+ };
55
+ if (input.apiKey) return {
56
+ mode: "hosted",
57
+ source: "api-key"
58
+ };
59
+ return {
60
+ mode: "local",
61
+ source: "default"
62
+ };
34
63
  }
35
64
  function parseAuthentication(value) {
36
65
  const parsed = stateSchema.safeParse(value);
37
- if (!parsed.success) throw new Error("Invalid authentication. Use a CLI session or gscdump user API key with a trusted API root.");
66
+ if (!parsed.success) throw new Error("Invalid Hosted credentials. Use a CLI session or a gscdump user API key with a trusted API root.");
38
67
  return parsed.data;
39
68
  }
40
69
  async function saveAuthentication(state) {
@@ -59,50 +88,70 @@ async function saveAuthentication(state) {
59
88
  async function clearAuthentication() {
60
89
  await fs.rm(path.join(useCliRuntime().configDir, "authentication.json"), { force: true });
61
90
  }
62
- async function revokeCloudSession(state) {
91
+ async function revokeHostedSession(state) {
63
92
  if (!("sessionId" in state)) return;
64
- await cloudRequest(state, "/cli/auth/logout", { method: "POST" });
93
+ await hostedRequest(state, "/cli/auth/logout", { method: "POST" });
94
+ }
95
+ async function readSavedAuthentication(configDir) {
96
+ const body = await fs.readFile(path.join(configDir, "authentication.json"), "utf8").catch((error) => {
97
+ if (error.code === "ENOENT") return null;
98
+ throw error;
99
+ });
100
+ if (body === null) return null;
101
+ const saved = stateSchema.safeParse(JSON.parse(body));
102
+ if (!saved.success) throw new Error(SAVED_STATE_INVALID);
103
+ return saved.data;
65
104
  }
66
105
  async function resolveAuthentication() {
67
106
  const runtime = useCliRuntime();
68
107
  const env = runtime.environment;
69
- const mode = runtime.authModeOverride ?? parseAuthMode(env.GSCDUMP_AUTH_MODE);
70
- if (mode === "local") return { _tag: "Local" };
71
- const body = await fs.readFile(path.join(runtime.configDir, "authentication.json"), "utf8").catch((error) => {
72
- if (error.code === "ENOENT") return null;
73
- throw error;
108
+ const flag = runtime.authModeOverride;
109
+ const envMode = parseAuthMode(env.GSCDUMP_AUTH_MODE);
110
+ if ((flag ?? envMode) === "local") return { _tag: "Local" };
111
+ const state = await readSavedAuthentication(runtime.configDir);
112
+ const { mode } = resolveAccessMode({
113
+ flag,
114
+ env: envMode,
115
+ saved: state ? state._tag === "Hosted" ? "hosted" : "local" : void 0,
116
+ apiKey: env.GSCDUMP_API_KEY
74
117
  });
75
- const saved = body === null ? null : stateSchema.safeParse(JSON.parse(body));
76
- if (saved && !saved.success) throw new Error("Saved authentication is invalid. Run `gscdump auth login --mode cloud` or `--mode local`.");
77
- const state = saved?.success ? saved.data : null;
78
- if (mode !== "cloud" && state?._tag === "Local") return state;
118
+ if (mode === "local") return { _tag: "Local" };
79
119
  if (env.GSCDUMP_API_KEY) {
80
120
  const parsed = stateSchema.safeParse({
81
- _tag: "Cloud",
121
+ _tag: "Hosted",
82
122
  apiKey: env.GSCDUMP_API_KEY,
83
- apiRoot: env.GSCDUMP_API_ROOT ?? (state?._tag === "Cloud" ? state.apiRoot : "https://gscdump.com/api")
123
+ apiRoot: env.GSCDUMP_API_ROOT ?? (state?._tag === "Hosted" ? state.apiRoot : "https://gscdump.com/api")
84
124
  });
85
- if (!parsed.success) throw new Error("Invalid hosted authentication. Set GSCDUMP_API_KEY to a gscdump user API key.");
125
+ if (!parsed.success) throw new Error("GSCDUMP_API_KEY is invalid. Set it to a gscdump user API key.");
86
126
  return parsed.data;
87
127
  }
88
- if (state?._tag === "Cloud") {
89
- if (env.GSCDUMP_API_ROOT && env.GSCDUMP_API_ROOT.replace(/\/+$/, "") !== state.apiRoot) throw new Error("The API root changed. Run `gscdump auth login --mode cloud` for the new API root.");
128
+ if (state?._tag === "Hosted") {
129
+ if (env.GSCDUMP_API_ROOT && env.GSCDUMP_API_ROOT.replace(/\/+$/, "") !== state.apiRoot) throw new Error("The API root changed. Run `gscdump auth login --mode hosted` for the new API root.");
90
130
  return state;
91
131
  }
92
- if (mode === "cloud") throw new Error("Hosted credentials are missing. Run `gscdump auth login --mode cloud`.");
93
- return { _tag: "Local" };
132
+ throw new Error(HOSTED_NOT_SET_UP);
94
133
  }
134
+ const hostedSiteSchema = z.object({
135
+ siteId: z.string(),
136
+ siteUrl: z.string(),
137
+ syncStatus: z.string().nullable().optional(),
138
+ syncProgress: z.object({
139
+ completed: z.number(),
140
+ total: z.number(),
141
+ percent: z.number()
142
+ }).passthrough().nullable().optional(),
143
+ lastSyncAt: z.number().nullable().optional(),
144
+ newestDateSynced: z.string().nullable().optional(),
145
+ oldestDateSynced: z.string().nullable().optional()
146
+ }).passthrough();
95
147
  const accountSchema = z.object({
96
148
  user: z.object({
97
149
  publicId: z.string(),
98
150
  email: z.string()
99
151
  }),
100
- sites: z.array(z.object({
101
- siteId: z.string(),
102
- siteUrl: z.string()
103
- }).passthrough())
152
+ sites: z.array(hostedSiteSchema)
104
153
  });
105
- async function cloudRequest(state, route, options = {}) {
154
+ async function hostedRequest(state, route, options = {}) {
106
155
  const response = await fetch(`${state.apiRoot}${route}`, {
107
156
  ...options,
108
157
  headers: {
@@ -122,22 +171,16 @@ async function cloudRequest(state, route, options = {}) {
122
171
  }
123
172
  return response.status === 204 ? void 0 : response.json();
124
173
  }
125
- function cloudCredential(state) {
174
+ function hostedCredential(state) {
126
175
  return "sessionId" in state ? state.sessionId : state.apiKey;
127
176
  }
128
- async function getCloudAccount(state) {
129
- const result = accountSchema.safeParse(await cloudRequest(state, "/cli/me"));
130
- if (!result.success) throw new Error("The hosted API returned invalid account data.");
131
- return result.data;
132
- }
133
- async function getCloudSites(state) {
134
- const result = gscdumpAvailableSiteSchema.array().safeParse(await cloudRequest(state, "/cli/sites/available"));
135
- if (!result.success) throw new Error("The hosted API returned invalid Site data.");
177
+ async function getHostedAccount(state) {
178
+ const result = accountSchema.safeParse(await hostedRequest(state, "/cli/me"));
179
+ if (!result.success) throw new Error("gscdump.com returned invalid account data.");
136
180
  return result.data;
137
181
  }
138
182
  function formatHostedSync(site) {
139
- if (!site.registered) return void 0;
140
183
  const status = site.syncStatus ?? "pending";
141
184
  return `${status}${site.syncProgress && site.syncProgress.total > 0 && status !== "synced" ? `: ${site.syncProgress.completed.toLocaleString("en-US")} of ${site.syncProgress.total.toLocaleString("en-US")} days (${Math.round(site.syncProgress.percent)}%)` : ""}${status === "synced" && site.oldestDateSynced && site.newestDateSynced ? `: ${site.oldestDateSynced} to ${site.newestDateSynced}` : ""}`;
142
185
  }
143
- export { HOSTED_SESSION_REJECTED, clearAuthentication, cloudCredential, cloudRequest, formatHostedSync, getCloudAccount, getCloudSites, parseAuthMode, parseAuthentication, resolveAuthentication, revokeCloudSession, saveAuthentication };
186
+ export { HOSTED_NOT_SET_UP, HOSTED_SESSION_REJECTED, LOCAL_MODE_REQUIRED, clearAuthentication, formatHostedSync, getHostedAccount, hostedCredential, hostedRequest, parseAuthMode, parseAuthentication, resolveAccessMode, resolveAuthentication, revokeHostedSession, saveAuthentication };
package/dist/auth.mjs CHANGED
@@ -1,16 +1,14 @@
1
1
  import { resolveAuthentication } from "./auth-state.mjs";
2
- import { getConfigDir, loadConfig } from "./config.mjs";
2
+ import { getConfigDir, loadConfig, saveConfig } from "./config.mjs";
3
3
  import { pickCliEnvironmentValue, resolveCliEnvironment } from "./environment.mjs";
4
4
  import { getAppliedEnvKeys, getLoadedEnvPath } from "./env-file.mjs";
5
5
  import { displayPath, logger } from "./utils.mjs";
6
- import { loginWithPlatform, refreshWithPlatform } from "./hosted-auth.mjs";
7
6
  import process from "node:process";
8
7
  import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
9
8
  import fs from "node:fs/promises";
10
9
  import path from "node:path";
11
10
  import { Buffer } from "node:buffer";
12
11
  import { createServer } from "node:http";
13
- import { setTimeout as setTimeout$1 } from "node:timers/promises";
14
12
  import { text } from "@clack/prompts";
15
13
  import { CodeChallengeMethod, JWT, OAuth2Client } from "google-auth-library";
16
14
  import { createAuth } from "gscdump/client";
@@ -67,14 +65,18 @@ function resolveBYOK(opts = {}) {
67
65
  function getTokensPath() {
68
66
  return path.join(getConfigDir(), "tokens.json");
69
67
  }
70
- const GOOGLE_NOT_CONNECTED = [
71
- "Google is not connected. Use one of these:",
72
- " Local: gscdump auth login",
73
- " Cloud: gscdump auth login --mode cloud (opens a browser)",
74
- " BYOK: set GSC_ACCESS_TOKEN, or GSC_CLIENT_ID, GSC_CLIENT_SECRET and GSC_REFRESH_TOKEN"
68
+ const ACCESS_NOT_SET_UP = [
69
+ "Google credentials are missing. Set up one of the 2 access modes:",
70
+ " Local mode uses your own Google credentials:",
71
+ " Service account (recommended): gscdump auth login --mode local --service-account ./key.json",
72
+ " OAuth client: set GSC_CLIENT_ID and GSC_CLIENT_SECRET, then run `gscdump auth login --mode local`",
73
+ " Hosted mode reads your gscdump.com record:",
74
+ " gscdump auth login --mode hosted",
75
+ "Guide: https://gscdump.com/gscdump-cli/guides/start/choose-access"
75
76
  ].join("\n");
76
77
  async function loadTokens() {
77
- return fs.readFile(getTokensPath(), "utf-8").then((data) => JSON.parse(data)).catch(() => null);
78
+ const tokens = await fs.readFile(getTokensPath(), "utf-8").then((data) => JSON.parse(data)).catch(() => null);
79
+ return tokens?.provider === "gscdump" ? null : tokens;
78
80
  }
79
81
  async function saveTokens(tokens) {
80
82
  await fs.mkdir(getConfigDir(), {
@@ -112,19 +114,20 @@ async function getAuthCredentials(interactive) {
112
114
  clientSecret: config.clientSecret
113
115
  };
114
116
  }
115
- if (!interactive) {
116
- logger.error("GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET required for non-interactive mode");
117
- process.exit(1);
118
- }
117
+ if (!interactive) throw new Error(ACCESS_NOT_SET_UP);
119
118
  console.log();
120
- console.log(" \x1B[1mOAuth 2.0 Setup Required\x1B[0m");
121
- console.log(" \x1B[90mThe Google Search Console API requires OAuth 2.0 credentials.\x1B[0m");
119
+ console.log(" \x1B[1mLocal mode: OAuth client setup\x1B[0m");
120
+ console.log(" \x1B[90mA service account is simpler and never expires. To use one, press Ctrl+C and run:\x1B[0m");
121
+ console.log(" \x1B[90m gscdump auth login --mode local --service-account ./key.json\x1B[0m");
122
+ console.log(" \x1B[90mTo read your gscdump.com record instead, run: gscdump auth login --mode hosted\x1B[0m");
122
123
  console.log();
123
124
  console.log(" \x1B[1mSteps:\x1B[0m");
124
- console.log(" \x1B[90m1.\x1B[0m Go to \x1B[36mhttps://console.developers.google.com/apis/credentials\x1B[0m");
125
+ console.log(" \x1B[90m1.\x1B[0m Go to \x1B[36mhttps://console.cloud.google.com/apis/credentials\x1B[0m");
125
126
  console.log(" \x1B[90m2.\x1B[0m Create credentials > OAuth client ID > Desktop application");
126
- console.log(" \x1B[90m3.\x1B[0m Enable \"Search Console API\" and \"Web Search Indexing API\" for your project");
127
- console.log(" \x1B[90m4.\x1B[0m Copy the Client ID and Client Secret");
127
+ console.log(" \x1B[90m3.\x1B[0m Enable \"Google Search Console API\" and \"Web Search Indexing API\" for your project");
128
+ console.log(" \x1B[90m4.\x1B[0m On the OAuth consent screen, set the publishing status to \"In production\"");
129
+ console.log(" \x1B[90m In \"Testing\" status, Google expires refresh tokens after 7 days.\x1B[0m");
130
+ console.log(" \x1B[90m5.\x1B[0m Copy the Client ID and Client Secret");
128
131
  console.log();
129
132
  const clientIdResult = await text({
130
133
  message: "Enter your Google OAuth Client ID:",
@@ -137,8 +140,13 @@ async function getAuthCredentials(interactive) {
137
140
  validate: (v) => v ? void 0 : "Required"
138
141
  });
139
142
  if (typeof clientSecretResult !== "string") process.exit(1);
143
+ await saveConfig({
144
+ ...config,
145
+ clientId: clientIdResult,
146
+ clientSecret: clientSecretResult
147
+ });
140
148
  console.log();
141
- logger.info("Tip: Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET env vars to skip prompts");
149
+ logger.info(`Saved the OAuth client to ${displayPath(`${getConfigDir()}/config.json`)}`);
142
150
  return {
143
151
  clientId: clientIdResult,
144
152
  clientSecret: clientSecretResult
@@ -270,8 +278,7 @@ async function authenticate(credentials, interactive, opts = {}) {
270
278
  }
271
279
  return oauth2Client;
272
280
  }
273
- const savedTokens = !opts.force ? await loadTokens() : null;
274
- const existingTokens = savedTokens?.provider === "gscdump" ? null : savedTokens;
281
+ const existingTokens = !opts.force ? await loadTokens() : null;
275
282
  let refreshFailed = false;
276
283
  let refreshError = null;
277
284
  if (existingTokens) {
@@ -299,7 +306,7 @@ async function authenticate(credentials, interactive, opts = {}) {
299
306
  }
300
307
  if (!interactive) {
301
308
  if (refreshFailed) throw new Error(`Token refresh failed${refreshError ? `: ${refreshError.message}` : ""}. The refresh token may be revoked or expired. Run \`gscdump auth login\` to sign in again.`);
302
- throw new Error(GOOGLE_NOT_CONNECTED);
309
+ throw new Error(ACCESS_NOT_SET_UP);
303
310
  }
304
311
  const state = randomBytes(32).toString("base64url");
305
312
  const codeVerifier = randomBytes(32).toString("base64url");
@@ -327,42 +334,11 @@ async function getAuth(opts = {}) {
327
334
  const { interactive = true, noBrowser = false, force = false } = opts;
328
335
  const env = resolveCliEnvironment();
329
336
  const config = opts.config ?? await loadConfig();
330
- if (env.clientId && env.clientSecret || config.clientId && config.clientSecret) return authenticate(await getAuthCredentials(interactive), interactive, {
337
+ if (!Boolean(env.clientId && env.clientSecret || config.clientId && config.clientSecret) && !interactive) throw new Error(ACCESS_NOT_SET_UP);
338
+ return authenticate(await getAuthCredentials(interactive), interactive, {
331
339
  noBrowser,
332
340
  force
333
341
  });
334
- let tokens = force ? null : await loadTokens();
335
- if (tokens?.provider !== "gscdump" || !tokens.refresh_token) {
336
- if (!interactive) throw new Error(GOOGLE_NOT_CONNECTED);
337
- tokens = await loginWithPlatform({
338
- force,
339
- request: fetch,
340
- now: Date.now,
341
- wait: setTimeout$1,
342
- authorize: async (url) => {
343
- logger.info(`Open this URL to connect Google:\n${url}`);
344
- if (!noBrowser) await open(url).catch((error) => logger.warn(`Browser could not open: ${error.message}. Open the URL above.`));
345
- }
346
- });
347
- await saveTokens(tokens);
348
- }
349
- const refreshToken = tokens.refresh_token;
350
- const client = new OAuth2Client();
351
- client.refreshHandler = async () => {
352
- const refreshed = await refreshWithPlatform(refreshToken);
353
- await saveTokens({
354
- provider: "gscdump",
355
- refresh_token: refreshToken,
356
- ...refreshed
357
- });
358
- return refreshed;
359
- };
360
- client.setCredentials({
361
- access_token: tokens.access_token,
362
- expiry_date: tokens.expiry_date
363
- });
364
- await client.getAccessToken();
365
- return client;
366
342
  }
367
343
  function isStaleServiceAccountPointer(error) {
368
344
  if (error instanceof SyntaxError) return true;
@@ -379,7 +355,7 @@ async function resolveAuth(opts = {}) {
379
355
  }
380
356
  const byok = resolveBYOK(opts.byok);
381
357
  if (byok) {
382
- if (typeof byok !== "string") logger.success("Using BYOK credentials");
358
+ if (typeof byok !== "string") logger.success("Using Google credentials from the environment");
383
359
  return byok;
384
360
  }
385
361
  return getAuth(opts);
@@ -474,7 +450,7 @@ async function describeAuthProvenance() {
474
450
  else if (accessTok) effective = "byok-access-token";
475
451
  else if (tokens) effective = "saved-tokens";
476
452
  else effective = "none";
477
- if ((effective === "byok-refresh-token" || effective === "byok-access-token") && tokens) warnings.push("BYOK env vars shadow saved tokens. If you just ran `auth login --force`, the env tokens are stale; unset them or update them.");
453
+ if ((effective === "byok-refresh-token" || effective === "byok-access-token") && tokens) warnings.push("Environment credentials take priority over saved tokens. If you just ran `auth login --force`, unset or update the environment tokens.");
478
454
  if (effective === "byok-refresh-token" && clientId && refreshTok) {
479
455
  if (getAppliedEnvKeys().has(clientId.envVar) !== getAppliedEnvKeys().has(refreshTok.envVar)) warnings.push(`client_id and refresh_token come from different sources (${envSourceLabel(clientId.envVar)} vs ${envSourceLabel(refreshTok.envVar)}); they may not match.`);
480
456
  }
@@ -501,9 +477,9 @@ async function formatAuthProvenance() {
501
477
  return lines.join("\n");
502
478
  }
503
479
  async function probeAuth() {
504
- if ((await resolveAuthentication())._tag === "Cloud") return "hosted";
480
+ if ((await resolveAuthentication())._tag === "Hosted") return "hosted";
505
481
  if (resolveBYOK()) return "google";
506
482
  if (await resolveServiceAccount().then(Boolean, (error) => !isStaleServiceAccountPointer(error))) return "google";
507
483
  return await loadTokens() !== null ? "google" : "none";
508
484
  }
509
- export { GOOGLE_NOT_CONNECTED, authenticate, clearTokens, describeAuthProvenance, formatAuthProvenance, getAuth, getAuthCredentials, loadServiceAccount, loadServiceAccountResult, loadTokens, probeAuth, resolveAuth, resolveBYOK, resolveServiceAccount, saveTokens };
485
+ export { ACCESS_NOT_SET_UP, authenticate, clearTokens, describeAuthProvenance, formatAuthProvenance, getAuth, getAuthCredentials, isStaleServiceAccountPointer, loadServiceAccount, loadServiceAccountResult, loadTokens, probeAuth, resolveAuth, resolveBYOK, resolveServiceAccount, saveTokens };
@@ -1,15 +1,15 @@
1
- import { cloudCredential, getCloudAccount } from "./auth-state.mjs";
1
+ import { getHostedAccount, hostedCredential } from "./auth-state.mjs";
2
2
  import { logger } from "./utils.mjs";
3
3
  import { writeBingDump } from "./bing-data.mjs";
4
4
  import { createGscdumpV1Client } from "@gscdump/sdk/v1";
5
5
  function hostedBingClient(state) {
6
6
  return createGscdumpV1Client({
7
7
  apiRoot: state.apiRoot,
8
- credential: cloudCredential(state)
8
+ credential: hostedCredential(state)
9
9
  });
10
10
  }
11
11
  async function listHostedBingSites(state) {
12
- return loadHostedBingConnections(state, (await getCloudAccount(state)).sites, { tolerateFailures: true });
12
+ return loadHostedBingConnections(state, (await getHostedAccount(state)).sites, { tolerateFailures: true });
13
13
  }
14
14
  async function loadHostedBingConnections(state, sites, options = {}) {
15
15
  const client = hostedBingClient(state);
@@ -29,7 +29,7 @@ async function loadHostedBingConnections(state, sites, options = {}) {
29
29
  }
30
30
  async function resolveHostedBingSites(state, input) {
31
31
  if (Boolean(input.site) === Boolean(input.allSites)) throw new Error("Choose --site or --all-sites.");
32
- const account = await getCloudAccount(state);
32
+ const account = await getHostedAccount(state);
33
33
  const exact = account.sites.filter((site) => site.siteId === input.site || site.siteUrl === input.site);
34
34
  const normalize = (value) => URL.parse(value.startsWith("sc-domain:") ? `https://${value.slice(10)}/` : value)?.toString();
35
35
  const requested = input.allSites ? account.sites : exact.length ? exact : account.sites.filter((site) => normalize(site.siteUrl) === normalize(input.site) && normalize(input.site) !== void 0);
@@ -8,7 +8,7 @@ const dumpCommandMeta = {
8
8
  };
9
9
  const queryCommandMeta = {
10
10
  name: "query",
11
- description: "Run a search analytics query (local Parquet by default, --live hits GSC API)"
11
+ description: "Run a search analytics query (Store or hosted record; --live calls Google)"
12
12
  };
13
13
  const sitesCommandMeta = {
14
14
  name: "sites",
@@ -53,7 +53,7 @@ const authCommandMeta = {
53
53
  },
54
54
  login: {
55
55
  name: "login",
56
- description: "Run OAuth flow and persist tokens (skip if BYOK env vars set)"
56
+ description: "Set up Local mode (service account or OAuth client) or Hosted mode (--mode hosted)"
57
57
  },
58
58
  logout: {
59
59
  name: "logout",