@canonry/canonry 4.153.0 → 4.154.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/assets/assets/{AuditHistoryPanel-CcoXjMxm.js → AuditHistoryPanel-WWT1n8jp.js} +1 -1
  2. package/assets/assets/{BacklinksPage-Cij_A80K.js → BacklinksPage-DIjUnsVL.js} +1 -1
  3. package/assets/assets/{HistoryPage-CdxhPEHF.js → HistoryPage-CrIBhGuW.js} +1 -1
  4. package/assets/assets/{MeasurementPropertyPage-CJaqCeMr.js → MeasurementPropertyPage-uN4BWigS.js} +1 -1
  5. package/assets/assets/ProjectPage-BR5ECwUP.js +12 -0
  6. package/assets/assets/{RunRow-xfuUNQ2O.js → RunRow-OE5tiuih.js} +1 -1
  7. package/assets/assets/{RunsPage-BirDqVe_.js → RunsPage-C6xBmjps.js} +1 -1
  8. package/assets/assets/{SettingsPage-BzMoD-42.js → SettingsPage-CUjjr8U0.js} +1 -1
  9. package/assets/assets/{TrafficPage-Du-7BL_j.js → TrafficPage-DrEfsHiE.js} +1 -1
  10. package/assets/assets/{TrafficSourceDetailPage-Dk6-TuNw.js → TrafficSourceDetailPage-B3DX5BEk.js} +1 -1
  11. package/assets/assets/{arrow-left-HyrLx9li.js → arrow-left-CLmqvCqU.js} +1 -1
  12. package/assets/assets/{extract-error-message-Db0s9Qon.js → extract-error-message-BEBd1a5v.js} +1 -1
  13. package/assets/assets/{index-Bio0Z1kJ.js → index-CsM-Xxia.js} +55 -55
  14. package/assets/assets/{index-CaYn5phz.css → index-DyL54s8x.css} +1 -1
  15. package/assets/assets/{refresh-cw-BFnkrZE9.js → refresh-cw-DYSS2Wd-.js} +1 -1
  16. package/assets/assets/{trash-2-CQtsaJ-3.js → trash-2-VoiF_ckU.js} +1 -1
  17. package/assets/assets/v2-overview-adapter-COMP5UGt.js +1 -0
  18. package/assets/index.html +2 -2
  19. package/dist/{chunk-LNJF7LL5.js → chunk-ANSQ6JNO.js} +1 -1
  20. package/dist/{chunk-VTL2WOIO.js → chunk-IFZS2TXV.js} +110 -70
  21. package/dist/{chunk-SKTXCZZD.js → chunk-NNW7TJUZ.js} +6 -1
  22. package/dist/{chunk-YV4VKT4M.js → chunk-X5I7CAXH.js} +172 -77
  23. package/dist/cli.js +4 -4
  24. package/dist/index.js +4 -4
  25. package/dist/{intelligence-service-APPDGUH6.js → intelligence-service-B5AETQ5N.js} +2 -2
  26. package/dist/mcp.js +2 -2
  27. package/package.json +7 -7
  28. package/assets/assets/ProjectPage-BJPVmD_-.js +0 -12
  29. package/assets/assets/v2-overview-adapter-D81QVAhK.js +0 -1
@@ -10,7 +10,7 @@ import {
10
10
  loadConfig,
11
11
  loadConfigRaw,
12
12
  saveConfigPatch
13
- } from "./chunk-LNJF7LL5.js";
13
+ } from "./chunk-ANSQ6JNO.js";
14
14
  import {
15
15
  CC_CACHE_DIR,
16
16
  DUCKDB_SPEC,
@@ -126,7 +126,7 @@ import {
126
126
  siteAuditSnapshots,
127
127
  toAlertView,
128
128
  usageCounters
129
- } from "./chunk-VTL2WOIO.js";
129
+ } from "./chunk-IFZS2TXV.js";
130
130
  import {
131
131
  AGENT_MEMORY_VALUE_MAX_BYTES,
132
132
  AGENT_PROVIDER_IDS,
@@ -218,7 +218,7 @@ import {
218
218
  validationError,
219
219
  winnabilityClassLabel,
220
220
  withRetry
221
- } from "./chunk-SKTXCZZD.js";
221
+ } from "./chunk-NNW7TJUZ.js";
222
222
 
223
223
  // src/telemetry.ts
224
224
  import crypto from "crypto";
@@ -549,7 +549,7 @@ function checkLatestVersionForServer(opts) {
549
549
 
550
550
  // src/server.ts
551
551
  import { createRequire as createRequire3 } from "module";
552
- import crypto23 from "crypto";
552
+ import crypto24 from "crypto";
553
553
  import fs8 from "fs";
554
554
  import path9 from "path";
555
555
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -5553,7 +5553,7 @@ async function executeAdsSync(db, runId, projectId, opts) {
5553
5553
  }
5554
5554
 
5555
5555
  // src/gsc-inspect-sitemap.ts
5556
- import crypto9 from "crypto";
5556
+ import crypto10 from "crypto";
5557
5557
  import { eq as eq7 } from "drizzle-orm";
5558
5558
 
5559
5559
  // src/sitemap-parser.ts
@@ -5652,6 +5652,30 @@ async function parseSitemapRecursive(url, urls, visited, depth, isChild) {
5652
5652
  LOC_REGEX.lastIndex = 0;
5653
5653
  }
5654
5654
 
5655
+ // src/inspect-rate-gate.ts
5656
+ import crypto9 from "crypto";
5657
+ var gates = /* @__PURE__ */ new Map();
5658
+ function sharedRateGate(key) {
5659
+ let entry = gates.get(key);
5660
+ if (!entry) {
5661
+ entry = { chain: Promise.resolve() };
5662
+ gates.set(key, entry);
5663
+ }
5664
+ const held = entry;
5665
+ return {
5666
+ take(delayMs, sleep3) {
5667
+ const wait = held.chain.then(() => sleep3(delayMs));
5668
+ held.chain = wait.catch(() => {
5669
+ });
5670
+ return wait;
5671
+ }
5672
+ };
5673
+ }
5674
+ function credentialGateKey(prefix, secret) {
5675
+ const digest = crypto9.createHash("sha256").update(secret).digest("hex").slice(0, 12);
5676
+ return `${prefix}:${digest}`;
5677
+ }
5678
+
5655
5679
  // src/gsc-inspect-paced.ts
5656
5680
  var INSPECT_BASE_DELAY_MS = 1e3;
5657
5681
  var INSPECT_MAX_CONCURRENCY = 5;
@@ -5670,41 +5694,43 @@ function isRetryableGscInspectError(err) {
5670
5694
  function defaultSleep(ms) {
5671
5695
  return new Promise((resolve) => setTimeout(resolve, ms));
5672
5696
  }
5673
- async function inspectUrlsPaced(urls, cb, deps = {}) {
5697
+ async function inspectUrlsPaced(urls, cb, deps) {
5674
5698
  const sleep3 = deps.sleep ?? defaultSleep;
5675
5699
  const jitter = deps.jitter ?? Math.random;
5676
5700
  const concurrency = Math.max(1, Math.min(deps.concurrency ?? INSPECT_MAX_CONCURRENCY, urls.length || 1));
5677
5701
  const isRetryable = deps.isRetryable ?? isRetryableGscInspectError;
5702
+ const spacingMs = deps.spacingMs ?? INSPECT_BASE_DELAY_MS;
5703
+ const maxRetries = deps.maxRetries ?? INSPECT_MAX_RETRIES;
5678
5704
  let inspected = 0;
5679
5705
  let errors = 0;
5680
5706
  let consecutiveRetryableFailures = 0;
5681
5707
  let nextIndex = 0;
5682
5708
  let aborted = false;
5683
5709
  let abortError;
5684
- let ratePromise = Promise.resolve();
5685
- const takeRateSlot = () => {
5686
- const wait = ratePromise.then(() => sleep3(INSPECT_BASE_DELAY_MS + jitter() * INSPECT_PACING_JITTER_MS));
5687
- ratePromise = wait;
5688
- return wait;
5689
- };
5710
+ const gate = sharedRateGate(deps.rateGateKey);
5711
+ const takeRateSlot = () => gate.take(spacingMs + jitter() * INSPECT_PACING_JITTER_MS, sleep3);
5690
5712
  async function worker() {
5691
5713
  for (; ; ) {
5692
5714
  if (aborted) return;
5693
5715
  const index = nextIndex++;
5694
5716
  if (index >= urls.length) return;
5695
5717
  const url = urls[index];
5696
- if (index > 0) await takeRateSlot();
5718
+ await takeRateSlot();
5697
5719
  if (aborted) return;
5698
5720
  try {
5699
- const result = await withRetry(() => cb.inspectOne(url), {
5700
- maxRetries: INSPECT_MAX_RETRIES,
5701
- baseDelayMs: INSPECT_BASE_DELAY_MS,
5721
+ let attempt = 0;
5722
+ const result = await withRetry(async () => {
5723
+ if (attempt++ > 0) await takeRateSlot();
5724
+ return cb.inspectOne(url);
5725
+ }, {
5726
+ maxRetries,
5727
+ baseDelayMs: spacingMs,
5702
5728
  maxDelayMs: INSPECT_MAX_BACKOFF_MS,
5703
5729
  isRetryable,
5704
5730
  sleep: sleep3,
5705
- onRetry: ({ attempt, delayMs, err }) => deps.log?.info("inspect.retry", {
5731
+ onRetry: ({ attempt: attempt2, delayMs, err }) => deps.log?.info("inspect.retry", {
5706
5732
  url,
5707
- attempt,
5733
+ attempt: attempt2,
5708
5734
  delayMs: Math.round(delayMs),
5709
5735
  error: err instanceof Error ? err.message : String(err)
5710
5736
  })
@@ -5801,7 +5827,7 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
5801
5827
  const rich = ir.richResultsResult;
5802
5828
  const inspectedAt = (/* @__PURE__ */ new Date()).toISOString();
5803
5829
  db.insert(gscUrlInspections).values({
5804
- id: crypto9.randomUUID(),
5830
+ id: crypto10.randomUUID(),
5805
5831
  projectId,
5806
5832
  syncRunId: runId,
5807
5833
  url: pageUrl,
@@ -5825,6 +5851,12 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
5825
5851
  }
5826
5852
  },
5827
5853
  {
5854
+ // Google meters URL Inspection per PROPERTY, so every sweep on this
5855
+ // property queues behind one clock. Latent rather than urgent here (a
5856
+ // property has one project today), but the per-call gate had the same
5857
+ // defect Bing was bitten by, and a manual sweep racing the chained
5858
+ // coverage refresh is exactly the overlap it fails on.
5859
+ rateGateKey: `gsc:${propertyId}`,
5828
5860
  log: {
5829
5861
  info: (action, ctx) => log6.info(action, { runId, projectId, ...ctx }),
5830
5862
  error: (action, ctx) => log6.error(action, { runId, projectId, ...ctx })
@@ -5853,9 +5885,20 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
5853
5885
  }
5854
5886
 
5855
5887
  // src/bing-inspect-sitemap.ts
5856
- import crypto10 from "crypto";
5888
+ import crypto11 from "crypto";
5857
5889
  import { eq as eq8, desc as desc3 } from "drizzle-orm";
5858
5890
  var log7 = createLogger("BingInspectSitemap");
5891
+ var BING_INSPECT_SPACING_MS = 2e3;
5892
+ function describeError(err) {
5893
+ if (err instanceof Error) return err.message;
5894
+ if (typeof err === "string") return err;
5895
+ if (err == null) return "unknown error";
5896
+ try {
5897
+ return JSON.stringify(err);
5898
+ } catch {
5899
+ return "unserializable error";
5900
+ }
5901
+ }
5859
5902
  function parseBingDate(value) {
5860
5903
  if (!value) return null;
5861
5904
  const match = /\/Date\((-?\d+)(?:[-+]\d+)?\)\//.exec(value);
@@ -5957,7 +6000,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
5957
6000
  derivedInIndex = false;
5958
6001
  }
5959
6002
  db.insert(bingUrlInspections).values({
5960
- id: crypto10.randomUUID(),
6003
+ id: crypto11.randomUUID(),
5961
6004
  projectId,
5962
6005
  url: pageUrl,
5963
6006
  httpCode,
@@ -5984,7 +6027,23 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
5984
6027
  });
5985
6028
  }
5986
6029
  },
5987
- { isRetryable: isRetryableHttpError, concurrency: 1, log: log7, ...opts.pacedDeps }
6030
+ {
6031
+ isRetryable: isRetryableHttpError,
6032
+ concurrency: 1,
6033
+ // Bing meters the ACCOUNT, and one API key serves every project here,
6034
+ // so all sweeps on that key must queue behind one clock. Without this
6035
+ // the daily `data-refresh` fired three sweeps in the same millisecond,
6036
+ // each politely pacing itself and collectively tripling the rate.
6037
+ rateGateKey: credentialGateKey("bing", conn.apiKey),
6038
+ spacingMs: BING_INSPECT_SPACING_MS,
6039
+ // The Bing client already retries (BING_MAX_RETRIES, Retry-After aware).
6040
+ // Retrying again here MULTIPLIED it: 5 client attempts x 4 driver
6041
+ // attempts = 20 requests for one throttled URL, so the breaker's
6042
+ // five-failure budget spent ~100 requests and inspected nothing.
6043
+ maxRetries: 0,
6044
+ log: log7,
6045
+ ...opts.pacedDeps
6046
+ }
5988
6047
  );
5989
6048
  if (outcome.aborted) {
5990
6049
  log7.error("inspect.circuit-break", {
@@ -5995,6 +6054,12 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
5995
6054
  note: "Bing inspection stopped early after sustained failures; the remaining pages were not attempted."
5996
6055
  });
5997
6056
  }
6057
+ if (outcome.aborted && inspected === 0) {
6058
+ const detail = describeError(outcome.abortError);
6059
+ throw new Error(
6060
+ `Bing inspection failed for every URL attempted (${errors} of ${targetUrls.length}); coverage was left unchanged rather than re-dated from stored inspections. Last error: ${detail}`
6061
+ );
6062
+ }
5998
6063
  const allInspections = db.select().from(bingUrlInspections).where(eq8(bingUrlInspections.projectId, projectId)).orderBy(desc3(bingUrlInspections.inspectedAt)).all();
5999
6064
  const latestByUrl = /* @__PURE__ */ new Map();
6000
6065
  const definitiveByUrl = /* @__PURE__ */ new Map();
@@ -6019,7 +6084,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
6019
6084
  const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6020
6085
  const snapNow = (/* @__PURE__ */ new Date()).toISOString();
6021
6086
  db.insert(bingCoverageSnapshots).values({
6022
- id: crypto10.randomUUID(),
6087
+ id: crypto11.randomUUID(),
6023
6088
  projectId,
6024
6089
  syncRunId: runId,
6025
6090
  date: snapshotDate,
@@ -6037,8 +6102,9 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
6037
6102
  syncRunId: runId
6038
6103
  }
6039
6104
  }).run();
6040
- const status = errors === sitemapUrls.length ? RunStatuses.failed : errors > 0 ? RunStatuses.partial : RunStatuses.completed;
6041
- db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq8(runs.id, runId)).run();
6105
+ const status = errors > 0 && errors >= targetUrls.length ? RunStatuses.failed : errors > 0 || outcome.aborted ? RunStatuses.partial : RunStatuses.completed;
6106
+ const degradedReason = status === RunStatuses.completed ? null : outcome.aborted ? `Stopped early after sustained failures; ${targetUrls.length - inspected} of ${targetUrls.length} pages were not inspected.` : `${errors} of ${targetUrls.length} pages could not be inspected.`;
6107
+ db.update(runs).set({ status, error: degradedReason, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq8(runs.id, runId)).run();
6042
6108
  log7.info("inspect.completed", {
6043
6109
  runId,
6044
6110
  projectId,
@@ -6059,24 +6125,42 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
6059
6125
  }
6060
6126
 
6061
6127
  // src/coverage-refresh.ts
6062
- import crypto11 from "crypto";
6128
+ import crypto12 from "crypto";
6063
6129
  import { and as and6, desc as desc4, eq as eq9, inArray as inArray3 } from "drizzle-orm";
6064
6130
  var log8 = createLogger("CoverageRefresh");
6065
6131
  var COVERAGE_REFRESH_MIN_INTERVAL_MS = 60 * 60 * 1e3;
6132
+ var COVERAGE_REFRESH_MANUAL_MIN_INTERVAL_MS = 2 * 60 * 1e3;
6066
6133
  var ACTIVE_OR_DONE_STATUSES = [
6067
6134
  RunStatuses.queued,
6068
6135
  RunStatuses.running,
6069
6136
  RunStatuses.completed,
6070
6137
  RunStatuses.partial
6071
6138
  ];
6139
+ var IN_FLIGHT_STATUSES = [RunStatuses.queued, RunStatuses.running];
6140
+ function runWasUserInitiated(db, runId) {
6141
+ const row = db.select({ trigger: runs.trigger }).from(runs).where(eq9(runs.id, runId)).get();
6142
+ return row?.trigger === RunTriggers.manual;
6143
+ }
6072
6144
  var defaultDeps = { executeInspectSitemap };
6073
- async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps, nowMs = Date.now()) {
6145
+ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps, nowMs = Date.now(), opts = {}) {
6074
6146
  const project = db.select({ canonicalDomain: projects.canonicalDomain }).from(projects).where(eq9(projects.id, projectId)).get();
6075
6147
  if (!project) return null;
6076
6148
  const { clientId, clientSecret } = getGoogleAuthConfig(config);
6077
6149
  if (!clientId || !clientSecret) return null;
6078
6150
  const conn = getGoogleConnection(config, project.canonicalDomain, "gsc");
6079
6151
  if (!conn?.refreshToken || !conn.propertyId) return null;
6152
+ const inFlight2 = db.select({ createdAt: runs.createdAt }).from(runs).where(
6153
+ and6(
6154
+ eq9(runs.projectId, projectId),
6155
+ eq9(runs.kind, RunKinds["inspect-sitemap"]),
6156
+ inArray3(runs.status, IN_FLIGHT_STATUSES)
6157
+ )
6158
+ ).orderBy(desc4(runs.createdAt)).limit(1).get();
6159
+ if (inFlight2) {
6160
+ log8.info("skip.in-flight", { projectId });
6161
+ return null;
6162
+ }
6163
+ const windowMs = opts.userInitiated ? COVERAGE_REFRESH_MANUAL_MIN_INTERVAL_MS : COVERAGE_REFRESH_MIN_INTERVAL_MS;
6080
6164
  const recent = db.select({ createdAt: runs.createdAt }).from(runs).where(
6081
6165
  and6(
6082
6166
  eq9(runs.projectId, projectId),
@@ -6086,12 +6170,12 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
6086
6170
  ).orderBy(desc4(runs.createdAt)).limit(1).get();
6087
6171
  if (recent) {
6088
6172
  const ageMs = nowMs - Date.parse(recent.createdAt);
6089
- if (Number.isFinite(ageMs) && ageMs < COVERAGE_REFRESH_MIN_INTERVAL_MS) {
6090
- log8.info("skip.recent", { projectId, ageMs });
6173
+ if (Number.isFinite(ageMs) && ageMs < windowMs) {
6174
+ log8.info("skip.recent", { projectId, ageMs, windowMs, userInitiated: opts.userInitiated === true });
6091
6175
  return null;
6092
6176
  }
6093
6177
  }
6094
- const runId = crypto11.randomUUID();
6178
+ const runId = crypto12.randomUUID();
6095
6179
  db.insert(runs).values({
6096
6180
  id: runId,
6097
6181
  projectId,
@@ -6114,7 +6198,7 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
6114
6198
  }
6115
6199
 
6116
6200
  // src/commoncrawl-sync.ts
6117
- import crypto12 from "crypto";
6201
+ import crypto13 from "crypto";
6118
6202
  import path4 from "path";
6119
6203
  import { and as and7, eq as eq10, sql as sql4 } from "drizzle-orm";
6120
6204
  var log9 = createLogger("CommonCrawlSync");
@@ -6189,7 +6273,7 @@ async function executeReleaseSync(db, syncId, opts) {
6189
6273
  if (!projectIds) continue;
6190
6274
  for (const projectId of projectIds) {
6191
6275
  expanded.push({
6192
- id: crypto12.randomUUID(),
6276
+ id: crypto13.randomUUID(),
6193
6277
  projectId,
6194
6278
  releaseSyncId: syncId,
6195
6279
  release,
@@ -6209,7 +6293,7 @@ async function executeReleaseSync(db, syncId, opts) {
6209
6293
  const projectRows = rowsByProject.get(p.id) ?? [];
6210
6294
  const summary = computeSummary(projectRows);
6211
6295
  tx.insert(backlinkSummaries).values({
6212
- id: crypto12.randomUUID(),
6296
+ id: crypto13.randomUUID(),
6213
6297
  projectId: p.id,
6214
6298
  releaseSyncId: syncId,
6215
6299
  source: BacklinkSources.commoncrawl,
@@ -6306,7 +6390,7 @@ function computeSummary(rows) {
6306
6390
  }
6307
6391
 
6308
6392
  // src/backlink-extract.ts
6309
- import crypto13 from "crypto";
6393
+ import crypto14 from "crypto";
6310
6394
  import fs3 from "fs";
6311
6395
  import { and as and8, desc as desc5, eq as eq11 } from "drizzle-orm";
6312
6396
  var log10 = createLogger("BacklinkExtract");
@@ -6362,7 +6446,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
6362
6446
  ).run();
6363
6447
  if (rows.length > 0) {
6364
6448
  const values = rows.map((r) => ({
6365
- id: crypto13.randomUUID(),
6449
+ id: crypto14.randomUUID(),
6366
6450
  projectId,
6367
6451
  releaseSyncId: syncId,
6368
6452
  source: BacklinkSources.commoncrawl,
@@ -6376,7 +6460,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
6376
6460
  }
6377
6461
  const summary = computeSummary2(rows);
6378
6462
  tx.insert(backlinkSummaries).values({
6379
- id: crypto13.randomUUID(),
6463
+ id: crypto14.randomUUID(),
6380
6464
  projectId,
6381
6465
  releaseSyncId: syncId,
6382
6466
  source: BacklinkSources.commoncrawl,
@@ -6419,7 +6503,7 @@ function computeSummary2(rows) {
6419
6503
  }
6420
6504
 
6421
6505
  // src/discovery-run.ts
6422
- import crypto14 from "crypto";
6506
+ import crypto15 from "crypto";
6423
6507
  import { and as and9, eq as eq12 } from "drizzle-orm";
6424
6508
  var log11 = createLogger("DiscoveryRun");
6425
6509
  var DEFAULT_SEED_COUNT = 30;
@@ -6774,7 +6858,7 @@ function writeDiscoveryInsight(db, input) {
6774
6858
  eq12(insights.dismissed, false)
6775
6859
  )).run();
6776
6860
  tx.insert(insights).values({
6777
- id: crypto14.randomUUID(),
6861
+ id: crypto15.randomUUID(),
6778
6862
  projectId: input.projectId,
6779
6863
  runId: input.runId,
6780
6864
  type: "discovery.basket-divergence",
@@ -6810,7 +6894,7 @@ function buildDiscoveryInsightTitle(input) {
6810
6894
  }
6811
6895
 
6812
6896
  // src/execute-site-audit.ts
6813
- import crypto15 from "crypto";
6897
+ import crypto16 from "crypto";
6814
6898
  import { eq as eq13 } from "drizzle-orm";
6815
6899
  import { runSitemapAudit } from "@ainyc/aeo-audit";
6816
6900
  var log12 = createLogger("SiteAudit");
@@ -6909,7 +6993,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
6909
6993
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
6910
6994
  db.transaction((tx) => {
6911
6995
  tx.insert(siteAuditSnapshots).values({
6912
- id: crypto15.randomUUID(),
6996
+ id: crypto16.randomUUID(),
6913
6997
  projectId,
6914
6998
  runId,
6915
6999
  sitemapUrl: report.sitemapUrl,
@@ -6938,7 +7022,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
6938
7022
  }).run();
6939
7023
  for (const page of report.pages) {
6940
7024
  tx.insert(siteAuditPages).values({
6941
- id: crypto15.randomUUID(),
7025
+ id: crypto16.randomUUID(),
6942
7026
  projectId,
6943
7027
  runId,
6944
7028
  url: page.url,
@@ -7432,7 +7516,7 @@ function readStoredGroundingSources(rawResponse) {
7432
7516
  return result;
7433
7517
  }
7434
7518
  async function backfillInsightsCommand(project, opts) {
7435
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-APPDGUH6.js");
7519
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-B5AETQ5N.js");
7436
7520
  const config = loadConfig();
7437
7521
  const db = createClient(config.database);
7438
7522
  migrate(db);
@@ -7953,7 +8037,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
7953
8037
  }
7954
8038
 
7955
8039
  // src/commands/skills.ts
7956
- import crypto16 from "crypto";
8040
+ import crypto17 from "crypto";
7957
8041
  import fs4 from "fs";
7958
8042
  import os4 from "os";
7959
8043
  import path5 from "path";
@@ -8008,7 +8092,7 @@ function walkRelative(dir, prefix = "") {
8008
8092
  return out.sort();
8009
8093
  }
8010
8094
  function sha256File(filePath) {
8011
- return crypto16.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
8095
+ return crypto17.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
8012
8096
  }
8013
8097
  function readSkillManifest(skillDir) {
8014
8098
  try {
@@ -8350,7 +8434,7 @@ var ProviderRegistry = class {
8350
8434
  };
8351
8435
 
8352
8436
  // src/scheduler.ts
8353
- import crypto17 from "crypto";
8437
+ import crypto18 from "crypto";
8354
8438
  import cron from "node-cron";
8355
8439
  import { and as and11, eq as eq15, inArray as inArray5, notExists, sql as sql6 } from "drizzle-orm";
8356
8440
  var log13 = createLogger("Scheduler");
@@ -8393,7 +8477,7 @@ var Scheduler = class {
8393
8477
  const now = (/* @__PURE__ */ new Date()).toISOString();
8394
8478
  for (const project of projectsWithoutHealth) {
8395
8479
  this.db.insert(schedules).values({
8396
- id: crypto17.randomUUID(),
8480
+ id: crypto18.randomUUID(),
8397
8481
  projectId: project.id,
8398
8482
  kind: SchedulableRunKinds.doctor,
8399
8483
  cronExpr: DEFAULT_HEALTH_CRON,
@@ -8552,7 +8636,7 @@ var Scheduler = class {
8552
8636
  log13.warn("gbp-sync.no-callback", { scheduleId, projectId, msg: "host did not register onGbpSyncRequested" });
8553
8637
  return;
8554
8638
  }
8555
- const runId2 = crypto17.randomUUID();
8639
+ const runId2 = crypto18.randomUUID();
8556
8640
  this.db.insert(runs).values({
8557
8641
  id: runId2,
8558
8642
  projectId,
@@ -8585,7 +8669,7 @@ var Scheduler = class {
8585
8669
  this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq15(schedules.id, currentSchedule.id)).run();
8586
8670
  return;
8587
8671
  }
8588
- const runId2 = crypto17.randomUUID();
8672
+ const runId2 = crypto18.randomUUID();
8589
8673
  this.db.insert(runs).values({
8590
8674
  id: runId2,
8591
8675
  projectId,
@@ -8660,7 +8744,7 @@ var Scheduler = class {
8660
8744
  this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq15(schedules.id, currentSchedule.id)).run();
8661
8745
  return;
8662
8746
  }
8663
- const runId2 = crypto17.randomUUID();
8747
+ const runId2 = crypto18.randomUUID();
8664
8748
  this.db.insert(runs).values({
8665
8749
  id: runId2,
8666
8750
  projectId,
@@ -8748,7 +8832,7 @@ async function refreshAllIntegrations(client, projectName) {
8748
8832
 
8749
8833
  // src/notifier.ts
8750
8834
  import { eq as eq16, desc as desc6, and as and12, inArray as inArray6, or } from "drizzle-orm";
8751
- import crypto18 from "crypto";
8835
+ import crypto19 from "crypto";
8752
8836
  var log15 = createLogger("Notifier");
8753
8837
  var Notifier = class {
8754
8838
  db;
@@ -9074,7 +9158,7 @@ var Notifier = class {
9074
9158
  }
9075
9159
  logDelivery(projectId, notificationId, event, status, error) {
9076
9160
  this.db.insert(auditLog).values({
9077
- id: crypto18.randomUUID(),
9161
+ id: crypto19.randomUUID(),
9078
9162
  projectId,
9079
9163
  actor: "scheduler",
9080
9164
  action: `notification.${status}`,
@@ -9236,7 +9320,7 @@ function analysisResultFromInsights(insights2) {
9236
9320
  }
9237
9321
 
9238
9322
  // src/agent/session-registry.ts
9239
- import crypto22 from "crypto";
9323
+ import crypto23 from "crypto";
9240
9324
  import { eq as eq19 } from "drizzle-orm";
9241
9325
 
9242
9326
  // src/agent/session.ts
@@ -9980,7 +10064,7 @@ function buildAeroStateTools(ctx, opts = {}) {
9980
10064
  }
9981
10065
 
9982
10066
  // src/agent/llm-usage.ts
9983
- import crypto19 from "crypto";
10067
+ import crypto20 from "crypto";
9984
10068
  var AeroLlmUsageFeatures = {
9985
10069
  turn: "aero.turn"
9986
10070
  };
@@ -9999,7 +10083,7 @@ function recordLlmUsageEvent(args) {
9999
10083
  const usage = args.message.usage;
10000
10084
  const now = (/* @__PURE__ */ new Date()).toISOString();
10001
10085
  args.db.insert(llmUsageEvents).values({
10002
- id: crypto19.randomUUID(),
10086
+ id: crypto20.randomUUID(),
10003
10087
  projectId: args.projectId,
10004
10088
  runId: args.runId,
10005
10089
  agentSessionId: args.agentSessionId,
@@ -10075,7 +10159,7 @@ function splitAeroAnthropicSystemCachePayload(payload, model) {
10075
10159
  }
10076
10160
 
10077
10161
  // src/agent/tool-usage.ts
10078
- import crypto20 from "crypto";
10162
+ import crypto21 from "crypto";
10079
10163
  var AeroToolEventStatuses = {
10080
10164
  success: "success",
10081
10165
  error: "error"
@@ -10138,7 +10222,7 @@ function createAeroToolUsageHooks(args) {
10138
10222
  function recordAgentToolEvent(args) {
10139
10223
  try {
10140
10224
  args.db.insert(agentToolEvents).values({
10141
- id: crypto20.randomUUID(),
10225
+ id: crypto21.randomUUID(),
10142
10226
  projectId: args.projectId,
10143
10227
  agentSessionId: args.agentSessionId,
10144
10228
  toolCallId: args.toolCallId,
@@ -10365,7 +10449,7 @@ async function loadExternalMcpTools(servers, opts = {}) {
10365
10449
  }
10366
10450
 
10367
10451
  // src/agent/memory-store.ts
10368
- import crypto21 from "crypto";
10452
+ import crypto22 from "crypto";
10369
10453
  import { and as and13, desc as desc7, eq as eq18, like, sql as sql7 } from "drizzle-orm";
10370
10454
  var COMPACTION_KEY_PREFIX = "compaction:";
10371
10455
  var COMPACTION_NOTES_PER_SESSION = 3;
@@ -10394,7 +10478,7 @@ function upsertMemoryEntry(db, args) {
10394
10478
  throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
10395
10479
  }
10396
10480
  const now = (/* @__PURE__ */ new Date()).toISOString();
10397
- const id = crypto21.randomUUID();
10481
+ const id = crypto22.randomUUID();
10398
10482
  db.insert(agentMemory).values({
10399
10483
  id,
10400
10484
  projectId: args.projectId,
@@ -10431,7 +10515,7 @@ function writeCompactionNote(db, args) {
10431
10515
  }
10432
10516
  const now = (/* @__PURE__ */ new Date()).toISOString();
10433
10517
  const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
10434
- const id = crypto21.randomUUID();
10518
+ const id = crypto22.randomUUID();
10435
10519
  let inserted;
10436
10520
  db.transaction((tx) => {
10437
10521
  tx.insert(agentMemory).values({
@@ -10710,7 +10794,7 @@ var SessionRegistry = class {
10710
10794
  }
10711
10795
  const { provider, modelId } = resolveSessionProviderAndModel(this.opts.config, preferences);
10712
10796
  const systemPrompt = loadAeroSystemPrompt();
10713
- const sessionId = crypto22.randomUUID();
10797
+ const sessionId = crypto23.randomUUID();
10714
10798
  const agent = createAeroSession({
10715
10799
  projectName,
10716
10800
  client: this.opts.client,
@@ -11089,7 +11173,7 @@ ${lines.join("\n")}
11089
11173
  insertRow(params) {
11090
11174
  const now = (/* @__PURE__ */ new Date()).toISOString();
11091
11175
  this.opts.db.insert(agentSessions).values({
11092
- id: params.id ?? crypto22.randomUUID(),
11176
+ id: params.id ?? crypto23.randomUUID(),
11093
11177
  projectId: params.projectId,
11094
11178
  systemPrompt: params.systemPrompt,
11095
11179
  modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
@@ -11708,9 +11792,9 @@ var SnapshotService = class {
11708
11792
  };
11709
11793
  }
11710
11794
  async runSnapshotQueries(ctx) {
11711
- const gates = /* @__PURE__ */ new Map();
11795
+ const gates2 = /* @__PURE__ */ new Map();
11712
11796
  for (const provider of ctx.providers) {
11713
- gates.set(
11797
+ gates2.set(
11714
11798
  provider.adapter.name,
11715
11799
  getSharedProviderExecutionGate(
11716
11800
  provider.adapter.name,
@@ -11723,7 +11807,7 @@ var SnapshotService = class {
11723
11807
  return Promise.all(ctx.queries.map(async (query) => ({
11724
11808
  query,
11725
11809
  providerResults: await Promise.all(ctx.providers.map(async (provider) => {
11726
- const gate = gates.get(provider.adapter.name);
11810
+ const gate = gates2.get(provider.adapter.name);
11727
11811
  return gate.run(async () => {
11728
11812
  try {
11729
11813
  const raw = await provider.adapter.executeTrackedQuery(
@@ -12265,14 +12349,14 @@ function summarizeProviderConfig(config) {
12265
12349
  };
12266
12350
  }
12267
12351
  function hashApiKey(key) {
12268
- return crypto23.createHash("sha256").update(key).digest("hex");
12352
+ return crypto24.createHash("sha256").update(key).digest("hex");
12269
12353
  }
12270
12354
  var DASHBOARD_SCRYPT_KEYLEN = 64;
12271
12355
  var DASHBOARD_SCRYPT_COST = 1 << 15;
12272
12356
  var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
12273
12357
  function hashDashboardPassword(password) {
12274
- const salt = crypto23.randomBytes(16);
12275
- const derived = crypto23.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
12358
+ const salt = crypto24.randomBytes(16);
12359
+ const derived = crypto24.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
12276
12360
  N: DASHBOARD_SCRYPT_COST,
12277
12361
  maxmem: DASHBOARD_SCRYPT_MAXMEM
12278
12362
  });
@@ -12293,14 +12377,14 @@ function verifyDashboardPassword(password, storedHash) {
12293
12377
  } catch {
12294
12378
  return { ok: false, needsRehash: false };
12295
12379
  }
12296
- const derived = crypto23.scryptSync(password, salt, expected.length, {
12380
+ const derived = crypto24.scryptSync(password, salt, expected.length, {
12297
12381
  N: DASHBOARD_SCRYPT_COST,
12298
12382
  maxmem: DASHBOARD_SCRYPT_MAXMEM
12299
12383
  });
12300
12384
  if (derived.length !== expected.length)
12301
12385
  return { ok: false, needsRehash: false };
12302
12386
  return {
12303
- ok: crypto23.timingSafeEqual(derived, expected),
12387
+ ok: crypto24.timingSafeEqual(derived, expected),
12304
12388
  needsRehash: false
12305
12389
  };
12306
12390
  }
@@ -12309,7 +12393,7 @@ function verifyDashboardPassword(password, storedHash) {
12309
12393
  const expected = Buffer.from(storedHash, "hex");
12310
12394
  if (candidate.length !== expected.length)
12311
12395
  return { ok: false, needsRehash: false };
12312
- const ok = crypto23.timingSafeEqual(candidate, expected);
12396
+ const ok = crypto24.timingSafeEqual(candidate, expected);
12313
12397
  return { ok, needsRehash: ok };
12314
12398
  }
12315
12399
  return { ok: false, needsRehash: false };
@@ -13045,7 +13129,7 @@ async function createServer(opts) {
13045
13129
  return removed;
13046
13130
  }
13047
13131
  };
13048
- const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto23.randomBytes(32).toString("hex");
13132
+ const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto24.randomBytes(32).toString("hex");
13049
13133
  const googleConnectionStore = {
13050
13134
  listConnections: (domain) => listGoogleConnections(opts.config, domain),
13051
13135
  getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
@@ -13124,7 +13208,7 @@ async function createServer(opts) {
13124
13208
  if (!existing) {
13125
13209
  const prefix = opts.config.apiKey.slice(0, 12);
13126
13210
  opts.db.insert(apiKeys).values({
13127
- id: `key_${crypto23.randomBytes(8).toString("hex")}`,
13211
+ id: `key_${crypto24.randomBytes(8).toString("hex")}`,
13128
13212
  name: "default",
13129
13213
  keyHash,
13130
13214
  keyPrefix: prefix,
@@ -13148,7 +13232,7 @@ async function createServer(opts) {
13148
13232
  };
13149
13233
  const createSession = (apiKeyId) => {
13150
13234
  pruneExpiredSessions();
13151
- const sessionId = crypto23.randomBytes(32).toString("hex");
13235
+ const sessionId = crypto24.randomBytes(32).toString("hex");
13152
13236
  sessions.set(sessionId, {
13153
13237
  apiKeyId,
13154
13238
  expiresAt: Date.now() + SESSION_TTL_MS
@@ -13447,7 +13531,11 @@ async function createServer(opts) {
13447
13531
  executeGscSync(opts.db, runId, projectId, {
13448
13532
  ...syncOpts,
13449
13533
  config: opts.config
13450
- }).then(() => maybeRefreshGscCoverage(opts.db, opts.config, projectId)).catch((err) => {
13534
+ }).then(
13535
+ () => maybeRefreshGscCoverage(opts.db, opts.config, projectId, void 0, Date.now(), {
13536
+ userInitiated: runWasUserInitiated(opts.db, runId)
13537
+ })
13538
+ ).catch((err) => {
13451
13539
  app.log.error({ runId, err }, "GSC sync failed");
13452
13540
  });
13453
13541
  },
@@ -13504,7 +13592,7 @@ async function createServer(opts) {
13504
13592
  deps: {
13505
13593
  enqueueAutoExtract: ({ projectId, release: r }) => {
13506
13594
  const now = (/* @__PURE__ */ new Date()).toISOString();
13507
- const runId = crypto23.randomUUID();
13595
+ const runId = crypto24.randomUUID();
13508
13596
  opts.db.insert(runs).values({
13509
13597
  id: runId,
13510
13598
  projectId,
@@ -13631,7 +13719,14 @@ async function createServer(opts) {
13631
13719
  }).then(() => {
13632
13720
  const finished = opts.db.select({ status: runs.status }).from(runs).where(eq22(runs.id, runId)).get();
13633
13721
  if (finished?.status === RunStatuses.completed || finished?.status === RunStatuses.partial) {
13634
- return maybeRefreshGscCoverage(opts.db, opts.config, projectId);
13722
+ return maybeRefreshGscCoverage(
13723
+ opts.db,
13724
+ opts.config,
13725
+ projectId,
13726
+ void 0,
13727
+ Date.now(),
13728
+ { userInitiated: runWasUserInitiated(opts.db, runId) }
13729
+ );
13635
13730
  }
13636
13731
  return null;
13637
13732
  }).catch((err) => {
@@ -13724,7 +13819,7 @@ async function createServer(opts) {
13724
13819
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
13725
13820
  opts.db.insert(auditLog).values(
13726
13821
  targetProjectIds.map((projectId) => ({
13727
- id: crypto23.randomUUID(),
13822
+ id: crypto24.randomUUID(),
13728
13823
  projectId,
13729
13824
  actor: "api",
13730
13825
  action: existing ? "provider.updated" : "provider.created",