@gscdump/cli 4.2.3 → 4.3.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 +1 -1
- package/dist/auth-scopes.mjs +4 -4
- package/dist/auth-state.mjs +84 -41
- package/dist/auth.mjs +35 -59
- package/dist/bing-hosted.mjs +4 -4
- package/dist/command-meta.mjs +2 -2
- package/dist/commands/auth.mjs +71 -86
- package/dist/commands/bing.mjs +10 -10
- package/dist/commands/doctor.mjs +64 -14
- package/dist/commands/init.mjs +31 -15
- package/dist/commands/mcp.mjs +13 -7
- package/dist/commands/query.mjs +44 -1
- package/dist/commands/sites.mjs +32 -19
- package/dist/context.mjs +4 -6
- package/dist/dump-bing.mjs +1 -1
- package/dist/error-handler.mjs +1 -1
- package/dist/hosted-auth.mjs +10 -51
- package/dist/hosted-query.mjs +31 -0
- package/dist/hosted-site.mjs +6 -6
- package/dist/mcp/errors.mjs +4 -14
- package/dist/mcp/handlers/diagnostics.mjs +0 -31
- package/dist/package.mjs +1 -1
- package/dist/route.mjs +10 -4
- package/dist/runtime.d.mts +1 -1
- package/dist/sync-plan.mjs +1 -1
- package/package.json +7 -7
- package/skills/gscdump/SKILL.md +77 -62
- package/dist/cloud-google.mjs +0 -99
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Google Search Console CLI and MCP server.
|
|
4
4
|
|
|
5
|
-
Choose [
|
|
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
|
package/dist/auth-scopes.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { GSC_INDEXING_SCOPE,
|
|
2
|
-
function missingRequiredScopes(scopes
|
|
3
|
-
return
|
|
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
|
-
]
|
|
7
|
+
].filter((scope) => !hasGoogleScope(scopes, scope));
|
|
8
8
|
}
|
|
9
9
|
export { missingRequiredScopes };
|
package/dist/auth-state.mjs
CHANGED
|
@@ -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
|
|
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("
|
|
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("
|
|
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 === "
|
|
33
|
-
throw new Error("
|
|
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
|
|
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
|
|
91
|
+
async function revokeHostedSession(state) {
|
|
63
92
|
if (!("sessionId" in state)) return;
|
|
64
|
-
await
|
|
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
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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: "
|
|
121
|
+
_tag: "Hosted",
|
|
82
122
|
apiKey: env.GSCDUMP_API_KEY,
|
|
83
|
-
apiRoot: env.GSCDUMP_API_ROOT ?? (state?._tag === "
|
|
123
|
+
apiRoot: env.GSCDUMP_API_ROOT ?? (state?._tag === "Hosted" ? state.apiRoot : "https://gscdump.com/api")
|
|
84
124
|
});
|
|
85
|
-
if (!parsed.success) throw new Error("
|
|
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 === "
|
|
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
|
|
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
|
-
|
|
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(
|
|
101
|
-
siteId: z.string(),
|
|
102
|
-
siteUrl: z.string()
|
|
103
|
-
}).passthrough())
|
|
152
|
+
sites: z.array(hostedSiteSchema)
|
|
104
153
|
});
|
|
105
|
-
async function
|
|
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
|
|
174
|
+
function hostedCredential(state) {
|
|
126
175
|
return "sessionId" in state ? state.sessionId : state.apiKey;
|
|
127
176
|
}
|
|
128
|
-
async function
|
|
129
|
-
const result = accountSchema.safeParse(await
|
|
130
|
-
if (!result.success) throw new Error("
|
|
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 {
|
|
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
|
|
71
|
-
"Google
|
|
72
|
-
" Local
|
|
73
|
-
"
|
|
74
|
-
"
|
|
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
|
-
|
|
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[
|
|
121
|
-
console.log(" \x1B[
|
|
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.
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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)
|
|
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
|
|
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("
|
|
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 === "
|
|
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 {
|
|
485
|
+
export { ACCESS_NOT_SET_UP, authenticate, clearTokens, describeAuthProvenance, formatAuthProvenance, getAuth, getAuthCredentials, isStaleServiceAccountPointer, loadServiceAccount, loadServiceAccountResult, loadTokens, probeAuth, resolveAuth, resolveBYOK, resolveServiceAccount, saveTokens };
|
package/dist/bing-hosted.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
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:
|
|
8
|
+
credential: hostedCredential(state)
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
async function listHostedBingSites(state) {
|
|
12
|
-
return loadHostedBingConnections(state, (await
|
|
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
|
|
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);
|
package/dist/command-meta.mjs
CHANGED
|
@@ -8,7 +8,7 @@ const dumpCommandMeta = {
|
|
|
8
8
|
};
|
|
9
9
|
const queryCommandMeta = {
|
|
10
10
|
name: "query",
|
|
11
|
-
description: "Run a search analytics query (
|
|
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: "
|
|
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",
|