@ainyc/canonry 4.80.0 → 4.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +20 -12
  2. package/assets/assets/{BacklinksPage-dRc62jAY.js → BacklinksPage-DHShKKpo.js} +1 -1
  3. package/assets/assets/{ChartPrimitives-D2_IvTkk.js → ChartPrimitives-udHScxjY.js} +1 -1
  4. package/assets/assets/ProjectPage-BsS1anh7.js +6 -0
  5. package/assets/assets/{RunRow-C0MA3yuQ.js → RunRow-CXyPHMVQ.js} +1 -1
  6. package/assets/assets/{RunsPage-4uxTYgGy.js → RunsPage-BpQ_NpFt.js} +1 -1
  7. package/assets/assets/{SettingsPage-3-SLhcJ7.js → SettingsPage-1ep4ch7n.js} +1 -1
  8. package/assets/assets/{TrafficPage-DZ50qwme.js → TrafficPage-C3Hx-sE7.js} +1 -1
  9. package/assets/assets/TrafficSourceDetailPage-B26n2R6G.js +1 -0
  10. package/assets/assets/{arrow-left-BaZIkAXX.js → arrow-left-Dc_IPJxw.js} +1 -1
  11. package/assets/assets/{extract-error-message-cpvfuFqW.js → extract-error-message-B3PoKkHW.js} +1 -1
  12. package/assets/assets/{index-EnY_OBRd.js → index-DhdFTQkU.js} +86 -86
  13. package/assets/assets/{trash-2-JpcztiS5.js → trash-2-BQ69cGl0.js} +1 -1
  14. package/assets/index.html +1 -1
  15. package/dist/{chunk-2QBSRHSN.js → chunk-6XOZSS3Y.js} +72 -8
  16. package/dist/{chunk-AVN6Q6LM.js → chunk-GMT3YPLT.js} +76 -2
  17. package/dist/{chunk-CXIGHPBE.js → chunk-UAQ42NVJ.js} +440 -123
  18. package/dist/{chunk-LCABGFYN.js → chunk-VX5C7DK7.js} +404 -242
  19. package/dist/cli.js +103 -8
  20. package/dist/index.js +4 -4
  21. package/dist/{intelligence-service-ZWW3I3NL.js → intelligence-service-CAAQAKPN.js} +2 -2
  22. package/dist/mcp.js +2 -2
  23. package/package.json +8 -8
  24. package/assets/assets/ProjectPage-DSuvRUIf.js +0 -6
  25. package/assets/assets/TrafficSourceDetailPage-CzK5TMFp.js +0 -1
@@ -9,7 +9,7 @@ import {
9
9
  loadConfig,
10
10
  loadConfigRaw,
11
11
  saveConfigPatch
12
- } from "./chunk-2QBSRHSN.js";
12
+ } from "./chunk-6XOZSS3Y.js";
13
13
  import {
14
14
  CC_CACHE_DIR,
15
15
  DUCKDB_SPEC,
@@ -62,9 +62,11 @@ import {
62
62
  gbpPlaceActions,
63
63
  gbpPlaceDetails,
64
64
  getCrawlIssues,
65
+ getLinkCounts,
65
66
  getLodging,
66
67
  getPlaceDetails,
67
68
  getUrlInfo,
69
+ getUrlLinks,
68
70
  groupRunsByCreatedAt,
69
71
  gscCoverageSnapshots,
70
72
  gscSearchData,
@@ -102,13 +104,14 @@ import {
102
104
  siteAuditPages,
103
105
  siteAuditSnapshots,
104
106
  usageCounters
105
- } from "./chunk-CXIGHPBE.js";
107
+ } from "./chunk-UAQ42NVJ.js";
106
108
  import {
107
109
  AGENT_MEMORY_VALUE_MAX_BYTES,
108
110
  AGENT_PROVIDER_IDS,
109
111
  AI_ENGINE_DOMAINS,
110
112
  AI_ENGINE_SELF_DOMAINS,
111
113
  AgentProviderIds,
114
+ BacklinkSources,
112
115
  CcReleaseSyncStatuses,
113
116
  CodingAgents,
114
117
  DiscoveryCompetitorTypes,
@@ -136,6 +139,7 @@ import {
136
139
  buildRunErrorFromMessages,
137
140
  classifySkillFile,
138
141
  coerceSkillManifest,
142
+ computeBacklinkSummaryMetrics,
139
143
  contentActionLabel,
140
144
  contentBriefDtoSchema,
141
145
  determineAnswerMentioned,
@@ -143,6 +147,7 @@ import {
143
147
  effectiveBrandNames,
144
148
  effectiveDomains,
145
149
  factorStatusFromScore,
150
+ hostOf,
146
151
  isAgentProviderId,
147
152
  isBrowserProvider,
148
153
  isRetryableHttpError,
@@ -155,7 +160,7 @@ import {
155
160
  validationError,
156
161
  winnabilityClassLabel,
157
162
  withRetry
158
- } from "./chunk-AVN6Q6LM.js";
163
+ } from "./chunk-GMT3YPLT.js";
159
164
 
160
165
  // src/telemetry.ts
161
166
  import crypto from "crypto";
@@ -443,11 +448,11 @@ function checkLatestVersionForServer(opts) {
443
448
 
444
449
  // src/server.ts
445
450
  import { createRequire as createRequire3 } from "module";
446
- import crypto19 from "crypto";
451
+ import crypto20 from "crypto";
447
452
  import fs8 from "fs";
448
453
  import path9 from "path";
449
454
  import { fileURLToPath as fileURLToPath3 } from "url";
450
- import { and as and13, eq as eq19 } from "drizzle-orm";
455
+ import { and as and14, eq as eq20 } from "drizzle-orm";
451
456
  import Fastify from "fastify";
452
457
  import os5 from "os";
453
458
 
@@ -5003,6 +5008,7 @@ async function executeReleaseSync(db, syncId, opts) {
5003
5008
  id: crypto10.randomUUID(),
5004
5009
  projectId: p.id,
5005
5010
  releaseSyncId: syncId,
5011
+ source: BacklinkSources.commoncrawl,
5006
5012
  release,
5007
5013
  targetDomain: p.canonicalDomain,
5008
5014
  totalLinkingDomains: summary.totalLinkingDomains,
@@ -5011,7 +5017,7 @@ async function executeReleaseSync(db, syncId, opts) {
5011
5017
  queriedAt,
5012
5018
  createdAt: queriedAt
5013
5019
  }).onConflictDoUpdate({
5014
- target: [backlinkSummaries.projectId, backlinkSummaries.release],
5020
+ target: [backlinkSummaries.projectId, backlinkSummaries.source, backlinkSummaries.release],
5015
5021
  set: {
5016
5022
  releaseSyncId: syncId,
5017
5023
  targetDomain: p.canonicalDomain,
@@ -5144,13 +5150,18 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
5144
5150
  const targetDomain = project.canonicalDomain;
5145
5151
  db.transaction((tx) => {
5146
5152
  tx.delete(backlinkDomains).where(
5147
- and7(eq9(backlinkDomains.projectId, projectId), eq9(backlinkDomains.release, release))
5153
+ and7(
5154
+ eq9(backlinkDomains.projectId, projectId),
5155
+ eq9(backlinkDomains.source, BacklinkSources.commoncrawl),
5156
+ eq9(backlinkDomains.release, release)
5157
+ )
5148
5158
  ).run();
5149
5159
  if (rows.length > 0) {
5150
5160
  const values = rows.map((r) => ({
5151
5161
  id: crypto11.randomUUID(),
5152
5162
  projectId,
5153
5163
  releaseSyncId: syncId,
5164
+ source: BacklinkSources.commoncrawl,
5154
5165
  release,
5155
5166
  targetDomain,
5156
5167
  linkingDomain: r.linkingDomain,
@@ -5164,6 +5175,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
5164
5175
  id: crypto11.randomUUID(),
5165
5176
  projectId,
5166
5177
  releaseSyncId: syncId,
5178
+ source: BacklinkSources.commoncrawl,
5167
5179
  release,
5168
5180
  targetDomain,
5169
5181
  totalLinkingDomains: summary.totalLinkingDomains,
@@ -5172,7 +5184,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
5172
5184
  queriedAt,
5173
5185
  createdAt: queriedAt
5174
5186
  }).onConflictDoUpdate({
5175
- target: [backlinkSummaries.projectId, backlinkSummaries.release],
5187
+ target: [backlinkSummaries.projectId, backlinkSummaries.source, backlinkSummaries.release],
5176
5188
  set: {
5177
5189
  releaseSyncId: syncId,
5178
5190
  targetDomain,
@@ -5199,33 +5211,167 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
5199
5211
  }
5200
5212
  }
5201
5213
  function computeSummary2(rows) {
5202
- if (rows.length === 0) {
5203
- return { totalLinkingDomains: 0, totalHosts: 0, top10HostsShare: "0" };
5204
- }
5205
- const sorted = [...rows].sort((a, b) => b.numHosts - a.numHosts);
5206
- const totalHosts = sorted.reduce((acc, r) => acc + r.numHosts, 0);
5207
- const top10Hosts = sorted.slice(0, 10).reduce((acc, r) => acc + r.numHosts, 0);
5208
- const share = totalHosts > 0 ? top10Hosts / totalHosts : 0;
5209
- return {
5210
- totalLinkingDomains: rows.length,
5211
- totalHosts,
5212
- top10HostsShare: share.toFixed(6)
5213
- };
5214
+ return computeBacklinkSummaryMetrics(rows);
5214
5215
  }
5215
5216
 
5216
- // src/discovery-run.ts
5217
+ // src/bing-backlinks-sync.ts
5217
5218
  import crypto12 from "crypto";
5218
5219
  import { and as and8, eq as eq10 } from "drizzle-orm";
5219
- var log11 = createLogger("DiscoveryRun");
5220
+ var log11 = createLogger("BingBacklinkSync");
5221
+ var DEFAULT_MAX_PAGES = 200;
5222
+ var LINK_COUNTS_MAX_PAGES = 50;
5223
+ var URL_LINKS_MAX_PAGES = 20;
5224
+ function bingReleaseId(date) {
5225
+ return `bing-${date.toISOString().slice(0, 10)}`;
5226
+ }
5227
+ function aggregateInboundLinksByDomain(links, targetDomain) {
5228
+ const target = hostOf(targetDomain);
5229
+ const byHost = /* @__PURE__ */ new Map();
5230
+ for (const link of links) {
5231
+ const host = hostOf(link.Url);
5232
+ if (!host) continue;
5233
+ if (target && (host === target || host.endsWith(`.${target}`))) continue;
5234
+ let urls = byHost.get(host);
5235
+ if (!urls) {
5236
+ urls = /* @__PURE__ */ new Set();
5237
+ byHost.set(host, urls);
5238
+ }
5239
+ urls.add(link.Url);
5240
+ }
5241
+ return [...byHost.entries()].map(([linkingDomain, urls]) => ({ linkingDomain, numHosts: urls.size })).sort((a, b) => b.numHosts - a.numHosts || a.linkingDomain.localeCompare(b.linkingDomain));
5242
+ }
5243
+ function computeBingSummary(rows) {
5244
+ return computeBacklinkSummaryMetrics(rows);
5245
+ }
5246
+ function defaultDeps4() {
5247
+ return { getLinkCounts, getUrlLinks, now: () => /* @__PURE__ */ new Date() };
5248
+ }
5249
+ async function executeBingBacklinkSync(db, runId, projectId, opts) {
5250
+ const deps = { ...defaultDeps4(), ...opts.deps };
5251
+ const startedAt = deps.now().toISOString();
5252
+ db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq10(runs.id, runId)).run();
5253
+ try {
5254
+ const project = db.select().from(projects).where(eq10(projects.id, projectId)).get();
5255
+ if (!project) throw new Error(`Project not found: ${projectId}`);
5256
+ const conn = opts.resolveConnection(project.canonicalDomain);
5257
+ if (!conn) throw new Error(`No Bing Webmaster connection for ${project.canonicalDomain}`);
5258
+ const siteUrl = conn.siteUrl;
5259
+ if (!siteUrl) {
5260
+ throw new Error(`Bing connection for ${project.canonicalDomain} has no verified site selected`);
5261
+ }
5262
+ const pages = await deps.getLinkCounts(conn.apiKey, siteUrl, { maxPages: LINK_COUNTS_MAX_PAGES });
5263
+ const maxPages = Math.max(1, opts.maxPages ?? DEFAULT_MAX_PAGES);
5264
+ const targetPages = [...pages].sort((a, b) => b.Count - a.Count).slice(0, maxPages);
5265
+ if (pages.length > targetPages.length) {
5266
+ log11.info("bing-sync.pages-capped", {
5267
+ runId,
5268
+ projectId,
5269
+ sitePagesFound: pages.length,
5270
+ sitePagesPulled: targetPages.length
5271
+ });
5272
+ }
5273
+ const allLinks = [];
5274
+ let pageFailures = 0;
5275
+ for (const page of targetPages) {
5276
+ try {
5277
+ const links = await deps.getUrlLinks(conn.apiKey, siteUrl, page.Url, { maxPages: URL_LINKS_MAX_PAGES });
5278
+ allLinks.push(...links);
5279
+ } catch (err) {
5280
+ pageFailures++;
5281
+ log11.warn("bing-sync.page-failed", {
5282
+ runId,
5283
+ projectId,
5284
+ page: page.Url,
5285
+ error: err instanceof Error ? err.message : String(err)
5286
+ });
5287
+ }
5288
+ }
5289
+ if (targetPages.length > 0 && pageFailures === targetPages.length) {
5290
+ throw new Error(`All ${targetPages.length} Bing inbound-link page fetch(es) failed`);
5291
+ }
5292
+ const rows = aggregateInboundLinksByDomain(allLinks, project.canonicalDomain);
5293
+ const summary = computeBingSummary(rows);
5294
+ const queriedAt = deps.now().toISOString();
5295
+ const release = bingReleaseId(deps.now());
5296
+ const targetDomain = project.canonicalDomain;
5297
+ const source = BacklinkSources["bing-webmaster"];
5298
+ const existing = db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and8(
5299
+ eq10(backlinkSummaries.projectId, projectId),
5300
+ eq10(backlinkSummaries.source, source),
5301
+ eq10(backlinkSummaries.release, release)
5302
+ )).get();
5303
+ const preserveExisting = pageFailures > 0 && !!existing;
5304
+ if (!preserveExisting) {
5305
+ db.transaction((tx) => {
5306
+ tx.delete(backlinkDomains).where(and8(
5307
+ eq10(backlinkDomains.projectId, projectId),
5308
+ eq10(backlinkDomains.source, source),
5309
+ eq10(backlinkDomains.release, release)
5310
+ )).run();
5311
+ if (rows.length > 0) {
5312
+ tx.insert(backlinkDomains).values(rows.map((r) => ({
5313
+ id: crypto12.randomUUID(),
5314
+ projectId,
5315
+ releaseSyncId: null,
5316
+ source,
5317
+ release,
5318
+ targetDomain,
5319
+ linkingDomain: r.linkingDomain,
5320
+ numHosts: r.numHosts,
5321
+ createdAt: queriedAt
5322
+ }))).run();
5323
+ }
5324
+ tx.insert(backlinkSummaries).values({
5325
+ id: crypto12.randomUUID(),
5326
+ projectId,
5327
+ releaseSyncId: null,
5328
+ source,
5329
+ release,
5330
+ targetDomain,
5331
+ totalLinkingDomains: summary.totalLinkingDomains,
5332
+ totalHosts: summary.totalHosts,
5333
+ top10HostsShare: summary.top10HostsShare,
5334
+ queriedAt,
5335
+ createdAt: queriedAt
5336
+ }).onConflictDoUpdate({
5337
+ target: [backlinkSummaries.projectId, backlinkSummaries.source, backlinkSummaries.release],
5338
+ set: {
5339
+ targetDomain,
5340
+ totalLinkingDomains: summary.totalLinkingDomains,
5341
+ totalHosts: summary.totalHosts,
5342
+ top10HostsShare: summary.top10HostsShare,
5343
+ queriedAt
5344
+ }
5345
+ }).run();
5346
+ });
5347
+ }
5348
+ const finishedAt = deps.now().toISOString();
5349
+ const status = pageFailures > 0 ? RunStatuses.partial : RunStatuses.completed;
5350
+ const error = preserveExisting ? `Kept existing ${release} snapshot; ${pageFailures} of ${targetPages.length} inbound-link page fetches failed` : pageFailures > 0 ? `${pageFailures} of ${targetPages.length} inbound-link page fetches failed` : null;
5351
+ db.update(runs).set({ status, error, finishedAt }).where(eq10(runs.id, runId)).run();
5352
+ log11.info("bing-sync.completed", { runId, projectId, release, rows: rows.length, status, pageFailures, preserveExisting });
5353
+ } catch (err) {
5354
+ const errorMsg = err instanceof Error ? err.message : String(err);
5355
+ const finishedAt = deps.now().toISOString();
5356
+ db.update(runs).set({ status: RunStatuses.failed, error: errorMsg, finishedAt }).where(eq10(runs.id, runId)).run();
5357
+ log11.error("bing-sync.failed", { runId, projectId, error: errorMsg });
5358
+ throw err;
5359
+ }
5360
+ }
5361
+
5362
+ // src/discovery-run.ts
5363
+ import crypto13 from "crypto";
5364
+ import { and as and9, eq as eq11 } from "drizzle-orm";
5365
+ var log12 = createLogger("DiscoveryRun");
5220
5366
  var DEFAULT_SEED_COUNT = 30;
5221
5367
  var QUERIES_PER_INTENT_BUCKET = 6;
5222
5368
  async function executeDiscoveryRun(opts) {
5223
5369
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
5224
- opts.db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq10(runs.id, opts.runId)).run();
5370
+ opts.db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq11(runs.id, opts.runId)).run();
5225
5371
  try {
5226
- const projectRow = opts.db.select().from(projects).where(eq10(projects.id, opts.projectId)).get();
5372
+ const projectRow = opts.db.select().from(projects).where(eq11(projects.id, opts.projectId)).get();
5227
5373
  if (!projectRow) throw new Error(`Project ${opts.projectId} not found`);
5228
- const projectCompetitors = opts.db.select({ domain: competitors.domain }).from(competitors).where(eq10(competitors.projectId, opts.projectId)).all().map((r) => r.domain.toLowerCase());
5374
+ const projectCompetitors = opts.db.select({ domain: competitors.domain }).from(competitors).where(eq11(competitors.projectId, opts.projectId)).all().map((r) => r.domain.toLowerCase());
5229
5375
  const canonicalDomains = effectiveDomains({
5230
5376
  canonicalDomain: projectRow.canonicalDomain,
5231
5377
  ownedDomains: projectRow.ownedDomains
@@ -5255,8 +5401,8 @@ async function executeDiscoveryRun(opts) {
5255
5401
  seedProvider: result.seedProvider,
5256
5402
  result
5257
5403
  });
5258
- opts.db.update(runs).set({ status: RunStatuses.completed, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq10(runs.id, opts.runId)).run();
5259
- log11.info("discovery.completed", {
5404
+ opts.db.update(runs).set({ status: RunStatuses.completed, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq11(runs.id, opts.runId)).run();
5405
+ log12.info("discovery.completed", {
5260
5406
  runId: opts.runId,
5261
5407
  sessionId: opts.sessionId,
5262
5408
  buckets: result.buckets,
@@ -5264,13 +5410,13 @@ async function executeDiscoveryRun(opts) {
5264
5410
  });
5265
5411
  } catch (err) {
5266
5412
  const errorMsg = err instanceof Error ? err.message : String(err);
5267
- log11.error("discovery.failed", { runId: opts.runId, sessionId: opts.sessionId, error: errorMsg });
5413
+ log12.error("discovery.failed", { runId: opts.runId, sessionId: opts.sessionId, error: errorMsg });
5268
5414
  markSessionFailed(opts.db, opts.sessionId, errorMsg);
5269
5415
  opts.db.update(runs).set({
5270
5416
  status: RunStatuses.failed,
5271
5417
  finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
5272
5418
  error: errorMsg
5273
- }).where(eq10(runs.id, opts.runId)).run();
5419
+ }).where(eq11(runs.id, opts.runId)).run();
5274
5420
  }
5275
5421
  }
5276
5422
  function buildDefaultDeps(registry) {
@@ -5475,13 +5621,13 @@ function writeDiscoveryInsight(db, input) {
5475
5621
  totalProbes
5476
5622
  });
5477
5623
  db.transaction((tx) => {
5478
- tx.update(insights).set({ dismissed: true }).where(and8(
5479
- eq10(insights.projectId, input.projectId),
5480
- eq10(insights.type, "discovery.basket-divergence"),
5481
- eq10(insights.dismissed, false)
5624
+ tx.update(insights).set({ dismissed: true }).where(and9(
5625
+ eq11(insights.projectId, input.projectId),
5626
+ eq11(insights.type, "discovery.basket-divergence"),
5627
+ eq11(insights.dismissed, false)
5482
5628
  )).run();
5483
5629
  tx.insert(insights).values({
5484
- id: crypto12.randomUUID(),
5630
+ id: crypto13.randomUUID(),
5485
5631
  projectId: input.projectId,
5486
5632
  runId: input.runId,
5487
5633
  type: "discovery.basket-divergence",
@@ -5517,10 +5663,10 @@ function buildDiscoveryInsightTitle(input) {
5517
5663
  }
5518
5664
 
5519
5665
  // src/execute-site-audit.ts
5520
- import crypto13 from "crypto";
5521
- import { eq as eq11 } from "drizzle-orm";
5666
+ import crypto14 from "crypto";
5667
+ import { eq as eq12 } from "drizzle-orm";
5522
5668
  import { runSitemapAudit } from "@ainyc/aeo-audit";
5523
- var log12 = createLogger("SiteAudit");
5669
+ var log13 = createLogger("SiteAudit");
5524
5670
  var SITE_AUDIT_DEFAULT_PAGE_LIMIT = 500;
5525
5671
  var SITE_AUDIT_MAX_PAGE_LIMIT = 2e3;
5526
5672
  function toHomepageUrl(canonicalDomain) {
@@ -5581,15 +5727,15 @@ function computeFactorAverages(pages) {
5581
5727
  }
5582
5728
  async function executeSiteAudit(db, runId, projectId, opts = {}) {
5583
5729
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
5584
- db.update(runs).set({ status: "running", startedAt }).where(eq11(runs.id, runId)).run();
5730
+ db.update(runs).set({ status: "running", startedAt }).where(eq12(runs.id, runId)).run();
5585
5731
  try {
5586
- const project = db.select().from(projects).where(eq11(projects.id, projectId)).get();
5732
+ const project = db.select().from(projects).where(eq12(projects.id, projectId)).get();
5587
5733
  if (!project) {
5588
5734
  throw new Error(`Project not found: ${projectId}`);
5589
5735
  }
5590
5736
  const homepageUrl = toHomepageUrl(project.canonicalDomain);
5591
5737
  const limit = clampSiteAuditLimit(opts.limit);
5592
- log12.info("start", { runId, projectId, homepageUrl, sitemapUrl: opts.sitemapUrl ?? null, limit });
5738
+ log13.info("start", { runId, projectId, homepageUrl, sitemapUrl: opts.sitemapUrl ?? null, limit });
5593
5739
  await assertSiteAuditUrlAllowed(homepageUrl, "canonicalDomain");
5594
5740
  if (opts.sitemapUrl) await assertSiteAuditUrlAllowed(opts.sitemapUrl, "sitemapUrl");
5595
5741
  const report = await runSitemapAudit(homepageUrl, { sitemapUrl: opts.sitemapUrl, limit });
@@ -5597,7 +5743,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
5597
5743
  const pagesErrored = report.pages.filter((page) => page.status === "error").length;
5598
5744
  const auditable = report.pagesDiscovered - report.pagesSkipped;
5599
5745
  if (auditable > report.pagesAudited) {
5600
- log12.info("truncated", {
5746
+ log13.info("truncated", {
5601
5747
  runId,
5602
5748
  projectId,
5603
5749
  auditable,
@@ -5616,7 +5762,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
5616
5762
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
5617
5763
  db.transaction((tx) => {
5618
5764
  tx.insert(siteAuditSnapshots).values({
5619
- id: crypto13.randomUUID(),
5765
+ id: crypto14.randomUUID(),
5620
5766
  projectId,
5621
5767
  runId,
5622
5768
  sitemapUrl: report.sitemapUrl,
@@ -5645,7 +5791,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
5645
5791
  }).run();
5646
5792
  for (const page of report.pages) {
5647
5793
  tx.insert(siteAuditPages).values({
5648
- id: crypto13.randomUUID(),
5794
+ id: crypto14.randomUUID(),
5649
5795
  projectId,
5650
5796
  runId,
5651
5797
  url: page.url,
@@ -5656,9 +5802,9 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
5656
5802
  createdAt: finishedAt
5657
5803
  }).run();
5658
5804
  }
5659
- tx.update(runs).set({ status, finishedAt }).where(eq11(runs.id, runId)).run();
5805
+ tx.update(runs).set({ status, finishedAt }).where(eq12(runs.id, runId)).run();
5660
5806
  });
5661
- log12.info("completed", {
5807
+ log13.info("completed", {
5662
5808
  runId,
5663
5809
  projectId,
5664
5810
  status,
@@ -5668,14 +5814,14 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
5668
5814
  });
5669
5815
  } catch (err) {
5670
5816
  const errorMsg = err instanceof Error ? err.message : String(err);
5671
- db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq11(runs.id, runId)).run();
5672
- log12.error("failed", { runId, projectId, error: errorMsg });
5817
+ db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq12(runs.id, runId)).run();
5818
+ log13.error("failed", { runId, projectId, error: errorMsg });
5673
5819
  throw err;
5674
5820
  }
5675
5821
  }
5676
5822
 
5677
5823
  // src/commands/backfill.ts
5678
- import { and as and9, eq as eq12, inArray as inArray4, isNull, sql as sql4 } from "drizzle-orm";
5824
+ import { and as and10, eq as eq13, inArray as inArray4, isNull, sql as sql4 } from "drizzle-orm";
5679
5825
  var SNAPSHOT_BATCH_SIZE = 500;
5680
5826
  async function backfillAnswerVisibilityCommand(opts) {
5681
5827
  const config = loadConfig();
@@ -5683,7 +5829,7 @@ async function backfillAnswerVisibilityCommand(opts) {
5683
5829
  migrate(db);
5684
5830
  const projectFilter = opts?.project?.trim();
5685
5831
  const isDryRun = opts?.dryRun === true;
5686
- const scopedProjects = projectFilter ? db.select().from(projects).where(eq12(projects.name, projectFilter)).all() : db.select().from(projects).all();
5832
+ const scopedProjects = projectFilter ? db.select().from(projects).where(eq13(projects.name, projectFilter)).all() : db.select().from(projects).all();
5687
5833
  let examined = 0;
5688
5834
  let updated = 0;
5689
5835
  let wouldUpdate = 0;
@@ -5691,10 +5837,10 @@ async function backfillAnswerVisibilityCommand(opts) {
5691
5837
  let reparsed = 0;
5692
5838
  let providerErrors = 0;
5693
5839
  if (scopedProjects.length > 0) {
5694
- const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and9(
5695
- eq12(runs.kind, RunKinds["answer-visibility"]),
5840
+ const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and10(
5841
+ eq13(runs.kind, RunKinds["answer-visibility"]),
5696
5842
  inArray4(runs.projectId, scopedProjects.map((project) => project.id))
5697
- )).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq12(runs.kind, RunKinds["answer-visibility"])).all();
5843
+ )).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq13(runs.kind, RunKinds["answer-visibility"])).all();
5698
5844
  const runIdsByProject = /* @__PURE__ */ new Map();
5699
5845
  for (const run of runRows) {
5700
5846
  const existing = runIdsByProject.get(run.projectId);
@@ -5702,7 +5848,7 @@ async function backfillAnswerVisibilityCommand(opts) {
5702
5848
  else runIdsByProject.set(run.projectId, [run.id]);
5703
5849
  }
5704
5850
  for (const project of scopedProjects) {
5705
- const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq12(competitors.projectId, project.id)).all().map((row) => row.domain);
5851
+ const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq13(competitors.projectId, project.id)).all().map((row) => row.domain);
5706
5852
  const runIds = runIdsByProject.get(project.id) ?? [];
5707
5853
  if (runIds.length === 0) continue;
5708
5854
  const projectDomains = effectiveDomains({
@@ -5790,7 +5936,7 @@ async function backfillAnswerVisibilityCommand(opts) {
5790
5936
  } else {
5791
5937
  db.transaction((tx) => {
5792
5938
  for (const update of pendingUpdates) {
5793
- tx.update(querySnapshots).set(update.patch).where(eq12(querySnapshots.id, update.id)).run();
5939
+ tx.update(querySnapshots).set(update.patch).where(eq13(querySnapshots.id, update.id)).run();
5794
5940
  }
5795
5941
  });
5796
5942
  updated += pendingUpdates.length;
@@ -5839,13 +5985,13 @@ No DB writes performed. Re-run without --dry-run to apply.`);
5839
5985
  function backfillNormalizedPaths(db, opts) {
5840
5986
  const baseConditions = [];
5841
5987
  if (opts?.projectId) {
5842
- baseConditions.push(eq12(gaTrafficSnapshots.projectId, opts.projectId));
5988
+ baseConditions.push(eq13(gaTrafficSnapshots.projectId, opts.projectId));
5843
5989
  }
5844
5990
  const rows = db.select({
5845
5991
  id: gaTrafficSnapshots.id,
5846
5992
  landingPage: gaTrafficSnapshots.landingPage,
5847
5993
  landingPageNormalized: gaTrafficSnapshots.landingPageNormalized
5848
- }).from(gaTrafficSnapshots).where(baseConditions.length > 0 ? and9(...baseConditions) : void 0).all();
5994
+ }).from(gaTrafficSnapshots).where(baseConditions.length > 0 ? and10(...baseConditions) : void 0).all();
5849
5995
  let updated = 0;
5850
5996
  let unchanged = 0;
5851
5997
  if (rows.length > 0) {
@@ -5860,7 +6006,7 @@ function backfillNormalizedPaths(db, opts) {
5860
6006
  unchanged++;
5861
6007
  continue;
5862
6008
  }
5863
- tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq12(gaTrafficSnapshots.id, row.id)).run();
6009
+ tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq13(gaTrafficSnapshots.id, row.id)).run();
5864
6010
  updated++;
5865
6011
  }
5866
6012
  });
@@ -5874,7 +6020,7 @@ async function backfillNormalizedPathsCommand(opts) {
5874
6020
  const projectFilter = opts?.project?.trim();
5875
6021
  let projectId;
5876
6022
  if (projectFilter) {
5877
- const project = db.select({ id: projects.id }).from(projects).where(eq12(projects.name, projectFilter)).get();
6023
+ const project = db.select({ id: projects.id }).from(projects).where(eq13(projects.name, projectFilter)).get();
5878
6024
  if (!project) {
5879
6025
  const result2 = {
5880
6026
  project: projectFilter,
@@ -5911,13 +6057,13 @@ async function backfillNormalizedPathsCommand(opts) {
5911
6057
  function backfillAiReferralPaths(db, opts) {
5912
6058
  const baseConditions = [];
5913
6059
  if (opts?.projectId) {
5914
- baseConditions.push(eq12(gaAiReferrals.projectId, opts.projectId));
6060
+ baseConditions.push(eq13(gaAiReferrals.projectId, opts.projectId));
5915
6061
  }
5916
6062
  const rows = db.select({
5917
6063
  id: gaAiReferrals.id,
5918
6064
  landingPage: gaAiReferrals.landingPage,
5919
6065
  landingPageNormalized: gaAiReferrals.landingPageNormalized
5920
- }).from(gaAiReferrals).where(baseConditions.length > 0 ? and9(...baseConditions) : void 0).all();
6066
+ }).from(gaAiReferrals).where(baseConditions.length > 0 ? and10(...baseConditions) : void 0).all();
5921
6067
  let updated = 0;
5922
6068
  let unchanged = 0;
5923
6069
  if (rows.length > 0) {
@@ -5932,7 +6078,7 @@ function backfillAiReferralPaths(db, opts) {
5932
6078
  unchanged++;
5933
6079
  continue;
5934
6080
  }
5935
- tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(eq12(gaAiReferrals.id, row.id)).run();
6081
+ tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(eq13(gaAiReferrals.id, row.id)).run();
5936
6082
  updated++;
5937
6083
  }
5938
6084
  });
@@ -5946,7 +6092,7 @@ async function backfillAiReferralPathsCommand(opts) {
5946
6092
  const projectFilter = opts?.project?.trim();
5947
6093
  let projectId;
5948
6094
  if (projectFilter) {
5949
- const project = db.select({ id: projects.id }).from(projects).where(eq12(projects.name, projectFilter)).get();
6095
+ const project = db.select({ id: projects.id }).from(projects).where(eq13(projects.name, projectFilter)).get();
5950
6096
  if (!project) {
5951
6097
  const result2 = {
5952
6098
  project: projectFilter,
@@ -5982,10 +6128,10 @@ async function backfillAiReferralPathsCommand(opts) {
5982
6128
  }
5983
6129
  function backfillProjectAnswerMentions(db, projectId, opts) {
5984
6130
  const isDryRun = opts?.dryRun === true;
5985
- const project = db.select().from(projects).where(eq12(projects.id, projectId)).get();
6131
+ const project = db.select().from(projects).where(eq13(projects.id, projectId)).get();
5986
6132
  if (!project) return { examined: 0, updated: 0, mentioned: 0 };
5987
- const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq12(competitors.projectId, projectId)).all().map((row) => row.domain);
5988
- const runRows = db.select({ id: runs.id }).from(runs).where(and9(eq12(runs.kind, RunKinds["answer-visibility"]), eq12(runs.projectId, projectId))).all();
6133
+ const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq13(competitors.projectId, projectId)).all().map((row) => row.domain);
6134
+ const runRows = db.select({ id: runs.id }).from(runs).where(and10(eq13(runs.kind, RunKinds["answer-visibility"]), eq13(runs.projectId, projectId))).all();
5989
6135
  const runIds = runRows.map((r) => r.id);
5990
6136
  let examined = 0;
5991
6137
  let updated = 0;
@@ -6057,7 +6203,7 @@ function backfillProjectAnswerMentions(db, projectId, opts) {
6057
6203
  } else {
6058
6204
  db.transaction((tx) => {
6059
6205
  for (const update of pendingUpdates) {
6060
- tx.update(querySnapshots).set(update.patch).where(eq12(querySnapshots.id, update.id)).run();
6206
+ tx.update(querySnapshots).set(update.patch).where(eq13(querySnapshots.id, update.id)).run();
6061
6207
  }
6062
6208
  });
6063
6209
  updated += pendingUpdates.length;
@@ -6072,7 +6218,7 @@ async function backfillAnswerMentionsCommand(opts) {
6072
6218
  migrate(db);
6073
6219
  const projectFilter = opts?.project?.trim();
6074
6220
  const isDryRun = opts?.dryRun === true;
6075
- const scopedProjects = projectFilter ? db.select().from(projects).where(eq12(projects.name, projectFilter)).all() : db.select().from(projects).all();
6221
+ const scopedProjects = projectFilter ? db.select().from(projects).where(eq13(projects.name, projectFilter)).all() : db.select().from(projects).all();
6076
6222
  let examined = 0;
6077
6223
  let updated = 0;
6078
6224
  let wouldUpdate = 0;
@@ -6132,7 +6278,7 @@ function readStoredGroundingSources(rawResponse) {
6132
6278
  return result;
6133
6279
  }
6134
6280
  async function backfillInsightsCommand(project, opts) {
6135
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-ZWW3I3NL.js");
6281
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-CAAQAKPN.js");
6136
6282
  const config = loadConfig();
6137
6283
  const db = createClient(config.database);
6138
6284
  migrate(db);
@@ -6291,7 +6437,7 @@ async function backfillSnapshotAttributionCommand(opts) {
6291
6437
  const config = loadConfig();
6292
6438
  const db = createClient(config.database);
6293
6439
  migrate(db);
6294
- const project = db.select().from(projects).where(eq12(projects.name, opts.project)).get();
6440
+ const project = db.select().from(projects).where(eq13(projects.name, opts.project)).get();
6295
6441
  if (!project) {
6296
6442
  throw new Error(`Project "${opts.project}" not found`);
6297
6443
  }
@@ -6302,8 +6448,8 @@ async function backfillSnapshotAttributionCommand(opts) {
6302
6448
  process.stderr.write(`Recovering orphan snapshot attribution for "${project.name}"${mode}...
6303
6449
  `);
6304
6450
  }
6305
- const events = db.select({ createdAt: auditLog.createdAt, action: auditLog.action, diff: auditLog.diff }).from(auditLog).where(and9(
6306
- eq12(auditLog.projectId, project.id),
6451
+ const events = db.select({ createdAt: auditLog.createdAt, action: auditLog.action, diff: auditLog.diff }).from(auditLog).where(and10(
6452
+ eq13(auditLog.projectId, project.id),
6307
6453
  inArray4(auditLog.action, ["keywords.appended", "keywords.deleted", "queries.appended", "queries.deleted", "queries.replaced"])
6308
6454
  )).orderBy(auditLog.createdAt).all();
6309
6455
  const history = replayQueryAuditLog(events);
@@ -6311,8 +6457,8 @@ async function backfillSnapshotAttributionCommand(opts) {
6311
6457
  runId: runs.id,
6312
6458
  createdAt: runs.createdAt,
6313
6459
  location: runs.location
6314
- }).from(runs).innerJoin(querySnapshots, eq12(querySnapshots.runId, runs.id)).where(and9(
6315
- eq12(runs.projectId, project.id),
6460
+ }).from(runs).innerJoin(querySnapshots, eq13(querySnapshots.runId, runs.id)).where(and10(
6461
+ eq13(runs.projectId, project.id),
6316
6462
  isNull(querySnapshots.queryId),
6317
6463
  isNull(querySnapshots.queryText)
6318
6464
  )).groupBy(runs.id).orderBy(runs.createdAt).all();
@@ -6334,8 +6480,8 @@ async function backfillSnapshotAttributionCommand(opts) {
6334
6480
  provider: querySnapshots.provider,
6335
6481
  createdAt: querySnapshots.createdAt,
6336
6482
  answerText: querySnapshots.answerText
6337
- }).from(querySnapshots).where(and9(
6338
- eq12(querySnapshots.runId, run.runId),
6483
+ }).from(querySnapshots).where(and10(
6484
+ eq13(querySnapshots.runId, run.runId),
6339
6485
  isNull(querySnapshots.queryId),
6340
6486
  isNull(querySnapshots.queryText)
6341
6487
  )).orderBy(querySnapshots.provider, querySnapshots.createdAt).all();
@@ -6401,7 +6547,7 @@ async function backfillSnapshotAttributionCommand(opts) {
6401
6547
  if (!isDryRun && updates.length > 0) {
6402
6548
  db.transaction((tx) => {
6403
6549
  for (const u of updates) {
6404
- tx.update(querySnapshots).set({ queryText: u.queryText }).where(eq12(querySnapshots.id, u.id)).run();
6550
+ tx.update(querySnapshots).set({ queryText: u.queryText }).where(eq13(querySnapshots.id, u.id)).run();
6405
6551
  }
6406
6552
  });
6407
6553
  }
@@ -6475,7 +6621,7 @@ async function backfillTrafficClassificationCommand(opts) {
6475
6621
  const projectFilter = opts?.project?.trim();
6476
6622
  const isDryRun = opts?.dryRun === true;
6477
6623
  const isJson = isMachineFormat(opts?.format);
6478
- const scopedProjects = projectFilter ? db.select().from(projects).where(eq12(projects.name, projectFilter)).all() : db.select().from(projects).all();
6624
+ const scopedProjects = projectFilter ? db.select().from(projects).where(eq13(projects.name, projectFilter)).all() : db.select().from(projects).all();
6479
6625
  if (scopedProjects.length === 0) {
6480
6626
  if (projectFilter && !isJson) {
6481
6627
  process.stderr.write(`No project named "${projectFilter}".
@@ -6500,8 +6646,8 @@ async function backfillTrafficClassificationCommand(opts) {
6500
6646
  dryRun: isDryRun,
6501
6647
  byBot: {}
6502
6648
  };
6503
- const unknownCountRow = db.select({ n: sql4`count(*)` }).from(rawEventSamples).where(and9(
6504
- eq12(rawEventSamples.eventType, "unknown"),
6649
+ const unknownCountRow = db.select({ n: sql4`count(*)` }).from(rawEventSamples).where(and10(
6650
+ eq13(rawEventSamples.eventType, "unknown"),
6505
6651
  inArray4(rawEventSamples.projectId, projectIds)
6506
6652
  )).get();
6507
6653
  result.unknownBefore = Number(unknownCountRow?.n ?? 0);
@@ -6513,8 +6659,8 @@ async function backfillTrafficClassificationCommand(opts) {
6513
6659
  userAgent: rawEventSamples.userAgent,
6514
6660
  pathNormalized: rawEventSamples.pathNormalized,
6515
6661
  status: rawEventSamples.status
6516
- }).from(rawEventSamples).where(and9(
6517
- eq12(rawEventSamples.eventType, "unknown"),
6662
+ }).from(rawEventSamples).where(and10(
6663
+ eq13(rawEventSamples.eventType, "unknown"),
6518
6664
  inArray4(rawEventSamples.projectId, projectIds)
6519
6665
  )).all();
6520
6666
  result.examined = unknownSamples.length;
@@ -6553,7 +6699,7 @@ async function backfillTrafficClassificationCommand(opts) {
6553
6699
  result.reclassified++;
6554
6700
  result.byBot[classified.botId] = (result.byBot[classified.botId] ?? 0) + 1;
6555
6701
  if (isDryRun) continue;
6556
- db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(eq12(rawEventSamples.id, snap.id)).run();
6702
+ db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(eq13(rawEventSamples.id, snap.id)).run();
6557
6703
  const tsHour = new Date(snap.ts);
6558
6704
  tsHour.setUTCMinutes(0, 0, 0);
6559
6705
  if (userFetch) {
@@ -6617,8 +6763,8 @@ async function backfillTrafficClassificationCommand(opts) {
6617
6763
  }
6618
6764
  }
6619
6765
  if (!isDryRun) {
6620
- const afterRow = db.select({ n: sql4`count(*)` }).from(rawEventSamples).where(and9(
6621
- eq12(rawEventSamples.eventType, "unknown"),
6766
+ const afterRow = db.select({ n: sql4`count(*)` }).from(rawEventSamples).where(and10(
6767
+ eq13(rawEventSamples.eventType, "unknown"),
6622
6768
  inArray4(rawEventSamples.projectId, projectIds)
6623
6769
  )).get();
6624
6770
  result.unknownAfter = Number(afterRow?.n ?? 0);
@@ -6653,7 +6799,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
6653
6799
  }
6654
6800
 
6655
6801
  // src/commands/skills.ts
6656
- import crypto14 from "crypto";
6802
+ import crypto15 from "crypto";
6657
6803
  import fs4 from "fs";
6658
6804
  import os4 from "os";
6659
6805
  import path5 from "path";
@@ -6708,7 +6854,7 @@ function walkRelative(dir, prefix = "") {
6708
6854
  return out.sort();
6709
6855
  }
6710
6856
  function sha256File(filePath) {
6711
- return crypto14.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
6857
+ return crypto15.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
6712
6858
  }
6713
6859
  function readSkillManifest(skillDir) {
6714
6860
  try {
@@ -7031,10 +7177,10 @@ var ProviderRegistry = class {
7031
7177
  };
7032
7178
 
7033
7179
  // src/scheduler.ts
7034
- import crypto15 from "crypto";
7180
+ import crypto16 from "crypto";
7035
7181
  import cron from "node-cron";
7036
- import { and as and10, eq as eq13, inArray as inArray5 } from "drizzle-orm";
7037
- var log13 = createLogger("Scheduler");
7182
+ import { and as and11, eq as eq14, inArray as inArray5 } from "drizzle-orm";
7183
+ var log14 = createLogger("Scheduler");
7038
7184
  function taskKey(projectId, kind) {
7039
7185
  return `${projectId}::${kind}`;
7040
7186
  }
@@ -7048,16 +7194,16 @@ var Scheduler = class {
7048
7194
  }
7049
7195
  /** Load all enabled schedules from DB and register cron jobs. */
7050
7196
  start() {
7051
- const allSchedules = this.db.select().from(schedules).where(eq13(schedules.enabled, true)).all();
7197
+ const allSchedules = this.db.select().from(schedules).where(eq14(schedules.enabled, true)).all();
7052
7198
  for (const schedule of allSchedules) {
7053
7199
  const missedRunAt = schedule.nextRunAt;
7054
7200
  this.registerCronTask(schedule);
7055
7201
  if (missedRunAt && new Date(missedRunAt) < /* @__PURE__ */ new Date()) {
7056
- log13.info("run.catch-up", { projectId: schedule.projectId, kind: schedule.kind, missedRunAt });
7202
+ log14.info("run.catch-up", { projectId: schedule.projectId, kind: schedule.kind, missedRunAt });
7057
7203
  this.triggerRun(schedule.id, schedule.projectId, schedule.kind);
7058
7204
  }
7059
7205
  }
7060
- log13.info("started", { scheduleCount: allSchedules.length });
7206
+ log14.info("started", { scheduleCount: allSchedules.length });
7061
7207
  }
7062
7208
  /** Stop all cron tasks for graceful shutdown. */
7063
7209
  stop() {
@@ -7078,7 +7224,7 @@ var Scheduler = class {
7078
7224
  this.stopTask(key, existing, "Stopped");
7079
7225
  this.tasks.delete(key);
7080
7226
  }
7081
- const schedule = this.db.select().from(schedules).where(and10(eq13(schedules.projectId, projectId), eq13(schedules.kind, kind))).get();
7227
+ const schedule = this.db.select().from(schedules).where(and11(eq14(schedules.projectId, projectId), eq14(schedules.kind, kind))).get();
7082
7228
  if (schedule && schedule.enabled) {
7083
7229
  this.registerCronTask(schedule);
7084
7230
  }
@@ -7101,13 +7247,13 @@ var Scheduler = class {
7101
7247
  stopTask(key, task, verb) {
7102
7248
  void task.stop();
7103
7249
  void task.destroy();
7104
- log13.info(`task.${verb.toLowerCase()}`, { key });
7250
+ log14.info(`task.${verb.toLowerCase()}`, { key });
7105
7251
  }
7106
7252
  registerCronTask(schedule) {
7107
7253
  const { id: scheduleId, projectId, cronExpr, timezone } = schedule;
7108
7254
  const kind = schedule.kind;
7109
7255
  if (!cron.validate(cronExpr)) {
7110
- log13.error("cron.invalid", { projectId, kind, cronExpr });
7256
+ log14.error("cron.invalid", { projectId, kind, cronExpr });
7111
7257
  return;
7112
7258
  }
7113
7259
  const task = cron.schedule(cronExpr, () => {
@@ -7119,51 +7265,51 @@ var Scheduler = class {
7119
7265
  this.db.update(schedules).set({
7120
7266
  nextRunAt: nextRunFromCron(cronExpr, timezone),
7121
7267
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7122
- }).where(eq13(schedules.id, scheduleId)).run();
7268
+ }).where(eq14(schedules.id, scheduleId)).run();
7123
7269
  const label = schedule.preset ?? cronExpr;
7124
- log13.info("cron.registered", { projectId, kind, schedule: label, timezone });
7270
+ log14.info("cron.registered", { projectId, kind, schedule: label, timezone });
7125
7271
  }
7126
7272
  triggerRun(scheduleId, projectId, kind) {
7127
7273
  try {
7128
7274
  const now = (/* @__PURE__ */ new Date()).toISOString();
7129
- const currentSchedule = this.db.select().from(schedules).where(eq13(schedules.id, scheduleId)).get();
7275
+ const currentSchedule = this.db.select().from(schedules).where(eq14(schedules.id, scheduleId)).get();
7130
7276
  if (!currentSchedule || !currentSchedule.enabled) {
7131
- log13.warn("schedule.stale", { scheduleId, projectId, kind, msg: "schedule no longer exists or is disabled" });
7277
+ log14.warn("schedule.stale", { scheduleId, projectId, kind, msg: "schedule no longer exists or is disabled" });
7132
7278
  this.remove(projectId, kind);
7133
7279
  return;
7134
7280
  }
7135
7281
  const nextRunAt = nextRunFromCron(currentSchedule.cronExpr, currentSchedule.timezone);
7136
- const project = this.db.select().from(projects).where(eq13(projects.id, projectId)).get();
7282
+ const project = this.db.select().from(projects).where(eq14(projects.id, projectId)).get();
7137
7283
  if (!project) {
7138
- log13.error("project.not-found", { projectId, kind, msg: "skipping scheduled run" });
7284
+ log14.error("project.not-found", { projectId, kind, msg: "skipping scheduled run" });
7139
7285
  this.remove(projectId, kind);
7140
7286
  return;
7141
7287
  }
7142
7288
  if (kind === SchedulableRunKinds["traffic-sync"]) {
7143
7289
  const sourceId = currentSchedule.sourceId;
7144
7290
  if (!sourceId) {
7145
- log13.warn("traffic-sync.missing-source", { scheduleId, projectId });
7291
+ log14.warn("traffic-sync.missing-source", { scheduleId, projectId });
7146
7292
  return;
7147
7293
  }
7148
7294
  if (!this.callbacks.onTrafficSyncRequested) {
7149
- log13.warn("traffic-sync.no-callback", { scheduleId, projectId, msg: "host did not register onTrafficSyncRequested" });
7295
+ log14.warn("traffic-sync.no-callback", { scheduleId, projectId, msg: "host did not register onTrafficSyncRequested" });
7150
7296
  return;
7151
7297
  }
7152
7298
  this.db.update(schedules).set({
7153
7299
  lastRunAt: now,
7154
7300
  nextRunAt,
7155
7301
  updatedAt: now
7156
- }).where(eq13(schedules.id, currentSchedule.id)).run();
7157
- log13.info("traffic-sync.triggered", { projectName: project.name, sourceId });
7302
+ }).where(eq14(schedules.id, currentSchedule.id)).run();
7303
+ log14.info("traffic-sync.triggered", { projectName: project.name, sourceId });
7158
7304
  this.callbacks.onTrafficSyncRequested(project.name, sourceId);
7159
7305
  return;
7160
7306
  }
7161
7307
  if (kind === SchedulableRunKinds["gbp-sync"]) {
7162
7308
  if (!this.callbacks.onGbpSyncRequested) {
7163
- log13.warn("gbp-sync.no-callback", { scheduleId, projectId, msg: "host did not register onGbpSyncRequested" });
7309
+ log14.warn("gbp-sync.no-callback", { scheduleId, projectId, msg: "host did not register onGbpSyncRequested" });
7164
7310
  return;
7165
7311
  }
7166
- const runId2 = crypto15.randomUUID();
7312
+ const runId2 = crypto16.randomUUID();
7167
7313
  this.db.insert(runs).values({
7168
7314
  id: runId2,
7169
7315
  projectId,
@@ -7176,27 +7322,27 @@ var Scheduler = class {
7176
7322
  lastRunAt: now,
7177
7323
  nextRunAt,
7178
7324
  updatedAt: now
7179
- }).where(eq13(schedules.id, currentSchedule.id)).run();
7180
- log13.info("gbp-sync.triggered", { runId: runId2, projectName: project.name });
7325
+ }).where(eq14(schedules.id, currentSchedule.id)).run();
7326
+ log14.info("gbp-sync.triggered", { runId: runId2, projectName: project.name });
7181
7327
  this.callbacks.onGbpSyncRequested(runId2, projectId);
7182
7328
  return;
7183
7329
  }
7184
7330
  if (kind === SchedulableRunKinds["ads-sync"]) {
7185
7331
  if (!this.callbacks.onAdsSyncRequested) {
7186
- log13.warn("ads-sync.no-callback", { scheduleId, projectId, msg: "host did not register onAdsSyncRequested" });
7332
+ log14.warn("ads-sync.no-callback", { scheduleId, projectId, msg: "host did not register onAdsSyncRequested" });
7187
7333
  return;
7188
7334
  }
7189
- const activeAdsRun = this.db.select({ id: runs.id }).from(runs).where(and10(
7190
- eq13(runs.projectId, projectId),
7191
- eq13(runs.kind, RunKinds["ads-sync"]),
7335
+ const activeAdsRun = this.db.select({ id: runs.id }).from(runs).where(and11(
7336
+ eq14(runs.projectId, projectId),
7337
+ eq14(runs.kind, RunKinds["ads-sync"]),
7192
7338
  inArray5(runs.status, [RunStatuses.queued, RunStatuses.running])
7193
7339
  )).get();
7194
7340
  if (activeAdsRun) {
7195
- log13.info("ads-sync.skipped-active", { projectName: project.name, activeRunId: activeAdsRun.id });
7196
- this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq13(schedules.id, currentSchedule.id)).run();
7341
+ log14.info("ads-sync.skipped-active", { projectName: project.name, activeRunId: activeAdsRun.id });
7342
+ this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq14(schedules.id, currentSchedule.id)).run();
7197
7343
  return;
7198
7344
  }
7199
- const runId2 = crypto15.randomUUID();
7345
+ const runId2 = crypto16.randomUUID();
7200
7346
  this.db.insert(runs).values({
7201
7347
  id: runId2,
7202
7348
  projectId,
@@ -7209,55 +7355,55 @@ var Scheduler = class {
7209
7355
  lastRunAt: now,
7210
7356
  nextRunAt,
7211
7357
  updatedAt: now
7212
- }).where(eq13(schedules.id, currentSchedule.id)).run();
7213
- log13.info("ads-sync.triggered", { runId: runId2, projectName: project.name });
7358
+ }).where(eq14(schedules.id, currentSchedule.id)).run();
7359
+ log14.info("ads-sync.triggered", { runId: runId2, projectName: project.name });
7214
7360
  this.callbacks.onAdsSyncRequested(runId2, projectId);
7215
7361
  return;
7216
7362
  }
7217
7363
  if (kind === SchedulableRunKinds["data-refresh"]) {
7218
7364
  if (!this.callbacks.onDataRefreshRequested) {
7219
- log13.warn("data-refresh.no-callback", { scheduleId, projectId, msg: "host did not register onDataRefreshRequested" });
7365
+ log14.warn("data-refresh.no-callback", { scheduleId, projectId, msg: "host did not register onDataRefreshRequested" });
7220
7366
  return;
7221
7367
  }
7222
7368
  this.db.update(schedules).set({
7223
7369
  lastRunAt: now,
7224
7370
  nextRunAt,
7225
7371
  updatedAt: now
7226
- }).where(eq13(schedules.id, currentSchedule.id)).run();
7227
- log13.info("data-refresh.triggered", { projectName: project.name });
7372
+ }).where(eq14(schedules.id, currentSchedule.id)).run();
7373
+ log14.info("data-refresh.triggered", { projectName: project.name });
7228
7374
  this.callbacks.onDataRefreshRequested(project.name);
7229
7375
  return;
7230
7376
  }
7231
7377
  if (kind === SchedulableRunKinds["backlinks-sync"]) {
7232
7378
  if (!this.callbacks.onBacklinksSyncRequested) {
7233
- log13.warn("backlinks-sync.no-callback", { scheduleId, projectId, msg: "host did not register onBacklinksSyncRequested" });
7379
+ log14.warn("backlinks-sync.no-callback", { scheduleId, projectId, msg: "host did not register onBacklinksSyncRequested" });
7234
7380
  return;
7235
7381
  }
7236
7382
  this.db.update(schedules).set({
7237
7383
  lastRunAt: now,
7238
7384
  nextRunAt,
7239
7385
  updatedAt: now
7240
- }).where(eq13(schedules.id, currentSchedule.id)).run();
7241
- log13.info("backlinks-sync.triggered", { projectName: project.name });
7386
+ }).where(eq14(schedules.id, currentSchedule.id)).run();
7387
+ log14.info("backlinks-sync.triggered", { projectName: project.name });
7242
7388
  this.callbacks.onBacklinksSyncRequested(project.name);
7243
7389
  return;
7244
7390
  }
7245
7391
  if (kind === SchedulableRunKinds["site-audit"]) {
7246
7392
  if (!this.callbacks.onSiteAuditRequested) {
7247
- log13.warn("site-audit.no-callback", { scheduleId, projectId, msg: "host did not register onSiteAuditRequested" });
7393
+ log14.warn("site-audit.no-callback", { scheduleId, projectId, msg: "host did not register onSiteAuditRequested" });
7248
7394
  return;
7249
7395
  }
7250
- const active = this.db.select({ id: runs.id }).from(runs).where(and10(
7251
- eq13(runs.projectId, projectId),
7252
- eq13(runs.kind, RunKinds["site-audit"]),
7396
+ const active = this.db.select({ id: runs.id }).from(runs).where(and11(
7397
+ eq14(runs.projectId, projectId),
7398
+ eq14(runs.kind, RunKinds["site-audit"]),
7253
7399
  inArray5(runs.status, [RunStatuses.queued, RunStatuses.running])
7254
7400
  )).get();
7255
7401
  if (active) {
7256
- log13.info("site-audit.skipped-active", { projectName: project.name, activeRunId: active.id });
7257
- this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq13(schedules.id, currentSchedule.id)).run();
7402
+ log14.info("site-audit.skipped-active", { projectName: project.name, activeRunId: active.id });
7403
+ this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq14(schedules.id, currentSchedule.id)).run();
7258
7404
  return;
7259
7405
  }
7260
- const runId2 = crypto15.randomUUID();
7406
+ const runId2 = crypto16.randomUUID();
7261
7407
  this.db.insert(runs).values({
7262
7408
  id: runId2,
7263
7409
  projectId,
@@ -7270,8 +7416,8 @@ var Scheduler = class {
7270
7416
  lastRunAt: now,
7271
7417
  nextRunAt,
7272
7418
  updatedAt: now
7273
- }).where(eq13(schedules.id, currentSchedule.id)).run();
7274
- log13.info("site-audit.triggered", { runId: runId2, projectName: project.name });
7419
+ }).where(eq14(schedules.id, currentSchedule.id)).run();
7420
+ log14.info("site-audit.triggered", { runId: runId2, projectName: project.name });
7275
7421
  this.callbacks.onSiteAuditRequested(runId2, projectId);
7276
7422
  return;
7277
7423
  }
@@ -7280,7 +7426,7 @@ var Scheduler = class {
7280
7426
  if (project.defaultLocation) {
7281
7427
  const loc = projectLocations.find((l) => l.label === project.defaultLocation);
7282
7428
  if (!loc) {
7283
- log13.warn("default-location.stale", { scheduleId, projectId, label: project.defaultLocation });
7429
+ log14.warn("default-location.stale", { scheduleId, projectId, label: project.defaultLocation });
7284
7430
  return;
7285
7431
  }
7286
7432
  resolvedLocation = loc;
@@ -7294,11 +7440,11 @@ var Scheduler = class {
7294
7440
  location: locationLabel
7295
7441
  });
7296
7442
  if (queueResult.conflict) {
7297
- log13.info("run.skipped-active", { projectName: project.name, activeRunId: queueResult.activeRunId });
7443
+ log14.info("run.skipped-active", { projectName: project.name, activeRunId: queueResult.activeRunId });
7298
7444
  this.db.update(schedules).set({
7299
7445
  nextRunAt,
7300
7446
  updatedAt: now
7301
- }).where(eq13(schedules.id, currentSchedule.id)).run();
7447
+ }).where(eq14(schedules.id, currentSchedule.id)).run();
7302
7448
  return;
7303
7449
  }
7304
7450
  const runId = queueResult.runId;
@@ -7306,19 +7452,19 @@ var Scheduler = class {
7306
7452
  lastRunAt: now,
7307
7453
  nextRunAt,
7308
7454
  updatedAt: now
7309
- }).where(eq13(schedules.id, currentSchedule.id)).run();
7455
+ }).where(eq14(schedules.id, currentSchedule.id)).run();
7310
7456
  const scheduleProviders = currentSchedule.providers;
7311
7457
  const providers = scheduleProviders.length > 0 ? scheduleProviders : void 0;
7312
- log13.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
7458
+ log14.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
7313
7459
  this.callbacks.onRunCreated(runId, projectId, providers, resolvedLocation);
7314
7460
  } catch (err) {
7315
- log13.error("trigger.error", { scheduleId, projectId, kind, error: err instanceof Error ? err.message : String(err) });
7461
+ log14.error("trigger.error", { scheduleId, projectId, kind, error: err instanceof Error ? err.message : String(err) });
7316
7462
  }
7317
7463
  }
7318
7464
  };
7319
7465
 
7320
7466
  // src/data-refresh.ts
7321
- var log14 = createLogger("DataRefresh");
7467
+ var log15 = createLogger("DataRefresh");
7322
7468
  async function refreshAllIntegrations(client, projectName) {
7323
7469
  const integrations = [
7324
7470
  { name: "gsc", run: () => client.gscSync(projectName, {}) },
@@ -7331,19 +7477,19 @@ async function refreshAllIntegrations(client, projectName) {
7331
7477
  results.forEach((result, idx) => {
7332
7478
  const integration = integrations[idx].name;
7333
7479
  if (result.status === "fulfilled") {
7334
- log14.info("integration.refreshed", { projectName, integration });
7480
+ log15.info("integration.refreshed", { projectName, integration });
7335
7481
  } else {
7336
7482
  const reason = result.reason;
7337
7483
  const message = reason instanceof Error ? reason.message : String(reason);
7338
- log14.warn("integration.refresh-failed", { projectName, integration, error: message });
7484
+ log15.warn("integration.refresh-failed", { projectName, integration, error: message });
7339
7485
  }
7340
7486
  });
7341
7487
  }
7342
7488
 
7343
7489
  // src/notifier.ts
7344
- import { eq as eq14, desc as desc5, and as and11, inArray as inArray6, or } from "drizzle-orm";
7345
- import crypto16 from "crypto";
7346
- var log15 = createLogger("Notifier");
7490
+ import { eq as eq15, desc as desc5, and as and12, inArray as inArray6, or } from "drizzle-orm";
7491
+ import crypto17 from "crypto";
7492
+ var log16 = createLogger("Notifier");
7347
7493
  var Notifier = class {
7348
7494
  db;
7349
7495
  serverUrl;
@@ -7353,26 +7499,26 @@ var Notifier = class {
7353
7499
  }
7354
7500
  /** Called after a run completes (success, partial, or failed). */
7355
7501
  async onRunCompleted(runId, projectId) {
7356
- log15.info("run.completed", { runId, projectId });
7357
- const notifs = this.db.select().from(notifications).where(eq14(notifications.projectId, projectId)).all().filter((n) => n.enabled);
7502
+ log16.info("run.completed", { runId, projectId });
7503
+ const notifs = this.db.select().from(notifications).where(eq15(notifications.projectId, projectId)).all().filter((n) => n.enabled);
7358
7504
  if (notifs.length === 0) {
7359
- log15.info("notifications.none-enabled", { projectId });
7505
+ log16.info("notifications.none-enabled", { projectId });
7360
7506
  return;
7361
7507
  }
7362
- log15.info("notifications.found", { projectId, count: notifs.length });
7363
- const run = this.db.select().from(runs).where(eq14(runs.id, runId)).get();
7508
+ log16.info("notifications.found", { projectId, count: notifs.length });
7509
+ const run = this.db.select().from(runs).where(eq15(runs.id, runId)).get();
7364
7510
  if (!run) {
7365
- log15.error("run.not-found", { runId, msg: "skipping notification dispatch" });
7511
+ log16.error("run.not-found", { runId, msg: "skipping notification dispatch" });
7366
7512
  return;
7367
7513
  }
7368
- const project = this.db.select().from(projects).where(eq14(projects.id, projectId)).get();
7514
+ const project = this.db.select().from(projects).where(eq15(projects.id, projectId)).get();
7369
7515
  if (!project) {
7370
- log15.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
7516
+ log16.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
7371
7517
  return;
7372
7518
  }
7373
7519
  const transitions = this.computeTransitions(runId, projectId);
7374
7520
  const events = [];
7375
- log15.info("run.status", { runId: run.id, status: run.status, projectId });
7521
+ log16.info("run.status", { runId: run.id, status: run.status, projectId });
7376
7522
  if (run.status === "completed" || run.status === "partial") {
7377
7523
  events.push("run.completed");
7378
7524
  }
@@ -7388,7 +7534,7 @@ var Notifier = class {
7388
7534
  if (!config.url) continue;
7389
7535
  const subscribedEvents = config.events;
7390
7536
  const matchingEvents = events.filter((e) => subscribedEvents.includes(e));
7391
- log15.info("notification.match", { notificationId: notif.id, subscribedEvents, matchedEvents: matchingEvents });
7537
+ log16.info("notification.match", { notificationId: notif.id, subscribedEvents, matchedEvents: matchingEvents });
7392
7538
  if (matchingEvents.length === 0) continue;
7393
7539
  for (const event of matchingEvents) {
7394
7540
  const relevantTransitions = event === "citation.lost" ? lostTransitions : event === "citation.gained" ? gainedTransitions : transitions;
@@ -7412,11 +7558,11 @@ var Notifier = class {
7412
7558
  if (criticalInsights.length > 0) insightEvents.push("insight.critical");
7413
7559
  if (highInsights.length > 0) insightEvents.push("insight.high");
7414
7560
  if (insightEvents.length === 0) return;
7415
- const notifs = this.db.select().from(notifications).where(eq14(notifications.projectId, projectId)).all().filter((n) => n.enabled);
7561
+ const notifs = this.db.select().from(notifications).where(eq15(notifications.projectId, projectId)).all().filter((n) => n.enabled);
7416
7562
  if (notifs.length === 0) return;
7417
- const run = this.db.select().from(runs).where(eq14(runs.id, runId)).get();
7563
+ const run = this.db.select().from(runs).where(eq15(runs.id, runId)).get();
7418
7564
  if (!run) return;
7419
- const project = this.db.select().from(projects).where(eq14(projects.id, projectId)).get();
7565
+ const project = this.db.select().from(projects).where(eq15(projects.id, projectId)).get();
7420
7566
  if (!project) return;
7421
7567
  for (const notif of notifs) {
7422
7568
  const config = notif.config;
@@ -7446,12 +7592,12 @@ var Notifier = class {
7446
7592
  }
7447
7593
  }
7448
7594
  computeTransitions(runId, projectId) {
7449
- const thisRun = this.db.select().from(runs).where(eq14(runs.id, runId)).get();
7595
+ const thisRun = this.db.select().from(runs).where(eq15(runs.id, runId)).get();
7450
7596
  if (!thisRun) return [];
7451
- const groupSiblings = this.db.select().from(runs).where(and11(
7452
- eq14(runs.projectId, projectId),
7453
- eq14(runs.kind, thisRun.kind),
7454
- eq14(runs.createdAt, thisRun.createdAt)
7597
+ const groupSiblings = this.db.select().from(runs).where(and12(
7598
+ eq15(runs.projectId, projectId),
7599
+ eq15(runs.kind, thisRun.kind),
7600
+ eq15(runs.createdAt, thisRun.createdAt)
7455
7601
  )).all();
7456
7602
  const stillPending = groupSiblings.some((r) => r.status === "queued" || r.status === "running");
7457
7603
  if (stillPending) return [];
@@ -7467,17 +7613,17 @@ var Notifier = class {
7467
7613
  return candidate.id > best.id ? candidate : best;
7468
7614
  });
7469
7615
  if (winner.id !== runId) return [];
7470
- const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq14(projects.id, projectId)).get();
7616
+ const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq15(projects.id, projectId)).get();
7471
7617
  const locationCount = Math.max(
7472
7618
  1,
7473
7619
  (projectLocations?.locations ?? []).length
7474
7620
  );
7475
7621
  const RECENT_FETCH_LIMIT = Math.max(8, locationCount * 4);
7476
7622
  const recentRuns = this.db.select().from(runs).where(
7477
- and11(
7478
- eq14(runs.projectId, projectId),
7479
- eq14(runs.kind, thisRun.kind),
7480
- or(eq14(runs.status, "completed"), eq14(runs.status, "partial"))
7623
+ and12(
7624
+ eq15(runs.projectId, projectId),
7625
+ eq15(runs.kind, thisRun.kind),
7626
+ or(eq15(runs.status, "completed"), eq15(runs.status, "partial"))
7481
7627
  )
7482
7628
  ).orderBy(desc5(runs.createdAt), desc5(runs.id)).limit(RECENT_FETCH_LIMIT).all();
7483
7629
  const groups = groupRunsByCreatedAt(recentRuns);
@@ -7494,7 +7640,7 @@ var Notifier = class {
7494
7640
  provider: querySnapshots.provider,
7495
7641
  location: querySnapshots.location,
7496
7642
  citationState: querySnapshots.citationState
7497
- }).from(querySnapshots).leftJoin(queries, eq14(querySnapshots.queryId, queries.id)).where(inArray6(querySnapshots.runId, currentRunIds)).all();
7643
+ }).from(querySnapshots).leftJoin(queries, eq15(querySnapshots.queryId, queries.id)).where(inArray6(querySnapshots.runId, currentRunIds)).all();
7498
7644
  const previousSnapshots = this.db.select({
7499
7645
  queryId: querySnapshots.queryId,
7500
7646
  provider: querySnapshots.provider,
@@ -7527,23 +7673,23 @@ var Notifier = class {
7527
7673
  const targetLabel = redactNotificationUrl(url).urlDisplay;
7528
7674
  const targetCheck = await resolveWebhookTarget(url);
7529
7675
  if (!targetCheck.ok) {
7530
- log15.error("webhook.ssrf-blocked", { url: targetLabel, reason: targetCheck.message });
7676
+ log16.error("webhook.ssrf-blocked", { url: targetLabel, reason: targetCheck.message });
7531
7677
  this.logDelivery(projectId, notificationId, payload.event, "failed", `SSRF: ${targetCheck.message}`);
7532
7678
  return;
7533
7679
  }
7534
- log15.info("webhook.send", { event: payload.event, url: targetLabel });
7680
+ log16.info("webhook.send", { event: payload.event, url: targetLabel });
7535
7681
  const maxRetries = 3;
7536
7682
  const delays = [1e3, 4e3, 16e3];
7537
7683
  for (let attempt = 0; attempt < maxRetries; attempt++) {
7538
7684
  try {
7539
7685
  const response = await deliverWebhook(targetCheck.target, payload, webhookSecret);
7540
7686
  if (response.status >= 200 && response.status < 300) {
7541
- log15.info("webhook.delivered", { event: payload.event, url: targetLabel, httpStatus: response.status });
7687
+ log16.info("webhook.delivered", { event: payload.event, url: targetLabel, httpStatus: response.status });
7542
7688
  this.logDelivery(projectId, notificationId, payload.event, "sent", null);
7543
7689
  return;
7544
7690
  }
7545
7691
  const errorDetail = response.error ?? `HTTP ${response.status}`;
7546
- log15.warn("webhook.attempt-failed", { event: payload.event, url: targetLabel, attempt: attempt + 1, maxRetries, httpStatus: response.status, error: errorDetail });
7692
+ log16.warn("webhook.attempt-failed", { event: payload.event, url: targetLabel, attempt: attempt + 1, maxRetries, httpStatus: response.status, error: errorDetail });
7547
7693
  if (attempt === maxRetries - 1) {
7548
7694
  this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
7549
7695
  }
@@ -7551,7 +7697,7 @@ var Notifier = class {
7551
7697
  const errorDetail = err instanceof Error ? err.message : String(err);
7552
7698
  if (attempt === maxRetries - 1) {
7553
7699
  this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
7554
- log15.error("webhook.exhausted", { event: payload.event, url: targetLabel, maxRetries, error: errorDetail });
7700
+ log16.error("webhook.exhausted", { event: payload.event, url: targetLabel, maxRetries, error: errorDetail });
7555
7701
  }
7556
7702
  }
7557
7703
  if (attempt < maxRetries - 1) {
@@ -7561,7 +7707,7 @@ var Notifier = class {
7561
7707
  }
7562
7708
  logDelivery(projectId, notificationId, event, status, error) {
7563
7709
  this.db.insert(auditLog).values({
7564
- id: crypto16.randomUUID(),
7710
+ id: crypto17.randomUUID(),
7565
7711
  projectId,
7566
7712
  actor: "scheduler",
7567
7713
  action: `notification.${status}`,
@@ -7574,8 +7720,8 @@ var Notifier = class {
7574
7720
  };
7575
7721
 
7576
7722
  // src/run-coordinator.ts
7577
- import { eq as eq15 } from "drizzle-orm";
7578
- var log16 = createLogger("RunCoordinator");
7723
+ import { eq as eq16 } from "drizzle-orm";
7724
+ var log17 = createLogger("RunCoordinator");
7579
7725
  var RunCoordinator = class {
7580
7726
  constructor(db, notifier, intelligenceService, onInsightsGenerated, onAeroEvent) {
7581
7727
  this.db = db;
@@ -7590,10 +7736,10 @@ var RunCoordinator = class {
7590
7736
  onInsightsGenerated;
7591
7737
  onAeroEvent;
7592
7738
  async onRunCompleted(runId, projectId) {
7593
- const runRow = this.db.select().from(runs).where(eq15(runs.id, runId)).get();
7739
+ const runRow = this.db.select().from(runs).where(eq16(runs.id, runId)).get();
7594
7740
  const kind = runRow?.kind ?? RunKinds["answer-visibility"];
7595
7741
  if (runRow?.trigger === RunTriggers.probe) {
7596
- log16.info("probe.skip-side-effects", { runId, projectId, kind });
7742
+ log17.info("probe.skip-side-effects", { runId, projectId, kind });
7597
7743
  return;
7598
7744
  }
7599
7745
  let insightCount = 0;
@@ -7610,12 +7756,12 @@ var RunCoordinator = class {
7610
7756
  try {
7611
7757
  await this.onInsightsGenerated(runId, projectId, result);
7612
7758
  } catch (err) {
7613
- log16.error("insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7759
+ log17.error("insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7614
7760
  }
7615
7761
  }
7616
7762
  }
7617
7763
  } catch (err) {
7618
- log16.error("intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7764
+ log17.error("intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7619
7765
  }
7620
7766
  } else if (kind === RunKinds["gbp-sync"]) {
7621
7767
  try {
@@ -7628,17 +7774,17 @@ var RunCoordinator = class {
7628
7774
  try {
7629
7775
  await this.onInsightsGenerated(runId, projectId, analysisResultFromInsights(gbpInsights));
7630
7776
  } catch (err) {
7631
- log16.error("gbp-insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7777
+ log17.error("gbp-insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7632
7778
  }
7633
7779
  }
7634
7780
  } catch (err) {
7635
- log16.error("gbp-intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7781
+ log17.error("gbp-intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7636
7782
  }
7637
7783
  }
7638
7784
  try {
7639
7785
  await this.notifier.onRunCompleted(runId, projectId);
7640
7786
  } catch (err) {
7641
- log16.error("notifier.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7787
+ log17.error("notifier.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7642
7788
  }
7643
7789
  if (this.onAeroEvent) {
7644
7790
  try {
@@ -7651,7 +7797,7 @@ var RunCoordinator = class {
7651
7797
  };
7652
7798
  await this.onAeroEvent(ctx);
7653
7799
  } catch (err) {
7654
- log16.error("aero.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7800
+ log17.error("aero.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7655
7801
  }
7656
7802
  }
7657
7803
  }
@@ -7666,7 +7812,7 @@ var RunCoordinator = class {
7666
7812
  * so the Aero queue is never starved of a follow-up.
7667
7813
  */
7668
7814
  buildDiscoveryAeroContext(runId, projectId, status, error) {
7669
- const session = this.db.select().from(discoverySessions).where(eq15(discoverySessions.runId, runId)).get();
7815
+ const session = this.db.select().from(discoverySessions).where(eq16(discoverySessions.runId, runId)).get();
7670
7816
  const competitorMap = session ? session.competitorMap : [];
7671
7817
  return {
7672
7818
  kind: RunKinds["aeo-discover-probe"],
@@ -7706,8 +7852,8 @@ function analysisResultFromInsights(insights2) {
7706
7852
  }
7707
7853
 
7708
7854
  // src/agent/session-registry.ts
7709
- import crypto18 from "crypto";
7710
- import { eq as eq17 } from "drizzle-orm";
7855
+ import crypto19 from "crypto";
7856
+ import { eq as eq18 } from "drizzle-orm";
7711
7857
 
7712
7858
  // src/agent/session.ts
7713
7859
  import fs7 from "fs";
@@ -8095,8 +8241,8 @@ function resolveSessionProviderAndModel(config, opts) {
8095
8241
  }
8096
8242
 
8097
8243
  // src/agent/memory-store.ts
8098
- import crypto17 from "crypto";
8099
- import { and as and12, desc as desc6, eq as eq16, like, sql as sql5 } from "drizzle-orm";
8244
+ import crypto18 from "crypto";
8245
+ import { and as and13, desc as desc6, eq as eq17, like, sql as sql5 } from "drizzle-orm";
8100
8246
  var COMPACTION_KEY_PREFIX = "compaction:";
8101
8247
  var COMPACTION_NOTES_PER_SESSION = 3;
8102
8248
  function rowToDto(row) {
@@ -8110,7 +8256,7 @@ function rowToDto(row) {
8110
8256
  };
8111
8257
  }
8112
8258
  function listMemoryEntries(db, projectId, opts = {}) {
8113
- const query = db.select().from(agentMemory).where(eq16(agentMemory.projectId, projectId)).orderBy(desc6(agentMemory.updatedAt));
8259
+ const query = db.select().from(agentMemory).where(eq17(agentMemory.projectId, projectId)).orderBy(desc6(agentMemory.updatedAt));
8114
8260
  const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
8115
8261
  return rows.map(rowToDto);
8116
8262
  }
@@ -8124,7 +8270,7 @@ function upsertMemoryEntry(db, args) {
8124
8270
  throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
8125
8271
  }
8126
8272
  const now = (/* @__PURE__ */ new Date()).toISOString();
8127
- const id = crypto17.randomUUID();
8273
+ const id = crypto18.randomUUID();
8128
8274
  db.insert(agentMemory).values({
8129
8275
  id,
8130
8276
  projectId: args.projectId,
@@ -8141,12 +8287,12 @@ function upsertMemoryEntry(db, args) {
8141
8287
  updatedAt: now
8142
8288
  }
8143
8289
  }).run();
8144
- const row = db.select().from(agentMemory).where(and12(eq16(agentMemory.projectId, args.projectId), eq16(agentMemory.key, args.key))).get();
8290
+ const row = db.select().from(agentMemory).where(and13(eq17(agentMemory.projectId, args.projectId), eq17(agentMemory.key, args.key))).get();
8145
8291
  if (!row) throw new Error("memory upsert produced no row");
8146
8292
  return rowToDto(row);
8147
8293
  }
8148
8294
  function deleteMemoryEntry(db, projectId, key) {
8149
- const result = db.delete(agentMemory).where(and12(eq16(agentMemory.projectId, projectId), eq16(agentMemory.key, key))).run();
8295
+ const result = db.delete(agentMemory).where(and13(eq17(agentMemory.projectId, projectId), eq17(agentMemory.key, key))).run();
8150
8296
  const changes = result.changes ?? 0;
8151
8297
  return changes > 0;
8152
8298
  }
@@ -8161,7 +8307,7 @@ function writeCompactionNote(db, args) {
8161
8307
  }
8162
8308
  const now = (/* @__PURE__ */ new Date()).toISOString();
8163
8309
  const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
8164
- const id = crypto17.randomUUID();
8310
+ const id = crypto18.randomUUID();
8165
8311
  let inserted;
8166
8312
  db.transaction((tx) => {
8167
8313
  tx.insert(agentMemory).values({
@@ -8175,8 +8321,8 @@ function writeCompactionNote(db, args) {
8175
8321
  }).run();
8176
8322
  const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
8177
8323
  const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
8178
- and12(
8179
- eq16(agentMemory.projectId, args.projectId),
8324
+ and13(
8325
+ eq17(agentMemory.projectId, args.projectId),
8180
8326
  like(agentMemory.key, `${sessionPrefix}%`)
8181
8327
  )
8182
8328
  ).orderBy(desc6(agentMemory.updatedAt)).all();
@@ -8184,7 +8330,7 @@ function writeCompactionNote(db, args) {
8184
8330
  if (stale.length > 0) {
8185
8331
  tx.delete(agentMemory).where(sql5`${agentMemory.id} IN (${sql5.join(stale.map((s) => sql5`${s}`), sql5`, `)})`).run();
8186
8332
  }
8187
- const row = tx.select().from(agentMemory).where(and12(eq16(agentMemory.projectId, args.projectId), eq16(agentMemory.key, key))).get();
8333
+ const row = tx.select().from(agentMemory).where(and13(eq17(agentMemory.projectId, args.projectId), eq17(agentMemory.key, key))).get();
8188
8334
  if (row) inserted = rowToDto(row);
8189
8335
  });
8190
8336
  if (!inserted) throw new Error("compaction note write produced no row");
@@ -8317,7 +8463,7 @@ async function compactMessages(args) {
8317
8463
  }
8318
8464
 
8319
8465
  // src/agent/session-registry.ts
8320
- var log17 = createLogger("SessionRegistry");
8466
+ var log18 = createLogger("SessionRegistry");
8321
8467
  var MAX_HYDRATE_NOTES = 20;
8322
8468
  var MAX_HYDRATE_BYTES = 32 * 1024;
8323
8469
  function escapeMemoryFragment(value) {
@@ -8366,7 +8512,7 @@ var SessionRegistry = class {
8366
8512
  modelProvider: effectiveProvider,
8367
8513
  modelId: effectiveModelId,
8368
8514
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
8369
- }).where(eq17(agentSessions.projectId, projectId)).run();
8515
+ }).where(eq18(agentSessions.projectId, projectId)).run();
8370
8516
  }
8371
8517
  const agent2 = createAeroSession({
8372
8518
  projectName,
@@ -8544,13 +8690,13 @@ ${lines.join("\n")}
8544
8690
  agent.state.messages = result.messages;
8545
8691
  agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, row.systemPrompt);
8546
8692
  this.save(projectName);
8547
- log17.info("compaction.completed", {
8693
+ log18.info("compaction.completed", {
8548
8694
  projectName,
8549
8695
  removedCount: result.removedCount,
8550
8696
  summaryBytes: Buffer.byteLength(result.summary, "utf8")
8551
8697
  });
8552
8698
  } catch (err) {
8553
- log17.error("compaction.failed", {
8699
+ log18.error("compaction.failed", {
8554
8700
  projectName,
8555
8701
  error: err instanceof Error ? err.message : String(err)
8556
8702
  });
@@ -8580,7 +8726,7 @@ ${lines.join("\n")}
8580
8726
  modelProvider: nextProvider,
8581
8727
  modelId: nextModelId,
8582
8728
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
8583
- }).where(eq17(agentSessions.projectId, projectId)).run();
8729
+ }).where(eq18(agentSessions.projectId, projectId)).run();
8584
8730
  }
8585
8731
  /** Persist a session's transcript back to the DB. Call after any run settles. */
8586
8732
  save(projectName) {
@@ -8647,7 +8793,7 @@ ${lines.join("\n")}
8647
8793
  await agent.prompt(msgs);
8648
8794
  this.save(projectName);
8649
8795
  } catch (err) {
8650
- log17.error("drain.failed", {
8796
+ log18.error("drain.failed", {
8651
8797
  projectName,
8652
8798
  error: err instanceof Error ? err.message : String(err)
8653
8799
  });
@@ -8742,17 +8888,17 @@ ${lines.join("\n")}
8742
8888
  return id;
8743
8889
  }
8744
8890
  tryResolveProjectId(projectName) {
8745
- const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq17(projects.name, projectName)).get();
8891
+ const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq18(projects.name, projectName)).get();
8746
8892
  return row?.id;
8747
8893
  }
8748
8894
  loadRow(projectId) {
8749
- const row = this.opts.db.select().from(agentSessions).where(eq17(agentSessions.projectId, projectId)).get();
8895
+ const row = this.opts.db.select().from(agentSessions).where(eq18(agentSessions.projectId, projectId)).get();
8750
8896
  return row ?? null;
8751
8897
  }
8752
8898
  insertRow(params) {
8753
8899
  const now = (/* @__PURE__ */ new Date()).toISOString();
8754
8900
  this.opts.db.insert(agentSessions).values({
8755
- id: crypto18.randomUUID(),
8901
+ id: crypto19.randomUUID(),
8756
8902
  projectId: params.projectId,
8757
8903
  systemPrompt: params.systemPrompt,
8758
8904
  modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
@@ -8765,14 +8911,14 @@ ${lines.join("\n")}
8765
8911
  }
8766
8912
  updateRow(projectId, patch) {
8767
8913
  const now = (/* @__PURE__ */ new Date()).toISOString();
8768
- this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq17(agentSessions.projectId, projectId)).run();
8914
+ this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq18(agentSessions.projectId, projectId)).run();
8769
8915
  }
8770
8916
  };
8771
8917
 
8772
8918
  // src/agent/agent-routes.ts
8773
- import { eq as eq18 } from "drizzle-orm";
8919
+ import { eq as eq19 } from "drizzle-orm";
8774
8920
  function resolveProject(db, name) {
8775
- const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq18(projects.name, name)).get();
8921
+ const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq19(projects.name, name)).get();
8776
8922
  if (!row) throw notFound("project", name);
8777
8923
  return row;
8778
8924
  }
@@ -8781,7 +8927,7 @@ function registerAgentRoutes(app, opts) {
8781
8927
  "/projects/:name/agent/transcript",
8782
8928
  async (request) => {
8783
8929
  const project = resolveProject(opts.db, request.params.name);
8784
- const row = opts.db.select().from(agentSessions).where(eq18(agentSessions.projectId, project.id)).get();
8930
+ const row = opts.db.select().from(agentSessions).where(eq19(agentSessions.projectId, project.id)).get();
8785
8931
  if (!row) {
8786
8932
  return { messages: [], modelProvider: null, modelId: null, updatedAt: null };
8787
8933
  }
@@ -8805,7 +8951,7 @@ function registerAgentRoutes(app, opts) {
8805
8951
  async (request) => {
8806
8952
  const project = resolveProject(opts.db, request.params.name);
8807
8953
  opts.sessionRegistry.reset(project.name);
8808
- opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq18(agentSessions.projectId, project.id)).run();
8954
+ opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq19(agentSessions.projectId, project.id)).run();
8809
8955
  return { status: "reset" };
8810
8956
  }
8811
8957
  );
@@ -9245,7 +9391,7 @@ function formatAuditFactorScore(factor) {
9245
9391
  }
9246
9392
 
9247
9393
  // src/snapshot-service.ts
9248
- var log18 = createLogger("Snapshot");
9394
+ var log19 = createLogger("Snapshot");
9249
9395
  var ANALYSIS_PROVIDER_PRIORITY = ["openai", "claude", "gemini", "perplexity", "local"];
9250
9396
  var SNAPSHOT_QUERY_COUNT = 6;
9251
9397
  var ProviderExecutionGate2 = class {
@@ -9391,7 +9537,7 @@ var SnapshotService = class {
9391
9537
  return mapAuditReport(report);
9392
9538
  } catch (err) {
9393
9539
  const message = err instanceof Error ? err.message : String(err);
9394
- log18.warn("audit.failed", { homepageUrl, error: message });
9540
+ log19.warn("audit.failed", { homepageUrl, error: message });
9395
9541
  return {
9396
9542
  url: homepageUrl,
9397
9543
  finalUrl: homepageUrl,
@@ -9420,7 +9566,7 @@ var SnapshotService = class {
9420
9566
  queries: parsedQueries
9421
9567
  };
9422
9568
  } catch (err) {
9423
- log18.warn("profile.generation-failed", {
9569
+ log19.warn("profile.generation-failed", {
9424
9570
  domain: ctx.domain,
9425
9571
  provider: ctx.analysisProvider.adapter.name,
9426
9572
  error: err instanceof Error ? err.message : String(err)
@@ -9562,7 +9708,7 @@ var SnapshotService = class {
9562
9708
  recommendedActions: uniqueStrings(parsed.recommendedActions ?? []).slice(0, 4)
9563
9709
  };
9564
9710
  } catch (err) {
9565
- log18.warn("response.analysis-failed", {
9711
+ log19.warn("response.analysis-failed", {
9566
9712
  provider: ctx.analysisProvider.adapter.name,
9567
9713
  error: err instanceof Error ? err.message : String(err)
9568
9714
  });
@@ -9844,7 +9990,7 @@ function clipText(value, length) {
9844
9990
  // src/server.ts
9845
9991
  var _require3 = createRequire3(import.meta.url);
9846
9992
  var { version: PKG_VERSION2 } = _require3("../package.json");
9847
- var log19 = createLogger("Server");
9993
+ var log20 = createLogger("Server");
9848
9994
  var DEFAULT_QUOTA = {
9849
9995
  maxConcurrency: 2,
9850
9996
  maxRequestsPerMinute: 10,
@@ -9879,14 +10025,14 @@ function summarizeProviderConfig(config) {
9879
10025
  };
9880
10026
  }
9881
10027
  function hashApiKey(key) {
9882
- return crypto19.createHash("sha256").update(key).digest("hex");
10028
+ return crypto20.createHash("sha256").update(key).digest("hex");
9883
10029
  }
9884
10030
  var DASHBOARD_SCRYPT_KEYLEN = 64;
9885
10031
  var DASHBOARD_SCRYPT_COST = 1 << 15;
9886
10032
  var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
9887
10033
  function hashDashboardPassword(password) {
9888
- const salt = crypto19.randomBytes(16);
9889
- const derived = crypto19.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
10034
+ const salt = crypto20.randomBytes(16);
10035
+ const derived = crypto20.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
9890
10036
  N: DASHBOARD_SCRYPT_COST,
9891
10037
  maxmem: DASHBOARD_SCRYPT_MAXMEM
9892
10038
  });
@@ -9907,18 +10053,18 @@ function verifyDashboardPassword(password, storedHash) {
9907
10053
  } catch {
9908
10054
  return { ok: false, needsRehash: false };
9909
10055
  }
9910
- const derived = crypto19.scryptSync(password, salt, expected.length, {
10056
+ const derived = crypto20.scryptSync(password, salt, expected.length, {
9911
10057
  N: DASHBOARD_SCRYPT_COST,
9912
10058
  maxmem: DASHBOARD_SCRYPT_MAXMEM
9913
10059
  });
9914
10060
  if (derived.length !== expected.length) return { ok: false, needsRehash: false };
9915
- return { ok: crypto19.timingSafeEqual(derived, expected), needsRehash: false };
10061
+ return { ok: crypto20.timingSafeEqual(derived, expected), needsRehash: false };
9916
10062
  }
9917
10063
  if (/^[a-f0-9]{64}$/i.test(storedHash)) {
9918
10064
  const candidate = Buffer.from(hashApiKey(password), "hex");
9919
10065
  const expected = Buffer.from(storedHash, "hex");
9920
10066
  if (candidate.length !== expected.length) return { ok: false, needsRehash: false };
9921
- const ok = crypto19.timingSafeEqual(candidate, expected);
10067
+ const ok = crypto20.timingSafeEqual(candidate, expected);
9922
10068
  return { ok, needsRehash: ok };
9923
10069
  }
9924
10070
  return { ok: false, needsRehash: false };
@@ -9977,7 +10123,7 @@ function applyLegacyCredentials(rows, config) {
9977
10123
  }
9978
10124
  if (migratedGoogle > 0) {
9979
10125
  saveConfigPatch({ google: config.google });
9980
- log19.info("credentials.migrated", { type: "google", count: migratedGoogle });
10126
+ log20.info("credentials.migrated", { type: "google", count: migratedGoogle });
9981
10127
  }
9982
10128
  let migratedGa4 = 0;
9983
10129
  for (const row of rows.ga4) {
@@ -9995,7 +10141,7 @@ function applyLegacyCredentials(rows, config) {
9995
10141
  }
9996
10142
  if (migratedGa4 > 0) {
9997
10143
  saveConfigPatch({ ga4: config.ga4 });
9998
- log19.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
10144
+ log20.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
9999
10145
  }
10000
10146
  }
10001
10147
  function isLoopbackBindHost(host) {
@@ -10034,11 +10180,11 @@ async function createServer(opts) {
10034
10180
  applyLegacyCredentials(legacyRows, opts.config);
10035
10181
  dropLegacyCredentialColumns(opts.db);
10036
10182
  } catch (err) {
10037
- log19.warn("credentials.migration.failed", {
10183
+ log20.warn("credentials.migration.failed", {
10038
10184
  error: err instanceof Error ? err.message : String(err)
10039
10185
  });
10040
10186
  }
10041
- log19.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
10187
+ log20.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
10042
10188
  const p = providers[k];
10043
10189
  return p?.apiKey || p?.baseUrl || p?.vertexProject;
10044
10190
  }) });
@@ -10087,7 +10233,7 @@ async function createServer(opts) {
10087
10233
  intelligenceService,
10088
10234
  (runId, projectId, result) => notifier.dispatchInsightWebhooks(runId, projectId, result),
10089
10235
  async (ctx) => {
10090
- const project = opts.db.select({ name: projects.name }).from(projects).where(eq19(projects.id, ctx.projectId)).get();
10236
+ const project = opts.db.select({ name: projects.name }).from(projects).where(eq20(projects.id, ctx.projectId)).get();
10091
10237
  if (!project) return;
10092
10238
  let content;
10093
10239
  if (ctx.kind === RunKinds["aeo-discover-probe"]) {
@@ -10192,9 +10338,9 @@ async function createServer(opts) {
10192
10338
  return null;
10193
10339
  });
10194
10340
  if (!probed) return;
10195
- const alreadySynced = opts.db.select().from(ccReleaseSyncs).where(and13(
10196
- eq19(ccReleaseSyncs.release, probed.release),
10197
- eq19(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
10341
+ const alreadySynced = opts.db.select().from(ccReleaseSyncs).where(and14(
10342
+ eq20(ccReleaseSyncs.release, probed.release),
10343
+ eq20(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
10198
10344
  )).limit(1).get();
10199
10345
  if (alreadySynced) {
10200
10346
  app.log.info({ projectName, release: probed.release }, "Scheduled backlinks sync: already up to date, skipping");
@@ -10207,6 +10353,15 @@ async function createServer(opts) {
10207
10353
  );
10208
10354
  });
10209
10355
  })();
10356
+ const project = opts.db.select().from(projects).where(eq20(projects.name, projectName)).get();
10357
+ if (project && bingConnectionStore.getConnection(project.canonicalDomain)) {
10358
+ aeroClient.backlinksBingSync(projectName).catch((err) => {
10359
+ app.log.error(
10360
+ { projectName, err: err instanceof Error ? err.message : String(err) },
10361
+ "Scheduled Bing backlinks sync failed"
10362
+ );
10363
+ });
10364
+ }
10210
10365
  },
10211
10366
  onSiteAuditRequested: (runId, projectId) => {
10212
10367
  runSiteAudit(runId, projectId);
@@ -10327,7 +10482,7 @@ async function createServer(opts) {
10327
10482
  return removed;
10328
10483
  }
10329
10484
  };
10330
- const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto19.randomBytes(32).toString("hex");
10485
+ const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto20.randomBytes(32).toString("hex");
10331
10486
  const googleConnectionStore = {
10332
10487
  listConnections: (domain) => listGoogleConnections(opts.config, domain),
10333
10488
  getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
@@ -10373,11 +10528,11 @@ async function createServer(opts) {
10373
10528
  const apiPrefix = basePath ? `${basePath}api/v1` : "/api/v1";
10374
10529
  if (opts.config.apiKey) {
10375
10530
  const keyHash = hashApiKey(opts.config.apiKey);
10376
- const existing = opts.db.select().from(apiKeys).where(eq19(apiKeys.keyHash, keyHash)).get();
10531
+ const existing = opts.db.select().from(apiKeys).where(eq20(apiKeys.keyHash, keyHash)).get();
10377
10532
  if (!existing) {
10378
10533
  const prefix = opts.config.apiKey.slice(0, 12);
10379
10534
  opts.db.insert(apiKeys).values({
10380
- id: `key_${crypto19.randomBytes(8).toString("hex")}`,
10535
+ id: `key_${crypto20.randomBytes(8).toString("hex")}`,
10381
10536
  name: "default",
10382
10537
  keyHash,
10383
10538
  keyPrefix: prefix,
@@ -10401,7 +10556,7 @@ async function createServer(opts) {
10401
10556
  };
10402
10557
  const createSession = (apiKeyId) => {
10403
10558
  pruneExpiredSessions();
10404
- const sessionId = crypto19.randomBytes(32).toString("hex");
10559
+ const sessionId = crypto20.randomBytes(32).toString("hex");
10405
10560
  sessions.set(sessionId, {
10406
10561
  apiKeyId,
10407
10562
  expiresAt: Date.now() + SESSION_TTL_MS
@@ -10425,7 +10580,7 @@ async function createServer(opts) {
10425
10580
  };
10426
10581
  const getDefaultApiKey = () => {
10427
10582
  if (!opts.config.apiKey) return void 0;
10428
- return opts.db.select().from(apiKeys).where(eq19(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
10583
+ return opts.db.select().from(apiKeys).where(eq20(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
10429
10584
  };
10430
10585
  const createPasswordSession = (reply) => {
10431
10586
  const key = getDefaultApiKey();
@@ -10446,7 +10601,7 @@ async function createServer(opts) {
10446
10601
  if (!header) return false;
10447
10602
  const parts = header.split(" ");
10448
10603
  if (parts.length !== 2 || parts[0] !== "Bearer") return false;
10449
- const key = opts.db.select().from(apiKeys).where(eq19(apiKeys.keyHash, hashApiKey(parts[1]))).get();
10604
+ const key = opts.db.select().from(apiKeys).where(eq20(apiKeys.keyHash, hashApiKey(parts[1]))).get();
10450
10605
  return Boolean(key && !key.revokedAt);
10451
10606
  };
10452
10607
  app.get(apiPrefix + "/session", async (request, reply) => {
@@ -10500,12 +10655,12 @@ async function createServer(opts) {
10500
10655
  return reply.send({ authenticated: true });
10501
10656
  }
10502
10657
  if (apiKey) {
10503
- const key = opts.db.select().from(apiKeys).where(eq19(apiKeys.keyHash, hashApiKey(apiKey))).get();
10658
+ const key = opts.db.select().from(apiKeys).where(eq20(apiKeys.keyHash, hashApiKey(apiKey))).get();
10504
10659
  if (!key || key.revokedAt) {
10505
10660
  const err2 = authInvalid();
10506
10661
  return reply.status(err2.statusCode).send(err2.toJSON());
10507
10662
  }
10508
- opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq19(apiKeys.id, key.id)).run();
10663
+ opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq20(apiKeys.id, key.id)).run();
10509
10664
  const sessionId = createSession(key.id);
10510
10665
  reply.header("set-cookie", serializeSessionCookie({
10511
10666
  name: SESSION_COOKIE_NAME,
@@ -10664,7 +10819,7 @@ async function createServer(opts) {
10664
10819
  deps: {
10665
10820
  enqueueAutoExtract: ({ projectId, release: r }) => {
10666
10821
  const now = (/* @__PURE__ */ new Date()).toISOString();
10667
- const runId = crypto19.randomUUID();
10822
+ const runId = crypto20.randomUUID();
10668
10823
  opts.db.insert(runs).values({
10669
10824
  id: runId,
10670
10825
  projectId,
@@ -10687,6 +10842,13 @@ async function createServer(opts) {
10687
10842
  app.log.error({ runId, err }, "Backlink extract failed");
10688
10843
  });
10689
10844
  },
10845
+ onBingBacklinkSyncRequested: (runId, projectId) => {
10846
+ executeBingBacklinkSync(opts.db, runId, projectId, {
10847
+ resolveConnection: (domain) => bingConnectionStore.getConnection(domain)
10848
+ }).catch((err) => {
10849
+ app.log.error({ runId, err }, "Bing backlink sync failed");
10850
+ });
10851
+ },
10690
10852
  onDiscoveryRunRequested: (input) => {
10691
10853
  executeDiscoveryRun({
10692
10854
  db: opts.db,
@@ -10750,7 +10912,7 @@ async function createServer(opts) {
10750
10912
  ...inspectOpts,
10751
10913
  config: opts.config
10752
10914
  }).then(() => {
10753
- const finished = opts.db.select({ status: runs.status }).from(runs).where(eq19(runs.id, runId)).get();
10915
+ const finished = opts.db.select({ status: runs.status }).from(runs).where(eq20(runs.id, runId)).get();
10754
10916
  if (finished?.status === RunStatuses.completed || finished?.status === RunStatuses.partial) {
10755
10917
  return maybeRefreshGscCoverage(opts.db, opts.config, projectId);
10756
10918
  }
@@ -10838,7 +11000,7 @@ async function createServer(opts) {
10838
11000
  const targetProjectIds = affectedProjectIds.length > 0 ? affectedProjectIds : [null];
10839
11001
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
10840
11002
  opts.db.insert(auditLog).values(targetProjectIds.map((projectId) => ({
10841
- id: crypto19.randomUUID(),
11003
+ id: crypto20.randomUUID(),
10842
11004
  projectId,
10843
11005
  actor: "api",
10844
11006
  action: existing ? "provider.updated" : "provider.created",