@canonry/canonry 4.148.8 → 4.148.11

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.
@@ -549,11 +549,11 @@ function checkLatestVersionForServer(opts) {
549
549
 
550
550
  // src/server.ts
551
551
  import { createRequire as createRequire3 } from "module";
552
- import crypto22 from "crypto";
552
+ import crypto23 from "crypto";
553
553
  import fs8 from "fs";
554
554
  import path9 from "path";
555
555
  import { fileURLToPath as fileURLToPath3 } from "url";
556
- import { and as and15, eq as eq21 } from "drizzle-orm";
556
+ import { and as and15, eq as eq22 } from "drizzle-orm";
557
557
  import Fastify from "fastify";
558
558
  import os5 from "os";
559
559
 
@@ -4225,8 +4225,83 @@ function maybeShowActivationNotice(io = {}) {
4225
4225
  }
4226
4226
 
4227
4227
  // src/gsc-sync.ts
4228
+ import crypto6 from "crypto";
4229
+ import { eq as eq4, and as and4, sql as sql3 } from "drizzle-orm";
4230
+
4231
+ // src/gsc-coverage-snapshot.ts
4228
4232
  import crypto5 from "crypto";
4229
- import { eq as eq3, and as and3, sql as sql3 } from "drizzle-orm";
4233
+ import { and as and3, desc, eq as eq3, gte } from "drizzle-orm";
4234
+ var COVERAGE_IMPRESSION_WINDOW_DAYS = 30;
4235
+ function pageKey(raw) {
4236
+ if (!raw) return null;
4237
+ try {
4238
+ return normalizeUrlPath(new URL(raw).pathname);
4239
+ } catch {
4240
+ return normalizeUrlPath(raw);
4241
+ }
4242
+ }
4243
+ function isoDaysAgo(days) {
4244
+ const d = /* @__PURE__ */ new Date();
4245
+ d.setUTCDate(d.getUTCDate() - days);
4246
+ return d.toISOString().split("T")[0];
4247
+ }
4248
+ function writeCoverageSnapshot(db, projectId, runId, opts = {}) {
4249
+ const windowDays = opts.windowDays ?? COVERAGE_IMPRESSION_WINDOW_DAYS;
4250
+ const since = isoDaysAgo(windowDays);
4251
+ const pageRows = db.select({ page: gscSearchData.page, impressions: gscSearchData.impressions }).from(gscSearchData).where(and3(eq3(gscSearchData.projectId, projectId), gte(gscSearchData.date, since))).all();
4252
+ const allInspections = db.select().from(gscUrlInspections).where(eq3(gscUrlInspections.projectId, projectId)).all();
4253
+ const latestAudit = db.select({ runId: siteAuditSnapshots.runId }).from(siteAuditSnapshots).where(eq3(siteAuditSnapshots.projectId, projectId)).orderBy(desc(siteAuditSnapshots.createdAt)).limit(1).get();
4254
+ const sitemapUrls = latestAudit ? db.select({ url: siteAuditPages.url }).from(siteAuditPages).where(eq3(siteAuditPages.runId, latestAudit.runId)).all() : [];
4255
+ const impressionsByKey = /* @__PURE__ */ new Map();
4256
+ for (const row of pageRows) {
4257
+ const key = pageKey(row.page);
4258
+ if (key) impressionsByKey.set(key, (impressionsByKey.get(key) ?? 0) + row.impressions);
4259
+ }
4260
+ for (const row of sitemapUrls) {
4261
+ const key = pageKey(row.url);
4262
+ if (key && !impressionsByKey.has(key)) impressionsByKey.set(key, 0);
4263
+ }
4264
+ const latestByKey = /* @__PURE__ */ new Map();
4265
+ for (const row of allInspections) {
4266
+ const key = pageKey(row.url);
4267
+ if (!key) continue;
4268
+ const existing = latestByKey.get(key);
4269
+ if (!existing || row.inspectedAt > existing.inspectedAt) latestByKey.set(key, row);
4270
+ }
4271
+ const coverage = deriveIndexCoverage({
4272
+ pages: [...impressionsByKey].map(([page, impressions]) => ({ page, impressions })),
4273
+ inspections: [...latestByKey].map(([key, r]) => ({
4274
+ url: key,
4275
+ indexingState: r.indexingState,
4276
+ coverageState: r.coverageState
4277
+ }))
4278
+ });
4279
+ const now = opts.now ?? /* @__PURE__ */ new Date();
4280
+ const snapshotDate = now.toISOString().split("T")[0];
4281
+ db.delete(gscCoverageSnapshots).where(and3(eq3(gscCoverageSnapshots.projectId, projectId), eq3(gscCoverageSnapshots.date, snapshotDate))).run();
4282
+ db.insert(gscCoverageSnapshots).values({
4283
+ id: crypto5.randomUUID(),
4284
+ projectId,
4285
+ syncRunId: runId,
4286
+ date: snapshotDate,
4287
+ indexed: coverage.indexed,
4288
+ notIndexed: coverage.notIndexed,
4289
+ unknownPages: coverage.unknown,
4290
+ verifiedByInspection: coverage.verifiedByInspection,
4291
+ derivedFromImpressions: coverage.derivedFromImpressions,
4292
+ reasonBreakdown: coverage.reasonBreakdown,
4293
+ createdAt: now.toISOString()
4294
+ }).run();
4295
+ return {
4296
+ indexed: coverage.indexed,
4297
+ notIndexed: coverage.notIndexed,
4298
+ unknown: coverage.unknown,
4299
+ verifiedByInspection: coverage.verifiedByInspection,
4300
+ derivedFromImpressions: coverage.derivedFromImpressions
4301
+ };
4302
+ }
4303
+
4304
+ // src/gsc-sync.ts
4230
4305
  var log2 = createLogger("GscSync");
4231
4306
  function formatDate(d) {
4232
4307
  return d.toISOString().split("T")[0];
@@ -4238,13 +4313,13 @@ function daysAgo(n) {
4238
4313
  }
4239
4314
  async function executeGscSync(db, runId, projectId, opts) {
4240
4315
  const now = (/* @__PURE__ */ new Date()).toISOString();
4241
- db.update(runs).set({ status: "running", startedAt: now }).where(eq3(runs.id, runId)).run();
4316
+ db.update(runs).set({ status: "running", startedAt: now }).where(eq4(runs.id, runId)).run();
4242
4317
  try {
4243
4318
  const { clientId: googleClientId, clientSecret: googleClientSecret } = getGoogleAuthConfig(opts.config);
4244
4319
  if (!googleClientId || !googleClientSecret) {
4245
4320
  throw new Error("Google OAuth is not configured in the local Canonry config");
4246
4321
  }
4247
- const project = db.select().from(projects).where(eq3(projects.id, projectId)).get();
4322
+ const project = db.select().from(projects).where(eq4(projects.id, projectId)).get();
4248
4323
  if (!project) {
4249
4324
  throw new Error(`Project not found: ${projectId}`);
4250
4325
  }
@@ -4279,8 +4354,8 @@ async function executeGscSync(db, runId, projectId, opts) {
4279
4354
  });
4280
4355
  log2.info("fetch.complete", { runId, projectId, rowCount: rows.length });
4281
4356
  db.delete(gscSearchData).where(
4282
- and3(
4283
- eq3(gscSearchData.projectId, projectId),
4357
+ and4(
4358
+ eq4(gscSearchData.projectId, projectId),
4284
4359
  sql3`${gscSearchData.date} >= ${startDate}`,
4285
4360
  sql3`${gscSearchData.date} <= ${endDate}`
4286
4361
  )
@@ -4292,7 +4367,7 @@ async function executeGscSync(db, runId, projectId, opts) {
4292
4367
  for (const row of batch) {
4293
4368
  const [query, page, country, device, date] = row.keys;
4294
4369
  db.insert(gscSearchData).values({
4295
- id: crypto5.randomUUID(),
4370
+ id: crypto6.randomUUID(),
4296
4371
  projectId,
4297
4372
  syncRunId: runId,
4298
4373
  date: date ?? "",
@@ -4314,8 +4389,8 @@ async function executeGscSync(db, runId, projectId, opts) {
4314
4389
  dimensions: ["date"]
4315
4390
  });
4316
4391
  db.delete(gscDailyTotals).where(
4317
- and3(
4318
- eq3(gscDailyTotals.projectId, projectId),
4392
+ and4(
4393
+ eq4(gscDailyTotals.projectId, projectId),
4319
4394
  sql3`${gscDailyTotals.date} >= ${startDate}`,
4320
4395
  sql3`${gscDailyTotals.date} <= ${endDate}`
4321
4396
  )
@@ -4324,7 +4399,7 @@ async function executeGscSync(db, runId, projectId, opts) {
4324
4399
  for (const row of totalRows) {
4325
4400
  const [date] = row.keys;
4326
4401
  db.insert(gscDailyTotals).values({
4327
- id: crypto5.randomUUID(),
4402
+ id: crypto6.randomUUID(),
4328
4403
  projectId,
4329
4404
  date: date ?? "",
4330
4405
  clicks: row.clicks,
@@ -4340,8 +4415,8 @@ async function executeGscSync(db, runId, projectId, opts) {
4340
4415
  dimensions: ["date", "query"]
4341
4416
  });
4342
4417
  db.delete(gscQueryDailyTotals).where(
4343
- and3(
4344
- eq3(gscQueryDailyTotals.projectId, projectId),
4418
+ and4(
4419
+ eq4(gscQueryDailyTotals.projectId, projectId),
4345
4420
  sql3`${gscQueryDailyTotals.date} >= ${startDate}`,
4346
4421
  sql3`${gscQueryDailyTotals.date} <= ${endDate}`
4347
4422
  )
@@ -4351,7 +4426,7 @@ async function executeGscSync(db, runId, projectId, opts) {
4351
4426
  const [date, query] = row.keys;
4352
4427
  if (!date || !query) continue;
4353
4428
  db.insert(gscQueryDailyTotals).values({
4354
- id: crypto5.randomUUID(),
4429
+ id: crypto6.randomUUID(),
4355
4430
  projectId,
4356
4431
  date,
4357
4432
  query,
@@ -4364,50 +4439,20 @@ async function executeGscSync(db, runId, projectId, opts) {
4364
4439
  }).run();
4365
4440
  }
4366
4441
  log2.info("query-totals.complete", { runId, projectId, rowCount: queryTotalRows.length });
4367
- const allInspections = db.select().from(gscUrlInspections).where(eq3(gscUrlInspections.projectId, projectId)).all();
4368
- const latestByUrl = /* @__PURE__ */ new Map();
4369
- for (const row of allInspections) {
4370
- const existing = latestByUrl.get(row.url);
4371
- if (!existing || row.inspectedAt > existing.inspectedAt) {
4372
- latestByUrl.set(row.url, row);
4373
- }
4374
- }
4375
- const coverage = deriveIndexCoverage({
4376
- pages: rows.map((row) => ({ page: row.keys[1] ?? "", impressions: row.impressions })),
4377
- inspections: [...latestByUrl.values()].map((row) => ({
4378
- url: row.url,
4379
- indexingState: row.indexingState,
4380
- coverageState: row.coverageState
4381
- }))
4382
- });
4383
- const snapshotDate = formatDate(/* @__PURE__ */ new Date());
4384
- db.delete(gscCoverageSnapshots).where(and3(eq3(gscCoverageSnapshots.projectId, projectId), eq3(gscCoverageSnapshots.date, snapshotDate))).run();
4385
- db.insert(gscCoverageSnapshots).values({
4386
- id: crypto5.randomUUID(),
4387
- projectId,
4388
- syncRunId: runId,
4389
- date: snapshotDate,
4390
- indexed: coverage.indexed,
4391
- notIndexed: coverage.notIndexed,
4392
- unknownPages: coverage.unknown,
4393
- verifiedByInspection: coverage.verifiedByInspection,
4394
- derivedFromImpressions: coverage.derivedFromImpressions,
4395
- reasonBreakdown: coverage.reasonBreakdown,
4396
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
4397
- }).run();
4398
- db.update(runs).set({ status: "completed", finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq3(runs.id, runId)).run();
4442
+ const coverage = writeCoverageSnapshot(db, projectId, runId);
4443
+ db.update(runs).set({ status: "completed", finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq4(runs.id, runId)).run();
4399
4444
  log2.info("sync.completed", { runId, projectId, searchDataRows: rows.length, indexed: coverage.indexed, notIndexed: coverage.notIndexed, unknown: coverage.unknown, verifiedByInspection: coverage.verifiedByInspection });
4400
4445
  } catch (err) {
4401
4446
  const errorMsg = err instanceof Error ? err.message : String(err);
4402
- db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq3(runs.id, runId)).run();
4447
+ db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq4(runs.id, runId)).run();
4403
4448
  log2.error("sync.failed", { runId, projectId, error: errorMsg });
4404
4449
  throw err;
4405
4450
  }
4406
4451
  }
4407
4452
 
4408
4453
  // src/gbp-sync.ts
4409
- import crypto6 from "crypto";
4410
- import { eq as eq4, and as and4, desc, inArray as inArray2, lt } from "drizzle-orm";
4454
+ import crypto7 from "crypto";
4455
+ import { eq as eq5, and as and5, desc as desc2, inArray as inArray2, lt } from "drizzle-orm";
4411
4456
  var MS_PER_DAY = 864e5;
4412
4457
  var log3 = createLogger("GbpSync");
4413
4458
  var LOCATION_CONCURRENCY = 4;
@@ -4428,13 +4473,13 @@ function monthMinus(n) {
4428
4473
  }
4429
4474
  async function executeGbpSync(db, runId, projectId, opts) {
4430
4475
  const now = (/* @__PURE__ */ new Date()).toISOString();
4431
- db.update(runs).set({ status: "running", startedAt: now }).where(eq4(runs.id, runId)).run();
4476
+ db.update(runs).set({ status: "running", startedAt: now }).where(eq5(runs.id, runId)).run();
4432
4477
  try {
4433
4478
  const { clientId, clientSecret } = getGoogleAuthConfig(opts.config);
4434
4479
  if (!clientId || !clientSecret) {
4435
4480
  throw new Error("Google OAuth is not configured in the local Canonry config");
4436
4481
  }
4437
- const project = db.select().from(projects).where(eq4(projects.id, projectId)).get();
4482
+ const project = db.select().from(projects).where(eq5(projects.id, projectId)).get();
4438
4483
  if (!project) throw new Error(`Project not found: ${projectId}`);
4439
4484
  const conn = getGoogleConnection(opts.config, project.canonicalDomain, "gbp");
4440
4485
  if (!conn || !conn.refreshToken) {
@@ -4452,7 +4497,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
4452
4497
  });
4453
4498
  saveConfigPatch(opts.config);
4454
4499
  }
4455
- let locationRows = db.select().from(gbpLocations).where(and4(eq4(gbpLocations.projectId, projectId), eq4(gbpLocations.selected, true))).all();
4500
+ let locationRows = db.select().from(gbpLocations).where(and5(eq5(gbpLocations.projectId, projectId), eq5(gbpLocations.selected, true))).all();
4456
4501
  if (opts.locationNames?.length) {
4457
4502
  const wanted = new Set(opts.locationNames);
4458
4503
  locationRows = locationRows.filter((l) => wanted.has(l.locationName));
@@ -4502,15 +4547,15 @@ async function executeGbpSync(db, runId, projectId, opts) {
4502
4547
  })))
4503
4548
  ]);
4504
4549
  const lodgingHash = lodging ? hashLodging(lodging) : null;
4505
- const latestLodging = lodging ? db.select().from(gbpLodgingSnapshots).where(and4(eq4(gbpLodgingSnapshots.projectId, projectId), eq4(gbpLodgingSnapshots.locationName, loc.locationName))).orderBy(desc(gbpLodgingSnapshots.syncedAt)).limit(1).get() : void 0;
4550
+ const latestLodging = lodging ? db.select().from(gbpLodgingSnapshots).where(and5(eq5(gbpLodgingSnapshots.projectId, projectId), eq5(gbpLodgingSnapshots.locationName, loc.locationName))).orderBy(desc2(gbpLodgingSnapshots.syncedAt)).limit(1).get() : void 0;
4506
4551
  const lodgingChanged = lodging !== null && latestLodging?.contentHash !== lodgingHash;
4507
4552
  const attributesHash = hashAttributes(attributes);
4508
- const latestAttributes = db.select().from(gbpAttributesSnapshots).where(and4(eq4(gbpAttributesSnapshots.projectId, projectId), eq4(gbpAttributesSnapshots.locationName, loc.locationName))).orderBy(desc(gbpAttributesSnapshots.syncedAt)).limit(1).get();
4553
+ const latestAttributes = db.select().from(gbpAttributesSnapshots).where(and5(eq5(gbpAttributesSnapshots.projectId, projectId), eq5(gbpAttributesSnapshots.locationName, loc.locationName))).orderBy(desc2(gbpAttributesSnapshots.syncedAt)).limit(1).get();
4509
4554
  const attributesChanged = latestAttributes?.contentHash !== attributesHash;
4510
4555
  let placeToWrite = null;
4511
4556
  let placeToTouch = null;
4512
4557
  if (placesTier !== "off" && placesApiKey && lodging !== null && loc.placeId) {
4513
- const latestPlace = db.select().from(gbpPlaceDetails).where(and4(eq4(gbpPlaceDetails.projectId, projectId), eq4(gbpPlaceDetails.locationName, loc.locationName))).orderBy(desc(gbpPlaceDetails.syncedAt)).limit(1).get();
4558
+ const latestPlace = db.select().from(gbpPlaceDetails).where(and5(eq5(gbpPlaceDetails.projectId, projectId), eq5(gbpPlaceDetails.locationName, loc.locationName))).orderBy(desc2(gbpPlaceDetails.syncedAt)).limit(1).get();
4514
4559
  const ageDays = latestPlace ? (Date.now() - new Date(latestPlace.syncedAt).getTime()) / MS_PER_DAY : Infinity;
4515
4560
  if (ageDays >= placesRefreshDays) {
4516
4561
  try {
@@ -4528,10 +4573,10 @@ async function executeGbpSync(db, runId, projectId, opts) {
4528
4573
  }
4529
4574
  const insertNow = (/* @__PURE__ */ new Date()).toISOString();
4530
4575
  db.transaction((tx) => {
4531
- tx.delete(gbpDailyMetrics).where(and4(eq4(gbpDailyMetrics.projectId, projectId), eq4(gbpDailyMetrics.locationName, loc.locationName))).run();
4576
+ tx.delete(gbpDailyMetrics).where(and5(eq5(gbpDailyMetrics.projectId, projectId), eq5(gbpDailyMetrics.locationName, loc.locationName))).run();
4532
4577
  for (const row of metricRows) {
4533
4578
  tx.insert(gbpDailyMetrics).values({
4534
- id: crypto6.randomUUID(),
4579
+ id: crypto7.randomUUID(),
4535
4580
  projectId,
4536
4581
  locationName: loc.locationName,
4537
4582
  date: row.date,
@@ -4540,10 +4585,10 @@ async function executeGbpSync(db, runId, projectId, opts) {
4540
4585
  syncRunId: runId
4541
4586
  }).run();
4542
4587
  }
4543
- tx.delete(gbpKeywordImpressions).where(and4(eq4(gbpKeywordImpressions.projectId, projectId), eq4(gbpKeywordImpressions.locationName, loc.locationName))).run();
4588
+ tx.delete(gbpKeywordImpressions).where(and5(eq5(gbpKeywordImpressions.projectId, projectId), eq5(gbpKeywordImpressions.locationName, loc.locationName))).run();
4544
4589
  for (const row of keywordRows) {
4545
4590
  tx.insert(gbpKeywordImpressions).values({
4546
- id: crypto6.randomUUID(),
4591
+ id: crypto7.randomUUID(),
4547
4592
  projectId,
4548
4593
  locationName: loc.locationName,
4549
4594
  periodStart: monthKey(keywordsStart),
@@ -4556,15 +4601,15 @@ async function executeGbpSync(db, runId, projectId, opts) {
4556
4601
  }
4557
4602
  const fetchedMonths = monthlyKeywordResults.map((r) => r.month);
4558
4603
  if (fetchedMonths.length > 0) {
4559
- tx.delete(gbpKeywordMonthly).where(and4(
4560
- eq4(gbpKeywordMonthly.projectId, projectId),
4561
- eq4(gbpKeywordMonthly.locationName, loc.locationName),
4604
+ tx.delete(gbpKeywordMonthly).where(and5(
4605
+ eq5(gbpKeywordMonthly.projectId, projectId),
4606
+ eq5(gbpKeywordMonthly.locationName, loc.locationName),
4562
4607
  inArray2(gbpKeywordMonthly.month, fetchedMonths)
4563
4608
  )).run();
4564
4609
  for (const { month, rows } of monthlyKeywordResults) {
4565
4610
  for (const row of rows) {
4566
4611
  tx.insert(gbpKeywordMonthly).values({
4567
- id: crypto6.randomUUID(),
4612
+ id: crypto7.randomUUID(),
4568
4613
  projectId,
4569
4614
  locationName: loc.locationName,
4570
4615
  month,
@@ -4576,16 +4621,16 @@ async function executeGbpSync(db, runId, projectId, opts) {
4576
4621
  }).run();
4577
4622
  }
4578
4623
  }
4579
- tx.delete(gbpKeywordMonthly).where(and4(
4580
- eq4(gbpKeywordMonthly.projectId, projectId),
4581
- eq4(gbpKeywordMonthly.locationName, loc.locationName),
4624
+ tx.delete(gbpKeywordMonthly).where(and5(
4625
+ eq5(gbpKeywordMonthly.projectId, projectId),
4626
+ eq5(gbpKeywordMonthly.locationName, loc.locationName),
4582
4627
  lt(gbpKeywordMonthly.month, keywordRetentionCutoff)
4583
4628
  )).run();
4584
4629
  }
4585
- tx.delete(gbpPlaceActions).where(and4(eq4(gbpPlaceActions.projectId, projectId), eq4(gbpPlaceActions.locationName, loc.locationName))).run();
4630
+ tx.delete(gbpPlaceActions).where(and5(eq5(gbpPlaceActions.projectId, projectId), eq5(gbpPlaceActions.locationName, loc.locationName))).run();
4586
4631
  for (const row of placeActionRows) {
4587
4632
  tx.insert(gbpPlaceActions).values({
4588
- id: crypto6.randomUUID(),
4633
+ id: crypto7.randomUUID(),
4589
4634
  projectId,
4590
4635
  locationName: loc.locationName,
4591
4636
  placeActionLinkName: row.placeActionLinkName,
@@ -4598,7 +4643,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
4598
4643
  }
4599
4644
  if (lodging !== null && lodgingChanged) {
4600
4645
  tx.insert(gbpLodgingSnapshots).values({
4601
- id: crypto6.randomUUID(),
4646
+ id: crypto7.randomUUID(),
4602
4647
  projectId,
4603
4648
  locationName: loc.locationName,
4604
4649
  contentHash: lodgingHash,
@@ -4608,11 +4653,11 @@ async function executeGbpSync(db, runId, projectId, opts) {
4608
4653
  syncRunId: runId
4609
4654
  }).run();
4610
4655
  } else if (lodging !== null && latestLodging) {
4611
- tx.update(gbpLodgingSnapshots).set({ syncedAt: insertNow, syncRunId: runId }).where(eq4(gbpLodgingSnapshots.id, latestLodging.id)).run();
4656
+ tx.update(gbpLodgingSnapshots).set({ syncedAt: insertNow, syncRunId: runId }).where(eq5(gbpLodgingSnapshots.id, latestLodging.id)).run();
4612
4657
  }
4613
4658
  if (attributesChanged) {
4614
4659
  tx.insert(gbpAttributesSnapshots).values({
4615
- id: crypto6.randomUUID(),
4660
+ id: crypto7.randomUUID(),
4616
4661
  projectId,
4617
4662
  locationName: loc.locationName,
4618
4663
  contentHash: attributesHash,
@@ -4622,11 +4667,11 @@ async function executeGbpSync(db, runId, projectId, opts) {
4622
4667
  syncRunId: runId
4623
4668
  }).run();
4624
4669
  } else if (latestAttributes) {
4625
- tx.update(gbpAttributesSnapshots).set({ syncedAt: insertNow, syncRunId: runId }).where(eq4(gbpAttributesSnapshots.id, latestAttributes.id)).run();
4670
+ tx.update(gbpAttributesSnapshots).set({ syncedAt: insertNow, syncRunId: runId }).where(eq5(gbpAttributesSnapshots.id, latestAttributes.id)).run();
4626
4671
  }
4627
4672
  if (placeToWrite) {
4628
4673
  tx.insert(gbpPlaceDetails).values({
4629
- id: crypto6.randomUUID(),
4674
+ id: crypto7.randomUUID(),
4630
4675
  projectId,
4631
4676
  locationName: loc.locationName,
4632
4677
  placeId: placeToWrite.placeId,
@@ -4637,9 +4682,9 @@ async function executeGbpSync(db, runId, projectId, opts) {
4637
4682
  syncRunId: runId
4638
4683
  }).run();
4639
4684
  } else if (placeToTouch) {
4640
- tx.update(gbpPlaceDetails).set({ syncedAt: insertNow, syncRunId: runId }).where(eq4(gbpPlaceDetails.id, placeToTouch)).run();
4685
+ tx.update(gbpPlaceDetails).set({ syncedAt: insertNow, syncRunId: runId }).where(eq5(gbpPlaceDetails.id, placeToTouch)).run();
4641
4686
  }
4642
- tx.update(gbpLocations).set({ syncedAt: insertNow, updatedAt: insertNow }).where(eq4(gbpLocations.id, loc.id)).run();
4687
+ tx.update(gbpLocations).set({ syncedAt: insertNow, updatedAt: insertNow }).where(eq5(gbpLocations.id, loc.id)).run();
4643
4688
  });
4644
4689
  okCount++;
4645
4690
  } catch (err) {
@@ -4650,24 +4695,24 @@ async function executeGbpSync(db, runId, projectId, opts) {
4650
4695
  }
4651
4696
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
4652
4697
  if (errors.size === 0) {
4653
- db.update(runs).set({ status: "completed", finishedAt }).where(eq4(runs.id, runId)).run();
4698
+ db.update(runs).set({ status: "completed", finishedAt }).where(eq5(runs.id, runId)).run();
4654
4699
  } else if (okCount > 0) {
4655
4700
  db.update(runs).set({
4656
4701
  status: "partial",
4657
4702
  error: serializeRunError(buildRunErrorFromMessages(errors)),
4658
4703
  finishedAt
4659
- }).where(eq4(runs.id, runId)).run();
4704
+ }).where(eq5(runs.id, runId)).run();
4660
4705
  } else {
4661
4706
  db.update(runs).set({
4662
4707
  status: "failed",
4663
4708
  error: serializeRunError(buildRunErrorFromMessages(errors)),
4664
4709
  finishedAt
4665
- }).where(eq4(runs.id, runId)).run();
4710
+ }).where(eq5(runs.id, runId)).run();
4666
4711
  }
4667
4712
  log3.info("sync.done", { runId, projectId, ok: okCount, failed: errors.size });
4668
4713
  } catch (err) {
4669
4714
  const errorMsg = err instanceof Error ? err.message : String(err);
4670
- db.update(runs).set({ status: "failed", error: serializeRunError({ message: errorMsg }), finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq4(runs.id, runId)).run();
4715
+ db.update(runs).set({ status: "failed", error: serializeRunError({ message: errorMsg }), finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq5(runs.id, runId)).run();
4671
4716
  log3.error("sync.failed", { runId, projectId, error: errorMsg });
4672
4717
  throw err;
4673
4718
  }
@@ -4705,7 +4750,7 @@ async function refreshSelectedLocationProfiles(db, projectId, accessToken, locat
4705
4750
  ...profile,
4706
4751
  updatedAt
4707
4752
  };
4708
- db.update(gbpLocations).set(update).where(and4(eq4(gbpLocations.projectId, projectId), eq4(gbpLocations.id, row.id))).run();
4753
+ db.update(gbpLocations).set(update).where(and5(eq5(gbpLocations.projectId, projectId), eq5(gbpLocations.id, row.id))).run();
4709
4754
  refreshedRows.set(row.id, { ...row, ...update });
4710
4755
  }
4711
4756
  }
@@ -4713,8 +4758,8 @@ async function refreshSelectedLocationProfiles(db, projectId, accessToken, locat
4713
4758
  }
4714
4759
 
4715
4760
  // src/ads-sync.ts
4716
- import crypto7 from "crypto";
4717
- import { eq as eq5 } from "drizzle-orm";
4761
+ import crypto8 from "crypto";
4762
+ import { eq as eq6 } from "drizzle-orm";
4718
4763
 
4719
4764
  // ../integration-openai-ads/src/constants.ts
4720
4765
  var OPENAI_ADS_API_BASE = "https://api.ads.openai.com/v1";
@@ -5322,11 +5367,11 @@ function toDailyUpserts(level, entityId, read) {
5322
5367
  }
5323
5368
  async function executeAdsSync(db, runId, projectId, opts) {
5324
5369
  const now = (/* @__PURE__ */ new Date()).toISOString();
5325
- db.update(runs).set({ status: "running", startedAt: now }).where(eq5(runs.id, runId)).run();
5370
+ db.update(runs).set({ status: "running", startedAt: now }).where(eq6(runs.id, runId)).run();
5326
5371
  try {
5327
- const project = db.select().from(projects).where(eq5(projects.id, projectId)).get();
5372
+ const project = db.select().from(projects).where(eq6(projects.id, projectId)).get();
5328
5373
  if (!project) throw new Error(`Project not found: ${projectId}`);
5329
- const connRow = db.select().from(adsConnections).where(eq5(adsConnections.projectId, projectId)).get();
5374
+ const connRow = db.select().from(adsConnections).where(eq6(adsConnections.projectId, projectId)).get();
5330
5375
  if (!connRow) {
5331
5376
  throw new Error('No ads connection found for this project. Run "canonry ads connect" first.');
5332
5377
  }
@@ -5386,7 +5431,7 @@ async function executeAdsSync(db, runId, projectId, opts) {
5386
5431
  );
5387
5432
  const insertNow = (/* @__PURE__ */ new Date()).toISOString();
5388
5433
  db.transaction((tx) => {
5389
- tx.delete(adsCampaigns).where(eq5(adsCampaigns.projectId, projectId)).run();
5434
+ tx.delete(adsCampaigns).where(eq6(adsCampaigns.projectId, projectId)).run();
5390
5435
  for (const campaign of syncedCampaigns) {
5391
5436
  tx.insert(adsCampaigns).values({
5392
5437
  id: campaign.id,
@@ -5441,7 +5486,7 @@ async function executeAdsSync(db, runId, projectId, opts) {
5441
5486
  }
5442
5487
  for (const upsert of insightUpserts) {
5443
5488
  tx.insert(adsInsightsDaily).values({
5444
- id: crypto7.randomUUID(),
5489
+ id: crypto8.randomUUID(),
5445
5490
  projectId,
5446
5491
  level: upsert.level,
5447
5492
  entityId: upsert.entityId,
@@ -5480,36 +5525,36 @@ async function executeAdsSync(db, runId, projectId, opts) {
5480
5525
  conversionTrackingConfigured,
5481
5526
  lastSyncedAt: insertNow,
5482
5527
  updatedAt: insertNow
5483
- }).where(eq5(adsConnections.projectId, projectId)).run();
5528
+ }).where(eq6(adsConnections.projectId, projectId)).run();
5484
5529
  });
5485
5530
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
5486
5531
  if (errors.size === 0) {
5487
- db.update(runs).set({ status: "completed", finishedAt }).where(eq5(runs.id, runId)).run();
5532
+ db.update(runs).set({ status: "completed", finishedAt }).where(eq6(runs.id, runId)).run();
5488
5533
  } else if (syncedCampaigns.length > 0) {
5489
5534
  db.update(runs).set({
5490
5535
  status: "partial",
5491
5536
  error: serializeRunError(buildRunErrorFromMessages(errors)),
5492
5537
  finishedAt
5493
- }).where(eq5(runs.id, runId)).run();
5538
+ }).where(eq6(runs.id, runId)).run();
5494
5539
  } else {
5495
5540
  db.update(runs).set({
5496
5541
  status: "failed",
5497
5542
  error: serializeRunError(buildRunErrorFromMessages(errors)),
5498
5543
  finishedAt
5499
- }).where(eq5(runs.id, runId)).run();
5544
+ }).where(eq6(runs.id, runId)).run();
5500
5545
  }
5501
5546
  log4.info("sync.done", { runId, projectId, campaigns: syncedCampaigns.length, insightRows: insightUpserts.length, failed: errors.size });
5502
5547
  } catch (err) {
5503
5548
  const errorMsg = err instanceof Error ? err.message : String(err);
5504
- db.update(runs).set({ status: "failed", error: serializeRunError({ message: errorMsg }), finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq5(runs.id, runId)).run();
5549
+ db.update(runs).set({ status: "failed", error: serializeRunError({ message: errorMsg }), finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq6(runs.id, runId)).run();
5505
5550
  log4.error("sync.failed", { runId, projectId, error: errorMsg });
5506
5551
  throw err;
5507
5552
  }
5508
5553
  }
5509
5554
 
5510
5555
  // src/gsc-inspect-sitemap.ts
5511
- import crypto8 from "crypto";
5512
- import { eq as eq6, and as and5 } from "drizzle-orm";
5556
+ import crypto9 from "crypto";
5557
+ import { eq as eq7 } from "drizzle-orm";
5513
5558
 
5514
5559
  // src/sitemap-parser.ts
5515
5560
  var log5 = createLogger("SitemapParser");
@@ -5610,6 +5655,8 @@ async function parseSitemapRecursive(url, urls, visited, depth, isChild) {
5610
5655
  // src/gsc-inspect-paced.ts
5611
5656
  var INSPECT_BASE_DELAY_MS = 1e3;
5612
5657
  var INSPECT_MAX_CONCURRENCY = 5;
5658
+ var INSPECT_DAILY_QUOTA = 2e3;
5659
+ var INSPECT_SWEEP_MAX_URLS = 1500;
5613
5660
  var INSPECT_PACING_JITTER_MS = 250;
5614
5661
  var INSPECT_MAX_RETRIES = 3;
5615
5662
  var INSPECT_MAX_BACKOFF_MS = 3e4;
@@ -5692,13 +5739,13 @@ async function inspectUrlsPaced(urls, cb, deps = {}) {
5692
5739
  var log6 = createLogger("InspectSitemap");
5693
5740
  async function executeInspectSitemap(db, runId, projectId, opts) {
5694
5741
  const now = (/* @__PURE__ */ new Date()).toISOString();
5695
- db.update(runs).set({ status: "running", startedAt: now }).where(eq6(runs.id, runId)).run();
5742
+ db.update(runs).set({ status: "running", startedAt: now }).where(eq7(runs.id, runId)).run();
5696
5743
  try {
5697
5744
  const { clientId: googleClientId, clientSecret: googleClientSecret } = getGoogleAuthConfig(opts.config);
5698
5745
  if (!googleClientId || !googleClientSecret) {
5699
5746
  throw new Error("Google OAuth is not configured in the local Canonry config");
5700
5747
  }
5701
- const project = db.select().from(projects).where(eq6(projects.id, projectId)).get();
5748
+ const project = db.select().from(projects).where(eq7(projects.id, projectId)).get();
5702
5749
  if (!project) {
5703
5750
  throw new Error(`Project not found: ${projectId}`);
5704
5751
  }
@@ -5729,8 +5776,21 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
5729
5776
  if (urls.length === 0) {
5730
5777
  throw new Error("No URLs found in sitemap");
5731
5778
  }
5779
+ const skipped = Math.max(0, urls.length - INSPECT_SWEEP_MAX_URLS);
5780
+ const targetUrls = skipped > 0 ? urls.slice(0, INSPECT_SWEEP_MAX_URLS) : urls;
5781
+ if (skipped > 0) {
5782
+ log6.warn("sitemap.over-budget", {
5783
+ runId,
5784
+ projectId,
5785
+ sitemapUrls: urls.length,
5786
+ inspecting: targetUrls.length,
5787
+ skipped,
5788
+ dailyQuota: INSPECT_DAILY_QUOTA,
5789
+ note: `Sitemap has ${urls.length} pages; Google allows ${INSPECT_DAILY_QUOTA} URL inspections per property per day. Inspecting the first ${targetUrls.length}; ${skipped} pages will not have a verdict from this run.`
5790
+ });
5791
+ }
5732
5792
  const { inspected, errors, aborted, abortError } = await inspectUrlsPaced(
5733
- urls,
5793
+ targetUrls,
5734
5794
  {
5735
5795
  inspectOne: (pageUrl) => inspectUrl(accessToken, pageUrl, propertyId),
5736
5796
  onResult: (pageUrl, result, index) => {
@@ -5740,7 +5800,7 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
5740
5800
  const rich = ir.richResultsResult;
5741
5801
  const inspectedAt = (/* @__PURE__ */ new Date()).toISOString();
5742
5802
  db.insert(gscUrlInspections).values({
5743
- id: crypto8.randomUUID(),
5803
+ id: crypto9.randomUUID(),
5744
5804
  projectId,
5745
5805
  syncRunId: runId,
5746
5806
  url: pageUrl,
@@ -5776,52 +5836,24 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
5776
5836
  `URL inspection aborted after ${INSPECT_FAILFAST_THRESHOLD} consecutive rate/access failures (likely GSC URL Inspection quota exhaustion or property access loss). Last error: ${detail}`
5777
5837
  );
5778
5838
  }
5779
- const allInspections = db.select().from(gscUrlInspections).where(eq6(gscUrlInspections.projectId, projectId)).all();
5780
- const latestByUrl = /* @__PURE__ */ new Map();
5781
- for (const row of allInspections) {
5782
- const existing = latestByUrl.get(row.url);
5783
- if (!existing || row.inspectedAt > existing.inspectedAt) {
5784
- latestByUrl.set(row.url, row);
5785
- }
5786
- }
5787
- let snapIndexed = 0;
5788
- let snapNotIndexed = 0;
5789
- const reasonCounts = {};
5790
- for (const [, row] of latestByUrl) {
5791
- if (row.indexingState === "INDEXING_ALLOWED") {
5792
- snapIndexed++;
5793
- } else {
5794
- snapNotIndexed++;
5795
- const reason = row.coverageState ?? "Unknown";
5796
- reasonCounts[reason] = (reasonCounts[reason] ?? 0) + 1;
5797
- }
5798
- }
5799
- const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5800
- db.delete(gscCoverageSnapshots).where(and5(eq6(gscCoverageSnapshots.projectId, projectId), eq6(gscCoverageSnapshots.date, snapshotDate))).run();
5801
- db.insert(gscCoverageSnapshots).values({
5802
- id: crypto8.randomUUID(),
5803
- projectId,
5804
- syncRunId: runId,
5805
- date: snapshotDate,
5806
- indexed: snapIndexed,
5807
- notIndexed: snapNotIndexed,
5808
- reasonBreakdown: reasonCounts,
5809
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
5810
- }).run();
5811
- const status = errors > 0 && inspected > 0 ? "partial" : errors === urls.length ? "failed" : "completed";
5812
- db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq6(runs.id, runId)).run();
5839
+ const coverage = writeCoverageSnapshot(db, projectId, runId);
5840
+ const snapIndexed = coverage.indexed;
5841
+ const snapNotIndexed = coverage.notIndexed;
5842
+ const attempted = targetUrls.length;
5843
+ const status = skipped > 0 || errors > 0 && inspected > 0 ? "partial" : errors === attempted ? "failed" : "completed";
5844
+ db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq7(runs.id, runId)).run();
5813
5845
  log6.info("inspect.completed", { runId, projectId, inspected, errors, total: urls.length, indexed: snapIndexed, notIndexed: snapNotIndexed });
5814
5846
  } catch (err) {
5815
5847
  const errorMsg = err instanceof Error ? err.message : String(err);
5816
- db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq6(runs.id, runId)).run();
5848
+ db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq7(runs.id, runId)).run();
5817
5849
  log6.error("inspect.failed", { runId, projectId, error: errorMsg });
5818
5850
  throw err;
5819
5851
  }
5820
5852
  }
5821
5853
 
5822
5854
  // src/bing-inspect-sitemap.ts
5823
- import crypto9 from "crypto";
5824
- import { eq as eq7, desc as desc2 } from "drizzle-orm";
5855
+ import crypto10 from "crypto";
5856
+ import { eq as eq8, desc as desc3 } from "drizzle-orm";
5825
5857
  var log7 = createLogger("BingInspectSitemap");
5826
5858
  function parseBingDate(value) {
5827
5859
  if (!value) return null;
@@ -5839,9 +5871,9 @@ function isBlockingIssueType(issueType) {
5839
5871
  }
5840
5872
  async function executeBingInspectSitemap(db, runId, projectId, opts) {
5841
5873
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
5842
- db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq7(runs.id, runId)).run();
5874
+ db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq8(runs.id, runId)).run();
5843
5875
  try {
5844
- const project = db.select().from(projects).where(eq7(projects.id, projectId)).get();
5876
+ const project = db.select().from(projects).where(eq8(projects.id, projectId)).get();
5845
5877
  if (!project) {
5846
5878
  throw new Error(`Project not found: ${projectId}`);
5847
5879
  }
@@ -5859,7 +5891,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
5859
5891
  if (sitemapUrls.length === 0) {
5860
5892
  throw new Error("No URLs found in sitemap");
5861
5893
  }
5862
- const trackedRows = db.select({ url: bingUrlInspections.url }).from(bingUrlInspections).where(eq7(bingUrlInspections.projectId, projectId)).all();
5894
+ const trackedRows = db.select({ url: bingUrlInspections.url }).from(bingUrlInspections).where(eq8(bingUrlInspections.projectId, projectId)).all();
5863
5895
  const trackedUrls = new Set(trackedRows.map((r) => r.url));
5864
5896
  const discovered = sitemapUrls.filter((u) => !trackedUrls.has(u));
5865
5897
  log7.info("sitemap.diff", {
@@ -5908,7 +5940,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
5908
5940
  derivedInIndex = false;
5909
5941
  }
5910
5942
  db.insert(bingUrlInspections).values({
5911
- id: crypto9.randomUUID(),
5943
+ id: crypto10.randomUUID(),
5912
5944
  projectId,
5913
5945
  url: pageUrl,
5914
5946
  httpCode,
@@ -5942,7 +5974,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
5942
5974
  await new Promise((r) => setTimeout(r, 1e3));
5943
5975
  }
5944
5976
  }
5945
- const allInspections = db.select().from(bingUrlInspections).where(eq7(bingUrlInspections.projectId, projectId)).orderBy(desc2(bingUrlInspections.inspectedAt)).all();
5977
+ const allInspections = db.select().from(bingUrlInspections).where(eq8(bingUrlInspections.projectId, projectId)).orderBy(desc3(bingUrlInspections.inspectedAt)).all();
5946
5978
  const latestByUrl = /* @__PURE__ */ new Map();
5947
5979
  const definitiveByUrl = /* @__PURE__ */ new Map();
5948
5980
  for (const row of allInspections) {
@@ -5966,7 +5998,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
5966
5998
  const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5967
5999
  const snapNow = (/* @__PURE__ */ new Date()).toISOString();
5968
6000
  db.insert(bingCoverageSnapshots).values({
5969
- id: crypto9.randomUUID(),
6001
+ id: crypto10.randomUUID(),
5970
6002
  projectId,
5971
6003
  syncRunId: runId,
5972
6004
  date: snapshotDate,
@@ -5985,7 +6017,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
5985
6017
  }
5986
6018
  }).run();
5987
6019
  const status = errors === sitemapUrls.length ? RunStatuses.failed : errors > 0 ? RunStatuses.partial : RunStatuses.completed;
5988
- db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq7(runs.id, runId)).run();
6020
+ db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq8(runs.id, runId)).run();
5989
6021
  log7.info("inspect.completed", {
5990
6022
  runId,
5991
6023
  projectId,
@@ -5999,15 +6031,15 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
5999
6031
  });
6000
6032
  } catch (err) {
6001
6033
  const errorMsg = err instanceof Error ? err.message : String(err);
6002
- db.update(runs).set({ status: RunStatuses.failed, error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq7(runs.id, runId)).run();
6034
+ db.update(runs).set({ status: RunStatuses.failed, error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq8(runs.id, runId)).run();
6003
6035
  log7.error("inspect.failed", { runId, projectId, error: errorMsg });
6004
6036
  throw err;
6005
6037
  }
6006
6038
  }
6007
6039
 
6008
6040
  // src/coverage-refresh.ts
6009
- import crypto10 from "crypto";
6010
- import { and as and6, desc as desc3, eq as eq8, inArray as inArray3 } from "drizzle-orm";
6041
+ import crypto11 from "crypto";
6042
+ import { and as and6, desc as desc4, eq as eq9, inArray as inArray3 } from "drizzle-orm";
6011
6043
  var log8 = createLogger("CoverageRefresh");
6012
6044
  var COVERAGE_REFRESH_MIN_INTERVAL_MS = 60 * 60 * 1e3;
6013
6045
  var ACTIVE_OR_DONE_STATUSES = [
@@ -6018,7 +6050,7 @@ var ACTIVE_OR_DONE_STATUSES = [
6018
6050
  ];
6019
6051
  var defaultDeps = { executeInspectSitemap };
6020
6052
  async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps, nowMs = Date.now()) {
6021
- const project = db.select({ canonicalDomain: projects.canonicalDomain }).from(projects).where(eq8(projects.id, projectId)).get();
6053
+ const project = db.select({ canonicalDomain: projects.canonicalDomain }).from(projects).where(eq9(projects.id, projectId)).get();
6022
6054
  if (!project) return null;
6023
6055
  const { clientId, clientSecret } = getGoogleAuthConfig(config);
6024
6056
  if (!clientId || !clientSecret) return null;
@@ -6026,11 +6058,11 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
6026
6058
  if (!conn?.refreshToken || !conn.propertyId) return null;
6027
6059
  const recent = db.select({ createdAt: runs.createdAt }).from(runs).where(
6028
6060
  and6(
6029
- eq8(runs.projectId, projectId),
6030
- eq8(runs.kind, RunKinds["inspect-sitemap"]),
6061
+ eq9(runs.projectId, projectId),
6062
+ eq9(runs.kind, RunKinds["inspect-sitemap"]),
6031
6063
  inArray3(runs.status, ACTIVE_OR_DONE_STATUSES)
6032
6064
  )
6033
- ).orderBy(desc3(runs.createdAt)).limit(1).get();
6065
+ ).orderBy(desc4(runs.createdAt)).limit(1).get();
6034
6066
  if (recent) {
6035
6067
  const ageMs = nowMs - Date.parse(recent.createdAt);
6036
6068
  if (Number.isFinite(ageMs) && ageMs < COVERAGE_REFRESH_MIN_INTERVAL_MS) {
@@ -6038,7 +6070,7 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
6038
6070
  return null;
6039
6071
  }
6040
6072
  }
6041
- const runId = crypto10.randomUUID();
6073
+ const runId = crypto11.randomUUID();
6042
6074
  db.insert(runs).values({
6043
6075
  id: runId,
6044
6076
  projectId,
@@ -6061,9 +6093,9 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
6061
6093
  }
6062
6094
 
6063
6095
  // src/commoncrawl-sync.ts
6064
- import crypto11 from "crypto";
6096
+ import crypto12 from "crypto";
6065
6097
  import path4 from "path";
6066
- import { and as and7, eq as eq9, sql as sql4 } from "drizzle-orm";
6098
+ import { and as and7, eq as eq10, sql as sql4 } from "drizzle-orm";
6067
6099
  var log9 = createLogger("CommonCrawlSync");
6068
6100
  var INSERT_CHUNK_SIZE = 1e4;
6069
6101
  function defaultDeps2() {
@@ -6089,7 +6121,7 @@ async function executeReleaseSync(db, syncId, opts) {
6089
6121
  phaseDetail: "downloading vertices + edges",
6090
6122
  updatedAt: downloadStartedAt,
6091
6123
  error: null
6092
- }).where(eq9(ccReleaseSyncs.id, syncId)).run();
6124
+ }).where(eq10(ccReleaseSyncs.id, syncId)).run();
6093
6125
  const paths = ccReleasePaths(release);
6094
6126
  const releaseCacheDir = path4.join(deps.cacheDir, release);
6095
6127
  const vertexPath = path4.join(releaseCacheDir, paths.vertexFilename);
@@ -6112,7 +6144,7 @@ async function executeReleaseSync(db, syncId, opts) {
6112
6144
  vertexSha256: vertex.sha256,
6113
6145
  edgesSha256: edges.sha256,
6114
6146
  updatedAt: downloadFinishedAt
6115
- }).where(eq9(ccReleaseSyncs.id, syncId)).run();
6147
+ }).where(eq10(ccReleaseSyncs.id, syncId)).run();
6116
6148
  const allProjects = db.select().from(projects).all();
6117
6149
  const targets = Array.from(new Set(allProjects.map((p) => p.canonicalDomain)));
6118
6150
  let rows = [];
@@ -6128,15 +6160,15 @@ async function executeReleaseSync(db, syncId, opts) {
6128
6160
  }
6129
6161
  const queriedAt = deps.now().toISOString();
6130
6162
  db.transaction((tx) => {
6131
- tx.delete(backlinkDomains).where(eq9(backlinkDomains.releaseSyncId, syncId)).run();
6132
- tx.delete(backlinkSummaries).where(eq9(backlinkSummaries.releaseSyncId, syncId)).run();
6163
+ tx.delete(backlinkDomains).where(eq10(backlinkDomains.releaseSyncId, syncId)).run();
6164
+ tx.delete(backlinkSummaries).where(eq10(backlinkSummaries.releaseSyncId, syncId)).run();
6133
6165
  const expanded = [];
6134
6166
  for (const r of rows) {
6135
6167
  const projectIds = projectsByDomain.get(r.targetDomain);
6136
6168
  if (!projectIds) continue;
6137
6169
  for (const projectId of projectIds) {
6138
6170
  expanded.push({
6139
- id: crypto11.randomUUID(),
6171
+ id: crypto12.randomUUID(),
6140
6172
  projectId,
6141
6173
  releaseSyncId: syncId,
6142
6174
  release,
@@ -6156,7 +6188,7 @@ async function executeReleaseSync(db, syncId, opts) {
6156
6188
  const projectRows = rowsByProject.get(p.id) ?? [];
6157
6189
  const summary = computeSummary(projectRows);
6158
6190
  tx.insert(backlinkSummaries).values({
6159
- id: crypto11.randomUUID(),
6191
+ id: crypto12.randomUUID(),
6160
6192
  projectId: p.id,
6161
6193
  releaseSyncId: syncId,
6162
6194
  source: BacklinkSources.commoncrawl,
@@ -6189,7 +6221,7 @@ async function executeReleaseSync(db, syncId, opts) {
6189
6221
  domainsDiscovered: rows.length,
6190
6222
  updatedAt: finishedAt,
6191
6223
  error: null
6192
- }).where(eq9(ccReleaseSyncs.id, syncId)).run();
6224
+ }).where(eq10(ccReleaseSyncs.id, syncId)).run();
6193
6225
  log9.info("sync.completed", {
6194
6226
  syncId,
6195
6227
  release,
@@ -6219,7 +6251,7 @@ async function executeReleaseSync(db, syncId, opts) {
6219
6251
  error: errorMsg,
6220
6252
  phaseDetail: null,
6221
6253
  updatedAt: finishedAt
6222
- }).where(eq9(ccReleaseSyncs.id, syncId)).run();
6254
+ }).where(eq10(ccReleaseSyncs.id, syncId)).run();
6223
6255
  log9.error("sync.failed", { syncId, release, error: errorMsg });
6224
6256
  throw err;
6225
6257
  }
@@ -6253,9 +6285,9 @@ function computeSummary(rows) {
6253
6285
  }
6254
6286
 
6255
6287
  // src/backlink-extract.ts
6256
- import crypto12 from "crypto";
6288
+ import crypto13 from "crypto";
6257
6289
  import fs3 from "fs";
6258
- import { and as and8, desc as desc4, eq as eq10 } from "drizzle-orm";
6290
+ import { and as and8, desc as desc5, eq as eq11 } from "drizzle-orm";
6259
6291
  var log10 = createLogger("BacklinkExtract");
6260
6292
  function defaultDeps3() {
6261
6293
  return {
@@ -6267,13 +6299,13 @@ function defaultDeps3() {
6267
6299
  async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
6268
6300
  const deps = { ...defaultDeps3(), ...opts.deps };
6269
6301
  const startedAt = deps.now().toISOString();
6270
- db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq10(runs.id, runId)).run();
6302
+ db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq11(runs.id, runId)).run();
6271
6303
  try {
6272
- const project = db.select().from(projects).where(eq10(projects.id, projectId)).get();
6304
+ const project = db.select().from(projects).where(eq11(projects.id, projectId)).get();
6273
6305
  if (!project) {
6274
6306
  throw new Error(`Project not found: ${projectId}`);
6275
6307
  }
6276
- const sync = opts.release ? db.select().from(ccReleaseSyncs).where(eq10(ccReleaseSyncs.release, opts.release)).get() : db.select().from(ccReleaseSyncs).where(eq10(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).orderBy(desc4(ccReleaseSyncs.createdAt)).limit(1).get();
6308
+ const sync = opts.release ? db.select().from(ccReleaseSyncs).where(eq11(ccReleaseSyncs.release, opts.release)).get() : db.select().from(ccReleaseSyncs).where(eq11(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).orderBy(desc5(ccReleaseSyncs.createdAt)).limit(1).get();
6277
6309
  if (!sync) {
6278
6310
  throw new Error("No ready release sync available \u2014 run `canonry backlinks sync` first");
6279
6311
  }
@@ -6302,14 +6334,14 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
6302
6334
  db.transaction((tx) => {
6303
6335
  tx.delete(backlinkDomains).where(
6304
6336
  and8(
6305
- eq10(backlinkDomains.projectId, projectId),
6306
- eq10(backlinkDomains.source, BacklinkSources.commoncrawl),
6307
- eq10(backlinkDomains.release, release)
6337
+ eq11(backlinkDomains.projectId, projectId),
6338
+ eq11(backlinkDomains.source, BacklinkSources.commoncrawl),
6339
+ eq11(backlinkDomains.release, release)
6308
6340
  )
6309
6341
  ).run();
6310
6342
  if (rows.length > 0) {
6311
6343
  const values = rows.map((r) => ({
6312
- id: crypto12.randomUUID(),
6344
+ id: crypto13.randomUUID(),
6313
6345
  projectId,
6314
6346
  releaseSyncId: syncId,
6315
6347
  source: BacklinkSources.commoncrawl,
@@ -6323,7 +6355,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
6323
6355
  }
6324
6356
  const summary = computeSummary2(rows);
6325
6357
  tx.insert(backlinkSummaries).values({
6326
- id: crypto12.randomUUID(),
6358
+ id: crypto13.randomUUID(),
6327
6359
  projectId,
6328
6360
  releaseSyncId: syncId,
6329
6361
  source: BacklinkSources.commoncrawl,
@@ -6347,7 +6379,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
6347
6379
  }).run();
6348
6380
  });
6349
6381
  const finishedAt = deps.now().toISOString();
6350
- db.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq10(runs.id, runId)).run();
6382
+ db.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq11(runs.id, runId)).run();
6351
6383
  log10.info("extract.completed", { runId, projectId, release, rows: rows.length });
6352
6384
  } catch (err) {
6353
6385
  const errorMsg = err instanceof Error ? err.message : String(err);
@@ -6356,7 +6388,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
6356
6388
  status: RunStatuses.failed,
6357
6389
  error: errorMsg,
6358
6390
  finishedAt
6359
- }).where(eq10(runs.id, runId)).run();
6391
+ }).where(eq11(runs.id, runId)).run();
6360
6392
  log10.error("extract.failed", { runId, projectId, error: errorMsg });
6361
6393
  throw err;
6362
6394
  }
@@ -6366,8 +6398,8 @@ function computeSummary2(rows) {
6366
6398
  }
6367
6399
 
6368
6400
  // src/discovery-run.ts
6369
- import crypto13 from "crypto";
6370
- import { and as and9, eq as eq11 } from "drizzle-orm";
6401
+ import crypto14 from "crypto";
6402
+ import { and as and9, eq as eq12 } from "drizzle-orm";
6371
6403
  var log11 = createLogger("DiscoveryRun");
6372
6404
  var DEFAULT_SEED_COUNT = 30;
6373
6405
  var EMBED_RETRY_MAX_RETRIES = 3;
@@ -6409,11 +6441,11 @@ async function embedWithRetry(fn, opts = {}) {
6409
6441
  var QUERIES_PER_INTENT_BUCKET = 6;
6410
6442
  async function executeDiscoveryRun(opts) {
6411
6443
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
6412
- opts.db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq11(runs.id, opts.runId)).run();
6444
+ opts.db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq12(runs.id, opts.runId)).run();
6413
6445
  try {
6414
- const projectRow = opts.db.select().from(projects).where(eq11(projects.id, opts.projectId)).get();
6446
+ const projectRow = opts.db.select().from(projects).where(eq12(projects.id, opts.projectId)).get();
6415
6447
  if (!projectRow) throw new Error(`Project ${opts.projectId} not found`);
6416
- const projectCompetitors = opts.db.select({ domain: competitors.domain }).from(competitors).where(eq11(competitors.projectId, opts.projectId)).all().map((r) => r.domain.toLowerCase());
6448
+ const projectCompetitors = opts.db.select({ domain: competitors.domain }).from(competitors).where(eq12(competitors.projectId, opts.projectId)).all().map((r) => r.domain.toLowerCase());
6417
6449
  const canonicalDomains = effectiveDomains({
6418
6450
  canonicalDomain: projectRow.canonicalDomain,
6419
6451
  ownedDomains: projectRow.ownedDomains
@@ -6451,7 +6483,7 @@ async function executeDiscoveryRun(opts) {
6451
6483
  seedProvider: result.seedProvider,
6452
6484
  result
6453
6485
  });
6454
- opts.db.update(runs).set({ status: RunStatuses.completed, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq11(runs.id, opts.runId)).run();
6486
+ opts.db.update(runs).set({ status: RunStatuses.completed, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq12(runs.id, opts.runId)).run();
6455
6487
  log11.info("discovery.completed", {
6456
6488
  runId: opts.runId,
6457
6489
  sessionId: opts.sessionId,
@@ -6466,7 +6498,7 @@ async function executeDiscoveryRun(opts) {
6466
6498
  status: RunStatuses.failed,
6467
6499
  finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
6468
6500
  error: errorMsg
6469
- }).where(eq11(runs.id, opts.runId)).run();
6501
+ }).where(eq12(runs.id, opts.runId)).run();
6470
6502
  }
6471
6503
  }
6472
6504
  function buildDefaultDeps(registry) {
@@ -6716,12 +6748,12 @@ function writeDiscoveryInsight(db, input) {
6716
6748
  });
6717
6749
  db.transaction((tx) => {
6718
6750
  tx.update(insights).set({ dismissed: true }).where(and9(
6719
- eq11(insights.projectId, input.projectId),
6720
- eq11(insights.type, "discovery.basket-divergence"),
6721
- eq11(insights.dismissed, false)
6751
+ eq12(insights.projectId, input.projectId),
6752
+ eq12(insights.type, "discovery.basket-divergence"),
6753
+ eq12(insights.dismissed, false)
6722
6754
  )).run();
6723
6755
  tx.insert(insights).values({
6724
- id: crypto13.randomUUID(),
6756
+ id: crypto14.randomUUID(),
6725
6757
  projectId: input.projectId,
6726
6758
  runId: input.runId,
6727
6759
  type: "discovery.basket-divergence",
@@ -6757,8 +6789,8 @@ function buildDiscoveryInsightTitle(input) {
6757
6789
  }
6758
6790
 
6759
6791
  // src/execute-site-audit.ts
6760
- import crypto14 from "crypto";
6761
- import { eq as eq12 } from "drizzle-orm";
6792
+ import crypto15 from "crypto";
6793
+ import { eq as eq13 } from "drizzle-orm";
6762
6794
  import { runSitemapAudit } from "@ainyc/aeo-audit";
6763
6795
  var log12 = createLogger("SiteAudit");
6764
6796
  var SITE_AUDIT_DEFAULT_PAGE_LIMIT = 500;
@@ -6821,9 +6853,9 @@ function computeFactorAverages(pages) {
6821
6853
  }
6822
6854
  async function executeSiteAudit(db, runId, projectId, opts = {}) {
6823
6855
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
6824
- db.update(runs).set({ status: "running", startedAt }).where(eq12(runs.id, runId)).run();
6856
+ db.update(runs).set({ status: "running", startedAt }).where(eq13(runs.id, runId)).run();
6825
6857
  try {
6826
- const project = db.select().from(projects).where(eq12(projects.id, projectId)).get();
6858
+ const project = db.select().from(projects).where(eq13(projects.id, projectId)).get();
6827
6859
  if (!project) {
6828
6860
  throw new Error(`Project not found: ${projectId}`);
6829
6861
  }
@@ -6856,7 +6888,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
6856
6888
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
6857
6889
  db.transaction((tx) => {
6858
6890
  tx.insert(siteAuditSnapshots).values({
6859
- id: crypto14.randomUUID(),
6891
+ id: crypto15.randomUUID(),
6860
6892
  projectId,
6861
6893
  runId,
6862
6894
  sitemapUrl: report.sitemapUrl,
@@ -6885,7 +6917,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
6885
6917
  }).run();
6886
6918
  for (const page of report.pages) {
6887
6919
  tx.insert(siteAuditPages).values({
6888
- id: crypto14.randomUUID(),
6920
+ id: crypto15.randomUUID(),
6889
6921
  projectId,
6890
6922
  runId,
6891
6923
  url: page.url,
@@ -6896,7 +6928,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
6896
6928
  createdAt: finishedAt
6897
6929
  }).run();
6898
6930
  }
6899
- tx.update(runs).set({ status, finishedAt }).where(eq12(runs.id, runId)).run();
6931
+ tx.update(runs).set({ status, finishedAt }).where(eq13(runs.id, runId)).run();
6900
6932
  });
6901
6933
  log12.info("completed", {
6902
6934
  runId,
@@ -6908,14 +6940,14 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
6908
6940
  });
6909
6941
  } catch (err) {
6910
6942
  const errorMsg = err instanceof Error ? err.message : String(err);
6911
- db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq12(runs.id, runId)).run();
6943
+ db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq13(runs.id, runId)).run();
6912
6944
  log12.error("failed", { runId, projectId, error: errorMsg });
6913
6945
  throw err;
6914
6946
  }
6915
6947
  }
6916
6948
 
6917
6949
  // src/commands/backfill.ts
6918
- import { and as and10, eq as eq13, inArray as inArray4, isNull, sql as sql5 } from "drizzle-orm";
6950
+ import { and as and10, eq as eq14, inArray as inArray4, isNull, sql as sql5 } from "drizzle-orm";
6919
6951
  var SNAPSHOT_BATCH_SIZE = 500;
6920
6952
  async function backfillAnswerVisibilityCommand(opts) {
6921
6953
  const config = loadConfig();
@@ -6923,7 +6955,7 @@ async function backfillAnswerVisibilityCommand(opts) {
6923
6955
  migrate(db);
6924
6956
  const projectFilter = opts?.project?.trim();
6925
6957
  const isDryRun = opts?.dryRun === true;
6926
- const scopedProjects = projectFilter ? db.select().from(projects).where(eq13(projects.name, projectFilter)).all() : db.select().from(projects).all();
6958
+ const scopedProjects = projectFilter ? db.select().from(projects).where(eq14(projects.name, projectFilter)).all() : db.select().from(projects).all();
6927
6959
  let examined = 0;
6928
6960
  let updated = 0;
6929
6961
  let wouldUpdate = 0;
@@ -6932,9 +6964,9 @@ async function backfillAnswerVisibilityCommand(opts) {
6932
6964
  let providerErrors = 0;
6933
6965
  if (scopedProjects.length > 0) {
6934
6966
  const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and10(
6935
- eq13(runs.kind, RunKinds["answer-visibility"]),
6967
+ eq14(runs.kind, RunKinds["answer-visibility"]),
6936
6968
  inArray4(runs.projectId, scopedProjects.map((project) => project.id))
6937
- )).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq13(runs.kind, RunKinds["answer-visibility"])).all();
6969
+ )).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq14(runs.kind, RunKinds["answer-visibility"])).all();
6938
6970
  const runIdsByProject = /* @__PURE__ */ new Map();
6939
6971
  for (const run of runRows) {
6940
6972
  const existing = runIdsByProject.get(run.projectId);
@@ -6942,7 +6974,7 @@ async function backfillAnswerVisibilityCommand(opts) {
6942
6974
  else runIdsByProject.set(run.projectId, [run.id]);
6943
6975
  }
6944
6976
  for (const project of scopedProjects) {
6945
- const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq13(competitors.projectId, project.id)).all().map((row) => row.domain);
6977
+ const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq14(competitors.projectId, project.id)).all().map((row) => row.domain);
6946
6978
  const runIds = runIdsByProject.get(project.id) ?? [];
6947
6979
  if (runIds.length === 0) continue;
6948
6980
  const projectDomains = effectiveDomains({
@@ -7035,7 +7067,7 @@ async function backfillAnswerVisibilityCommand(opts) {
7035
7067
  } else {
7036
7068
  db.transaction((tx) => {
7037
7069
  for (const update of pendingUpdates) {
7038
- tx.update(querySnapshots).set(update.patch).where(eq13(querySnapshots.id, update.id)).run();
7070
+ tx.update(querySnapshots).set(update.patch).where(eq14(querySnapshots.id, update.id)).run();
7039
7071
  }
7040
7072
  });
7041
7073
  updated += pendingUpdates.length;
@@ -7084,7 +7116,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
7084
7116
  function backfillNormalizedPaths(db, opts) {
7085
7117
  const baseConditions = [];
7086
7118
  if (opts?.projectId) {
7087
- baseConditions.push(eq13(gaTrafficSnapshots.projectId, opts.projectId));
7119
+ baseConditions.push(eq14(gaTrafficSnapshots.projectId, opts.projectId));
7088
7120
  }
7089
7121
  const rows = db.select({
7090
7122
  id: gaTrafficSnapshots.id,
@@ -7105,7 +7137,7 @@ function backfillNormalizedPaths(db, opts) {
7105
7137
  unchanged++;
7106
7138
  continue;
7107
7139
  }
7108
- tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq13(gaTrafficSnapshots.id, row.id)).run();
7140
+ tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq14(gaTrafficSnapshots.id, row.id)).run();
7109
7141
  updated++;
7110
7142
  }
7111
7143
  });
@@ -7119,7 +7151,7 @@ async function backfillNormalizedPathsCommand(opts) {
7119
7151
  const projectFilter = opts?.project?.trim();
7120
7152
  let projectId;
7121
7153
  if (projectFilter) {
7122
- const project = db.select({ id: projects.id }).from(projects).where(eq13(projects.name, projectFilter)).get();
7154
+ const project = db.select({ id: projects.id }).from(projects).where(eq14(projects.name, projectFilter)).get();
7123
7155
  if (!project) {
7124
7156
  const result2 = {
7125
7157
  project: projectFilter,
@@ -7156,7 +7188,7 @@ async function backfillNormalizedPathsCommand(opts) {
7156
7188
  function backfillAiReferralPaths(db, opts) {
7157
7189
  const baseConditions = [];
7158
7190
  if (opts?.projectId) {
7159
- baseConditions.push(eq13(gaAiReferrals.projectId, opts.projectId));
7191
+ baseConditions.push(eq14(gaAiReferrals.projectId, opts.projectId));
7160
7192
  }
7161
7193
  const rows = db.select({
7162
7194
  id: gaAiReferrals.id,
@@ -7177,7 +7209,7 @@ function backfillAiReferralPaths(db, opts) {
7177
7209
  unchanged++;
7178
7210
  continue;
7179
7211
  }
7180
- tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(eq13(gaAiReferrals.id, row.id)).run();
7212
+ tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(eq14(gaAiReferrals.id, row.id)).run();
7181
7213
  updated++;
7182
7214
  }
7183
7215
  });
@@ -7191,7 +7223,7 @@ async function backfillAiReferralPathsCommand(opts) {
7191
7223
  const projectFilter = opts?.project?.trim();
7192
7224
  let projectId;
7193
7225
  if (projectFilter) {
7194
- const project = db.select({ id: projects.id }).from(projects).where(eq13(projects.name, projectFilter)).get();
7226
+ const project = db.select({ id: projects.id }).from(projects).where(eq14(projects.name, projectFilter)).get();
7195
7227
  if (!project) {
7196
7228
  const result2 = {
7197
7229
  project: projectFilter,
@@ -7227,10 +7259,10 @@ async function backfillAiReferralPathsCommand(opts) {
7227
7259
  }
7228
7260
  function backfillProjectAnswerMentions(db, projectId, opts) {
7229
7261
  const isDryRun = opts?.dryRun === true;
7230
- const project = db.select().from(projects).where(eq13(projects.id, projectId)).get();
7262
+ const project = db.select().from(projects).where(eq14(projects.id, projectId)).get();
7231
7263
  if (!project) return { examined: 0, updated: 0, mentioned: 0 };
7232
- const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq13(competitors.projectId, projectId)).all().map((row) => row.domain);
7233
- const runRows = db.select({ id: runs.id }).from(runs).where(and10(eq13(runs.kind, RunKinds["answer-visibility"]), eq13(runs.projectId, projectId))).all();
7264
+ const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq14(competitors.projectId, projectId)).all().map((row) => row.domain);
7265
+ const runRows = db.select({ id: runs.id }).from(runs).where(and10(eq14(runs.kind, RunKinds["answer-visibility"]), eq14(runs.projectId, projectId))).all();
7234
7266
  const runIds = runRows.map((r) => r.id);
7235
7267
  let examined = 0;
7236
7268
  let updated = 0;
@@ -7304,7 +7336,7 @@ function backfillProjectAnswerMentions(db, projectId, opts) {
7304
7336
  } else {
7305
7337
  db.transaction((tx) => {
7306
7338
  for (const update of pendingUpdates) {
7307
- tx.update(querySnapshots).set(update.patch).where(eq13(querySnapshots.id, update.id)).run();
7339
+ tx.update(querySnapshots).set(update.patch).where(eq14(querySnapshots.id, update.id)).run();
7308
7340
  }
7309
7341
  });
7310
7342
  updated += pendingUpdates.length;
@@ -7319,7 +7351,7 @@ async function backfillAnswerMentionsCommand(opts) {
7319
7351
  migrate(db);
7320
7352
  const projectFilter = opts?.project?.trim();
7321
7353
  const isDryRun = opts?.dryRun === true;
7322
- const scopedProjects = projectFilter ? db.select().from(projects).where(eq13(projects.name, projectFilter)).all() : db.select().from(projects).all();
7354
+ const scopedProjects = projectFilter ? db.select().from(projects).where(eq14(projects.name, projectFilter)).all() : db.select().from(projects).all();
7323
7355
  let examined = 0;
7324
7356
  let updated = 0;
7325
7357
  let wouldUpdate = 0;
@@ -7538,7 +7570,7 @@ async function backfillSnapshotAttributionCommand(opts) {
7538
7570
  const config = loadConfig();
7539
7571
  const db = createClient(config.database);
7540
7572
  migrate(db);
7541
- const project = db.select().from(projects).where(eq13(projects.name, opts.project)).get();
7573
+ const project = db.select().from(projects).where(eq14(projects.name, opts.project)).get();
7542
7574
  if (!project) {
7543
7575
  throw new Error(`Project "${opts.project}" not found`);
7544
7576
  }
@@ -7550,7 +7582,7 @@ async function backfillSnapshotAttributionCommand(opts) {
7550
7582
  `);
7551
7583
  }
7552
7584
  const events = db.select({ createdAt: auditLog.createdAt, action: auditLog.action, diff: auditLog.diff }).from(auditLog).where(and10(
7553
- eq13(auditLog.projectId, project.id),
7585
+ eq14(auditLog.projectId, project.id),
7554
7586
  inArray4(auditLog.action, ["keywords.appended", "keywords.deleted", "queries.appended", "queries.deleted", "queries.replaced"])
7555
7587
  )).orderBy(auditLog.createdAt).all();
7556
7588
  const history = replayQueryAuditLog(events);
@@ -7558,8 +7590,8 @@ async function backfillSnapshotAttributionCommand(opts) {
7558
7590
  runId: runs.id,
7559
7591
  createdAt: runs.createdAt,
7560
7592
  location: runs.location
7561
- }).from(runs).innerJoin(querySnapshots, eq13(querySnapshots.runId, runs.id)).where(and10(
7562
- eq13(runs.projectId, project.id),
7593
+ }).from(runs).innerJoin(querySnapshots, eq14(querySnapshots.runId, runs.id)).where(and10(
7594
+ eq14(runs.projectId, project.id),
7563
7595
  isNull(querySnapshots.queryId),
7564
7596
  isNull(querySnapshots.queryText)
7565
7597
  )).groupBy(runs.id).orderBy(runs.createdAt).all();
@@ -7582,7 +7614,7 @@ async function backfillSnapshotAttributionCommand(opts) {
7582
7614
  createdAt: querySnapshots.createdAt,
7583
7615
  answerText: querySnapshots.answerText
7584
7616
  }).from(querySnapshots).where(and10(
7585
- eq13(querySnapshots.runId, run.runId),
7617
+ eq14(querySnapshots.runId, run.runId),
7586
7618
  isNull(querySnapshots.queryId),
7587
7619
  isNull(querySnapshots.queryText)
7588
7620
  )).orderBy(querySnapshots.provider, querySnapshots.createdAt).all();
@@ -7648,7 +7680,7 @@ async function backfillSnapshotAttributionCommand(opts) {
7648
7680
  if (!isDryRun && updates.length > 0) {
7649
7681
  db.transaction((tx) => {
7650
7682
  for (const u of updates) {
7651
- tx.update(querySnapshots).set({ queryText: u.queryText }).where(eq13(querySnapshots.id, u.id)).run();
7683
+ tx.update(querySnapshots).set({ queryText: u.queryText }).where(eq14(querySnapshots.id, u.id)).run();
7652
7684
  }
7653
7685
  });
7654
7686
  }
@@ -7722,7 +7754,7 @@ async function backfillTrafficClassificationCommand(opts) {
7722
7754
  const projectFilter = opts?.project?.trim();
7723
7755
  const isDryRun = opts?.dryRun === true;
7724
7756
  const isJson = isMachineFormat(opts?.format);
7725
- const scopedProjects = projectFilter ? db.select().from(projects).where(eq13(projects.name, projectFilter)).all() : db.select().from(projects).all();
7757
+ const scopedProjects = projectFilter ? db.select().from(projects).where(eq14(projects.name, projectFilter)).all() : db.select().from(projects).all();
7726
7758
  if (scopedProjects.length === 0) {
7727
7759
  if (projectFilter && !isJson) {
7728
7760
  process.stderr.write(`No project named "${projectFilter}".
@@ -7748,7 +7780,7 @@ async function backfillTrafficClassificationCommand(opts) {
7748
7780
  byBot: {}
7749
7781
  };
7750
7782
  const unknownCountRow = db.select({ n: sql5`count(*)` }).from(rawEventSamples).where(and10(
7751
- eq13(rawEventSamples.eventType, "unknown"),
7783
+ eq14(rawEventSamples.eventType, "unknown"),
7752
7784
  inArray4(rawEventSamples.projectId, projectIds)
7753
7785
  )).get();
7754
7786
  result.unknownBefore = Number(unknownCountRow?.n ?? 0);
@@ -7761,7 +7793,7 @@ async function backfillTrafficClassificationCommand(opts) {
7761
7793
  pathNormalized: rawEventSamples.pathNormalized,
7762
7794
  status: rawEventSamples.status
7763
7795
  }).from(rawEventSamples).where(and10(
7764
- eq13(rawEventSamples.eventType, "unknown"),
7796
+ eq14(rawEventSamples.eventType, "unknown"),
7765
7797
  inArray4(rawEventSamples.projectId, projectIds)
7766
7798
  )).all();
7767
7799
  result.examined = unknownSamples.length;
@@ -7800,7 +7832,7 @@ async function backfillTrafficClassificationCommand(opts) {
7800
7832
  result.reclassified++;
7801
7833
  result.byBot[classified.botId] = (result.byBot[classified.botId] ?? 0) + 1;
7802
7834
  if (isDryRun) continue;
7803
- db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(eq13(rawEventSamples.id, snap.id)).run();
7835
+ db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(eq14(rawEventSamples.id, snap.id)).run();
7804
7836
  const tsHour = new Date(snap.ts);
7805
7837
  tsHour.setUTCMinutes(0, 0, 0);
7806
7838
  if (userFetch) {
@@ -7865,7 +7897,7 @@ async function backfillTrafficClassificationCommand(opts) {
7865
7897
  }
7866
7898
  if (!isDryRun) {
7867
7899
  const afterRow = db.select({ n: sql5`count(*)` }).from(rawEventSamples).where(and10(
7868
- eq13(rawEventSamples.eventType, "unknown"),
7900
+ eq14(rawEventSamples.eventType, "unknown"),
7869
7901
  inArray4(rawEventSamples.projectId, projectIds)
7870
7902
  )).get();
7871
7903
  result.unknownAfter = Number(afterRow?.n ?? 0);
@@ -7900,7 +7932,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
7900
7932
  }
7901
7933
 
7902
7934
  // src/commands/skills.ts
7903
- import crypto15 from "crypto";
7935
+ import crypto16 from "crypto";
7904
7936
  import fs4 from "fs";
7905
7937
  import os4 from "os";
7906
7938
  import path5 from "path";
@@ -7955,7 +7987,7 @@ function walkRelative(dir, prefix = "") {
7955
7987
  return out.sort();
7956
7988
  }
7957
7989
  function sha256File(filePath) {
7958
- return crypto15.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
7990
+ return crypto16.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
7959
7991
  }
7960
7992
  function readSkillManifest(skillDir) {
7961
7993
  try {
@@ -8297,9 +8329,9 @@ var ProviderRegistry = class {
8297
8329
  };
8298
8330
 
8299
8331
  // src/scheduler.ts
8300
- import crypto16 from "crypto";
8332
+ import crypto17 from "crypto";
8301
8333
  import cron from "node-cron";
8302
- import { and as and11, eq as eq14, inArray as inArray5, notExists, sql as sql6 } from "drizzle-orm";
8334
+ import { and as and11, eq as eq15, inArray as inArray5, notExists, sql as sql6 } from "drizzle-orm";
8303
8335
  var log13 = createLogger("Scheduler");
8304
8336
  var DEFAULT_HEALTH_CRON = "0 */6 * * *";
8305
8337
  function taskKey(projectId, kind) {
@@ -8332,15 +8364,15 @@ var Scheduler = class {
8332
8364
  ensureHealthSchedules() {
8333
8365
  const projectsWithoutHealth = this.db.select({ id: projects.id }).from(projects).where(notExists(
8334
8366
  this.db.select({ one: sql6`1` }).from(schedules).where(and11(
8335
- eq14(schedules.projectId, projects.id),
8336
- eq14(schedules.kind, SchedulableRunKinds.doctor)
8367
+ eq15(schedules.projectId, projects.id),
8368
+ eq15(schedules.kind, SchedulableRunKinds.doctor)
8337
8369
  ))
8338
8370
  )).all();
8339
8371
  if (projectsWithoutHealth.length === 0) return;
8340
8372
  const now = (/* @__PURE__ */ new Date()).toISOString();
8341
8373
  for (const project of projectsWithoutHealth) {
8342
8374
  this.db.insert(schedules).values({
8343
- id: crypto16.randomUUID(),
8375
+ id: crypto17.randomUUID(),
8344
8376
  projectId: project.id,
8345
8377
  kind: SchedulableRunKinds.doctor,
8346
8378
  cronExpr: DEFAULT_HEALTH_CRON,
@@ -8384,7 +8416,7 @@ var Scheduler = class {
8384
8416
  start() {
8385
8417
  this.ensureHealthSchedules();
8386
8418
  this.ensureQueryBaskets();
8387
- const allSchedules = this.db.select().from(schedules).where(eq14(schedules.enabled, true)).all();
8419
+ const allSchedules = this.db.select().from(schedules).where(eq15(schedules.enabled, true)).all();
8388
8420
  for (const schedule of allSchedules) {
8389
8421
  const missedRunAt = schedule.nextRunAt;
8390
8422
  this.registerCronTask(schedule);
@@ -8414,7 +8446,7 @@ var Scheduler = class {
8414
8446
  this.stopTask(key, existing, "Stopped");
8415
8447
  this.tasks.delete(key);
8416
8448
  }
8417
- const schedule = this.db.select().from(schedules).where(and11(eq14(schedules.projectId, projectId), eq14(schedules.kind, kind))).get();
8449
+ const schedule = this.db.select().from(schedules).where(and11(eq15(schedules.projectId, projectId), eq15(schedules.kind, kind))).get();
8418
8450
  if (schedule && schedule.enabled) {
8419
8451
  this.registerCronTask(schedule);
8420
8452
  }
@@ -8455,21 +8487,21 @@ var Scheduler = class {
8455
8487
  this.db.update(schedules).set({
8456
8488
  nextRunAt: nextRunFromCron(cronExpr, timezone),
8457
8489
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
8458
- }).where(eq14(schedules.id, scheduleId)).run();
8490
+ }).where(eq15(schedules.id, scheduleId)).run();
8459
8491
  const label = schedule.preset ?? cronExpr;
8460
8492
  log13.info("cron.registered", { projectId, kind, schedule: label, timezone });
8461
8493
  }
8462
8494
  triggerRun(scheduleId, projectId, kind) {
8463
8495
  try {
8464
8496
  const now = (/* @__PURE__ */ new Date()).toISOString();
8465
- const currentSchedule = this.db.select().from(schedules).where(eq14(schedules.id, scheduleId)).get();
8497
+ const currentSchedule = this.db.select().from(schedules).where(eq15(schedules.id, scheduleId)).get();
8466
8498
  if (!currentSchedule || !currentSchedule.enabled) {
8467
8499
  log13.warn("schedule.stale", { scheduleId, projectId, kind, msg: "schedule no longer exists or is disabled" });
8468
8500
  this.remove(projectId, kind);
8469
8501
  return;
8470
8502
  }
8471
8503
  const nextRunAt = nextRunFromCron(currentSchedule.cronExpr, currentSchedule.timezone);
8472
- const project = this.db.select().from(projects).where(eq14(projects.id, projectId)).get();
8504
+ const project = this.db.select().from(projects).where(eq15(projects.id, projectId)).get();
8473
8505
  if (!project) {
8474
8506
  log13.error("project.not-found", { projectId, kind, msg: "skipping scheduled run" });
8475
8507
  this.remove(projectId, kind);
@@ -8489,7 +8521,7 @@ var Scheduler = class {
8489
8521
  lastRunAt: now,
8490
8522
  nextRunAt,
8491
8523
  updatedAt: now
8492
- }).where(eq14(schedules.id, currentSchedule.id)).run();
8524
+ }).where(eq15(schedules.id, currentSchedule.id)).run();
8493
8525
  log13.info("traffic-sync.triggered", { projectName: project.name, sourceId });
8494
8526
  this.callbacks.onTrafficSyncRequested(project.name, sourceId);
8495
8527
  return;
@@ -8499,7 +8531,7 @@ var Scheduler = class {
8499
8531
  log13.warn("gbp-sync.no-callback", { scheduleId, projectId, msg: "host did not register onGbpSyncRequested" });
8500
8532
  return;
8501
8533
  }
8502
- const runId2 = crypto16.randomUUID();
8534
+ const runId2 = crypto17.randomUUID();
8503
8535
  this.db.insert(runs).values({
8504
8536
  id: runId2,
8505
8537
  projectId,
@@ -8512,7 +8544,7 @@ var Scheduler = class {
8512
8544
  lastRunAt: now,
8513
8545
  nextRunAt,
8514
8546
  updatedAt: now
8515
- }).where(eq14(schedules.id, currentSchedule.id)).run();
8547
+ }).where(eq15(schedules.id, currentSchedule.id)).run();
8516
8548
  log13.info("gbp-sync.triggered", { runId: runId2, projectName: project.name });
8517
8549
  this.callbacks.onGbpSyncRequested(runId2, projectId);
8518
8550
  return;
@@ -8523,16 +8555,16 @@ var Scheduler = class {
8523
8555
  return;
8524
8556
  }
8525
8557
  const activeAdsRun = this.db.select({ id: runs.id }).from(runs).where(and11(
8526
- eq14(runs.projectId, projectId),
8527
- eq14(runs.kind, RunKinds["ads-sync"]),
8558
+ eq15(runs.projectId, projectId),
8559
+ eq15(runs.kind, RunKinds["ads-sync"]),
8528
8560
  inArray5(runs.status, [RunStatuses.queued, RunStatuses.running])
8529
8561
  )).get();
8530
8562
  if (activeAdsRun) {
8531
8563
  log13.info("ads-sync.skipped-active", { projectName: project.name, activeRunId: activeAdsRun.id });
8532
- this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq14(schedules.id, currentSchedule.id)).run();
8564
+ this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq15(schedules.id, currentSchedule.id)).run();
8533
8565
  return;
8534
8566
  }
8535
- const runId2 = crypto16.randomUUID();
8567
+ const runId2 = crypto17.randomUUID();
8536
8568
  this.db.insert(runs).values({
8537
8569
  id: runId2,
8538
8570
  projectId,
@@ -8545,7 +8577,7 @@ var Scheduler = class {
8545
8577
  lastRunAt: now,
8546
8578
  nextRunAt,
8547
8579
  updatedAt: now
8548
- }).where(eq14(schedules.id, currentSchedule.id)).run();
8580
+ }).where(eq15(schedules.id, currentSchedule.id)).run();
8549
8581
  log13.info("ads-sync.triggered", { runId: runId2, projectName: project.name });
8550
8582
  this.callbacks.onAdsSyncRequested(runId2, projectId);
8551
8583
  return;
@@ -8559,7 +8591,7 @@ var Scheduler = class {
8559
8591
  lastRunAt: now,
8560
8592
  nextRunAt,
8561
8593
  updatedAt: now
8562
- }).where(eq14(schedules.id, currentSchedule.id)).run();
8594
+ }).where(eq15(schedules.id, currentSchedule.id)).run();
8563
8595
  log13.info("data-refresh.triggered", { projectName: project.name });
8564
8596
  this.callbacks.onDataRefreshRequested(project.name);
8565
8597
  return;
@@ -8573,7 +8605,7 @@ var Scheduler = class {
8573
8605
  lastRunAt: now,
8574
8606
  nextRunAt,
8575
8607
  updatedAt: now
8576
- }).where(eq14(schedules.id, currentSchedule.id)).run();
8608
+ }).where(eq15(schedules.id, currentSchedule.id)).run();
8577
8609
  log13.info("doctor.triggered", { projectName: project.name });
8578
8610
  this.callbacks.onDoctorRequested(project.name);
8579
8611
  return;
@@ -8587,7 +8619,7 @@ var Scheduler = class {
8587
8619
  lastRunAt: now,
8588
8620
  nextRunAt,
8589
8621
  updatedAt: now
8590
- }).where(eq14(schedules.id, currentSchedule.id)).run();
8622
+ }).where(eq15(schedules.id, currentSchedule.id)).run();
8591
8623
  log13.info("backlinks-sync.triggered", { projectName: project.name });
8592
8624
  this.callbacks.onBacklinksSyncRequested(project.name);
8593
8625
  return;
@@ -8598,16 +8630,16 @@ var Scheduler = class {
8598
8630
  return;
8599
8631
  }
8600
8632
  const active = this.db.select({ id: runs.id }).from(runs).where(and11(
8601
- eq14(runs.projectId, projectId),
8602
- eq14(runs.kind, RunKinds["site-audit"]),
8633
+ eq15(runs.projectId, projectId),
8634
+ eq15(runs.kind, RunKinds["site-audit"]),
8603
8635
  inArray5(runs.status, [RunStatuses.queued, RunStatuses.running])
8604
8636
  )).get();
8605
8637
  if (active) {
8606
8638
  log13.info("site-audit.skipped-active", { projectName: project.name, activeRunId: active.id });
8607
- this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq14(schedules.id, currentSchedule.id)).run();
8639
+ this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq15(schedules.id, currentSchedule.id)).run();
8608
8640
  return;
8609
8641
  }
8610
- const runId2 = crypto16.randomUUID();
8642
+ const runId2 = crypto17.randomUUID();
8611
8643
  this.db.insert(runs).values({
8612
8644
  id: runId2,
8613
8645
  projectId,
@@ -8620,7 +8652,7 @@ var Scheduler = class {
8620
8652
  lastRunAt: now,
8621
8653
  nextRunAt,
8622
8654
  updatedAt: now
8623
- }).where(eq14(schedules.id, currentSchedule.id)).run();
8655
+ }).where(eq15(schedules.id, currentSchedule.id)).run();
8624
8656
  log13.info("site-audit.triggered", { runId: runId2, projectName: project.name });
8625
8657
  this.callbacks.onSiteAuditRequested(runId2, projectId);
8626
8658
  return;
@@ -8653,7 +8685,7 @@ var Scheduler = class {
8653
8685
  this.db.update(schedules).set({
8654
8686
  nextRunAt,
8655
8687
  updatedAt: now
8656
- }).where(eq14(schedules.id, currentSchedule.id)).run();
8688
+ }).where(eq15(schedules.id, currentSchedule.id)).run();
8657
8689
  return;
8658
8690
  }
8659
8691
  const runId = queueResult.runId;
@@ -8661,7 +8693,7 @@ var Scheduler = class {
8661
8693
  lastRunAt: now,
8662
8694
  nextRunAt,
8663
8695
  updatedAt: now
8664
- }).where(eq14(schedules.id, currentSchedule.id)).run();
8696
+ }).where(eq15(schedules.id, currentSchedule.id)).run();
8665
8697
  log13.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
8666
8698
  this.callbacks.onRunCreated(runId, projectId, providers, resolvedLocation);
8667
8699
  } catch (err) {
@@ -8694,8 +8726,8 @@ async function refreshAllIntegrations(client, projectName) {
8694
8726
  }
8695
8727
 
8696
8728
  // src/notifier.ts
8697
- import { eq as eq15, desc as desc5, and as and12, inArray as inArray6, or } from "drizzle-orm";
8698
- import crypto17 from "crypto";
8729
+ import { eq as eq16, desc as desc6, and as and12, inArray as inArray6, or } from "drizzle-orm";
8730
+ import crypto18 from "crypto";
8699
8731
  var log15 = createLogger("Notifier");
8700
8732
  var Notifier = class {
8701
8733
  db;
@@ -8707,18 +8739,18 @@ var Notifier = class {
8707
8739
  /** Called after a run completes (success, partial, or failed). */
8708
8740
  async onRunCompleted(runId, projectId) {
8709
8741
  log15.info("run.completed", { runId, projectId });
8710
- const notifs = this.db.select().from(notifications).where(eq15(notifications.projectId, projectId)).all().filter((n) => n.enabled);
8742
+ const notifs = this.db.select().from(notifications).where(eq16(notifications.projectId, projectId)).all().filter((n) => n.enabled);
8711
8743
  if (notifs.length === 0) {
8712
8744
  log15.info("notifications.none-enabled", { projectId });
8713
8745
  return;
8714
8746
  }
8715
8747
  log15.info("notifications.found", { projectId, count: notifs.length });
8716
- const run = this.db.select().from(runs).where(eq15(runs.id, runId)).get();
8748
+ const run = this.db.select().from(runs).where(eq16(runs.id, runId)).get();
8717
8749
  if (!run) {
8718
8750
  log15.error("run.not-found", { runId, msg: "skipping notification dispatch" });
8719
8751
  return;
8720
8752
  }
8721
- const project = this.db.select().from(projects).where(eq15(projects.id, projectId)).get();
8753
+ const project = this.db.select().from(projects).where(eq16(projects.id, projectId)).get();
8722
8754
  if (!project) {
8723
8755
  log15.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
8724
8756
  return;
@@ -8771,7 +8803,7 @@ var Notifier = class {
8771
8803
  * inferred from the absence of a webhook.
8772
8804
  */
8773
8805
  async onHealthChecked(projectId, report) {
8774
- const project = this.db.select().from(projects).where(eq15(projects.id, projectId)).get();
8806
+ const project = this.db.select().from(projects).where(eq16(projects.id, projectId)).get();
8775
8807
  if (!project) {
8776
8808
  log15.error("project.not-found", { projectId, msg: "skipping health notification" });
8777
8809
  return null;
@@ -8802,7 +8834,7 @@ var Notifier = class {
8802
8834
  const status = noSignal ? "warn" : worst ? worst.status : "ok";
8803
8835
  const code = noSignal ? "health.no-signal" : worst?.code ?? "health.ok";
8804
8836
  const summary = noSignal ? `No health check produced a result (${report.checks.length} skipped) \u2014 health is unknown, not confirmed.` : worst?.summary ?? `All ${graded.length} health check(s) passing.`;
8805
- const previous = this.db.select().from(doctorHealthState).where(eq15(doctorHealthState.projectId, projectId)).get();
8837
+ const previous = this.db.select().from(doctorHealthState).where(eq16(doctorHealthState.projectId, projectId)).get();
8806
8838
  const previousStatus = previous?.status ?? null;
8807
8839
  let event = null;
8808
8840
  if (previous === void 0) {
@@ -8817,13 +8849,13 @@ var Notifier = class {
8817
8849
  if (previous === void 0) {
8818
8850
  this.db.insert(doctorHealthState).values({ ...observation, notifiedAt: null }).run();
8819
8851
  } else {
8820
- this.db.update(doctorHealthState).set(observation).where(eq15(doctorHealthState.projectId, projectId)).run();
8852
+ this.db.update(doctorHealthState).set(observation).where(eq16(doctorHealthState.projectId, projectId)).run();
8821
8853
  }
8822
8854
  if (!event) {
8823
8855
  log15.info("health.unchanged", { projectId, status, code });
8824
8856
  return null;
8825
8857
  }
8826
- const notifs = this.db.select().from(notifications).where(eq15(notifications.projectId, projectId)).all().filter((n) => n.enabled);
8858
+ const notifs = this.db.select().from(notifications).where(eq16(notifications.projectId, projectId)).all().filter((n) => n.enabled);
8827
8859
  const payload = {
8828
8860
  source: "canonry",
8829
8861
  event,
@@ -8847,7 +8879,7 @@ var Notifier = class {
8847
8879
  delivered += 1;
8848
8880
  }
8849
8881
  if (delivered > 0) {
8850
- this.db.update(doctorHealthState).set({ notifiedAt: now }).where(eq15(doctorHealthState.projectId, projectId)).run();
8882
+ this.db.update(doctorHealthState).set({ notifiedAt: now }).where(eq16(doctorHealthState.projectId, projectId)).run();
8851
8883
  }
8852
8884
  log15.info("health.notified", { projectId, event, status, code, subscribers: notifs.length, delivered });
8853
8885
  return event;
@@ -8860,11 +8892,11 @@ var Notifier = class {
8860
8892
  if (criticalInsights.length > 0) insightEvents.push("insight.critical");
8861
8893
  if (highInsights.length > 0) insightEvents.push("insight.high");
8862
8894
  if (insightEvents.length === 0) return;
8863
- const notifs = this.db.select().from(notifications).where(eq15(notifications.projectId, projectId)).all().filter((n) => n.enabled);
8895
+ const notifs = this.db.select().from(notifications).where(eq16(notifications.projectId, projectId)).all().filter((n) => n.enabled);
8864
8896
  if (notifs.length === 0) return;
8865
- const run = this.db.select().from(runs).where(eq15(runs.id, runId)).get();
8897
+ const run = this.db.select().from(runs).where(eq16(runs.id, runId)).get();
8866
8898
  if (!run) return;
8867
- const project = this.db.select().from(projects).where(eq15(projects.id, projectId)).get();
8899
+ const project = this.db.select().from(projects).where(eq16(projects.id, projectId)).get();
8868
8900
  if (!project) return;
8869
8901
  for (const notif of notifs) {
8870
8902
  const config = notif.config;
@@ -8894,7 +8926,7 @@ var Notifier = class {
8894
8926
  }
8895
8927
  }
8896
8928
  computeTransitions(runId, projectId) {
8897
- const thisRun = this.db.select().from(runs).where(eq15(runs.id, runId)).get();
8929
+ const thisRun = this.db.select().from(runs).where(eq16(runs.id, runId)).get();
8898
8930
  if (!thisRun) return [];
8899
8931
  const completeness = measurementRunCompleteness(this.db, runId);
8900
8932
  if (completeness.planned && !completeness.complete) {
@@ -8906,9 +8938,9 @@ var Notifier = class {
8906
8938
  return [];
8907
8939
  }
8908
8940
  const groupSiblings = this.db.select().from(runs).where(and12(
8909
- eq15(runs.projectId, projectId),
8910
- eq15(runs.kind, thisRun.kind),
8911
- eq15(runs.createdAt, thisRun.createdAt)
8941
+ eq16(runs.projectId, projectId),
8942
+ eq16(runs.kind, thisRun.kind),
8943
+ eq16(runs.createdAt, thisRun.createdAt)
8912
8944
  )).all();
8913
8945
  const stillPending = groupSiblings.some((r) => r.status === "queued" || r.status === "running");
8914
8946
  if (stillPending) return [];
@@ -8924,7 +8956,7 @@ var Notifier = class {
8924
8956
  return candidate.id > best.id ? candidate : best;
8925
8957
  });
8926
8958
  if (winner.id !== runId) return [];
8927
- const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq15(projects.id, projectId)).get();
8959
+ const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq16(projects.id, projectId)).get();
8928
8960
  const locationCount = Math.max(
8929
8961
  1,
8930
8962
  (projectLocations?.locations ?? []).length
@@ -8932,11 +8964,11 @@ var Notifier = class {
8932
8964
  const RECENT_FETCH_LIMIT = Math.max(8, locationCount * 4);
8933
8965
  const recentRuns = this.db.select().from(runs).where(
8934
8966
  and12(
8935
- eq15(runs.projectId, projectId),
8936
- eq15(runs.kind, thisRun.kind),
8937
- or(eq15(runs.status, "completed"), eq15(runs.status, "partial"))
8967
+ eq16(runs.projectId, projectId),
8968
+ eq16(runs.kind, thisRun.kind),
8969
+ or(eq16(runs.status, "completed"), eq16(runs.status, "partial"))
8938
8970
  )
8939
- ).orderBy(desc5(runs.createdAt), desc5(runs.id)).limit(RECENT_FETCH_LIMIT).all();
8971
+ ).orderBy(desc6(runs.createdAt), desc6(runs.id)).limit(RECENT_FETCH_LIMIT).all();
8940
8972
  const groups = groupRunsByCreatedAt(recentRuns);
8941
8973
  const currentGroupIdx = groups.findIndex((g) => g[0]?.createdAt === thisRun.createdAt);
8942
8974
  if (currentGroupIdx < 0) return [];
@@ -8951,7 +8983,7 @@ var Notifier = class {
8951
8983
  provider: querySnapshots.provider,
8952
8984
  location: querySnapshots.location,
8953
8985
  citationState: querySnapshots.citationState
8954
- }).from(querySnapshots).leftJoin(queries, eq15(querySnapshots.queryId, queries.id)).where(inArray6(querySnapshots.runId, currentRunIds)).all();
8986
+ }).from(querySnapshots).leftJoin(queries, eq16(querySnapshots.queryId, queries.id)).where(inArray6(querySnapshots.runId, currentRunIds)).all();
8955
8987
  const previousSnapshots = this.db.select({
8956
8988
  queryId: querySnapshots.queryId,
8957
8989
  provider: querySnapshots.provider,
@@ -9021,7 +9053,7 @@ var Notifier = class {
9021
9053
  }
9022
9054
  logDelivery(projectId, notificationId, event, status, error) {
9023
9055
  this.db.insert(auditLog).values({
9024
- id: crypto17.randomUUID(),
9056
+ id: crypto18.randomUUID(),
9025
9057
  projectId,
9026
9058
  actor: "scheduler",
9027
9059
  action: `notification.${status}`,
@@ -9034,7 +9066,7 @@ var Notifier = class {
9034
9066
  };
9035
9067
 
9036
9068
  // src/run-coordinator.ts
9037
- import { eq as eq16 } from "drizzle-orm";
9069
+ import { eq as eq17 } from "drizzle-orm";
9038
9070
  var log16 = createLogger("RunCoordinator");
9039
9071
  var RunCoordinator = class {
9040
9072
  constructor(db, notifier, intelligenceService, onInsightsGenerated, onAeroEvent) {
@@ -9050,7 +9082,7 @@ var RunCoordinator = class {
9050
9082
  onInsightsGenerated;
9051
9083
  onAeroEvent;
9052
9084
  async onRunCompleted(runId, projectId) {
9053
- const runRow = this.db.select().from(runs).where(eq16(runs.id, runId)).get();
9085
+ const runRow = this.db.select().from(runs).where(eq17(runs.id, runId)).get();
9054
9086
  const kind = runRow?.kind ?? RunKinds["answer-visibility"];
9055
9087
  if (runRow?.trigger === RunTriggers.probe) {
9056
9088
  log16.info("probe.skip-side-effects", { runId, projectId, kind });
@@ -9141,7 +9173,7 @@ var RunCoordinator = class {
9141
9173
  * so the Aero queue is never starved of a follow-up.
9142
9174
  */
9143
9175
  buildDiscoveryAeroContext(runId, projectId, status, error) {
9144
- const session = this.db.select().from(discoverySessions).where(eq16(discoverySessions.runId, runId)).get();
9176
+ const session = this.db.select().from(discoverySessions).where(eq17(discoverySessions.runId, runId)).get();
9145
9177
  const competitorMap = session ? session.competitorMap : [];
9146
9178
  return {
9147
9179
  kind: RunKinds["aeo-discover-probe"],
@@ -9183,8 +9215,8 @@ function analysisResultFromInsights(insights2) {
9183
9215
  }
9184
9216
 
9185
9217
  // src/agent/session-registry.ts
9186
- import crypto21 from "crypto";
9187
- import { eq as eq18 } from "drizzle-orm";
9218
+ import crypto22 from "crypto";
9219
+ import { eq as eq19 } from "drizzle-orm";
9188
9220
 
9189
9221
  // src/agent/session.ts
9190
9222
  import fs7 from "fs";
@@ -9927,7 +9959,7 @@ function buildAeroStateTools(ctx, opts = {}) {
9927
9959
  }
9928
9960
 
9929
9961
  // src/agent/llm-usage.ts
9930
- import crypto18 from "crypto";
9962
+ import crypto19 from "crypto";
9931
9963
  var AeroLlmUsageFeatures = {
9932
9964
  turn: "aero.turn"
9933
9965
  };
@@ -9946,7 +9978,7 @@ function recordLlmUsageEvent(args) {
9946
9978
  const usage = args.message.usage;
9947
9979
  const now = (/* @__PURE__ */ new Date()).toISOString();
9948
9980
  args.db.insert(llmUsageEvents).values({
9949
- id: crypto18.randomUUID(),
9981
+ id: crypto19.randomUUID(),
9950
9982
  projectId: args.projectId,
9951
9983
  runId: args.runId,
9952
9984
  agentSessionId: args.agentSessionId,
@@ -10022,7 +10054,7 @@ function splitAeroAnthropicSystemCachePayload(payload, model) {
10022
10054
  }
10023
10055
 
10024
10056
  // src/agent/tool-usage.ts
10025
- import crypto19 from "crypto";
10057
+ import crypto20 from "crypto";
10026
10058
  var AeroToolEventStatuses = {
10027
10059
  success: "success",
10028
10060
  error: "error"
@@ -10085,7 +10117,7 @@ function createAeroToolUsageHooks(args) {
10085
10117
  function recordAgentToolEvent(args) {
10086
10118
  try {
10087
10119
  args.db.insert(agentToolEvents).values({
10088
- id: crypto19.randomUUID(),
10120
+ id: crypto20.randomUUID(),
10089
10121
  projectId: args.projectId,
10090
10122
  agentSessionId: args.agentSessionId,
10091
10123
  toolCallId: args.toolCallId,
@@ -10312,8 +10344,8 @@ async function loadExternalMcpTools(servers, opts = {}) {
10312
10344
  }
10313
10345
 
10314
10346
  // src/agent/memory-store.ts
10315
- import crypto20 from "crypto";
10316
- import { and as and13, desc as desc6, eq as eq17, like, sql as sql7 } from "drizzle-orm";
10347
+ import crypto21 from "crypto";
10348
+ import { and as and13, desc as desc7, eq as eq18, like, sql as sql7 } from "drizzle-orm";
10317
10349
  var COMPACTION_KEY_PREFIX = "compaction:";
10318
10350
  var COMPACTION_NOTES_PER_SESSION = 3;
10319
10351
  function rowToDto(row) {
@@ -10327,7 +10359,7 @@ function rowToDto(row) {
10327
10359
  };
10328
10360
  }
10329
10361
  function listMemoryEntries(db, projectId, opts = {}) {
10330
- const query = db.select().from(agentMemory).where(eq17(agentMemory.projectId, projectId)).orderBy(desc6(agentMemory.updatedAt));
10362
+ const query = db.select().from(agentMemory).where(eq18(agentMemory.projectId, projectId)).orderBy(desc7(agentMemory.updatedAt));
10331
10363
  const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
10332
10364
  return rows.map(rowToDto);
10333
10365
  }
@@ -10341,7 +10373,7 @@ function upsertMemoryEntry(db, args) {
10341
10373
  throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
10342
10374
  }
10343
10375
  const now = (/* @__PURE__ */ new Date()).toISOString();
10344
- const id = crypto20.randomUUID();
10376
+ const id = crypto21.randomUUID();
10345
10377
  db.insert(agentMemory).values({
10346
10378
  id,
10347
10379
  projectId: args.projectId,
@@ -10358,12 +10390,12 @@ function upsertMemoryEntry(db, args) {
10358
10390
  updatedAt: now
10359
10391
  }
10360
10392
  }).run();
10361
- const row = db.select().from(agentMemory).where(and13(eq17(agentMemory.projectId, args.projectId), eq17(agentMemory.key, args.key))).get();
10393
+ const row = db.select().from(agentMemory).where(and13(eq18(agentMemory.projectId, args.projectId), eq18(agentMemory.key, args.key))).get();
10362
10394
  if (!row) throw new Error("memory upsert produced no row");
10363
10395
  return rowToDto(row);
10364
10396
  }
10365
10397
  function deleteMemoryEntry(db, projectId, key) {
10366
- const result = db.delete(agentMemory).where(and13(eq17(agentMemory.projectId, projectId), eq17(agentMemory.key, key))).run();
10398
+ const result = db.delete(agentMemory).where(and13(eq18(agentMemory.projectId, projectId), eq18(agentMemory.key, key))).run();
10367
10399
  const changes = result.changes ?? 0;
10368
10400
  return changes > 0;
10369
10401
  }
@@ -10378,7 +10410,7 @@ function writeCompactionNote(db, args) {
10378
10410
  }
10379
10411
  const now = (/* @__PURE__ */ new Date()).toISOString();
10380
10412
  const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
10381
- const id = crypto20.randomUUID();
10413
+ const id = crypto21.randomUUID();
10382
10414
  let inserted;
10383
10415
  db.transaction((tx) => {
10384
10416
  tx.insert(agentMemory).values({
@@ -10393,15 +10425,15 @@ function writeCompactionNote(db, args) {
10393
10425
  const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
10394
10426
  const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
10395
10427
  and13(
10396
- eq17(agentMemory.projectId, args.projectId),
10428
+ eq18(agentMemory.projectId, args.projectId),
10397
10429
  like(agentMemory.key, `${sessionPrefix}%`)
10398
10430
  )
10399
- ).orderBy(desc6(agentMemory.updatedAt)).all();
10431
+ ).orderBy(desc7(agentMemory.updatedAt)).all();
10400
10432
  const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
10401
10433
  if (stale.length > 0) {
10402
10434
  tx.delete(agentMemory).where(sql7`${agentMemory.id} IN (${sql7.join(stale.map((s) => sql7`${s}`), sql7`, `)})`).run();
10403
10435
  }
10404
- const row = tx.select().from(agentMemory).where(and13(eq17(agentMemory.projectId, args.projectId), eq17(agentMemory.key, key))).get();
10436
+ const row = tx.select().from(agentMemory).where(and13(eq18(agentMemory.projectId, args.projectId), eq18(agentMemory.key, key))).get();
10405
10437
  if (row) inserted = rowToDto(row);
10406
10438
  });
10407
10439
  if (!inserted) throw new Error("compaction note write produced no row");
@@ -10628,7 +10660,7 @@ var SessionRegistry = class {
10628
10660
  modelProvider: effectiveProvider,
10629
10661
  modelId: effectiveModelId,
10630
10662
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
10631
- }).where(eq18(agentSessions.projectId, projectId)).run();
10663
+ }).where(eq19(agentSessions.projectId, projectId)).run();
10632
10664
  }
10633
10665
  const agent2 = createAeroSession({
10634
10666
  projectName,
@@ -10657,7 +10689,7 @@ var SessionRegistry = class {
10657
10689
  }
10658
10690
  const { provider, modelId } = resolveSessionProviderAndModel(this.opts.config, preferences);
10659
10691
  const systemPrompt = loadAeroSystemPrompt();
10660
- const sessionId = crypto21.randomUUID();
10692
+ const sessionId = crypto22.randomUUID();
10661
10693
  const agent = createAeroSession({
10662
10694
  projectName,
10663
10695
  client: this.opts.client,
@@ -10862,7 +10894,7 @@ ${lines.join("\n")}
10862
10894
  modelProvider: nextProvider,
10863
10895
  modelId: nextModelId,
10864
10896
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
10865
- }).where(eq18(agentSessions.projectId, projectId)).run();
10897
+ }).where(eq19(agentSessions.projectId, projectId)).run();
10866
10898
  }
10867
10899
  /** Persist a session's transcript back to the DB. Call after any run settles. */
10868
10900
  save(projectName) {
@@ -11026,17 +11058,17 @@ ${lines.join("\n")}
11026
11058
  return id;
11027
11059
  }
11028
11060
  tryResolveProjectId(projectName) {
11029
- const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq18(projects.name, projectName)).get();
11061
+ const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq19(projects.name, projectName)).get();
11030
11062
  return row?.id;
11031
11063
  }
11032
11064
  loadRow(projectId) {
11033
- const row = this.opts.db.select().from(agentSessions).where(eq18(agentSessions.projectId, projectId)).get();
11065
+ const row = this.opts.db.select().from(agentSessions).where(eq19(agentSessions.projectId, projectId)).get();
11034
11066
  return row ?? null;
11035
11067
  }
11036
11068
  insertRow(params) {
11037
11069
  const now = (/* @__PURE__ */ new Date()).toISOString();
11038
11070
  this.opts.db.insert(agentSessions).values({
11039
- id: params.id ?? crypto21.randomUUID(),
11071
+ id: params.id ?? crypto22.randomUUID(),
11040
11072
  projectId: params.projectId,
11041
11073
  systemPrompt: params.systemPrompt,
11042
11074
  modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
@@ -11049,14 +11081,14 @@ ${lines.join("\n")}
11049
11081
  }
11050
11082
  updateRow(projectId, patch) {
11051
11083
  const now = (/* @__PURE__ */ new Date()).toISOString();
11052
- this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq18(agentSessions.projectId, projectId)).run();
11084
+ this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq19(agentSessions.projectId, projectId)).run();
11053
11085
  }
11054
11086
  };
11055
11087
 
11056
11088
  // src/agent/agent-routes.ts
11057
- import { eq as eq19 } from "drizzle-orm";
11089
+ import { eq as eq20 } from "drizzle-orm";
11058
11090
  function resolveProject(db, name) {
11059
- const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq19(projects.name, name)).get();
11091
+ const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq20(projects.name, name)).get();
11060
11092
  if (!row) throw notFound("project", name);
11061
11093
  return row;
11062
11094
  }
@@ -11065,7 +11097,7 @@ function registerAgentRoutes(app, opts) {
11065
11097
  "/projects/:name/agent/transcript",
11066
11098
  async (request) => {
11067
11099
  const project = resolveProject(opts.db, request.params.name);
11068
- const row = opts.db.select().from(agentSessions).where(eq19(agentSessions.projectId, project.id)).get();
11100
+ const row = opts.db.select().from(agentSessions).where(eq20(agentSessions.projectId, project.id)).get();
11069
11101
  if (!row) {
11070
11102
  return { messages: [], modelProvider: null, modelId: null, updatedAt: null };
11071
11103
  }
@@ -11089,7 +11121,7 @@ function registerAgentRoutes(app, opts) {
11089
11121
  async (request) => {
11090
11122
  const project = resolveProject(opts.db, request.params.name);
11091
11123
  opts.sessionRegistry.reset(project.name);
11092
- opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq19(agentSessions.projectId, project.id)).run();
11124
+ opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq20(agentSessions.projectId, project.id)).run();
11093
11125
  return { status: "reset" };
11094
11126
  }
11095
11127
  );
@@ -12032,20 +12064,20 @@ function clipText(value, length) {
12032
12064
  }
12033
12065
 
12034
12066
  // src/research-runner.ts
12035
- import { and as and14, eq as eq20, inArray as inArray7, sql as sql8 } from "drizzle-orm";
12067
+ import { and as and14, eq as eq21, inArray as inArray7, sql as sql8 } from "drizzle-orm";
12036
12068
  var unfinishedResearchQueryStatuses = [ResearchQueryStatuses.queued, ResearchQueryStatuses.running];
12037
12069
  function finalResearchRunStatus(completed, failed) {
12038
12070
  if (failed === 0) return ResearchRunStatuses.completed;
12039
12071
  return completed > 0 ? ResearchRunStatuses.partial : ResearchRunStatuses.failed;
12040
12072
  }
12041
12073
  async function executeResearchRun(db, registry, runId, projectId) {
12042
- const run = db.select().from(researchRuns).where(and14(eq20(researchRuns.id, runId), eq20(researchRuns.projectId, projectId))).get();
12074
+ const run = db.select().from(researchRuns).where(and14(eq21(researchRuns.id, runId), eq21(researchRuns.projectId, projectId))).get();
12043
12075
  if (!run || run.status !== ResearchRunStatuses.queued) return;
12044
- const project = db.select().from(projects).where(eq20(projects.id, projectId)).get();
12076
+ const project = db.select().from(projects).where(eq21(projects.id, projectId)).get();
12045
12077
  if (!project) return;
12046
12078
  const provider = registry.get(run.provider);
12047
12079
  const now = (/* @__PURE__ */ new Date()).toISOString();
12048
- const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(and14(eq20(researchRuns.id, runId), eq20(researchRuns.status, ResearchRunStatuses.queued))).run();
12080
+ const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(and14(eq21(researchRuns.id, runId), eq21(researchRuns.status, ResearchRunStatuses.queued))).run();
12049
12081
  if (claim.changes !== 1) return;
12050
12082
  let reserved = 0;
12051
12083
  let dispatched = 0;
@@ -12069,8 +12101,8 @@ async function executeResearchRun(db, registry, runId, projectId) {
12069
12101
  }
12070
12102
  reserved = run.totalQueries;
12071
12103
  reservation = { scope, period };
12072
- const rows = db.select().from(researchRunQueries).where(eq20(researchRunQueries.researchRunId, runId)).orderBy(researchRunQueries.position).all();
12073
- const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq20(competitors.projectId, projectId)).all().map((row) => row.domain);
12104
+ const rows = db.select().from(researchRunQueries).where(eq21(researchRunQueries.researchRunId, runId)).orderBy(researchRunQueries.position).all();
12105
+ const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq21(competitors.projectId, projectId)).all().map((row) => row.domain);
12074
12106
  const domains = effectiveDomains(project);
12075
12107
  const brands = effectiveBrandNames(project);
12076
12108
  const config = { ...provider.config, model: run.resolvedModel };
@@ -12078,7 +12110,7 @@ async function executeResearchRun(db, registry, runId, projectId) {
12078
12110
  await mapWithConcurrency(rows, Math.max(1, provider.config.quotaPolicy.maxConcurrency), async (row) => {
12079
12111
  try {
12080
12112
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
12081
- db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(and14(eq20(researchRunQueries.id, row.id), eq20(researchRunQueries.status, ResearchQueryStatuses.queued))).run();
12113
+ db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(and14(eq21(researchRunQueries.id, row.id), eq21(researchRunQueries.status, ResearchQueryStatuses.queued))).run();
12082
12114
  const raw = await gate.run(async () => {
12083
12115
  dispatched++;
12084
12116
  return provider.adapter.executeTrackedQuery({ query: row.queryText, canonicalDomains: domains, competitorDomains, ...run.location ? { location: run.location } : {} }, config);
@@ -12105,7 +12137,7 @@ async function executeResearchRun(db, registry, runId, projectId) {
12105
12137
  citationState: determineCitationState(normalized, domains),
12106
12138
  rawResponse: raw.rawResponse,
12107
12139
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
12108
- }).where(and14(eq20(researchRunQueries.id, row.id), eq20(researchRunQueries.status, ResearchQueryStatuses.running))).run();
12140
+ }).where(and14(eq21(researchRunQueries.id, row.id), eq21(researchRunQueries.status, ResearchQueryStatuses.running))).run();
12109
12141
  if (completed.changes === 1) incrementResearchProgress(db, runId, "completedQueries");
12110
12142
  } catch (error) {
12111
12143
  try {
@@ -12129,18 +12161,18 @@ async function executeResearchRun(db, registry, runId, projectId) {
12129
12161
  }
12130
12162
  }
12131
12163
  function incrementResearchProgress(db, runId, column) {
12132
- db.update(researchRuns).set({ [column]: sql8`${researchRuns[column]} + 1` }).where(eq20(researchRuns.id, runId)).run();
12164
+ db.update(researchRuns).set({ [column]: sql8`${researchRuns[column]} + 1` }).where(eq21(researchRuns.id, runId)).run();
12133
12165
  }
12134
12166
  function markResearchQueryFailed(db, runId, queryId, error) {
12135
12167
  const message = error instanceof Error ? error.message : String(error);
12136
- const failed = db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error: message, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and14(eq20(researchRunQueries.id, queryId), inArray7(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
12168
+ const failed = db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error: message, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and14(eq21(researchRunQueries.id, queryId), inArray7(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
12137
12169
  if (failed.changes === 1) incrementResearchProgress(db, runId, "failedQueries");
12138
12170
  }
12139
12171
  function markUnfinishedResearchQueriesFailed(db, runId, error) {
12140
- db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and14(eq20(researchRunQueries.researchRunId, runId), inArray7(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
12172
+ db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and14(eq21(researchRunQueries.researchRunId, runId), inArray7(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
12141
12173
  }
12142
12174
  function finalizeResearchRun(db, runId, fatalError) {
12143
- const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(eq20(researchRunQueries.researchRunId, runId)).all();
12175
+ const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(eq21(researchRunQueries.researchRunId, runId)).all();
12144
12176
  const completed = rows.filter((row) => row.status === ResearchQueryStatuses.completed).length;
12145
12177
  const failed = rows.filter((row) => row.status === ResearchQueryStatuses.failed).length;
12146
12178
  db.update(researchRuns).set({
@@ -12149,7 +12181,7 @@ function finalizeResearchRun(db, runId, fatalError) {
12149
12181
  failedQueries: failed,
12150
12182
  error: fatalError ?? (failed === rows.length ? "Every research query failed." : null),
12151
12183
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
12152
- }).where(eq20(researchRuns.id, runId)).run();
12184
+ }).where(eq21(researchRuns.id, runId)).run();
12153
12185
  }
12154
12186
 
12155
12187
  // src/server.ts
@@ -12212,14 +12244,14 @@ function summarizeProviderConfig(config) {
12212
12244
  };
12213
12245
  }
12214
12246
  function hashApiKey(key) {
12215
- return crypto22.createHash("sha256").update(key).digest("hex");
12247
+ return crypto23.createHash("sha256").update(key).digest("hex");
12216
12248
  }
12217
12249
  var DASHBOARD_SCRYPT_KEYLEN = 64;
12218
12250
  var DASHBOARD_SCRYPT_COST = 1 << 15;
12219
12251
  var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
12220
12252
  function hashDashboardPassword(password) {
12221
- const salt = crypto22.randomBytes(16);
12222
- const derived = crypto22.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
12253
+ const salt = crypto23.randomBytes(16);
12254
+ const derived = crypto23.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
12223
12255
  N: DASHBOARD_SCRYPT_COST,
12224
12256
  maxmem: DASHBOARD_SCRYPT_MAXMEM
12225
12257
  });
@@ -12240,14 +12272,14 @@ function verifyDashboardPassword(password, storedHash) {
12240
12272
  } catch {
12241
12273
  return { ok: false, needsRehash: false };
12242
12274
  }
12243
- const derived = crypto22.scryptSync(password, salt, expected.length, {
12275
+ const derived = crypto23.scryptSync(password, salt, expected.length, {
12244
12276
  N: DASHBOARD_SCRYPT_COST,
12245
12277
  maxmem: DASHBOARD_SCRYPT_MAXMEM
12246
12278
  });
12247
12279
  if (derived.length !== expected.length)
12248
12280
  return { ok: false, needsRehash: false };
12249
12281
  return {
12250
- ok: crypto22.timingSafeEqual(derived, expected),
12282
+ ok: crypto23.timingSafeEqual(derived, expected),
12251
12283
  needsRehash: false
12252
12284
  };
12253
12285
  }
@@ -12256,7 +12288,7 @@ function verifyDashboardPassword(password, storedHash) {
12256
12288
  const expected = Buffer.from(storedHash, "hex");
12257
12289
  if (candidate.length !== expected.length)
12258
12290
  return { ok: false, needsRehash: false };
12259
- const ok = crypto22.timingSafeEqual(candidate, expected);
12291
+ const ok = crypto23.timingSafeEqual(candidate, expected);
12260
12292
  return { ok, needsRehash: ok };
12261
12293
  }
12262
12294
  return { ok: false, needsRehash: false };
@@ -12464,7 +12496,7 @@ async function createServer(opts) {
12464
12496
  (runId, projectId, result) => notifier.dispatchInsightWebhooks(runId, projectId, result),
12465
12497
  async (ctx) => {
12466
12498
  if (!sessionRegistry) return;
12467
- const project = opts.db.select({ name: projects.name }).from(projects).where(eq21(projects.id, ctx.projectId)).get();
12499
+ const project = opts.db.select({ name: projects.name }).from(projects).where(eq22(projects.id, ctx.projectId)).get();
12468
12500
  if (!project) return;
12469
12501
  let content;
12470
12502
  if (ctx.kind === RunKinds["aeo-discover-probe"]) {
@@ -12807,7 +12839,7 @@ async function createServer(opts) {
12807
12839
  void (async () => {
12808
12840
  try {
12809
12841
  const report = await aeroClient.runDoctor({ project: projectName });
12810
- const project = opts.db.select().from(projects).where(eq21(projects.name, projectName)).get();
12842
+ const project = opts.db.select().from(projects).where(eq22(projects.name, projectName)).get();
12811
12843
  if (!project) {
12812
12844
  app.log.warn({ projectName }, "doctor schedule fired for an unknown project");
12813
12845
  return;
@@ -12836,8 +12868,8 @@ async function createServer(opts) {
12836
12868
  if (!probed) return;
12837
12869
  const alreadySynced = opts.db.select().from(ccReleaseSyncs).where(
12838
12870
  and15(
12839
- eq21(ccReleaseSyncs.release, probed.release),
12840
- eq21(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
12871
+ eq22(ccReleaseSyncs.release, probed.release),
12872
+ eq22(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
12841
12873
  )
12842
12874
  ).limit(1).get();
12843
12875
  if (alreadySynced) {
@@ -12992,7 +13024,7 @@ async function createServer(opts) {
12992
13024
  return removed;
12993
13025
  }
12994
13026
  };
12995
- const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto22.randomBytes(32).toString("hex");
13027
+ const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto23.randomBytes(32).toString("hex");
12996
13028
  const googleConnectionStore = {
12997
13029
  listConnections: (domain) => listGoogleConnections(opts.config, domain),
12998
13030
  getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
@@ -13067,11 +13099,11 @@ async function createServer(opts) {
13067
13099
  const googlePublicUrl = resolveGooglePublicUrl(opts.config, basePath);
13068
13100
  if (opts.config.apiKey) {
13069
13101
  const keyHash = hashApiKey(opts.config.apiKey);
13070
- const existing = opts.db.select().from(apiKeys).where(eq21(apiKeys.keyHash, keyHash)).get();
13102
+ const existing = opts.db.select().from(apiKeys).where(eq22(apiKeys.keyHash, keyHash)).get();
13071
13103
  if (!existing) {
13072
13104
  const prefix = opts.config.apiKey.slice(0, 12);
13073
13105
  opts.db.insert(apiKeys).values({
13074
- id: `key_${crypto22.randomBytes(8).toString("hex")}`,
13106
+ id: `key_${crypto23.randomBytes(8).toString("hex")}`,
13075
13107
  name: "default",
13076
13108
  keyHash,
13077
13109
  keyPrefix: prefix,
@@ -13095,7 +13127,7 @@ async function createServer(opts) {
13095
13127
  };
13096
13128
  const createSession = (apiKeyId) => {
13097
13129
  pruneExpiredSessions();
13098
- const sessionId = crypto22.randomBytes(32).toString("hex");
13130
+ const sessionId = crypto23.randomBytes(32).toString("hex");
13099
13131
  sessions.set(sessionId, {
13100
13132
  apiKeyId,
13101
13133
  expiresAt: Date.now() + SESSION_TTL_MS
@@ -13119,7 +13151,7 @@ async function createServer(opts) {
13119
13151
  };
13120
13152
  const getDefaultApiKey = () => {
13121
13153
  if (!opts.config.apiKey) return void 0;
13122
- return opts.db.select().from(apiKeys).where(eq21(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
13154
+ return opts.db.select().from(apiKeys).where(eq22(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
13123
13155
  };
13124
13156
  const createPasswordSession = (reply) => {
13125
13157
  const key = getDefaultApiKey();
@@ -13143,7 +13175,7 @@ async function createServer(opts) {
13143
13175
  if (!header) return false;
13144
13176
  const parts = header.split(" ");
13145
13177
  if (parts.length !== 2 || parts[0] !== "Bearer") return false;
13146
- const key = opts.db.select().from(apiKeys).where(eq21(apiKeys.keyHash, hashApiKey(parts[1]))).get();
13178
+ const key = opts.db.select().from(apiKeys).where(eq22(apiKeys.keyHash, hashApiKey(parts[1]))).get();
13147
13179
  return Boolean(key && !key.revokedAt);
13148
13180
  };
13149
13181
  const namedAccountsInUse = () => anyUsersExist(opts.db);
@@ -13233,12 +13265,12 @@ async function createServer(opts) {
13233
13265
  return reply.send({ authenticated: true });
13234
13266
  }
13235
13267
  if (apiKey) {
13236
- const key = opts.db.select().from(apiKeys).where(eq21(apiKeys.keyHash, hashApiKey(apiKey))).get();
13268
+ const key = opts.db.select().from(apiKeys).where(eq22(apiKeys.keyHash, hashApiKey(apiKey))).get();
13237
13269
  if (!key || key.revokedAt) {
13238
13270
  const err2 = authInvalid();
13239
13271
  return reply.status(err2.statusCode).send(err2.toJSON());
13240
13272
  }
13241
- opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq21(apiKeys.id, key.id)).run();
13273
+ opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq22(apiKeys.id, key.id)).run();
13242
13274
  const sessionId = createSession(key.id);
13243
13275
  reply.header(
13244
13276
  "set-cookie",
@@ -13451,7 +13483,7 @@ async function createServer(opts) {
13451
13483
  deps: {
13452
13484
  enqueueAutoExtract: ({ projectId, release: r }) => {
13453
13485
  const now = (/* @__PURE__ */ new Date()).toISOString();
13454
- const runId = crypto22.randomUUID();
13486
+ const runId = crypto23.randomUUID();
13455
13487
  opts.db.insert(runs).values({
13456
13488
  id: runId,
13457
13489
  projectId,
@@ -13576,7 +13608,7 @@ async function createServer(opts) {
13576
13608
  ...inspectOpts,
13577
13609
  config: opts.config
13578
13610
  }).then(() => {
13579
- const finished = opts.db.select({ status: runs.status }).from(runs).where(eq21(runs.id, runId)).get();
13611
+ const finished = opts.db.select({ status: runs.status }).from(runs).where(eq22(runs.id, runId)).get();
13580
13612
  if (finished?.status === RunStatuses.completed || finished?.status === RunStatuses.partial) {
13581
13613
  return maybeRefreshGscCoverage(opts.db, opts.config, projectId);
13582
13614
  }
@@ -13671,7 +13703,7 @@ async function createServer(opts) {
13671
13703
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
13672
13704
  opts.db.insert(auditLog).values(
13673
13705
  targetProjectIds.map((projectId) => ({
13674
- id: crypto22.randomUUID(),
13706
+ id: crypto23.randomUUID(),
13675
13707
  projectId,
13676
13708
  actor: "api",
13677
13709
  action: existing ? "provider.updated" : "provider.created",
@@ -13939,7 +13971,7 @@ async function createServer(opts) {
13939
13971
  }
13940
13972
  checkLatestVersionForServer();
13941
13973
  scheduler.start();
13942
- for (const run of opts.db.select({ id: researchRuns.id, projectId: researchRuns.projectId }).from(researchRuns).where(eq21(researchRuns.status, ResearchRunStatuses.queued)).all()) {
13974
+ for (const run of opts.db.select({ id: researchRuns.id, projectId: researchRuns.projectId }).from(researchRuns).where(eq22(researchRuns.status, ResearchRunStatuses.queued)).all()) {
13943
13975
  dispatchResearchRun(run.id, run.projectId);
13944
13976
  }
13945
13977
  app.addHook("onClose", async () => {