@ainyc/canonry 4.180.7 → 4.180.8

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 (34) hide show
  1. package/README.md +4 -0
  2. package/assets/agent-workspace/skills/canonry/SKILL.md +13 -10
  3. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +7 -6
  4. package/assets/assets/{AuditHistoryPanel-B1lmI7wm.js → AuditHistoryPanel-qiuqNGvG.js} +1 -1
  5. package/assets/assets/{BacklinksPage-Bw61uQu8.js → BacklinksPage-D-g5f0Xv.js} +1 -1
  6. package/assets/assets/{HistoryPage-D8z9-NkT.js → HistoryPage-q8_pCuOs.js} +1 -1
  7. package/assets/assets/{MeasurementPropertyPage-CXX9GFj8.js → MeasurementPropertyPage-C_wMjUZe.js} +1 -1
  8. package/assets/assets/ProjectPage-33THfDtC.js +9 -0
  9. package/assets/assets/{RunRow-DXHy16T3.js → RunRow-D5hatdlY.js} +1 -1
  10. package/assets/assets/{RunsPage-D2aE620i.js → RunsPage-Bj2snMM3.js} +1 -1
  11. package/assets/assets/SettingsPage-Cf5YgyJv.js +1 -0
  12. package/assets/assets/SiteHealthSection-D8RLvMAE.js +4 -0
  13. package/assets/assets/{TrafficPage-DFHWgvkU.js → TrafficPage-CotJ14le.js} +1 -1
  14. package/assets/assets/{TrafficSourceDetailPage-DxjyRL8Y.js → TrafficSourceDetailPage-BcxYX-uq.js} +1 -1
  15. package/assets/assets/{extract-error-message-BdXUZQW9.js → extract-error-message-Bxt42scd.js} +1 -1
  16. package/assets/assets/index-BlILyZJ5.js +86 -0
  17. package/assets/assets/index-DXCQf5s1.css +1 -0
  18. package/assets/assets/{react-sigma_core.esm.min-DAcOZae7.js → react-sigma_core.esm.min-DxZINEmi.js} +1 -1
  19. package/assets/index.html +2 -2
  20. package/dist/{chunk-XYTSKRO3.js → chunk-G7M2KM7Q.js} +2 -2
  21. package/dist/{chunk-VZO6CLHY.js → chunk-GFVZOVG3.js} +105 -64
  22. package/dist/{chunk-RXXZ5BU5.js → chunk-GN2G7VFK.js} +24 -1
  23. package/dist/{chunk-CWXPBQR5.js → chunk-IM4HFNGF.js} +7 -5
  24. package/dist/{chunk-WJFBNCVD.js → chunk-ZJ2VGTYU.js} +197 -95
  25. package/dist/cli.js +187 -96
  26. package/dist/index.js +4 -4
  27. package/dist/{intelligence-service-MEPYXRBS.js → intelligence-service-IURLYRB3.js} +2 -2
  28. package/dist/mcp.js +3 -3
  29. package/package.json +10 -10
  30. package/assets/assets/ProjectPage-BaN_rvoV.js +0 -9
  31. package/assets/assets/SettingsPage-BiiFqarS.js +0 -1
  32. package/assets/assets/SiteHealthSection-DCH2Ejtc.js +0 -4
  33. package/assets/assets/index-BuEq5Rhg.css +0 -1
  34. package/assets/assets/index-CSictO9U.js +0 -83
@@ -480,6 +480,7 @@ import {
480
480
  mentionStateFromAnswerMentioned,
481
481
  missingDependency,
482
482
  modelIdsEquivalent,
483
+ nextScheduleUpdatedAt,
483
484
  noProvider,
484
485
  noQueries,
485
486
  normalizeIdTokens,
@@ -558,7 +559,9 @@ import {
558
559
  runtimeStateMissing,
559
560
  schedulableRunKindSchema,
560
561
  scheduleDtoSchema,
562
+ scheduleExpectedUpdatedAtSchema,
561
563
  scheduleUpsertRequestSchema,
564
+ scheduleVersionConflict,
562
565
  seedCollapseWarning,
563
566
  segmentCrawlerHits,
564
567
  serializeRunError,
@@ -643,10 +646,10 @@ import {
643
646
  wordpressSchemaDeployResultDtoSchema,
644
647
  wordpressSchemaStatusResultDtoSchema,
645
648
  wordpressStatusDtoSchema
646
- } from "./chunk-RXXZ5BU5.js";
649
+ } from "./chunk-GN2G7VFK.js";
647
650
 
648
651
  // src/intelligence-service.ts
649
- import { eq as eq62, desc as desc27, asc as asc11, and as and51, ne as ne8, or as or14, inArray as inArray22, gte as gte15, lte as lte13, isNull as isNull9, sql as sql25, exists } from "drizzle-orm";
652
+ import { eq as eq62, desc as desc27, asc as asc12, and as and51, ne as ne8, or as or14, inArray as inArray22, gte as gte15, lte as lte13, isNull as isNull9, sql as sql25, exists } from "drizzle-orm";
650
653
 
651
654
  // ../db/src/client.ts
652
655
  import { mkdirSync } from "fs";
@@ -11346,6 +11349,13 @@ function pruneProviderModelsForProviders(models, providers) {
11346
11349
 
11347
11350
  // ../api-routes/src/projects.ts
11348
11351
  async function projectRoutes(app, opts) {
11352
+ const notifyProjectCreated = (projectId, projectName) => {
11353
+ try {
11354
+ opts.onProjectCreated?.(projectId, projectName);
11355
+ } catch (error) {
11356
+ app.log.error({ error, projectId, projectName }, "Project-created callback failed after commit");
11357
+ }
11358
+ };
11349
11359
  app.post("/projects", async (request, reply) => {
11350
11360
  requireAdminSession(request);
11351
11361
  if (request.principal?.projectId) {
@@ -11440,6 +11450,7 @@ async function projectRoutes(app, opts) {
11440
11450
  return true;
11441
11451
  });
11442
11452
  if (!inserted) throw alreadyExists("Project", name);
11453
+ notifyProjectCreated(id, name);
11443
11454
  opts.onProjectUpserted?.(id, name);
11444
11455
  const created = app.db.select().from(projects).where(eq5(projects.id, id)).get();
11445
11456
  return reply.status(201).send(formatProject(created));
@@ -11559,6 +11570,7 @@ async function projectRoutes(app, opts) {
11559
11570
  entityId: id
11560
11571
  });
11561
11572
  });
11573
+ notifyProjectCreated(id, name);
11562
11574
  opts.onProjectUpserted?.(id, name);
11563
11575
  const created = app.db.select().from(projects).where(eq5(projects.id, id)).get();
11564
11576
  return reply.status(201).send(formatProject(created));
@@ -13481,11 +13493,20 @@ function normalizeProviders(values) {
13481
13493
  return [...new Set(values.map((value) => value.trim().toLocaleLowerCase("en")).filter(Boolean))].sort();
13482
13494
  }
13483
13495
  function resolveRunProviderSelection(input) {
13496
+ return resolveRunnableProviderSelection(input).selectedProviders;
13497
+ }
13498
+ function resolveRunnableProviderSelection(input) {
13499
+ const availableProviders = normalizeProviders(input.runnableProviders ?? []);
13484
13500
  const requested = normalizeProviders(input.requestedProviders ?? []);
13485
- if (requested.length) return requested;
13486
13501
  const project = normalizeProviders(input.projectProviders ?? []);
13487
- if (project.length) return project;
13488
- return normalizeProviders(input.runnableProviders ?? []);
13502
+ const selectedProviders = requested.length > 0 ? requested : project.length > 0 ? project : availableProviders;
13503
+ const available = new Set(availableProviders);
13504
+ return {
13505
+ availableProviders,
13506
+ selectedProviders,
13507
+ runnableProviders: selectedProviders.filter((provider) => available.has(provider)),
13508
+ selectionSource: requested.length > 0 ? "request" : project.length > 0 ? "project" : "instance"
13509
+ };
13489
13510
  }
13490
13511
  function providerRoster(tx, params) {
13491
13512
  return resolveRunProviderSelection({
@@ -14146,22 +14167,18 @@ function answerVisibilityPreflightError(input) {
14146
14167
  if (input.trackedQueryCount === 0) {
14147
14168
  return noQueries(input.projectName);
14148
14169
  }
14149
- const availableProviders = normalizeProviderNames(input.runnableProviderNames);
14150
- const requestedProviders = normalizeProviderNames(input.requestedProviders ?? []);
14151
- const projectProviders = normalizeProviderNames(input.projectProviders);
14152
- const selectedProviders = requestedProviders.length > 0 ? requestedProviders : projectProviders.length > 0 ? projectProviders : availableProviders;
14153
- const available = new Set(availableProviders);
14154
- const runnableProviders = selectedProviders.filter((provider) => available.has(provider));
14155
- if (runnableProviders.length > 0) return null;
14170
+ const selection = resolveRunnableProviderSelection({
14171
+ requestedProviders: input.requestedProviders,
14172
+ projectProviders: input.projectProviders,
14173
+ runnableProviders: input.runnableProviderNames
14174
+ });
14175
+ if (selection.runnableProviders.length > 0) return null;
14156
14176
  return noProvider(input.projectName, {
14157
- availableProviders,
14158
- selectedProviders,
14159
- selectionSource: requestedProviders.length > 0 ? "request" : projectProviders.length > 0 ? "project" : "instance"
14177
+ availableProviders: selection.availableProviders,
14178
+ selectedProviders: selection.selectedProviders,
14179
+ selectionSource: selection.selectionSource
14160
14180
  });
14161
14181
  }
14162
- function normalizeProviderNames(providerNames) {
14163
- return [...new Set(providerNames.map((name) => name.trim().toLowerCase()).filter(Boolean))];
14164
- }
14165
14182
  function parseRunTriggerRequest(value) {
14166
14183
  const result = runTriggerRequestSchema.safeParse(value);
14167
14184
  if (result.success) return result.data;
@@ -17463,6 +17480,13 @@ async function measurementDraftRoutes(app, opts = {}) {
17463
17480
  state,
17464
17481
  nextAction,
17465
17482
  mode: activeSchemaVersion === 1 ? "active-v1" : activeSchemaVersion === 2 ? "active-v2" : draft ? "draft-only" : "simple",
17483
+ // This project-readable response is also the dashboard's readiness
17484
+ // source. The boolean exposes no instance capability inventory and uses
17485
+ // the exact provider-selection decision enforced by run preflight.
17486
+ answerVisibilityProviderReady: resolveRunnableProviderSelection({
17487
+ projectProviders: project.providers,
17488
+ runnableProviders: opts.getRunnableProviderNames?.()
17489
+ }).runnableProviders.length > 0,
17466
17490
  activeRevision: active?.revision ?? null,
17467
17491
  activeSchemaVersion,
17468
17492
  draft: draft ? { etag: measurementDraftEtag(draft.etagVersion), updatedAt: draft.updatedAt } : null
@@ -20809,6 +20833,13 @@ function stripIpv6Brackets2(value) {
20809
20833
  // ../api-routes/src/apply.ts
20810
20834
  async function applyRoutes(app, opts) {
20811
20835
  const allowLoopback = opts?.allowLoopbackWebhooks === true;
20836
+ const notifyProjectCreated = (projectId, projectName) => {
20837
+ try {
20838
+ opts?.onProjectCreated?.(projectId, projectName);
20839
+ } catch (error) {
20840
+ app.log.error({ error, projectId, projectName }, "Project-created callback failed after commit");
20841
+ }
20842
+ };
20812
20843
  app.post("/apply", async (request, reply) => {
20813
20844
  const parsed = projectConfigSchema.safeParse(request.body);
20814
20845
  if (!parsed.success) {
@@ -20885,6 +20916,7 @@ async function applyRoutes(app, opts) {
20885
20916
  }
20886
20917
  assertProviderModelScope(request, target?.providerModels ?? {}, providerModels, specProviders);
20887
20918
  let projectId;
20919
+ const lifecycle = { projectCreated: false };
20888
20920
  let scheduleAction = null;
20889
20921
  let aliasesChanged = false;
20890
20922
  app.db.transaction((tx) => {
@@ -20923,6 +20955,7 @@ async function applyRoutes(app, opts) {
20923
20955
  });
20924
20956
  } else {
20925
20957
  projectId = crypto21.randomUUID();
20958
+ lifecycle.projectCreated = true;
20926
20959
  tx.insert(projects).values({
20927
20960
  id: projectId,
20928
20961
  name,
@@ -20992,7 +21025,7 @@ async function applyRoutes(app, opts) {
20992
21025
  timezone: resolvedSchedule.timezone,
20993
21026
  providers: config.spec.schedule?.providers ?? [],
20994
21027
  ...specEnabled === void 0 ? {} : { enabled: specEnabled },
20995
- updatedAt: now
21028
+ updatedAt: nextScheduleUpdatedAt(existingSched.updatedAt, Date.parse(now))
20996
21029
  }).where(eq22(schedules.id, existingSched.id)).run();
20997
21030
  } else {
20998
21031
  tx.insert(schedules).values({
@@ -21039,6 +21072,9 @@ async function applyRoutes(app, opts) {
21039
21072
  });
21040
21073
  }
21041
21074
  });
21075
+ if (lifecycle.projectCreated) {
21076
+ notifyProjectCreated(projectId, config.metadata.name);
21077
+ }
21042
21078
  if (scheduleAction) {
21043
21079
  opts?.onScheduleUpdated?.(scheduleAction, projectId, SchedulableRunKinds["answer-visibility"]);
21044
21080
  }
@@ -30765,6 +30801,12 @@ var scheduleKindQueryParameter = {
30765
30801
  description: 'Schedulable run kind. Defaults to "answer-visibility" for backward compatibility.',
30766
30802
  schema: { $ref: "#/components/schemas/SchedulableRunKind" }
30767
30803
  };
30804
+ var scheduleExpectedUpdatedAtQueryParameter = {
30805
+ name: "expectedUpdatedAt",
30806
+ in: "query",
30807
+ description: "Delete only the schedule version carrying this exact updatedAt timestamp. A mismatch returns 409.",
30808
+ schema: { type: "string", format: "date-time" }
30809
+ };
30768
30810
  var runsListKindQueryParameter = {
30769
30811
  name: "kind",
30770
30812
  in: "query",
@@ -33025,7 +33067,13 @@ var routeCatalog = [
33025
33067
  timezone: stringSchema,
33026
33068
  providers: stringArraySchema,
33027
33069
  enabled: booleanSchema,
33028
- sourceId: stringSchema
33070
+ sourceId: stringSchema,
33071
+ expectedUpdatedAt: {
33072
+ type: "string",
33073
+ format: "date-time",
33074
+ nullable: true,
33075
+ description: "Update only this exact version; null creates only while absent. Omit for legacy unconditional behavior."
33076
+ }
33029
33077
  }
33030
33078
  }
33031
33079
  }
@@ -33034,7 +33082,18 @@ var routeCatalog = [
33034
33082
  responses: {
33035
33083
  200: jsonResponse("Schedule updated.", "ScheduleDto"),
33036
33084
  201: jsonResponse("Schedule created.", "ScheduleDto"),
33037
- 400: errorResponse("Invalid payload (e.g. sourceId missing for kind=traffic-sync, or providers set for kind=traffic-sync).")
33085
+ 400: errorResponse("Invalid payload (e.g. sourceId missing for kind=traffic-sync, or providers set for kind=traffic-sync)."),
33086
+ 409: errorResponse("The schedule changed since the caller loaded it.")
33087
+ }
33088
+ },
33089
+ {
33090
+ method: "get",
33091
+ path: "/api/v1/projects/{name}/schedules",
33092
+ summary: "List schedules",
33093
+ tags: ["schedules"],
33094
+ parameters: [nameParameter],
33095
+ responses: {
33096
+ 200: jsonArrayResponse("Schedules returned.", "ScheduleDto")
33038
33097
  }
33039
33098
  },
33040
33099
  {
@@ -33053,10 +33112,11 @@ var routeCatalog = [
33053
33112
  path: "/api/v1/projects/{name}/schedule",
33054
33113
  summary: "Delete a schedule",
33055
33114
  tags: ["schedules"],
33056
- parameters: [nameParameter, scheduleKindQueryParameter],
33115
+ parameters: [nameParameter, scheduleKindQueryParameter, scheduleExpectedUpdatedAtQueryParameter],
33057
33116
  responses: {
33058
33117
  204: { description: "Schedule deleted." },
33059
- 404: errorResponse("Schedule not found.")
33118
+ 404: errorResponse("Schedule not found."),
33119
+ 409: errorResponse("The schedule changed since the caller loaded it.")
33060
33120
  }
33061
33121
  },
33062
33122
  {
@@ -37818,7 +37878,7 @@ async function telemetryRoutes(app, opts) {
37818
37878
 
37819
37879
  // ../api-routes/src/schedules.ts
37820
37880
  import crypto25 from "crypto";
37821
- import { and as and31, eq as eq38 } from "drizzle-orm";
37881
+ import { and as and31, asc as asc7, eq as eq38 } from "drizzle-orm";
37822
37882
  function parseKindParam(raw) {
37823
37883
  if (raw === void 0 || raw === null || raw === "") return SchedulableRunKinds["answer-visibility"];
37824
37884
  const parsed = schedulableRunKindSchema.safeParse(raw);
@@ -37827,6 +37887,12 @@ function parseKindParam(raw) {
37827
37887
  }
37828
37888
  return parsed.data;
37829
37889
  }
37890
+ function parseExpectedUpdatedAtParam(raw) {
37891
+ if (raw === void 0) return void 0;
37892
+ const parsed = scheduleExpectedUpdatedAtSchema.safeParse(raw);
37893
+ if (!parsed.success) throw validationError("Invalid expectedUpdatedAt timestamp");
37894
+ return parsed.data;
37895
+ }
37830
37896
  async function scheduleRoutes(app, opts) {
37831
37897
  app.put("/projects/:name/schedule", async (request, reply) => {
37832
37898
  const project = resolveProject(app.db, request.params.name);
@@ -37840,7 +37906,7 @@ async function scheduleRoutes(app, opts) {
37840
37906
  });
37841
37907
  }
37842
37908
  const kind = parsedBody.data.kind ?? parseKindParam(request.query?.kind);
37843
- const { preset, cron, timezone, providers, enabled, sourceId } = parsedBody.data;
37909
+ const { preset, cron, timezone, providers, enabled, sourceId, expectedUpdatedAt } = parsedBody.data;
37844
37910
  if (kind === SchedulableRunKinds["traffic-sync"]) {
37845
37911
  if (!sourceId) {
37846
37912
  throw validationError('"sourceId" is required when kind is "traffic-sync"');
@@ -37888,11 +37954,16 @@ async function scheduleRoutes(app, opts) {
37888
37954
  throw validationError(`Invalid cron expression: ${cronExpr}`);
37889
37955
  }
37890
37956
  }
37891
- const now = (/* @__PURE__ */ new Date()).toISOString();
37892
37957
  const enabledBool = enabled !== false;
37893
- const existing = app.db.select().from(schedules).where(and31(eq38(schedules.projectId, project.id), eq38(schedules.kind, kind))).get();
37894
- if (existing) {
37895
- app.db.update(schedules).set({
37958
+ const mutation = app.db.transaction((tx) => {
37959
+ const existing = tx.select().from(schedules).where(and31(eq38(schedules.projectId, project.id), eq38(schedules.kind, kind))).get();
37960
+ const actualUpdatedAt = existing?.updatedAt ?? null;
37961
+ if (expectedUpdatedAt !== void 0 && expectedUpdatedAt !== actualUpdatedAt) {
37962
+ throw scheduleVersionConflict(expectedUpdatedAt, actualUpdatedAt);
37963
+ }
37964
+ const now = nextScheduleUpdatedAt(existing?.updatedAt);
37965
+ const scheduleId = existing?.id ?? crypto25.randomUUID();
37966
+ const values = {
37896
37967
  cronExpr,
37897
37968
  preset: preset ?? null,
37898
37969
  timezone,
@@ -37900,32 +37971,48 @@ async function scheduleRoutes(app, opts) {
37900
37971
  sourceId: sourceId ?? null,
37901
37972
  enabled: enabledBool,
37902
37973
  updatedAt: now
37903
- }).where(eq38(schedules.id, existing.id)).run();
37904
- } else {
37905
- app.db.insert(schedules).values({
37906
- id: crypto25.randomUUID(),
37974
+ };
37975
+ if (existing) {
37976
+ const changed = tx.update(schedules).set(values).where(
37977
+ expectedUpdatedAt === void 0 ? eq38(schedules.id, existing.id) : and31(eq38(schedules.id, existing.id), eq38(schedules.updatedAt, expectedUpdatedAt ?? existing.updatedAt))
37978
+ ).run();
37979
+ if (changed.changes !== 1) {
37980
+ const actual = tx.select().from(schedules).where(eq38(schedules.id, existing.id)).get();
37981
+ throw scheduleVersionConflict(expectedUpdatedAt ?? existing.updatedAt, actual?.updatedAt ?? null);
37982
+ }
37983
+ } else {
37984
+ const insert = tx.insert(schedules).values({
37985
+ id: scheduleId,
37986
+ projectId: project.id,
37987
+ kind,
37988
+ ...values,
37989
+ createdAt: now
37990
+ });
37991
+ const inserted = expectedUpdatedAt === null ? insert.onConflictDoNothing().run() : insert.run();
37992
+ if (expectedUpdatedAt === null && inserted.changes !== 1) {
37993
+ const actual = tx.select().from(schedules).where(and31(eq38(schedules.projectId, project.id), eq38(schedules.kind, kind))).get();
37994
+ throw scheduleVersionConflict(null, actual?.updatedAt ?? null);
37995
+ }
37996
+ }
37997
+ writeAuditLog(tx, {
37907
37998
  projectId: project.id,
37908
- kind,
37909
- cronExpr,
37910
- preset: preset ?? null,
37911
- timezone,
37912
- enabled: enabledBool,
37913
- providers: providers ?? [],
37914
- sourceId: sourceId ?? null,
37915
- createdAt: now,
37916
- updatedAt: now
37917
- }).run();
37918
- }
37919
- writeAuditLog(app.db, {
37920
- projectId: project.id,
37921
- actor: "api",
37922
- action: existing ? "schedule.updated" : "schedule.created",
37923
- entityType: "schedule",
37924
- diff: { kind, cronExpr, preset, timezone, providers, sourceId }
37999
+ actor: "api",
38000
+ action: existing ? "schedule.updated" : "schedule.created",
38001
+ entityType: "schedule",
38002
+ diff: { kind, cronExpr, preset, timezone, providers, sourceId }
38003
+ });
38004
+ return {
38005
+ existed: existing !== void 0
38006
+ };
37925
38007
  });
37926
38008
  opts.onScheduleUpdated?.("upsert", project.id, kind);
37927
38009
  const schedule = app.db.select().from(schedules).where(and31(eq38(schedules.projectId, project.id), eq38(schedules.kind, kind))).get();
37928
- return reply.status(existing ? 200 : 201).send(formatSchedule(schedule));
38010
+ return reply.status(mutation.existed ? 200 : 201).send(formatSchedule(schedule));
38011
+ });
38012
+ app.get("/projects/:name/schedules", async (request, reply) => {
38013
+ const project = resolveProject(app.db, request.params.name);
38014
+ const rows = app.db.select().from(schedules).where(eq38(schedules.projectId, project.id)).orderBy(asc7(schedules.kind)).all();
38015
+ return reply.send(rows.map(formatSchedule));
37929
38016
  });
37930
38017
  app.get("/projects/:name/schedule", async (request, reply) => {
37931
38018
  const project = resolveProject(app.db, request.params.name);
@@ -37939,18 +38026,31 @@ async function scheduleRoutes(app, opts) {
37939
38026
  app.delete("/projects/:name/schedule", async (request, reply) => {
37940
38027
  const project = resolveProject(app.db, request.params.name);
37941
38028
  const kind = parseKindParam(request.query?.kind);
37942
- const schedule = app.db.select().from(schedules).where(and31(eq38(schedules.projectId, project.id), eq38(schedules.kind, kind))).get();
37943
- if (!schedule) {
37944
- throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
37945
- }
37946
- app.db.delete(schedules).where(eq38(schedules.id, schedule.id)).run();
37947
- writeAuditLog(app.db, {
37948
- projectId: project.id,
37949
- actor: "api",
37950
- action: "schedule.deleted",
37951
- entityType: "schedule",
37952
- entityId: schedule.id,
37953
- diff: { kind }
38029
+ const expectedUpdatedAt = parseExpectedUpdatedAtParam(request.query?.expectedUpdatedAt);
38030
+ app.db.transaction((tx) => {
38031
+ const schedule = tx.select().from(schedules).where(and31(eq38(schedules.projectId, project.id), eq38(schedules.kind, kind))).get();
38032
+ if (!schedule) {
38033
+ if (expectedUpdatedAt !== void 0) throw scheduleVersionConflict(expectedUpdatedAt, null);
38034
+ throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
38035
+ }
38036
+ if (expectedUpdatedAt !== void 0 && schedule.updatedAt !== expectedUpdatedAt) {
38037
+ throw scheduleVersionConflict(expectedUpdatedAt, schedule.updatedAt);
38038
+ }
38039
+ const deleted = tx.delete(schedules).where(
38040
+ expectedUpdatedAt === void 0 ? eq38(schedules.id, schedule.id) : and31(eq38(schedules.id, schedule.id), eq38(schedules.updatedAt, expectedUpdatedAt))
38041
+ ).run();
38042
+ if (deleted.changes !== 1) {
38043
+ const actual = tx.select().from(schedules).where(eq38(schedules.id, schedule.id)).get();
38044
+ throw scheduleVersionConflict(expectedUpdatedAt ?? schedule.updatedAt, actual?.updatedAt ?? null);
38045
+ }
38046
+ writeAuditLog(tx, {
38047
+ projectId: project.id,
38048
+ actor: "api",
38049
+ action: "schedule.deleted",
38050
+ entityType: "schedule",
38051
+ entityId: schedule.id,
38052
+ diff: { kind }
38053
+ });
37954
38054
  });
37955
38055
  opts.onScheduleUpdated?.("delete", project.id, kind);
37956
38056
  return reply.status(204).send();
@@ -43474,7 +43574,7 @@ async function googleMarketingRoutes(app, opts) {
43474
43574
 
43475
43575
  // ../api-routes/src/ads.ts
43476
43576
  import crypto35 from "crypto";
43477
- import { eq as eq43, and as and35, asc as asc8, desc as desc19, gt as gt2, gte as gte10, lt as lt8, lte as lte9, ne as ne5, inArray as inArray17, or as or10, isNull as isNull5, isNotNull as isNotNull2, sql as sql17 } from "drizzle-orm";
43577
+ import { eq as eq43, and as and35, asc as asc9, desc as desc19, gt as gt2, gte as gte10, lt as lt8, lte as lte9, ne as ne5, inArray as inArray17, or as or10, isNull as isNull5, isNotNull as isNotNull2, sql as sql17 } from "drizzle-orm";
43478
43578
 
43479
43579
  // ../api-routes/src/ads-activation.ts
43480
43580
  import crypto33 from "crypto";
@@ -44860,7 +44960,7 @@ async function executeApprovedAdsActivation(dependencies, request) {
44860
44960
 
44861
44961
  // ../api-routes/src/ads-activation-routes.ts
44862
44962
  import crypto34 from "crypto";
44863
- import { and as and34, asc as asc7, eq as eq42, gt, isNotNull, isNull as isNull4, lte as lte8, or as or9, sql as sql16 } from "drizzle-orm";
44963
+ import { and as and34, asc as asc8, eq as eq42, gt, isNotNull, isNull as isNull4, lte as lte8, or as or9, sql as sql16 } from "drizzle-orm";
44864
44964
  var MAX_ACTIVATION_GRANT_LIFETIME_MS = 24 * 60 * 60 * 1e3;
44865
44965
  var ACTIVATION_LEASE_MS = 5 * 6e4;
44866
44966
  var ACTIVATION_REVOCATION_SETTLEMENT_MS = ACTIVATION_LEASE_MS;
@@ -44899,7 +44999,7 @@ function loadActivationOperation(db, operationId) {
44899
44999
  if (row.kind !== AdsOperationKinds.campaign_tree_activate) {
44900
45000
  throw internalError("Ads activation grant is bound to a non-activation receipt");
44901
45001
  }
44902
- const steps = db.select().from(adsOperationSteps).where(eq42(adsOperationSteps.operationId, operationId)).orderBy(asc7(adsOperationSteps.ordinal)).all().map(toStepDto);
45002
+ const steps = db.select().from(adsOperationSteps).where(eq42(adsOperationSteps.operationId, operationId)).orderBy(asc8(adsOperationSteps.ordinal)).all().map(toStepDto);
44903
45003
  return {
44904
45004
  ...row,
44905
45005
  kind: AdsOperationKinds.campaign_tree_activate,
@@ -45455,9 +45555,9 @@ function selectWatchdogOperations(app, nowIso, batchSize, allowLiveRevocationLea
45455
45555
  eq42(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
45456
45556
  eligibleState
45457
45557
  )).orderBy(
45458
- asc7(freshRevocationPriority),
45459
- asc7(roundRobinCursor),
45460
- asc7(adsOperations.updatedAt)
45558
+ asc8(freshRevocationPriority),
45559
+ asc8(roundRobinCursor),
45560
+ asc8(adsOperations.updatedAt)
45461
45561
  ).limit(batchSize).all().map((row) => row.operation);
45462
45562
  }
45463
45563
  function selectRecoveryWatchdogOperations(app, nowIso, batchSize) {
@@ -46508,7 +46608,7 @@ function semanticallyMatchesMaterializedEntity(app, input) {
46508
46608
  eq43(adsOperations.entityType, input.entityType),
46509
46609
  eq43(adsOperations.entityId, input.entityId),
46510
46610
  inArray17(adsOperations.kind, [createKind, updateKind])
46511
- )).orderBy(asc8(adsOperations.createdAt), asc8(adsOperations.id)).all();
46611
+ )).orderBy(asc9(adsOperations.createdAt), asc9(adsOperations.id)).all();
46512
46612
  if (rows.some((row) => row.state === AdsOperationStates.pending || row.state === AdsOperationStates.reconciling || row.state === AdsOperationStates.unknown || row.kind === updateKind && row.state === AdsOperationStates.succeeded)) {
46513
46613
  return false;
46514
46614
  }
@@ -47195,7 +47295,7 @@ async function sweepAdsOperationsOnce(app, opts, config) {
47195
47295
  isNull5(adsOperations.errorCode),
47196
47296
  ne5(adsOperations.errorCode, ADS_RECONCILIATION_QUARANTINED)
47197
47297
  )
47198
- )).orderBy(asc8(adsOperations.updatedAt)).limit(config.batchSize).all();
47298
+ )).orderBy(asc9(adsOperations.updatedAt)).limit(config.batchSize).all();
47199
47299
  for (const row of exhaustedRows) {
47200
47300
  quarantineExhaustedReconciliation(app, row, now, config.policy, { actor: "system" });
47201
47301
  }
@@ -47218,7 +47318,7 @@ async function sweepAdsOperationsOnce(app, opts, config) {
47218
47318
  const rows = app.db.select().from(adsOperations).where(and35(
47219
47319
  inArray17(adsOperations.projectId, eligibleProjectIds),
47220
47320
  staleReconcileWhere
47221
- )).orderBy(asc8(adsOperations.updatedAt)).limit(config.batchSize).all();
47321
+ )).orderBy(asc9(adsOperations.updatedAt)).limit(config.batchSize).all();
47222
47322
  for (const row of rows) {
47223
47323
  const credential = credentialsByProjectId.get(row.projectId);
47224
47324
  if (!credential) continue;
@@ -47546,7 +47646,7 @@ async function adsRoutes(app, opts) {
47546
47646
  gt2(adsOperations.id, cursor.id)
47547
47647
  )
47548
47648
  ) : void 0
47549
- )).orderBy(asc8(adsOperations.createdAt), asc8(adsOperations.id)).limit(parsed.data.limit + 1).all();
47649
+ )).orderBy(asc9(adsOperations.createdAt), asc9(adsOperations.id)).limit(parsed.data.limit + 1).all();
47550
47650
  const hasMore = rows.length > parsed.data.limit;
47551
47651
  const page2 = hasMore ? rows.slice(0, parsed.data.limit) : rows;
47552
47652
  const response = {
@@ -48066,7 +48166,7 @@ async function adsRoutes(app, opts) {
48066
48166
  if (entityId) conditions.push(eq43(adsInsightsDaily.entityId, entityId));
48067
48167
  if (from) conditions.push(gte10(adsInsightsDaily.date, from));
48068
48168
  if (to) conditions.push(lte9(adsInsightsDaily.date, to));
48069
- const rows = app.db.select().from(adsInsightsDaily).where(and35(...conditions)).orderBy(asc8(adsInsightsDaily.date)).all();
48169
+ const rows = app.db.select().from(adsInsightsDaily).where(and35(...conditions)).orderBy(asc9(adsInsightsDaily.date)).all();
48070
48170
  const conn = app.db.select().from(adsConnections).where(eq43(adsConnections.projectId, project.id)).get();
48071
48171
  const accountToday = adsAccountToday(conn?.timezone, (/* @__PURE__ */ new Date()).toISOString());
48072
48172
  const dtoRows = rows.map((row) => ({
@@ -52268,7 +52368,7 @@ async function wordpressRoutes(app, opts) {
52268
52368
 
52269
52369
  // ../api-routes/src/backlinks.ts
52270
52370
  import crypto40 from "crypto";
52271
- import { and as and40, asc as asc9, desc as desc22, eq as eq47, sql as sql19 } from "drizzle-orm";
52371
+ import { and as and40, asc as asc10, desc as desc22, eq as eq47, sql as sql19 } from "drizzle-orm";
52272
52372
 
52273
52373
  // ../integration-commoncrawl/src/constants.ts
52274
52374
  import os2 from "os";
@@ -53026,7 +53126,7 @@ async function backlinksRoutes(app, opts) {
53026
53126
  async (request, reply) => {
53027
53127
  const project = resolveProject(app.db, request.params.name);
53028
53128
  const source = parseSourceParam(request.query.source);
53029
- const rows = app.db.select().from(backlinkSummaries).where(and40(eq47(backlinkSummaries.projectId, project.id), eq47(backlinkSummaries.source, source))).orderBy(asc9(backlinkSummaries.queriedAt)).all();
53129
+ const rows = app.db.select().from(backlinkSummaries).where(and40(eq47(backlinkSummaries.projectId, project.id), eq47(backlinkSummaries.source, source))).orderBy(asc10(backlinkSummaries.queriedAt)).all();
53030
53130
  const response = rows.map((r) => ({
53031
53131
  release: r.release,
53032
53132
  totalLinkingDomains: r.totalLinkingDomains,
@@ -70387,7 +70487,7 @@ function bindTrafficSyncSchedule(tx, projectId, sourceId, now, createIfMissing =
70387
70487
  )).get();
70388
70488
  if (schedule) {
70389
70489
  if (schedule.sourceId === sourceId) return { changed: false, created: false };
70390
- tx.update(schedules).set({ sourceId, updatedAt: now }).where(eq51(schedules.id, schedule.id)).run();
70490
+ tx.update(schedules).set({ sourceId, updatedAt: nextScheduleUpdatedAt(schedule.updatedAt, Date.parse(now)) }).where(eq51(schedules.id, schedule.id)).run();
70391
70491
  return { changed: true, created: false };
70392
70492
  }
70393
70493
  if (!createIfMissing) return { changed: false, created: false };
@@ -74771,10 +74871,10 @@ var providersConfiguredCheck = {
74771
74871
  const total = summary.length;
74772
74872
  if (configured.length === 0) {
74773
74873
  return {
74774
- status: CheckStatuses.fail,
74874
+ status: CheckStatuses.warn,
74775
74875
  code: "providers.none-configured",
74776
- summary: "No answer-engine providers have credentials configured.",
74777
- remediation: "Run `canonry init` to set provider keys interactively, or add them via flags (`--gemini-key`, `--openai-key`, `--claude-key`, `--perplexity-key`).",
74876
+ summary: "No answer-engine provider is configured. Page Health remains available; AI Visibility is disabled.",
74877
+ remediation: "To enable AI Visibility, set a provider environment variable and rerun `canonry bootstrap`, or run `canonry settings provider <name> --api-key <key>` while the server is running.",
74778
74878
  details: { available: summary.map((entry) => entry.name) }
74779
74879
  };
74780
74880
  }
@@ -76715,7 +76815,7 @@ function dedupeStrings(input) {
76715
76815
 
76716
76816
  // ../api-routes/src/technical-aeo.ts
76717
76817
  import crypto47 from "crypto";
76718
- import { and as and49, asc as asc10, count, desc as desc25, eq as eq60, inArray as inArray21, isNotNull as isNotNull3, isNull as isNull8, lt as lt11, or as or13, sql as sql24 } from "drizzle-orm";
76818
+ import { and as and49, asc as asc11, count, desc as desc25, eq as eq60, inArray as inArray21, isNotNull as isNotNull3, isNull as isNull8, lt as lt11, or as or13, sql as sql24 } from "drizzle-orm";
76719
76819
  import { alias } from "drizzle-orm/sqlite-core";
76720
76820
  var FETCHED_SITE_CRAWL_STATES = /* @__PURE__ */ new Set([
76721
76821
  ...SiteCrawlFetchedStates,
@@ -77055,14 +77155,14 @@ async function technicalAeoRoutes(app, opts) {
77055
77155
  eq60(siteCrawlPages.runId, scope.runId),
77056
77156
  eq60(siteCrawlPages.attemptId, scope.attemptId),
77057
77157
  selector.nodeKey ? eq60(siteCrawlPages.nodeKey, selector.nodeKey) : eq60(siteCrawlPages.url, selector.url)
77058
- )).orderBy(asc10(siteCrawlPages.nodeKey)).limit(1).get() ?? null;
77158
+ )).orderBy(asc11(siteCrawlPages.nodeKey)).limit(1).get() ?? null;
77059
77159
  };
77060
77160
  const rootPageInScope = (scope, rootUrl) => pageInScope(scope, { url: rootUrl }) ?? app.db.select().from(siteCrawlPages).where(and49(
77061
77161
  eq60(siteCrawlPages.projectId, scope.projectId),
77062
77162
  eq60(siteCrawlPages.runId, scope.runId),
77063
77163
  eq60(siteCrawlPages.attemptId, scope.attemptId),
77064
77164
  eq60(siteCrawlPages.depth, 0)
77065
- )).orderBy(asc10(siteCrawlPages.nodeKey)).limit(1).get() ?? null;
77165
+ )).orderBy(asc11(siteCrawlPages.nodeKey)).limit(1).get() ?? null;
77066
77166
  const isSurfaceableAuditRun = (projectId, runId) => Boolean(app.db.select({ id: runs.id }).from(runs).where(and49(
77067
77167
  eq60(runs.id, runId),
77068
77168
  eq60(runs.projectId, projectId),
@@ -77160,7 +77260,7 @@ async function technicalAeoRoutes(app, opts) {
77160
77260
  const total = totalRow?.value ?? 0;
77161
77261
  const limit = parsePositiveInt(request.query.limit, 100, 500);
77162
77262
  const offset = parsePositiveInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER);
77163
- const orderBy = request.query.sort === "score-desc" ? desc25(siteAuditPages.overallScore) : request.query.sort === "url" ? asc10(siteAuditPages.url) : asc10(siteAuditPages.overallScore);
77263
+ const orderBy = request.query.sort === "score-desc" ? desc25(siteAuditPages.overallScore) : request.query.sort === "url" ? asc11(siteAuditPages.url) : asc11(siteAuditPages.overallScore);
77164
77264
  const rows = app.db.select().from(siteAuditPages).where(where).orderBy(orderBy).limit(limit).offset(offset).all();
77165
77265
  const pages = rows.map((row) => ({
77166
77266
  url: row.url,
@@ -77357,7 +77457,7 @@ async function technicalAeoRoutes(app, opts) {
77357
77457
  eq60(siteCrawlPages.runId, siteCrawlGraphNodes.runId),
77358
77458
  eq60(siteCrawlPages.attemptId, siteCrawlGraphNodes.attemptId),
77359
77459
  eq60(siteCrawlPages.nodeKey, siteCrawlGraphNodes.nodeKey)
77360
- )).where(and49(...graphScope, lt11(siteCrawlGraphNodes.sampleRank, maxNodes))).orderBy(asc10(siteCrawlGraphNodes.sampleRank)).all();
77460
+ )).where(and49(...graphScope, lt11(siteCrawlGraphNodes.sampleRank, maxNodes))).orderBy(asc11(siteCrawlGraphNodes.sampleRank)).all();
77361
77461
  const nodes = nodeRows.map(({ indexabilityReasons, canonicalNodeKey, ...row }) => ({
77362
77462
  ...row,
77363
77463
  healthState: deriveSiteHealthState({ ...row, indexabilityReasons, canonicalNodeKey })
@@ -77388,7 +77488,7 @@ async function technicalAeoRoutes(app, opts) {
77388
77488
  eq60(siteCrawlGraphEdges.attemptId, snapshot.attemptId),
77389
77489
  lt11(siteCrawlGraphEdges.sampleRank, maxEdges),
77390
77490
  graphLinkKindFilter
77391
- )).orderBy(asc10(siteCrawlGraphEdges.sampleRank)).all();
77491
+ )).orderBy(asc11(siteCrawlGraphEdges.sampleRank)).all();
77392
77492
  const totalNodes = persistedLayout.totalNodes;
77393
77493
  const totalEdges = persistedLayout.totalEdges;
77394
77494
  const totalTemplateEdges = persistedLayout.totalTemplateEdges;
@@ -77489,7 +77589,7 @@ async function technicalAeoRoutes(app, opts) {
77489
77589
  inArray21(siteCrawlEdges.sourceNodeKey, frontier),
77490
77590
  inArray21(siteCrawlEdges.targetNodeKey, frontier)
77491
77591
  )
77492
- )).orderBy(asc10(siteCrawlEdges.edgeKey)).limit(maxEdges + maxNodes + 1).all();
77592
+ )).orderBy(asc11(siteCrawlEdges.edgeKey)).limit(maxEdges + maxNodes + 1).all();
77493
77593
  if (candidates.length > maxEdges + maxNodes) queryTruncated = true;
77494
77594
  const next = /* @__PURE__ */ new Set();
77495
77595
  for (const edge of candidates.slice(0, maxEdges + maxNodes)) {
@@ -77639,7 +77739,7 @@ async function technicalAeoRoutes(app, opts) {
77639
77739
  eq60(siteCrawlEdges.relation, "anchor"),
77640
77740
  isNotNull3(siteCrawlEdges.targetNodeKey),
77641
77741
  inArray21(siteCrawlEdges.sourceNodeKey, frontier)
77642
- )).orderBy(asc10(siteCrawlEdges.edgeKey)).limit(SITE_HEALTH_PATH_MAX_VISITED_NODES + 1).all();
77742
+ )).orderBy(asc11(siteCrawlEdges.edgeKey)).limit(SITE_HEALTH_PATH_MAX_VISITED_NODES + 1).all();
77643
77743
  if (candidates.length > SITE_HEALTH_PATH_MAX_VISITED_NODES) truncated = true;
77644
77744
  const candidateTargetKeys = [...new Set(candidates.map((edge) => edge.targetNodeKey).filter((nodeKey) => nodeKey != null))];
77645
77745
  const persistedTargetKeys = new Set(candidateTargetKeys.length === 0 ? [] : app.db.select({ nodeKey: siteCrawlPages.nodeKey }).from(siteCrawlPages).where(and49(
@@ -78119,7 +78219,7 @@ async function technicalAeoRoutes(app, opts) {
78119
78219
  const healthState = parseSiteHealthState(request.query.healthState);
78120
78220
  const limit = parseBoundedLimit(request.query.limit, 100, 200);
78121
78221
  const offset = decodeCursor(request.query.cursor);
78122
- const orderBy = request.query.sort === "score-desc" ? [desc25(siteCrawlPages.auditScore), asc10(siteCrawlPages.nodeKey)] : request.query.sort === "score-asc" ? [asc10(siteCrawlPages.auditScore), asc10(siteCrawlPages.nodeKey)] : request.query.sort === "path" ? [asc10(siteCrawlPages.path), asc10(siteCrawlPages.nodeKey)] : [asc10(siteCrawlPages.url), asc10(siteCrawlPages.nodeKey)];
78222
+ const orderBy = request.query.sort === "score-desc" ? [desc25(siteCrawlPages.auditScore), asc11(siteCrawlPages.nodeKey)] : request.query.sort === "score-asc" ? [asc11(siteCrawlPages.auditScore), asc11(siteCrawlPages.nodeKey)] : request.query.sort === "path" ? [asc11(siteCrawlPages.path), asc11(siteCrawlPages.nodeKey)] : [asc11(siteCrawlPages.url), asc11(siteCrawlPages.nodeKey)];
78123
78223
  let healthStateFilter = null;
78124
78224
  if (healthState) {
78125
78225
  const legacyRow = app.db.select({ id: siteCrawlPages.id }).from(siteCrawlPages).where(and49(
@@ -78269,7 +78369,7 @@ async function technicalAeoRoutes(app, opts) {
78269
78369
  const total = app.db.select({ value: count() }).from(siteCrawlEdges).where(where).get()?.value ?? 0;
78270
78370
  const limit = parseBoundedLimit(request.query.limit, 100, 200);
78271
78371
  const offset = decodeCursor(request.query.cursor);
78272
- const rows = app.db.select().from(siteCrawlEdges).where(where).orderBy(asc10(siteCrawlEdges.edgeKey)).limit(limit).offset(offset).all();
78372
+ const rows = app.db.select().from(siteCrawlEdges).where(where).orderBy(asc11(siteCrawlEdges.edgeKey)).limit(limit).offset(offset).all();
78273
78373
  const nextOffset = offset + rows.length;
78274
78374
  return {
78275
78375
  project: project.name,
@@ -78332,8 +78432,8 @@ async function technicalAeoRoutes(app, opts) {
78332
78432
  const inboundMatch = request.query.nodeKey && request.query.url ? or13(eq60(siteCrawlEdges.targetNodeKey, request.query.nodeKey), eq60(siteCrawlEdges.targetUrl, request.query.url)) : request.query.nodeKey ? eq60(siteCrawlEdges.targetNodeKey, request.query.nodeKey) : eq60(siteCrawlEdges.targetUrl, request.query.url);
78333
78433
  const outboundMatch = request.query.nodeKey && request.query.url ? or13(eq60(siteCrawlEdges.sourceNodeKey, request.query.nodeKey), eq60(siteCrawlEdges.sourceUrl, request.query.url)) : request.query.nodeKey ? eq60(siteCrawlEdges.sourceNodeKey, request.query.nodeKey) : eq60(siteCrawlEdges.sourceUrl, request.query.url);
78334
78434
  const limit = parseBoundedLimit(request.query.limit, 50, 100);
78335
- const inboundRows = app.db.select().from(siteCrawlEdges).where(and49(...scope, inboundMatch)).orderBy(asc10(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
78336
- const outboundRows = app.db.select().from(siteCrawlEdges).where(and49(...scope, outboundMatch)).orderBy(asc10(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
78435
+ const inboundRows = app.db.select().from(siteCrawlEdges).where(and49(...scope, inboundMatch)).orderBy(asc11(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
78436
+ const outboundRows = app.db.select().from(siteCrawlEdges).where(and49(...scope, outboundMatch)).orderBy(asc11(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
78337
78437
  return {
78338
78438
  project: project.name,
78339
78439
  hasCrawlData: true,
@@ -78385,7 +78485,7 @@ async function technicalAeoRoutes(app, opts) {
78385
78485
  const total = app.db.select({ value: count() }).from(siteCrawlFindings).where(where).get()?.value ?? 0;
78386
78486
  const limit = parseBoundedLimit(request.query.limit, 100, 200);
78387
78487
  const offset = decodeCursor(request.query.cursor);
78388
- const rows = app.db.select().from(siteCrawlFindings).where(where).orderBy(asc10(siteCrawlFindings.findingKey)).limit(limit).offset(offset).all();
78488
+ const rows = app.db.select().from(siteCrawlFindings).where(where).orderBy(asc11(siteCrawlFindings.findingKey)).limit(limit).offset(offset).all();
78389
78489
  const nextOffset = offset + rows.length;
78390
78490
  const deadLinks = rows.map((row) => ({
78391
78491
  findingKey: row.findingKey,
@@ -78533,7 +78633,7 @@ async function technicalAeoRoutes(app, opts) {
78533
78633
  auditedWhere,
78534
78634
  isNotNull3(siteCrawlPages.auditScore),
78535
78635
  lt11(siteCrawlPages.auditScore, 70)
78536
- )).orderBy(asc10(siteCrawlPages.auditScore), asc10(siteCrawlPages.nodeKey)).limit(LIVE_PAGE_HEALTH_CANDIDATE_LIMIT).all();
78636
+ )).orderBy(asc11(siteCrawlPages.auditScore), asc11(siteCrawlPages.nodeKey)).limit(LIVE_PAGE_HEALTH_CANDIDATE_LIMIT).all();
78537
78637
  const examples = candidates.flatMap((candidate) => {
78538
78638
  if (candidate.auditScore == null) return [];
78539
78639
  const checksNeedingAttention = countLivePageHealthChecks(candidate.auditFields);
@@ -78769,6 +78869,7 @@ async function apiRoutes(app, opts) {
78769
78869
  onProjectDeleting: opts.onProjectDeleting,
78770
78870
  onProjectDeleted: opts.onProjectDeleted,
78771
78871
  onProjectUpserted: opts.onProjectUpserted,
78872
+ onProjectCreated: opts.onProjectCreated,
78772
78873
  onAliasesChanged: opts.onAliasesChanged,
78773
78874
  providerAdapters: opts.providerAdapters
78774
78875
  });
@@ -78801,6 +78902,7 @@ async function apiRoutes(app, opts) {
78801
78902
  await api.register(applyRoutes, {
78802
78903
  onScheduleUpdated: opts.onScheduleUpdated,
78803
78904
  onProjectUpserted: opts.onProjectUpserted,
78905
+ onProjectCreated: opts.onProjectCreated,
78804
78906
  onAliasesChanged: opts.onAliasesChanged,
78805
78907
  providerAdapters: opts.providerAdapters,
78806
78908
  allowLoopbackWebhooks: opts.allowLoopbackWebhooks,
@@ -79750,7 +79852,7 @@ var IntelligenceService = class {
79750
79852
  }
79751
79853
  sinceTimestamp = parsed;
79752
79854
  }
79753
- const allRuns = this.db.select().from(runs).where(and51(...analyzableRunPredicates(project.id))).orderBy(asc11(runs.finishedAt)).all();
79855
+ const allRuns = this.db.select().from(runs).where(and51(...analyzableRunPredicates(project.id))).orderBy(asc12(runs.finishedAt)).all();
79754
79856
  const measuredRunIds = this.runIdsWithSnapshots(allRuns.map((r) => r.id));
79755
79857
  const measuredRuns = allRuns.filter((r) => measuredRunIds.has(r.id));
79756
79858
  let startIdx = 0;