seo 0.13.0 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createRequire as __seoCreateRequire } from 'node:module'; const require = __seoCreateRequire(import.meta.url);
2
- import { n as version, t as load } from "./esm-BfNQ2FiC.js";
2
+ import { n as version, t as load } from "./esm-Dmtr8qrP.js";
3
3
  import { SEO_PROVIDER_API_VERSION } from "./provider-sdk.js";
4
4
  import crypto, { createHash, randomUUID, sign } from "node:crypto";
5
5
  import { z } from "zod";
@@ -1400,6 +1400,44 @@ function agentActionsView(report, options = {}) {
1400
1400
  };
1401
1401
  }
1402
1402
  //#endregion
1403
+ //#region packages/core/src/providers/errors.ts
1404
+ const PROVIDER_ERROR_CODES = {
1405
+ configuration: "AUTH_CONFIG_REQUIRED",
1406
+ authentication: "AUTH_CONFIG_REQUIRED",
1407
+ "rate-limit": "RATE_LIMITED",
1408
+ timeout: "PROVIDER_UNAVAILABLE",
1409
+ "response-too-large": "PROVIDER_UNAVAILABLE",
1410
+ "invalid-response": "PROVIDER_UNAVAILABLE",
1411
+ "remote-error": "PROVIDER_UNAVAILABLE"
1412
+ };
1413
+ var ProviderError = class extends Error {
1414
+ provider;
1415
+ operation;
1416
+ code;
1417
+ status;
1418
+ retryable;
1419
+ constructor(input) {
1420
+ super(input.message, { cause: input.cause });
1421
+ this.name = "ProviderError";
1422
+ this.provider = input.provider;
1423
+ this.operation = input.operation;
1424
+ this.code = input.code;
1425
+ this.status = input.status ?? null;
1426
+ this.retryable = input.retryable ?? false;
1427
+ }
1428
+ toJSON() {
1429
+ return {
1430
+ name: this.name,
1431
+ provider: this.provider,
1432
+ operation: this.operation,
1433
+ code: this.code,
1434
+ message: this.message,
1435
+ status: this.status,
1436
+ retryable: this.retryable
1437
+ };
1438
+ }
1439
+ };
1440
+ //#endregion
1403
1441
  //#region packages/core/src/errors.ts
1404
1442
  const errorMetadata = {
1405
1443
  ACCESS_DENIED: {
@@ -1465,6 +1503,7 @@ function errorMessage$1(error) {
1465
1503
  function toSeoError(error) {
1466
1504
  if (error instanceof SeoError) return error;
1467
1505
  const message = errorMessage$1(error);
1506
+ if (error instanceof ProviderError) return new SeoError(PROVIDER_ERROR_CODES[error.code], message);
1468
1507
  if (/not logged in/i.test(message)) return new SeoError("AUTH_REQUIRED", message);
1469
1508
  if (/oauth client config.*missing|shared seo google app/i.test(message)) return new SeoError("AUTH_CONFIG_REQUIRED", message);
1470
1509
  if (/invalid_grant|refresh token.*no longer valid/i.test(message)) return new SeoError("AUTH_EXPIRED", message);
@@ -2249,15 +2288,18 @@ function factsOnly(value) {
2249
2288
  if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).filter(([key]) => !ADVICE_KEYS.test(key)).map(([key, child]) => [key, factsOnly(child)]));
2250
2289
  return value;
2251
2290
  }
2291
+ function realIsoDate(value) {
2292
+ return /^\d{4}-\d{2}-\d{2}$/.test(value) && (/* @__PURE__ */ new Date(`${value}T00:00:00Z`)).toISOString().startsWith(value);
2293
+ }
2252
2294
  function dateWindow(input, fallbackDays) {
2253
- if (input.startDate || input.endDate) {
2254
- if (!input.startDate || !input.endDate || input.startDate > input.endDate) throw new CommandError("INVALID_INPUT", "Send a start date and an end date together, in order.");
2255
- return {
2256
- startDate: input.startDate,
2257
- endDate: input.endDate
2258
- };
2259
- }
2260
- return finalGscDateRange(fallbackDays);
2295
+ if (!input.startDate && !input.endDate) return finalGscDateRange(fallbackDays);
2296
+ const endDate = input.endDate ?? latestFinalGscDate();
2297
+ const startDate = input.startDate ?? shiftIsoDate(endDate, 1 - fallbackDays);
2298
+ if (!realIsoDate(startDate) || !realIsoDate(endDate) || startDate > endDate) throw new CommandError("INVALID_INPUT", `Send real YYYY-MM-DD dates with the start before the end (got ${startDate} to ${endDate}).`);
2299
+ return {
2300
+ startDate,
2301
+ endDate
2302
+ };
2261
2303
  }
2262
2304
  function earlierRange(range) {
2263
2305
  return decayComparisonRange(range, "previous-period");
@@ -2341,20 +2383,38 @@ function listOfUpdates(value, checked) {
2341
2383
  if (value.length === 0) return `Google updates ${where}: none.`;
2342
2384
  return `Google updates ${where} (timing only, not cause): ${value.map((update) => `${update.name} (${update.start.slice(0, 10)} to ${update.end?.slice(0, 10) ?? "ongoing"})`).join("; ")}.`;
2343
2385
  }
2386
+ function readable(names, who = "it") {
2387
+ if (!names.length) return `Search Console properties ${who} can read: none.`;
2388
+ const more = names.length > 20 ? `, and ${names.length - 20} more` : "";
2389
+ return `Search Console properties ${who} can read: ${names.slice(0, 20).join(", ")}${more}.`;
2390
+ }
2344
2391
  function matchProperty(names, site) {
2345
- if (names.includes(site)) return site;
2346
- let host;
2347
- let origin;
2392
+ const value = site.trim();
2393
+ if (names.includes(value)) return value;
2394
+ let url;
2348
2395
  try {
2349
- const url = new URL(site.includes("://") ? site : `https://${site.replace(/^sc-domain:/, "")}`);
2350
- host = url.hostname.replace(/^www\./, "");
2351
- origin = url.origin;
2396
+ url = new URL(value.includes("://") ? value : `https://${value.replace(/^sc-domain:/i, "")}`);
2352
2397
  } catch {
2353
2398
  return null;
2354
2399
  }
2355
- const domain = names.filter((name) => name.startsWith("sc-domain:")).map((name) => name.slice(10)).filter((name) => host === name || host.endsWith(`.${name}`)).sort((a, b) => b.length - a.length)[0];
2356
- if (domain) return `sc-domain:${domain}`;
2357
- return names.find((name) => name === `${origin}/` || name === `${origin.replace("://", "://www.")}/` || name === `${origin.replace("://www.", "://")}/`) ?? null;
2400
+ const bare = (name) => name.replace(/^www\./, "");
2401
+ const host = bare(url.hostname);
2402
+ const domain = names.filter((name) => name.startsWith("sc-domain:")).filter((name) => {
2403
+ const own = name.slice(10);
2404
+ return own.startsWith("www.") ? bare(own) === host : host === own || host.endsWith(`.${own}`);
2405
+ }).sort((a, b) => b.length - a.length || a.localeCompare(b))[0];
2406
+ if (domain) return domain;
2407
+ const schemes = value.includes("://") ? [url.protocol] : ["https:", "http:"];
2408
+ return names.map((name) => {
2409
+ try {
2410
+ return {
2411
+ name,
2412
+ url: new URL(name)
2413
+ };
2414
+ } catch {
2415
+ return null;
2416
+ }
2417
+ }).filter((entry) => entry !== null && schemes.includes(entry.url.protocol) && bare(entry.url.hostname) === host && `${url.pathname}/`.startsWith(entry.url.pathname)).sort((a, b) => b.url.pathname.length - a.url.pathname.length || schemes.indexOf(a.url.protocol) - schemes.indexOf(b.url.protocol) || Number(b.url.hostname === url.hostname) - Number(a.url.hostname === url.hostname) || a.name.localeCompare(b.name))[0]?.name ?? null;
2358
2418
  }
2359
2419
  //#endregion
2360
2420
  //#region packages/core/src/commands/present.ts
@@ -2776,9 +2836,10 @@ var LinkedAccounts = class {
2776
2836
  }
2777
2837
  async resolve(site) {
2778
2838
  if (this.google.length === 0) throw new CommandError("NOT_CONNECTED", `No Google account is connected. ${this.hint.signIn}`);
2779
- const found = matchProperty([...(await this.ownerMap()).keys()], site);
2839
+ const names = await this.properties();
2840
+ const found = matchProperty(names, site);
2780
2841
  if (found) return found;
2781
- throw new CommandError("NO_ACCESS", `None of the connected Google accounts (${this.google.map((linked) => linked.email).join(", ")}) has a Search Console property for ${site}. ${this.hint.addGoogle}`);
2842
+ throw new CommandError("NO_ACCESS", `None of the connected Google accounts (${this.google.map((linked) => linked.email).join(", ")}) has a Search Console property for ${site}. ${readable(names, "they")} ${this.hint.addGoogle}`);
2782
2843
  }
2783
2844
  async owner(site) {
2784
2845
  const owner = (await this.ownerMap()).get(await this.resolve(site));
@@ -3179,6 +3240,10 @@ function classify(error) {
3179
3240
  errorCategory: "crawl_timeout",
3180
3241
  failureReason: "crawl_timeout"
3181
3242
  };
3243
+ const provider = signals.find((signal) => signal.name === "ProviderError");
3244
+ const providerCode = PROVIDER_ERROR_CODES[provider?.code];
3245
+ const fromProvider = providerCode && PRODUCT_FAILURES[providerCode];
3246
+ if (fromProvider) return fromProvider;
3182
3247
  for (const code of codes) {
3183
3248
  const product = PRODUCT_FAILURES[code];
3184
3249
  if (product) return product;
@@ -3397,6 +3462,19 @@ function detectTelemetryAgent(input = {}) {
3397
3462
  if (env.CODEX_HOME || env.CODEX_THREAD_ID || env.CODEX_SANDBOX) return "codex";
3398
3463
  return "cli";
3399
3464
  }
3465
+ function detectAgentName(env = process.env) {
3466
+ const named = (env.AI_AGENT || env.AGENT)?.trim().toLowerCase();
3467
+ if (named) return named.slice(0, 40);
3468
+ const agent = detectTelemetryAgent({ env });
3469
+ if (agent !== "cli") return agent;
3470
+ if (env.GEMINI_CLI) return "gemini";
3471
+ if (env.OPENCODE || env.OPENCODE_CLIENT) return "opencode";
3472
+ if (env.ANTIGRAVITY_AGENT) return "antigravity";
3473
+ if (env.AUGMENT_AGENT) return "augment";
3474
+ if (env.COPILOT_MODEL || env.COPILOT_ALLOW_ALL) return "github-copilot";
3475
+ if (env.REPL_ID) return "replit";
3476
+ return null;
3477
+ }
3400
3478
  function telemetryAgent(options) {
3401
3479
  return options.agent ?? detectTelemetryAgent({
3402
3480
  clientName: options.clientName,
@@ -3689,7 +3767,7 @@ async function startCloudLogin() {
3689
3767
  method: "POST",
3690
3768
  body: JSON.stringify({
3691
3769
  label: `seo on ${hostname().split(".")[0] || "this machine"}`.slice(0, 60),
3692
- agent: getTelemetryStatus().enabled ? detectTelemetryAgent() : void 0
3770
+ agent: getTelemetryStatus().enabled ? detectAgentName() : void 0
3693
3771
  })
3694
3772
  });
3695
3773
  const { device, code, url, expiresIn } = result.body;
@@ -16121,35 +16199,6 @@ async function fetchSitemapUrls(input) {
16121
16199
  };
16122
16200
  }
16123
16201
  //#endregion
16124
- //#region packages/core/src/providers/errors.ts
16125
- var ProviderError = class extends Error {
16126
- provider;
16127
- operation;
16128
- code;
16129
- status;
16130
- retryable;
16131
- constructor(input) {
16132
- super(input.message, { cause: input.cause });
16133
- this.name = "ProviderError";
16134
- this.provider = input.provider;
16135
- this.operation = input.operation;
16136
- this.code = input.code;
16137
- this.status = input.status ?? null;
16138
- this.retryable = input.retryable ?? false;
16139
- }
16140
- toJSON() {
16141
- return {
16142
- name: this.name,
16143
- provider: this.provider,
16144
- operation: this.operation,
16145
- code: this.code,
16146
- message: this.message,
16147
- status: this.status,
16148
- retryable: this.retryable
16149
- };
16150
- }
16151
- };
16152
- //#endregion
16153
16202
  //#region packages/core/src/providers/cache.ts
16154
16203
  function stableValue(value) {
16155
16204
  if (Array.isArray(value)) return value.map(stableValue);
@@ -22746,7 +22795,7 @@ function analyzeTrafficAnomalyRows(input) {
22746
22795
  const comparisonStart = shiftIsoDate(input.endDate, -(input.recentDays - 1));
22747
22796
  const baselineEnd = shiftIsoDate(comparisonStart, -1);
22748
22797
  const baselineExpectedDays = calendarDays(input.startDate, baselineEnd);
22749
- if (comparisonStart < input.startDate || baselineExpectedDays < 14) throw new SeoError("INSUFFICIENT_DATA", "The requested calendar range does not contain at least 14 baseline days before the comparison window.");
22798
+ if (comparisonStart < input.startDate || baselineExpectedDays < 14) throw new SeoError("INSUFFICIENT_DATA", `The date range needs at least ${input.recentDays + 14} days: 14 baseline days before the last ${input.recentDays}. Send a longer range, or leave the dates out for the default.`);
22750
22799
  const aggregated = aggregateDailyRows({
22751
22800
  rows: input.rows,
22752
22801
  startDate: input.startDate,
@@ -22754,7 +22803,7 @@ function analyzeTrafficAnomalyRows(input) {
22754
22803
  });
22755
22804
  const baselineRows = aggregated.rows.filter((row) => (row.keys[0] ?? "") <= baselineEnd);
22756
22805
  const comparisonRows = aggregated.rows.filter((row) => (row.keys[0] ?? "") >= comparisonStart);
22757
- if (baselineRows.length < 14 || comparisonRows.length < 3) throw new SeoError("INSUFFICIENT_DATA", `Not enough observed daily GSC data for anomaly detection (${baselineRows.length} baseline days and ${comparisonRows.length} comparison days).`);
22806
+ if (baselineRows.length < 14 || comparisonRows.length < 3) throw new SeoError("INSUFFICIENT_DATA", `Not enough daily Search Console data to look for a drop: ${baselineRows.length} baseline days and ${comparisonRows.length} comparison days, and it needs 14 and 3. The site may be new or get too few clicks for daily rows.`);
22758
22807
  const metrics = ["clicks", "impressions"];
22759
22808
  const stepCaveats = [];
22760
22809
  const anomalies = metrics.map((metric) => {
@@ -29308,25 +29357,64 @@ function need(headers, provider) {
29308
29357
  if (values.some((value) => !value)) throw new CommandError("NO_KEY", `No ${provider} key. Hosted MCP: save it at https://seoskill.dev/account#keys. CLI and local MCP: run \`seo login\` to use the account's keys, or \`seo providers ${provider} connect\`.`);
29309
29358
  return values;
29310
29359
  }
29360
+ const TASK_TIMEOUT_MS = 6e4;
29361
+ const READ_TIMEOUT_MS = 3e4;
29362
+ const RETRY_DELAY_MS = 250;
29363
+ const retryStatus = (status) => status === 429 || status >= 500;
29311
29364
  async function call(provider, request) {
29312
- const response = await fetch(request);
29313
- if (response.ok) return response;
29314
- const text = (await response.text()).slice(0, 300);
29315
- throw new CommandError(response.status === 401 || response.status === 403 ? "NO_KEY" : "PROVIDER_ERROR", `${provider} answered ${response.status}. ${text}`);
29365
+ const read = request.method === "GET";
29366
+ const timeoutMs = read ? READ_TIMEOUT_MS : TASK_TIMEOUT_MS;
29367
+ for (let attempt = 1;; attempt += 1) {
29368
+ const last = attempt === 2;
29369
+ let response;
29370
+ try {
29371
+ response = await fetch(request.clone(), { signal: AbortSignal.timeout(timeoutMs) });
29372
+ } catch (error) {
29373
+ if (read && !last) {
29374
+ await wait(RETRY_DELAY_MS);
29375
+ continue;
29376
+ }
29377
+ throw new CommandError("PROVIDER_ERROR", error.name === "TimeoutError" ? `${provider} did not answer within ${timeoutMs / 1e3} seconds.` : `${provider} could not be reached: ${error.message}`);
29378
+ }
29379
+ if (response.ok) return response;
29380
+ if (retryStatus(response.status) && !last) {
29381
+ await response.body?.cancel();
29382
+ await wait(RETRY_DELAY_MS);
29383
+ continue;
29384
+ }
29385
+ const text = (await response.text()).slice(0, 300);
29386
+ throw new CommandError(response.status === 401 || response.status === 403 ? "NO_KEY" : response.status === 429 ? "RATE_LIMITED" : "PROVIDER_ERROR", `${provider} answered ${response.status}. ${text}`);
29387
+ }
29316
29388
  }
29389
+ const wait = (ms) => new Promise((done) => setTimeout(done, ms));
29317
29390
  function requireInput(input) {
29318
29391
  const needsKeywords = input.action === "keyword_metrics" || input.action === "serp";
29319
29392
  if (needsKeywords && input.keywords.length === 0) throw new CommandError("INVALID_INPUT", `${input.action} needs keywords.`);
29320
29393
  if (!needsKeywords && !input.target) throw new CommandError("INVALID_INPUT", `${input.action} needs a target domain or URL.`);
29321
29394
  }
29395
+ async function eachKeyword(keywords, missed, rowsFor) {
29396
+ const rows = [];
29397
+ let failure;
29398
+ for (const keyword of keywords) try {
29399
+ rows.push(...await rowsFor(keyword));
29400
+ } catch (error) {
29401
+ if (error instanceof CommandError && error.code === "NO_KEY") throw error;
29402
+ failure ??= error;
29403
+ missed.push(keyword);
29404
+ }
29405
+ if (missed.length === keywords.length) throw failure;
29406
+ return rows;
29407
+ }
29322
29408
  async function research(provider, headers, input) {
29323
29409
  requireInput(input);
29324
29410
  const keys = need(headers, provider);
29325
29411
  if (provider === "dataforseo") return dataForSeo(keys, input);
29412
+ const missed = [];
29326
29413
  return {
29327
- rows: await ahrefs(keys[0] ?? "", input),
29414
+ rows: await ahrefs(keys[0] ?? "", input, missed),
29328
29415
  costUsd: null,
29329
- partial: false
29416
+ partial: false,
29417
+ missed
29330
29418
  };
29331
29419
  }
29332
29420
  function countryName(code) {
@@ -29392,12 +29480,14 @@ const PARTIAL = 40106;
29392
29480
  async function dataForSeo(keys, input) {
29393
29481
  const spent = {
29394
29482
  cost: 0,
29395
- partial: false
29483
+ partial: false,
29484
+ missed: []
29396
29485
  };
29397
29486
  return {
29398
29487
  rows: await dataForSeoRows(keys, input, spent),
29399
29488
  costUsd: Math.round(spent.cost * 1e4) / 1e4,
29400
- partial: spent.partial
29489
+ partial: spent.partial,
29490
+ missed: spent.missed
29401
29491
  };
29402
29492
  }
29403
29493
  async function dataForSeoRows(keys, input, spent) {
@@ -29439,28 +29529,20 @@ async function dataForSeoRows(keys, input, spent) {
29439
29529
  keyword_difficulty: "keyword_data.keyword_properties.keyword_difficulty",
29440
29530
  main_intent: "keyword_data.search_intent_info.main_intent"
29441
29531
  }));
29442
- if (input.action === "serp") {
29443
- const rows = [];
29444
- for (const keyword of input.keywords.slice(0, 5)) {
29445
- const items = await post("v3/serp/google/organic/live/advanced", {
29446
- keyword,
29447
- depth: Math.min(input.limit, 100)
29448
- });
29449
- for (const item of items) {
29450
- if (item.type !== "organic") continue;
29451
- rows.push({
29452
- keyword,
29453
- ...pick(item, {
29454
- position: "rank_absolute",
29455
- url: "url",
29456
- domain: "domain",
29457
- title: "title"
29458
- })
29459
- });
29460
- }
29461
- }
29462
- return rows;
29463
- }
29532
+ if (input.action === "serp") return eachKeyword(input.keywords.slice(0, 5), spent.missed, async (keyword) => {
29533
+ return (await post("v3/serp/google/organic/live/advanced", {
29534
+ keyword,
29535
+ depth: Math.min(input.limit, 100)
29536
+ })).filter((item) => item.type === "organic").map((item) => ({
29537
+ keyword,
29538
+ ...pick(item, {
29539
+ position: "rank_absolute",
29540
+ url: "url",
29541
+ domain: "domain",
29542
+ title: "title"
29543
+ })
29544
+ }));
29545
+ });
29464
29546
  if (input.action === "backlinks") return (await post("v3/backlinks/referring_domains/live", {
29465
29547
  target: bareTarget(input.target),
29466
29548
  limit: input.limit,
@@ -29489,7 +29571,7 @@ function isPage(target) {
29489
29571
  function bareTarget(target) {
29490
29572
  return target.replace(/^https?:\/\//, "").replace(/\/$/, "");
29491
29573
  }
29492
- async function ahrefs(key, input) {
29574
+ async function ahrefs(key, input, missed) {
29493
29575
  const get = async (path, params) => {
29494
29576
  const url = new URL(`https://api.ahrefs.com/v3/${path}`);
29495
29577
  for (const [name, value] of Object.entries(params)) url.searchParams.set(name, value);
@@ -29513,22 +29595,17 @@ async function ahrefs(key, input) {
29513
29595
  limit: String(input.limit),
29514
29596
  order_by: "sum_traffic:desc"
29515
29597
  })).keywords ?? [];
29516
- if (input.action === "serp") {
29517
- const rows = [];
29518
- for (const keyword of input.keywords.slice(0, 5)) {
29519
- const data = await get("serp-overview/serp-overview", {
29520
- select: "position,type,url,title,domain_rating,traffic,keywords",
29521
- keyword,
29522
- country: input.country,
29523
- top_positions: String(Math.min(input.limit, 100))
29524
- });
29525
- for (const row of data.positions ?? []) rows.push({
29526
- keyword,
29527
- ...row
29528
- });
29529
- }
29530
- return rows;
29531
- }
29598
+ if (input.action === "serp") return eachKeyword(input.keywords.slice(0, 5), missed, async (keyword) => {
29599
+ return ((await get("serp-overview/serp-overview", {
29600
+ select: "position,type,url,title,domain_rating,traffic,keywords",
29601
+ keyword,
29602
+ country: input.country,
29603
+ top_positions: String(Math.min(input.limit, 100))
29604
+ })).positions ?? []).map((row) => ({
29605
+ keyword,
29606
+ ...row
29607
+ }));
29608
+ });
29532
29609
  if (input.action === "backlinks") return (await get("site-explorer/refdomains", {
29533
29610
  select: "domain,domain_rating,links_to_target,dofollow_links,first_seen,last_seen",
29534
29611
  target: input.target,
@@ -32196,7 +32273,7 @@ async function runResearch(input, keys, options) {
32196
32273
  const offset = input.offset ?? 0;
32197
32274
  const language = input.provider === "dataforseo" ? dataForSeoLanguage(country) : null;
32198
32275
  if (input.action === "ai_answers") throw new CommandError("INVALID_INPUT", "ai_answers runs on its own.");
32199
- const { rows: found, costUsd, partial } = await research(input.provider, keys, {
32276
+ const { rows: found, costUsd, partial, missed } = await research(input.provider, keys, {
32200
32277
  action: input.action,
32201
32278
  keywords: input.keywords ?? [],
32202
32279
  target,
@@ -32222,6 +32299,7 @@ async function runResearch(input, keys, options) {
32222
32299
  `${input.provider} estimates, in the provider’s own units.`,
32223
32300
  ...costUsd !== null ? [`DataForSEO cost: $${costUsd}.`] : [],
32224
32301
  ...partial ? ["Partial: DataForSEO could not fetch some pages of results, so rows may be missing."] : [],
32302
+ ...missed.length ? [`Partial: ${input.provider} failed for ${missed.join(", ")}, so those keywords have no rows. Ask for them again.`] : [],
32225
32303
  ...note ? [note] : [],
32226
32304
  ...hasTargetDr ? [`${bareDomain(target)} domain_rating: ${targetDr}.`] : [],
32227
32305
  ...rated.added || hasTargetDr ? [DR_ATTRIBUTION] : []
@@ -32239,6 +32317,7 @@ async function runResearch(input, keys, options) {
32239
32317
  leftOut: note,
32240
32318
  costUsd,
32241
32319
  partial,
32320
+ missedKeywords: missed,
32242
32321
  total: rated.rows.length,
32243
32322
  offset,
32244
32323
  rows: rated.rows.slice(offset, offset + 50)
@@ -32493,10 +32572,10 @@ function markdownText(markdown, url) {
32493
32572
  return `Main content as Markdown (live page${markdown.truncated ? `, cut to ${markdown.markdown.length.toLocaleString("en-US")} of ${markdown.characters.toLocaleString("en-US")} characters` : ""}):\n\n${markdown.markdown}`;
32494
32573
  }
32495
32574
  async function runCompare(input, deps) {
32496
- if (!/^\d{4}-\d{2}-\d{2}$/.test(input.date)) throw new CommandError("INVALID_INPUT", "The date must be YYYY-MM-DD.");
32575
+ if (!realIsoDate(input.date)) throw new CommandError("INVALID_INPUT", "The date must be a real day as YYYY-MM-DD.");
32497
32576
  const last = latestFinalGscDate(deps.now?.() ?? /* @__PURE__ */ new Date());
32498
32577
  const daysAfter = Math.round((Date.parse(last) - Date.parse(input.date)) / 864e5) + 1;
32499
- if (daysAfter < 3) throw new CommandError("INVALID_INPUT", `Search Console has final data to ${last}. Use a date at least 3 days before that.`);
32578
+ if (daysAfter < 3) throw new CommandError("INVALID_INPUT", `Search Console has final data to ${last}, and a comparison needs 3 days after the change. The latest date that works is ${shiftIsoDate(last, -2)}.`);
32500
32579
  const days = Math.min(28, daysAfter);
32501
32580
  const after = {
32502
32581
  startDate: input.date,
@@ -32838,8 +32917,15 @@ function weekMarkdown(data) {
32838
32917
  out.push("", "## Ask your agent", "", "Paste this into your agent:", "", "```", prompt, "```");
32839
32918
  return out.join("\n");
32840
32919
  }
32841
- async function runAllSites(deps) {
32920
+ async function runAllSites(deps, sites) {
32842
32921
  const data = deps.week ? await deps.week() : null;
32922
+ if (!data && sites) {
32923
+ const list = await sites();
32924
+ return {
32925
+ text: `No site given. Pick one below and send it: ${deps.call("report", { site: "<site>" })}. The summary of every site is for accounts in the weekly site audit beta.\n\n${list.text}`,
32926
+ data: list.data
32927
+ };
32928
+ }
32843
32929
  if (!data) throw new CommandError("INVALID_INPUT", `No site given. Name one: ${deps.call("report", { site: "<site>" })}. The summary of every site is for accounts in the weekly site audit beta.`);
32844
32930
  const startMs = Date.parse(`${data.start}T00:00:00Z`);
32845
32931
  const endMs = Date.parse(`${data.end}T00:00:00Z`) + DAY_MS;
@@ -32855,8 +32941,8 @@ async function runAllSites(deps) {
32855
32941
  //#region packages/core/src/commands/tools.ts
32856
32942
  const site = z.string().min(1).describe("Search Console property or site, such as sc-domain:example.com, https://example.com/ or example.com.");
32857
32943
  const dates = {
32858
- start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("YYYY-MM-DD. Optional; send with end_date."),
32859
- end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("YYYY-MM-DD. Optional; send with start_date.")
32944
+ start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("YYYY-MM-DD. Optional; without end_date it runs to the latest final day."),
32945
+ end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("YYYY-MM-DD. Optional.")
32860
32946
  };
32861
32947
  const detail = {
32862
32948
  detail: z.enum([
@@ -32875,6 +32961,14 @@ const DATA_SOURCES = [
32875
32961
  function tool(spec) {
32876
32962
  return spec;
32877
32963
  }
32964
+ const listSites$1 = async (surface) => runSites({
32965
+ accounts: await surface.accounts(),
32966
+ keys: await surface.keys(),
32967
+ clicky: clickySites(await surface.keys()).map(clickyLabel),
32968
+ signIn: surface.signIn,
32969
+ bing: surface.deps.bing ? await surface.deps.bing() : void 0,
32970
+ connect: surface.connect
32971
+ });
32878
32972
  const SHARED_TOOLS = [
32879
32973
  tool({
32880
32974
  name: "seo_sites",
@@ -32882,20 +32976,13 @@ const SHARED_TOOLS = [
32882
32976
  title: "List sites",
32883
32977
  description: "Lists the Search Console and Google Analytics properties the connected Google accounts can read, the Bing Webmaster site for each, and which research keys are set. Call it first.",
32884
32978
  input: z.object({}),
32885
- run: async (surface) => runSites({
32886
- accounts: await surface.accounts(),
32887
- keys: await surface.keys(),
32888
- clicky: clickySites(await surface.keys()).map(clickyLabel),
32889
- signIn: surface.signIn,
32890
- bing: surface.deps.bing ? await surface.deps.bing() : void 0,
32891
- connect: surface.connect
32892
- })
32979
+ run: listSites$1
32893
32980
  }),
32894
32981
  tool({
32895
32982
  name: "seo_report",
32896
32983
  command: "report",
32897
32984
  title: "Search Console report",
32898
- description: `Start here. Without site: a short summary of every site in the person's weekly site audit (beta) for last week, with what needs a look and the next step for each, ending with the site to ask about next; an account outside that beta must send site. With site: the main report for that Search Console property, the overview plus each section's row count and top rows. With site and section: that section's rows; page sections also check their top 20 URLs live. Sections:\n${Object.entries(SECTIONS).map(([id, text]) => `- ${id}: ${text}`).join("\n")}`,
32985
+ description: `Start here. Without site: a short summary of every site in the person's weekly site audit (beta) for last week, with what needs a look and the next step for each, ending with the site to ask about next; an account outside that beta gets its site list to pick from. With site: the main report for that Search Console property, the overview plus each section's row count and top rows. With site and section: that section's rows; page sections also check their top 20 URLs live. Sections:\n${Object.entries(SECTIONS).map(([id, text]) => `- ${id}: ${text}`).join("\n")}`,
32899
32986
  input: z.object({
32900
32987
  site: site.optional().describe("Leave it out for the summary of every audited site (weekly site audit beta). Otherwise a Search Console property or site, such as sc-domain:example.com, https://example.com/ or example.com."),
32901
32988
  section: z.enum(SECTION_IDS).optional().describe("Optional, needs site. Leave it out first; then ask for one section."),
@@ -32908,7 +32995,7 @@ const SHARED_TOOLS = [
32908
32995
  site: "<site>",
32909
32996
  section: input.section
32910
32997
  })}.`);
32911
- return runAllSites(surface.deps);
32998
+ return runAllSites(surface.deps, () => listSites$1(surface));
32912
32999
  }
32913
33000
  const target = input.site;
32914
33001
  const resolved = await surface.resolveSite(target).catch((error) => error instanceof Error ? error : new Error(String(error)));
@@ -33434,8 +33521,9 @@ async function localRefreshClicky() {
33434
33521
  return localResearchKeys();
33435
33522
  }
33436
33523
  async function resolveLocalSite(site, options = {}) {
33437
- const found = matchProperty((await listSites(options.refresh, options.account)).map((entry) => entry.siteUrl), site);
33438
- if (!found) throw new CommandError("NO_ACCESS", `This Google login has no Search Console property for ${site}. Run \`seo sites\` to list the properties it can read.`);
33524
+ const names = (await listSites(options.refresh, options.account)).map((entry) => entry.siteUrl);
33525
+ const found = matchProperty(names, site);
33526
+ if (!found) throw new CommandError("NO_ACCESS", `This Google login has no Search Console property for ${site}. ${readable(names)}`);
33439
33527
  return found;
33440
33528
  }
33441
33529
  const ERROR_CODES = {
@@ -33443,6 +33531,7 @@ const ERROR_CODES = {
33443
33531
  NOT_CONNECTED: "AUTH_REQUIRED",
33444
33532
  NO_ACCESS: "PROPERTY_NOT_FOUND",
33445
33533
  NO_KEY: "AUTH_CONFIG_REQUIRED",
33534
+ NO_DATA: "INSUFFICIENT_DATA",
33446
33535
  RATE_LIMITED: "RATE_LIMITED",
33447
33536
  GOOGLE_ERROR: "PROVIDER_UNAVAILABLE",
33448
33537
  PROVIDER_ERROR: "PROVIDER_UNAVAILABLE"
@@ -35139,6 +35228,6 @@ function isoDate(daysAgo = 0) {
35139
35228
  return date.toISOString().slice(0, 10);
35140
35229
  }
35141
35230
  //#endregion
35142
- export { AHREFS_API_KEY_ENV, AHREFS_API_KEY_SECRET, AI_REFERRAL_SOURCES, AI_REFERRAL_SOURCES_VERSION, ANALYTICS_PROVIDER_IDS, AUDIT_PAGE_RULE_IDS, AhrefsClient, BING_API_KEY_ENV, BING_API_KEY_SECRET, BROWSER_USER_AGENT, CANNIBAL_MAXIMUM_DOMINANT_SHARE, CANNIBAL_MINIMUM_PAGE_SHARE, CLICKY_SITEKEY_ENV, CLICKY_SITE_KEYS_SECRET, CLOUD_SIGN_IN, CLOUD_URL, CRAWL_SKIP_REASONS, CTR_DEFAULT_LIMIT, CTR_MAX_LIMIT, CTR_MAX_MIN_IMPRESSIONS, DATAFORSEO_CREDENTIAL_SECRET, DATAFORSEO_LOGIN_ENV, DATAFORSEO_PASSWORD_ENV, DEFAULT_MAX_PAGES, DEFAULT_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, DEFAULT_UNREACHED_PAGE_LIMIT, DataForSeoClient, DecisionModelError, DecisionRunner, GSC_FINAL_DATA_LAG_DAYS, GSC_TIME_ZONE, GoogleApiError, INCIDENTS_URL, JsonFileStorageAdapter, LINK_ROLES, LOCAL_BING_CONNECT, LOCAL_SIGN_IN, LOG_MAX_AGE_MS, LOG_MAX_FILE_BYTES, LOG_MAX_TOTAL_BYTES, LocalCredentialsProvider, MANAGED_PROVIDER_SECRET_NAMES, MAX_CRAWL_CONCURRENCY, MAX_CRAWL_DEPTH, MAX_CRAWL_PAGES, MAX_CRAWL_TIMEOUT_MS, MAX_MAX_PAGES, MAX_SEARCH_CONSOLE_EXPORT_FILES, MAX_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, PAGE_TYPES, PROVIDER_SECRET_NAMES, ProviderError, ProviderExtensionRegistry, RESEARCH_IMPORT_COLUMN_KEYS, ResponseSizeLimitError, SEARCH_INTENTS, SEO_CRAWLER_IDENTITY, SEO_CRAWLER_TOKEN, SEO_CRAWLER_USER_AGENT, SEO_PROVIDER_API_VERSION, SEO_VERSION, SERP_TITLE_REFERENCE_WIDTH_PX, SERP_TITLE_WIDTH_PROFILE, STEP_CHANGE_RULES, SeoError, ServiceAccountAccessTokenClient, SessionLedger, TELEMETRY_AGENTS, TELEMETRY_ENDPOINT, TELEMETRY_ERROR_CATEGORIES, TELEMETRY_EVENTS, TELEMETRY_FAILURE_CONTEXTS, TELEMETRY_FAILURE_REASONS, TELEMETRY_NOTICE, TELEMETRY_OPERATIONS, TELEMETRY_REPORTS, TELEMETRY_SCHEMA_VERSION, TELEMETRY_TIMEOUT_MS, TYPESAFE_API_KEY_ENV, agentActionsView, aiReferralSourceForValue, aiReferralsReport, analyticsConnectionLabel, analyticsConnectionProviderId, analyticsConnectionSchema, analyticsProviderDetails, analyticsProviderSchema, analyzeAiReferralRows, analyzeCannibalImportRows, analyzeCannibalRows, analyzeCtrUnderperformersFromRows, analyzeDecay, analyzeIndexCoverageSignals, analyzePageOpportunitiesFromRows, analyzeQueryClustersFromRows, analyzeQuickWinsFromRows, analyzeSecondPageRows, analyzeSegmentRows, analyzeStrikingDistanceRows, analyzeTrafficAnomalyRows, assertCrawlConfigLimits, assertUrlMatchesGscProperty, auditCrawlPages, auditCrawlRequests, auditPage, authStatus, bearerFetch, boundedSitemapInventory, buildAgentFindings, buildQueryClusterReportFromRows, buildTelemetryPayload, cacheDb, cannibalImportReport, cannibalReport, cannibalReportWith, changeWindow, checkDatabaseReadiness, classifyTelemetryFailure, clearCache, cloudAccountFor, cloudBing, cloudCredentials, cloudGoogleEmails, cloudGoogleToken, cloudKeys, cloudLinked, cloudLogout, cloudWeek, clusterQueryRows, combineRobotsValues, compareSegmentRows, configSchema, contentCoverageRecommendation, contentCoverageType, contentOptimizationFromPageOpportunities, contentOptimizationReport, countLabel, crawlConfigHash, crawlConfigSchema, crawlDefinitionId, crawlFixToQueueItem, crawlImplementationQueueWorkflow, crawlIssueGroupSchema, crawlIssueSchema, crawlOne, crawlPageSnapshotSchema, crawlReportSchema, crawlReportSummarySchema, crawlRequestObservationSchema, crawlRunId, crawlSite, crawlSkipImpact, crawlSkipReasonCountsFromRecord, crawlStatusOnly, crawlTopFixSchema, crawlerJsonSchemas, crawlerRuleCategorySchema, crawlerRuleIdSchema, crawlerRuleInfoSchema, crawlerRuleRecommendationSchema, crawlerRuleSeveritySchema, crawlerSchemas, createAuthorizedClient, createCrawlReport, createCtrBenchmarkContext, createGoogleAccessTokenClient, createLogger, createPageRenderer, ctrUnderperformersReport, ctrUnderperformersReportWith, decayClusterDrafts, decayComparisonRange, decayingReport, decayingReportWith, deleteAhrefsApiKey, deleteBingApiKey, deleteClickySiteKey, deleteDataForSeoCredentials, deleteManagedProviderSecrets, deleteProviderExtensionAccount, deleteProviderExtensionCredentials, deleteProviderSecret, deleteTokens, deriveBrandTerms, describeInstalledProviderExtension, detectBrandTerms, detectTelemetryAgent, diagnoseCsvFiles, diagnoseProperty, diagnosePropertyWorkflow, effectiveRobotsDirectives, effectiveSnippetControl, ensurePrivateDatabaseFiles, ensureSeoCliDirs, estimateSerpTitleWidth, expectedCtrForPosition, explainRule, fetchCruxFieldData, fetchPage, fetchPageStatus, fetchSearchStatusIncidents, fetchSitemapUrls, fileSize, finalGscDateRange, findOverlappingSearchUpdates, finishCloudLogin, formatRelativeExpiry, ga4MatchReason, ga4PropertyIdFromName, ga4ReportQualityWarnings, ga4RequestCanUseCache, ga4RowsToObjects, getAuthModeStatus, getCacheStats, getClientConfig, getDb, getPrivacySnapshot, getSeoCliPaths, getServiceAccountConfig, getServiceAccountStatus, getTelemetryStatus, getTokenStorageStatus, googleJson, groupCrawlIssues, hasMetaRobotsDirective, hasRule, hasXRobotsDirective, hashKey, indexCoverageSignals, inferTemplateMovement, initializeTelemetry, inspectProviderPackage, inspectSearchConsoleUrl, inspectUrl, installProviderPackage, installedProviderPackageSchema, installedProviderPackagesSchema, internalLinksReport, interpretUpdateCorrelation, isBrandQuery, isNewerVersion, isSkippableReportError, isTelemetryCi, isTelemetryOperation, isTelemetryReport, isoDate, isoWeek, latestFinalGscDate, linkRecover, linkRecoverRecommendation, listAnalyticsAccounts, listAnalyticsDataStreams, listGa4AccountSummaries, listGa4DataStreams, listGoogleAccounts, listInstalledProviderExtensions, listRules, listSearchConsoleSites, listSearchUpdates, listSites, loadInstalledProviderExtensions, loadProviderExtensions, loadProviderPackage, loadSearchConsoleExport, localAccounts, localBing, localCredentialsProvider, localError, localGoogleFetch, localInternalLinksDependencies, localRefreshClicky, localResearchKeys, loginWithLoopback, makeRunId, matchGa4WebStreams, measureCoverage, metaRobotsDirectives, migrateLegacyMacConfig, missingOAuthClientMessage, missingSharedClientLoginMessage, nodeCommandDeps, normalizeBrandText, normalizeClickySiteId, normalizeClickySiteKey, normalizeCrawlConfig, normalizeCrawlSkipReasonCounts, normalizeForCoverage, normalizeHttpUrl, normalizeJavaScriptRenderingMode, normalizeLoadedCrawlReport, normalizeProviderExtensionAccount, normalizedResearchColumnName, notSignedIn, noteCacheWrite, openCloudLink, pageEvidenceRecommendation, pageOpportunitiesReport, parseOlderThan, pendingCloudLogin, performanceAudit, performanceReportIsCacheable, plural, providerCapabilitySchema, providerConnectionFieldSchema, providerExactVersionSchema, providerExtensionIdSchema, providerIdSchema, providerIntegritySchema, providerKindSchema, providerPackageManifestSchema, providerPackageNameSchema, providerPreferenceSchema, providerRegistrationSchema, providerRequestJson, providerRequestText, pruneLogs, publicHttpFetch, queryClusterReport, queryClusterTokens, queryContentCoverageFromPage, queryContentFieldsFromPage, queryFieldCoverage, queryOpportunityRecommendation, queryPageMetrics, queryPageTopQuery, queryPagesMetrics, queryPagesMetricsBatch, queryPagesTopQueries, queryPagesTopQueriesBatch, querySearchAnalytics, quickWinsCsvRows, quickWinsReport, quickWinsReportWith, readAhrefsApiKey, readAllTokens, readBingApiKey, readBoundedResponseText, readClickySiteKey, readClickySiteKeys, readConfig, readDataForSeoCredentials, readInstalledProviderPackages, readJson, readJsonOrUndefined, readOauthClient, readProviderExtensionAccount, readProviderExtensionCredentials, readProviderSecret, readTokens, recommendationForRule, reconcileExportPagesWithCrawl, redirectTrace, refreshAuthToken, refreshPrioritiesCsvFiles, refreshPrioritiesWorkflow, registrableDomain, removeInstalledProviderPackage, renderCrawlCsv, renderCrawlHtml, renderCrawlJunit, renderCrawlMarkdownTickets, renderCrawlPagesCsv, renderCrawlPretty, renderCsv, renderPresentationTablesMarkdown, renderWorkflowMarkdown, reportNarrative, requestHeaders, requirePageVerification, researchImportColumns, resetSeoData, resolveLocalSite, resolveSegmentRanges, resolveSeoCliPaths, reviewObservations, roundedPosition, runAnalyticsProviderLandingPages, runAnalyticsReport, runDoctor, runGa4Report, runProviderExtensionAction, runtimeForProvider, safeProviderAdapterError, saveInstalledProviderPackage, searchAnalyticsRows, searchUpdatesFromIncidents, secondPage, secondPageWith, segmentComparisonRange, segmentImpact, segmentRangeDays, seoErrorEnvelope, sessionSourceInListFilter, setActiveGoogleAccount, setTelemetryEnabled, setTokenStorageMode, shiftIsoDate, shouldExcludeBrandQuery, siteStartUrl, startCloudLogin, strikingDistance, strikingDistanceWith, summarizeCrawlReport, pageTechnicalSignals as technicalPageSignals, telemetryErrorCategory, toSeoError, tokenSchema, tokenStoreSchema, topFixes, trackTelemetryCommandFailed, trackTelemetryReportComplete, trackTelemetryReportFailed, trackTelemetryReportStart, trackTelemetrySetupComplete, trafficAnomaly, trafficAnomalyWith, typesafeDecisionModel, unavailableSegmentImpactReport, unicodeTokens, uninstallProviderExtension, updateCorrelation, updateNotice, updatePostmortemCsvFiles, updatePostmortemWorkflow, urlInspectionQuota, validateResearchImportColumnSources, verifyAnalyticsProvider, verifyProviderExtension, verifyQueryContent, verifyQueryContentWith, withAgentReportContract, withRankingHistory, workflowPresentation, writeAhrefsApiKey, writeBingApiKey, writeClickySiteKey, writeConfig, writeDataForSeoCredentials, writeInstalledProviderPackages, writeOauthClient, writeProviderExtensionAccount, writeProviderExtensionCredentials, writeProviderSecret, writeToCache, writeTokens, xRobotsDirectives };
35231
+ export { AHREFS_API_KEY_ENV, AHREFS_API_KEY_SECRET, AI_REFERRAL_SOURCES, AI_REFERRAL_SOURCES_VERSION, ANALYTICS_PROVIDER_IDS, AUDIT_PAGE_RULE_IDS, AhrefsClient, BING_API_KEY_ENV, BING_API_KEY_SECRET, BROWSER_USER_AGENT, CANNIBAL_MAXIMUM_DOMINANT_SHARE, CANNIBAL_MINIMUM_PAGE_SHARE, CLICKY_SITEKEY_ENV, CLICKY_SITE_KEYS_SECRET, CLOUD_SIGN_IN, CLOUD_URL, CRAWL_SKIP_REASONS, CTR_DEFAULT_LIMIT, CTR_MAX_LIMIT, CTR_MAX_MIN_IMPRESSIONS, DATAFORSEO_CREDENTIAL_SECRET, DATAFORSEO_LOGIN_ENV, DATAFORSEO_PASSWORD_ENV, DEFAULT_MAX_PAGES, DEFAULT_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, DEFAULT_UNREACHED_PAGE_LIMIT, DataForSeoClient, DecisionModelError, DecisionRunner, GSC_FINAL_DATA_LAG_DAYS, GSC_TIME_ZONE, GoogleApiError, INCIDENTS_URL, JsonFileStorageAdapter, LINK_ROLES, LOCAL_BING_CONNECT, LOCAL_SIGN_IN, LOG_MAX_AGE_MS, LOG_MAX_FILE_BYTES, LOG_MAX_TOTAL_BYTES, LocalCredentialsProvider, MANAGED_PROVIDER_SECRET_NAMES, MAX_CRAWL_CONCURRENCY, MAX_CRAWL_DEPTH, MAX_CRAWL_PAGES, MAX_CRAWL_TIMEOUT_MS, MAX_MAX_PAGES, MAX_SEARCH_CONSOLE_EXPORT_FILES, MAX_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, PAGE_TYPES, PROVIDER_ERROR_CODES, PROVIDER_SECRET_NAMES, ProviderError, ProviderExtensionRegistry, RESEARCH_IMPORT_COLUMN_KEYS, ResponseSizeLimitError, SEARCH_INTENTS, SEO_CRAWLER_IDENTITY, SEO_CRAWLER_TOKEN, SEO_CRAWLER_USER_AGENT, SEO_PROVIDER_API_VERSION, SEO_VERSION, SERP_TITLE_REFERENCE_WIDTH_PX, SERP_TITLE_WIDTH_PROFILE, STEP_CHANGE_RULES, SeoError, ServiceAccountAccessTokenClient, SessionLedger, TELEMETRY_AGENTS, TELEMETRY_ENDPOINT, TELEMETRY_ERROR_CATEGORIES, TELEMETRY_EVENTS, TELEMETRY_FAILURE_CONTEXTS, TELEMETRY_FAILURE_REASONS, TELEMETRY_NOTICE, TELEMETRY_OPERATIONS, TELEMETRY_REPORTS, TELEMETRY_SCHEMA_VERSION, TELEMETRY_TIMEOUT_MS, TYPESAFE_API_KEY_ENV, agentActionsView, aiReferralSourceForValue, aiReferralsReport, analyticsConnectionLabel, analyticsConnectionProviderId, analyticsConnectionSchema, analyticsProviderDetails, analyticsProviderSchema, analyzeAiReferralRows, analyzeCannibalImportRows, analyzeCannibalRows, analyzeCtrUnderperformersFromRows, analyzeDecay, analyzeIndexCoverageSignals, analyzePageOpportunitiesFromRows, analyzeQueryClustersFromRows, analyzeQuickWinsFromRows, analyzeSecondPageRows, analyzeSegmentRows, analyzeStrikingDistanceRows, analyzeTrafficAnomalyRows, assertCrawlConfigLimits, assertUrlMatchesGscProperty, auditCrawlPages, auditCrawlRequests, auditPage, authStatus, bearerFetch, boundedSitemapInventory, buildAgentFindings, buildQueryClusterReportFromRows, buildTelemetryPayload, cacheDb, cannibalImportReport, cannibalReport, cannibalReportWith, changeWindow, checkDatabaseReadiness, classifyTelemetryFailure, clearCache, cloudAccountFor, cloudBing, cloudCredentials, cloudGoogleEmails, cloudGoogleToken, cloudKeys, cloudLinked, cloudLogout, cloudWeek, clusterQueryRows, combineRobotsValues, compareSegmentRows, configSchema, contentCoverageRecommendation, contentCoverageType, contentOptimizationFromPageOpportunities, contentOptimizationReport, countLabel, crawlConfigHash, crawlConfigSchema, crawlDefinitionId, crawlFixToQueueItem, crawlImplementationQueueWorkflow, crawlIssueGroupSchema, crawlIssueSchema, crawlOne, crawlPageSnapshotSchema, crawlReportSchema, crawlReportSummarySchema, crawlRequestObservationSchema, crawlRunId, crawlSite, crawlSkipImpact, crawlSkipReasonCountsFromRecord, crawlStatusOnly, crawlTopFixSchema, crawlerJsonSchemas, crawlerRuleCategorySchema, crawlerRuleIdSchema, crawlerRuleInfoSchema, crawlerRuleRecommendationSchema, crawlerRuleSeveritySchema, crawlerSchemas, createAuthorizedClient, createCrawlReport, createCtrBenchmarkContext, createGoogleAccessTokenClient, createLogger, createPageRenderer, ctrUnderperformersReport, ctrUnderperformersReportWith, decayClusterDrafts, decayComparisonRange, decayingReport, decayingReportWith, deleteAhrefsApiKey, deleteBingApiKey, deleteClickySiteKey, deleteDataForSeoCredentials, deleteManagedProviderSecrets, deleteProviderExtensionAccount, deleteProviderExtensionCredentials, deleteProviderSecret, deleteTokens, deriveBrandTerms, describeInstalledProviderExtension, detectAgentName, detectBrandTerms, detectTelemetryAgent, diagnoseCsvFiles, diagnoseProperty, diagnosePropertyWorkflow, effectiveRobotsDirectives, effectiveSnippetControl, ensurePrivateDatabaseFiles, ensureSeoCliDirs, estimateSerpTitleWidth, expectedCtrForPosition, explainRule, fetchCruxFieldData, fetchPage, fetchPageStatus, fetchSearchStatusIncidents, fetchSitemapUrls, fileSize, finalGscDateRange, findOverlappingSearchUpdates, finishCloudLogin, formatRelativeExpiry, ga4MatchReason, ga4PropertyIdFromName, ga4ReportQualityWarnings, ga4RequestCanUseCache, ga4RowsToObjects, getAuthModeStatus, getCacheStats, getClientConfig, getDb, getPrivacySnapshot, getSeoCliPaths, getServiceAccountConfig, getServiceAccountStatus, getTelemetryStatus, getTokenStorageStatus, googleJson, groupCrawlIssues, hasMetaRobotsDirective, hasRule, hasXRobotsDirective, hashKey, indexCoverageSignals, inferTemplateMovement, initializeTelemetry, inspectProviderPackage, inspectSearchConsoleUrl, inspectUrl, installProviderPackage, installedProviderPackageSchema, installedProviderPackagesSchema, internalLinksReport, interpretUpdateCorrelation, isBrandQuery, isNewerVersion, isSkippableReportError, isTelemetryCi, isTelemetryOperation, isTelemetryReport, isoDate, isoWeek, latestFinalGscDate, linkRecover, linkRecoverRecommendation, listAnalyticsAccounts, listAnalyticsDataStreams, listGa4AccountSummaries, listGa4DataStreams, listGoogleAccounts, listInstalledProviderExtensions, listRules, listSearchConsoleSites, listSearchUpdates, listSites, loadInstalledProviderExtensions, loadProviderExtensions, loadProviderPackage, loadSearchConsoleExport, localAccounts, localBing, localCredentialsProvider, localError, localGoogleFetch, localInternalLinksDependencies, localRefreshClicky, localResearchKeys, loginWithLoopback, makeRunId, matchGa4WebStreams, measureCoverage, metaRobotsDirectives, migrateLegacyMacConfig, missingOAuthClientMessage, missingSharedClientLoginMessage, nodeCommandDeps, normalizeBrandText, normalizeClickySiteId, normalizeClickySiteKey, normalizeCrawlConfig, normalizeCrawlSkipReasonCounts, normalizeForCoverage, normalizeHttpUrl, normalizeJavaScriptRenderingMode, normalizeLoadedCrawlReport, normalizeProviderExtensionAccount, normalizedResearchColumnName, notSignedIn, noteCacheWrite, openCloudLink, pageEvidenceRecommendation, pageOpportunitiesReport, parseOlderThan, pendingCloudLogin, performanceAudit, performanceReportIsCacheable, plural, providerCapabilitySchema, providerConnectionFieldSchema, providerExactVersionSchema, providerExtensionIdSchema, providerIdSchema, providerIntegritySchema, providerKindSchema, providerPackageManifestSchema, providerPackageNameSchema, providerPreferenceSchema, providerRegistrationSchema, providerRequestJson, providerRequestText, pruneLogs, publicHttpFetch, queryClusterReport, queryClusterTokens, queryContentCoverageFromPage, queryContentFieldsFromPage, queryFieldCoverage, queryOpportunityRecommendation, queryPageMetrics, queryPageTopQuery, queryPagesMetrics, queryPagesMetricsBatch, queryPagesTopQueries, queryPagesTopQueriesBatch, querySearchAnalytics, quickWinsCsvRows, quickWinsReport, quickWinsReportWith, readAhrefsApiKey, readAllTokens, readBingApiKey, readBoundedResponseText, readClickySiteKey, readClickySiteKeys, readConfig, readDataForSeoCredentials, readInstalledProviderPackages, readJson, readJsonOrUndefined, readOauthClient, readProviderExtensionAccount, readProviderExtensionCredentials, readProviderSecret, readTokens, recommendationForRule, reconcileExportPagesWithCrawl, redirectTrace, refreshAuthToken, refreshPrioritiesCsvFiles, refreshPrioritiesWorkflow, registrableDomain, removeInstalledProviderPackage, renderCrawlCsv, renderCrawlHtml, renderCrawlJunit, renderCrawlMarkdownTickets, renderCrawlPagesCsv, renderCrawlPretty, renderCsv, renderPresentationTablesMarkdown, renderWorkflowMarkdown, reportNarrative, requestHeaders, requirePageVerification, researchImportColumns, resetSeoData, resolveLocalSite, resolveSegmentRanges, resolveSeoCliPaths, reviewObservations, roundedPosition, runAnalyticsProviderLandingPages, runAnalyticsReport, runDoctor, runGa4Report, runProviderExtensionAction, runtimeForProvider, safeProviderAdapterError, saveInstalledProviderPackage, searchAnalyticsRows, searchUpdatesFromIncidents, secondPage, secondPageWith, segmentComparisonRange, segmentImpact, segmentRangeDays, seoErrorEnvelope, sessionSourceInListFilter, setActiveGoogleAccount, setTelemetryEnabled, setTokenStorageMode, shiftIsoDate, shouldExcludeBrandQuery, siteStartUrl, startCloudLogin, strikingDistance, strikingDistanceWith, summarizeCrawlReport, pageTechnicalSignals as technicalPageSignals, telemetryErrorCategory, toSeoError, tokenSchema, tokenStoreSchema, topFixes, trackTelemetryCommandFailed, trackTelemetryReportComplete, trackTelemetryReportFailed, trackTelemetryReportStart, trackTelemetrySetupComplete, trafficAnomaly, trafficAnomalyWith, typesafeDecisionModel, unavailableSegmentImpactReport, unicodeTokens, uninstallProviderExtension, updateCorrelation, updateNotice, updatePostmortemCsvFiles, updatePostmortemWorkflow, urlInspectionQuota, validateResearchImportColumnSources, verifyAnalyticsProvider, verifyProviderExtension, verifyQueryContent, verifyQueryContentWith, withAgentReportContract, withRankingHistory, workflowPresentation, writeAhrefsApiKey, writeBingApiKey, writeClickySiteKey, writeConfig, writeDataForSeoCredentials, writeInstalledProviderPackages, writeOauthClient, writeProviderExtensionAccount, writeProviderExtensionCredentials, writeProviderSecret, writeToCache, writeTokens, xRobotsDirectives };
35143
35232
 
35144
35233
  //# sourceMappingURL=index.js.map