@canonry/canonry 7.11.0 → 7.13.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 (32) hide show
  1. package/assets/assets/{AuditHistoryPanel-D1WdkJKU.js → AuditHistoryPanel-BYxGn9mS.js} +1 -1
  2. package/assets/assets/{BacklinksPage-Uf1djxaV.js → BacklinksPage-BQxB_eFt.js} +1 -1
  3. package/assets/assets/{HistoryPage-CZPxzlS_.js → HistoryPage-DgGUM2tr.js} +1 -1
  4. package/assets/assets/{MeasurementPropertyPage-CaWq1f2G.js → MeasurementPropertyPage-Cl91vUu5.js} +1 -1
  5. package/assets/assets/ProjectPage-B_S900QN.js +8 -0
  6. package/assets/assets/{RunsPage-CDX5RxHi.js → RunsPage-BtjVuurK.js} +1 -1
  7. package/assets/assets/{SettingsPage-BfzKP-IR.js → SettingsPage-p0x0ZwGY.js} +1 -1
  8. package/assets/assets/{SiteHealthSection-42_E1ueq.js → SiteHealthSection-DMHWsfM4.js} +3 -3
  9. package/assets/assets/{TrafficPage-RjkpfGMw.js → TrafficPage-Bf0NkbIR.js} +1 -1
  10. package/assets/assets/{TrafficSourceDetailPage-DIWDPo-8.js → TrafficSourceDetailPage-BtwOdTAO.js} +1 -1
  11. package/assets/assets/{extract-error-message-D_e8B2s_.js → extract-error-message-Cyc59Xi7.js} +1 -1
  12. package/assets/assets/index-D4QB1Igv.css +1 -0
  13. package/assets/assets/index-DR_kNdE4.js +86 -0
  14. package/assets/assets/{react-sigma_core.esm.min-Br-N5hoB.js → react-sigma_core.esm.min-CFB4bYg_.js} +1 -1
  15. package/assets/assets/{v2-overview-adapter-BCSXgy2l.js → v2-overview-adapter-CLF10Pij.js} +1 -1
  16. package/assets/index.html +2 -2
  17. package/dist/{chunk-OH7E7TJD.js → chunk-7I64KEGM.js} +2 -2
  18. package/dist/{chunk-CF7G2HGX.js → chunk-AU7OUDWR.js} +5 -0
  19. package/dist/{chunk-7G7YVTDA.js → chunk-BTHEFJCI.js} +5 -3
  20. package/dist/{chunk-JRBQFRNT.js → chunk-EPSHIH63.js} +3 -3
  21. package/dist/{chunk-S46DWUNR.js → chunk-GBH7N3VZ.js} +59 -32
  22. package/dist/{chunk-HA2BAJ6Z.js → chunk-GQMSJEMZ.js} +107 -36
  23. package/dist/{chunk-GQAESNJT.js → chunk-SM6ZZAPK.js} +5 -5
  24. package/dist/cli.js +20 -11
  25. package/dist/{demo-server-BYLZFDKI.js → demo-server-WJUA4EIG.js} +4 -4
  26. package/dist/index.js +6 -6
  27. package/dist/{intelligence-service-TIM4XTYH.js → intelligence-service-DJRH4Z3D.js} +2 -2
  28. package/dist/mcp.js +4 -4
  29. package/package.json +16 -16
  30. package/assets/assets/ProjectPage-BimakE9X.js +0 -8
  31. package/assets/assets/index-BNeiohKS.css +0 -1
  32. package/assets/assets/index-DKl_jsi3.js +0 -86
@@ -820,10 +820,10 @@ import {
820
820
  wordpressSchemaDeployResultDtoSchema,
821
821
  wordpressSchemaStatusResultDtoSchema,
822
822
  wordpressStatusDtoSchema
823
- } from "./chunk-CF7G2HGX.js";
823
+ } from "./chunk-AU7OUDWR.js";
824
824
 
825
825
  // src/intelligence-service.ts
826
- import { eq as eq87, desc as desc39, asc as asc17, and as and73, ne as ne15, or as or21, inArray as inArray35, gte as gte23, lte as lte20, isNull as isNull16, sql as sql32, exists as exists2 } from "drizzle-orm";
826
+ import { eq as eq87, desc as desc39, asc as asc17, and as and73, ne as ne16, or as or21, inArray as inArray35, gte as gte23, lte as lte20, isNull as isNull16, sql as sql32, exists as exists2 } from "drizzle-orm";
827
827
 
828
828
  // ../db/src/client.ts
829
829
  import { mkdirSync } from "fs";
@@ -863,6 +863,7 @@ __export(schema_exports, {
863
863
  conversionTrackingContracts: () => conversionTrackingContracts,
864
864
  crawlerEventsHourly: () => crawlerEventsHourly,
865
865
  crawlerVerificationManifestsHourly: () => crawlerVerificationManifestsHourly,
866
+ dashboardSessions: () => dashboardSessions,
866
867
  dataRepairCompletions: () => dataRepairCompletions,
867
868
  discoveryProbes: () => discoveryProbes,
868
869
  discoverySessions: () => discoverySessions,
@@ -1587,6 +1588,18 @@ var userSessions = sqliteTable("user_sessions", {
1587
1588
  index("idx_user_sessions_user").on(table.userId),
1588
1589
  index("idx_user_sessions_expires").on(table.expiresAt)
1589
1590
  ]);
1591
+ var dashboardSessions = sqliteTable("dashboard_sessions", {
1592
+ tokenHash: text("token_hash").primaryKey(),
1593
+ apiKeyId: text("api_key_id").notNull().references(() => apiKeys.id, { onDelete: "cascade" }),
1594
+ apiKeyHash: text("api_key_hash").notNull().default(""),
1595
+ /** Null for API-key sign-ins; password sign-ins bind the configured password hash's digest. */
1596
+ passwordFingerprint: text("password_fingerprint"),
1597
+ createdAt: text("created_at").notNull(),
1598
+ expiresAt: text("expires_at").notNull()
1599
+ }, (table) => [
1600
+ index("idx_dashboard_sessions_key").on(table.apiKeyId),
1601
+ index("idx_dashboard_sessions_expires").on(table.expiresAt)
1602
+ ]);
1590
1603
  var oauthClients = sqliteTable("oauth_clients", {
1591
1604
  id: text("id").primaryKey(),
1592
1605
  name: text("name").notNull(),
@@ -8287,6 +8300,28 @@ var MIGRATION_VERSIONS = [
8287
8300
  )`,
8288
8301
  `CREATE UNIQUE INDEX IF NOT EXISTS idx_market_competitor_names_project_domain ON market_competitor_names(project_id, domain)`
8289
8302
  ]
8303
+ },
8304
+ {
8305
+ version: 173,
8306
+ name: "durable-dashboard-sessions",
8307
+ statements: [
8308
+ `CREATE TABLE IF NOT EXISTS dashboard_sessions (
8309
+ token_hash TEXT PRIMARY KEY,
8310
+ api_key_id TEXT NOT NULL REFERENCES api_keys(id) ON DELETE CASCADE,
8311
+ created_at TEXT NOT NULL,
8312
+ expires_at TEXT NOT NULL
8313
+ )`,
8314
+ `CREATE INDEX IF NOT EXISTS idx_dashboard_sessions_key ON dashboard_sessions(api_key_id)`,
8315
+ `CREATE INDEX IF NOT EXISTS idx_dashboard_sessions_expires ON dashboard_sessions(expires_at)`
8316
+ ]
8317
+ },
8318
+ {
8319
+ version: 174,
8320
+ name: "dashboard-session-credential-binding",
8321
+ statements: [
8322
+ `ALTER TABLE dashboard_sessions ADD COLUMN api_key_hash TEXT NOT NULL DEFAULT ''`,
8323
+ `ALTER TABLE dashboard_sessions ADD COLUMN password_fingerprint TEXT`
8324
+ ]
8290
8325
  }
8291
8326
  ];
8292
8327
  var AERO_LEDGER_REBUILDS = [
@@ -12504,6 +12539,7 @@ async function userSessionRoutes(app, opts = {}) {
12504
12539
  const err = authRequired("Sign in to see where you are signed in.");
12505
12540
  return reply.status(err.statusCode).send(err.toJSON());
12506
12541
  }
12542
+ if (resolved.renewedExpiresAt) setSessionCookie(request, reply, token);
12507
12543
  const currentHash = hashSessionToken(token);
12508
12544
  const rows = app.db.select().from(userSessions).where(eq3(userSessions.userId, resolved.user.id)).all();
12509
12545
  return reply.send({
@@ -14507,7 +14543,7 @@ function registerOAuthRoutesIn(app, opts) {
14507
14543
  if (grantedScope === null) {
14508
14544
  return badRequest(reply, "invalid_scope", `Supported scopes: ${SUPPORTED_SCOPES.join(", ")}.`);
14509
14545
  }
14510
- const user = opts.resolveUser(request);
14546
+ const user = opts.resolveUser(request, reply);
14511
14547
  if (!user) {
14512
14548
  return sendGatePage(reply, signInPage(request.url, null));
14513
14549
  }
@@ -14525,7 +14561,7 @@ function registerOAuthRoutesIn(app, opts) {
14525
14561
  assertCookieWriteOrigin(request);
14526
14562
  const q = request.query;
14527
14563
  const body = request.body ?? {};
14528
- const user = opts.resolveUser(request);
14564
+ const user = opts.resolveUser(request, reply);
14529
14565
  if (!user) return badRequest(reply, "access_denied", "Not signed in.");
14530
14566
  const clientId = q.client_id;
14531
14567
  const redirectUri = q.redirect_uri;
@@ -20613,7 +20649,7 @@ function mergePins(...collections) {
20613
20649
 
20614
20650
  // ../api-routes/src/runs.ts
20615
20651
  import crypto16 from "crypto";
20616
- import { and as and21, eq as eq28, asc as asc4, desc as desc11, inArray as inArray13, or as or7, sql as sql11 } from "drizzle-orm";
20652
+ import { and as and21, eq as eq28, ne as ne6, asc as asc4, desc as desc11, inArray as inArray13, notInArray, or as or7, sql as sql11 } from "drizzle-orm";
20617
20653
  import { gte as gte4 } from "drizzle-orm";
20618
20654
 
20619
20655
  // ../api-routes/src/run-fill.ts
@@ -21059,9 +21095,11 @@ async function runRoutes(app, opts) {
21059
21095
  const project = resolveProject(app.db, request.params.name);
21060
21096
  const parsedLimit = parseInt(request.query.limit ?? "", 10);
21061
21097
  const limit = Number.isNaN(parsedLimit) || parsedLimit <= 0 ? void 0 : parsedLimit;
21062
- const { kind, status } = parseListFilters(request.query);
21098
+ const { kind, excludeKind, excludeKinds, status } = parseListFilters(request.query);
21063
21099
  const filters = [eq28(runs.projectId, project.id)];
21064
21100
  if (kind) filters.push(eq28(runs.kind, kind));
21101
+ if (excludeKind) filters.push(ne6(runs.kind, excludeKind));
21102
+ if (excludeKinds?.length) filters.push(notInArray(runs.kind, excludeKinds));
21065
21103
  if (status) filters.push(eq28(runs.status, status));
21066
21104
  const where = and21(...filters);
21067
21105
  const rows = limit == null ? app.db.select().from(runs).where(where).orderBy(asc4(runs.createdAt)).all() : app.db.select().from(runs).where(where).orderBy(desc11(runs.createdAt)).limit(limit).all().reverse();
@@ -21084,10 +21122,12 @@ async function runRoutes(app, opts) {
21084
21122
  const limit = parseListLimit(request.query.limit, 500, 5e3);
21085
21123
  const since = parseListSince(request.query.since);
21086
21124
  const includeProbe = request.query.includeProbe === "1" || request.query.includeProbe === "true";
21087
- const { kind, status } = parseListFilters(request.query);
21125
+ const { kind, excludeKind, excludeKinds, status } = parseListFilters(request.query);
21088
21126
  const filters = [gte4(runs.createdAt, since)];
21089
21127
  if (!includeProbe) filters.push(notProbeRun());
21090
21128
  if (kind) filters.push(eq28(runs.kind, kind));
21129
+ if (excludeKind) filters.push(ne6(runs.kind, excludeKind));
21130
+ if (excludeKinds?.length) filters.push(notInArray(runs.kind, excludeKinds));
21091
21131
  if (status) filters.push(eq28(runs.status, status));
21092
21132
  const scopedProjectId = request.apiKey?.projectId;
21093
21133
  if (scopedProjectId) filters.push(eq28(runs.projectId, scopedProjectId));
@@ -21342,6 +21382,8 @@ function parseListLimit(raw, defaultValue, max2) {
21342
21382
  }
21343
21383
  var RUN_LIST_FILTER_OPTIONS = {
21344
21384
  kind: runKindSchema.options,
21385
+ excludeKind: runKindSchema.options,
21386
+ excludeKinds: runKindSchema.options,
21345
21387
  status: runStatusSchema.options
21346
21388
  };
21347
21389
  function isRunListFilterField(field) {
@@ -21350,10 +21392,12 @@ function isRunListFilterField(field) {
21350
21392
  function parseListFilters(query) {
21351
21393
  const parsed = runListFilterQuerySchema.safeParse({
21352
21394
  kind: query.kind === "" ? void 0 : query.kind,
21395
+ excludeKind: query.excludeKind === "" ? void 0 : query.excludeKind,
21396
+ excludeKinds: query.excludeKinds,
21353
21397
  status: query.status === "" ? void 0 : query.status
21354
21398
  });
21355
21399
  if (parsed.success) return parsed.data;
21356
- const invalid = parsed.error.issues.map((issue) => issue.path[0]).filter(isRunListFilterField);
21400
+ const invalid = [...new Set(parsed.error.issues.map((issue) => issue.path[0]).filter(isRunListFilterField))];
21357
21401
  const message = invalid.length > 0 ? invalid.map((field) => `"${field}" must be one of: ${RUN_LIST_FILTER_OPTIONS[field].join(", ")}`).join("; ") : "Invalid run list filters";
21358
21402
  throw validationError(message, { invalid: Object.fromEntries(invalid.map((field) => [field, query[field]])) });
21359
21403
  }
@@ -29597,7 +29641,7 @@ function readVisibilityComparisonRuns(db, projectId, since, until, includeEviden
29597
29641
 
29598
29642
  // ../api-routes/src/query-tracking.ts
29599
29643
  import crypto23 from "crypto";
29600
- import { and as and29, desc as desc16, eq as eq37, inArray as inArray18, isNotNull as isNotNull5, isNull as isNull9, ne as ne6 } from "drizzle-orm";
29644
+ import { and as and29, desc as desc16, eq as eq37, inArray as inArray18, isNotNull as isNotNull5, isNull as isNull9, ne as ne7 } from "drizzle-orm";
29601
29645
  var SOURCE_LIMIT = 100;
29602
29646
  var MAX_REVIEW_AGE_MS = 15 * 60 * 1e3;
29603
29647
  var MAX_REVIEW_FUTURE_SKEW_MS = 60 * 1e3;
@@ -29784,7 +29828,7 @@ function simpleMeasuredAtByQuery(db, project, queryRows2, opts) {
29784
29828
  eq37(runs.projectId, project.id),
29785
29829
  eq37(runs.kind, RunKinds["answer-visibility"]),
29786
29830
  inArray18(runs.status, [RunStatuses.completed, RunStatuses.partial]),
29787
- ne6(runs.trigger, RunTriggers.probe),
29831
+ ne7(runs.trigger, RunTriggers.probe),
29788
29832
  isNull9(runs.measurementScope),
29789
29833
  isNull9(runs.measurementPlanVersionId)
29790
29834
  )).orderBy(desc16(runs.createdAt), desc16(runs.id)).limit(SOURCE_LIMIT).all();
@@ -29906,7 +29950,7 @@ function measuredAtByQuery(db, state, opts) {
29906
29950
  eq37(runs.projectId, project.id),
29907
29951
  eq37(runs.kind, RunKinds["answer-visibility"]),
29908
29952
  inArray18(runs.status, [RunStatuses.completed, RunStatuses.partial]),
29909
- ne6(runs.trigger, RunTriggers.probe),
29953
+ ne7(runs.trigger, RunTriggers.probe),
29910
29954
  isNull9(runs.measurementScope),
29911
29955
  isNotNull5(runs.measurementPlanVersionId)
29912
29956
  )).orderBy(desc16(runs.createdAt), desc16(runs.id)).limit(SOURCE_LIMIT).all();
@@ -31799,7 +31843,7 @@ function aliasArraysEqual2(a, b) {
31799
31843
  }
31800
31844
 
31801
31845
  // ../api-routes/src/history.ts
31802
- import { and as and31, asc as asc6, count, desc as desc17, eq as eq39, gte as gte6, inArray as inArray19, like as like2, ne as ne7, or as or10 } from "drizzle-orm";
31846
+ import { and as and31, asc as asc6, count, desc as desc17, eq as eq39, gte as gte6, inArray as inArray19, like as like2, ne as ne8, or as or10 } from "drizzle-orm";
31803
31847
 
31804
31848
  // ../api-routes/src/notification-redaction.ts
31805
31849
  var REDACTED_URL = {
@@ -31880,7 +31924,7 @@ async function historyRoutes(app) {
31880
31924
  app.get("/projects/:name/history", async (request, reply) => {
31881
31925
  const project = resolveProject(app.db, request.params.name);
31882
31926
  const filters = [eq39(auditLog.projectId, project.id)];
31883
- if (request.operatorAccess !== true) filters.push(ne7(auditLog.entityType, "telemetry"));
31927
+ if (request.operatorAccess !== true) filters.push(ne8(auditLog.entityType, "telemetry"));
31884
31928
  addAuditHistoryFilters(filters, request.query);
31885
31929
  const rows = app.db.select().from(auditLog).where(and31(...filters)).orderBy(desc17(auditLog.createdAt), desc17(auditLog.id)).limit(parseBoundedInt(request.query.limit, 100, 500)).offset(parseBoundedInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER)).all();
31886
31930
  return reply.send(rows.map(formatAuditEntry));
@@ -31888,7 +31932,7 @@ async function historyRoutes(app) {
31888
31932
  app.get("/history", async (request, reply) => {
31889
31933
  const scopedProjectId = request.apiKey?.projectId;
31890
31934
  const filters = scopedProjectId ? [eq39(auditLog.projectId, scopedProjectId)] : [];
31891
- if (request.operatorAccess !== true) filters.push(ne7(auditLog.entityType, "telemetry"));
31935
+ if (request.operatorAccess !== true) filters.push(ne8(auditLog.entityType, "telemetry"));
31892
31936
  addAuditHistoryFilters(filters, request.query);
31893
31937
  const rows = app.db.select().from(auditLog).where(filters.length > 0 ? and31(...filters) : void 0).orderBy(desc17(auditLog.createdAt), desc17(auditLog.id)).limit(parseBoundedInt(request.query.limit, 100, 500)).offset(parseBoundedInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER)).all();
31894
31938
  return reply.send(rows.map(formatAuditEntry));
@@ -33357,7 +33401,7 @@ async function intelligenceRoutes(app) {
33357
33401
  }
33358
33402
 
33359
33403
  // ../api-routes/src/traffic-analytics.ts
33360
- import { and as and35, desc as desc20, eq as eq42, gte as gte8, lt as lt8, lte as lte7, ne as ne8, sql as sql14 } from "drizzle-orm";
33404
+ import { and as and35, desc as desc20, eq as eq42, gte as gte8, lt as lt8, lte as lte7, ne as ne9, sql as sql14 } from "drizzle-orm";
33361
33405
 
33362
33406
  // ../api-routes/src/ai-referral-status.ts
33363
33407
  import { and as and34, sql as sql13 } from "drizzle-orm";
@@ -33398,7 +33442,7 @@ function readTrafficActivity(db, projectId, windowDays2) {
33398
33442
  const sourceRows = db.select({ id: trafficSources.id }).from(trafficSources).where(
33399
33443
  and35(
33400
33444
  eq42(trafficSources.projectId, projectId),
33401
- ne8(trafficSources.status, TrafficSourceStatuses.archived)
33445
+ ne9(trafficSources.status, TrafficSourceStatuses.archived)
33402
33446
  )
33403
33447
  ).all();
33404
33448
  if (sourceRows.length === 0) return null;
@@ -33432,7 +33476,7 @@ function readTrafficActivity(db, projectId, windowDays2) {
33432
33476
  db.select({ total: sql14`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
33433
33477
  and35(
33434
33478
  eq42(crawlerEventsHourly.projectId, projectId),
33435
- ne8(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
33479
+ ne9(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
33436
33480
  gte8(crawlerEventsHourly.tsHour, windowStartIso),
33437
33481
  exclusiveEnd ? lt8(crawlerEventsHourly.tsHour, windowEndIso) : lte7(crawlerEventsHourly.tsHour, windowEndIso)
33438
33482
  )
@@ -33721,7 +33765,7 @@ async function trafficAnalyticsRoutes(app) {
33721
33765
  }
33722
33766
 
33723
33767
  // ../api-routes/src/referral-assessment.ts
33724
- import { and as and36, desc as desc21, eq as eq43, gte as gte9, lte as lte8, ne as ne9, sql as sql15 } from "drizzle-orm";
33768
+ import { and as and36, desc as desc21, eq as eq43, gte as gte9, lte as lte8, ne as ne10, sql as sql15 } from "drizzle-orm";
33725
33769
 
33726
33770
  // ../api-routes/src/ga-ai-referral-aggregation.ts
33727
33771
  function normalizeAiTrafficClass(value) {
@@ -33876,7 +33920,7 @@ function latestGaSyncQueriedWindow(db, projectId, startDate, endDate) {
33876
33920
  eq43(gaAiReferrals.projectId, projectId),
33877
33921
  gte9(gaAiReferrals.date, summary3.start),
33878
33922
  lte8(gaAiReferrals.date, summary3.end),
33879
- ne9(gaAiReferrals.syncedAt, summary3.syncedAt)
33923
+ ne10(gaAiReferrals.syncedAt, summary3.syncedAt)
33880
33924
  )).limit(1).get();
33881
33925
  return leftByAnotherSync === void 0;
33882
33926
  }
@@ -38499,6 +38543,20 @@ var runsListKindQueryParameter = {
38499
38543
  description: "Restrict results to a single run kind. Without this filter, integration syncs (bing-inspect, gsc-sync, ga-sync) can fill the default 500-row cap within minutes on busy projects and push answer-visibility runs out of the response. Unknown values are rejected with 400.",
38500
38544
  schema: { type: "string", enum: [...runKindSchema.options] }
38501
38545
  };
38546
+ var runsListExcludeKindQueryParameter = {
38547
+ name: "excludeKind",
38548
+ in: "query",
38549
+ description: "Exclude one run kind before applying the row limit. Combines with kind and status filters. Unknown values are rejected with 400.",
38550
+ schema: { type: "string", enum: [...runKindSchema.options] }
38551
+ };
38552
+ var runsListExcludeKindsQueryParameter = {
38553
+ name: "excludeKinds",
38554
+ in: "query",
38555
+ description: "Exclude multiple run kinds before applying the row limit. Accepts repeated or comma-separated values. Combines with excludeKind, kind, and status; unknown values are rejected with 400.",
38556
+ style: "form",
38557
+ explode: true,
38558
+ schema: { type: "array", items: { type: "string", enum: [...runKindSchema.options] }, maxItems: runKindSchema.options.length }
38559
+ };
38502
38560
  var runsListStatusQueryParameter = {
38503
38561
  name: "status",
38504
38562
  in: "query",
@@ -40510,7 +40568,7 @@ var routeCatalog = [
40510
40568
  path: "/api/v1/projects/{name}/runs",
40511
40569
  summary: "List project runs",
40512
40570
  tags: ["runs"],
40513
- parameters: [nameParameter, limitQueryParameter, runsListKindQueryParameter, runsListStatusQueryParameter],
40571
+ parameters: [nameParameter, limitQueryParameter, runsListKindQueryParameter, runsListExcludeKindQueryParameter, runsListExcludeKindsQueryParameter, runsListStatusQueryParameter],
40514
40572
  responses: {
40515
40573
  200: jsonArrayResponse("Runs returned.", "RunDto")
40516
40574
  }
@@ -40535,6 +40593,8 @@ var routeCatalog = [
40535
40593
  runsListSinceQueryParameter,
40536
40594
  runsListIncludeProbeQueryParameter,
40537
40595
  runsListKindQueryParameter,
40596
+ runsListExcludeKindQueryParameter,
40597
+ runsListExcludeKindsQueryParameter,
40538
40598
  runsListStatusQueryParameter
40539
40599
  ],
40540
40600
  responses: {
@@ -42320,6 +42380,7 @@ var routeCatalog = [
42320
42380
  responses: {
42321
42381
  200: jsonResponse("List of discovered locations and selection summary returned.", "GbpLocationListResponse"),
42322
42382
  400: errorResponse("Invalid discover request, unknown account, account-switch not opted into, or scope/API problem."),
42383
+ 403: errorResponse("The Google connection was rejected; reconnect Google Business Profile."),
42323
42384
  404: errorResponse("Project not found."),
42324
42385
  429: errorResponse("GBP API quota exceeded (access form may not be approved).")
42325
42386
  }
@@ -42333,6 +42394,7 @@ var routeCatalog = [
42333
42394
  responses: {
42334
42395
  200: jsonResponse("Accounts the OAuth user manages or owns.", "GbpAccountListResponse"),
42335
42396
  400: errorResponse("No GBP connection or scope/API problem."),
42397
+ 403: errorResponse("The Google connection was rejected; reconnect Google Business Profile."),
42336
42398
  404: errorResponse("Project not found."),
42337
42399
  429: errorResponse("GBP API quota exceeded (access form may not be approved).")
42338
42400
  }
@@ -45938,6 +46000,7 @@ async function keysRoutes(app) {
45938
46000
  }
45939
46001
  const now = (/* @__PURE__ */ new Date()).toISOString();
45940
46002
  app.db.transaction((tx) => {
46003
+ tx.delete(dashboardSessions).where(eq53(dashboardSessions.apiKeyId, id)).run();
45941
46004
  tx.update(apiKeys).set({ revokedAt: now }).where(eq53(apiKeys.id, id)).run();
45942
46005
  writeAuditLog(tx, auditFromRequest(request, {
45943
46006
  actor: "api",
@@ -50116,7 +50179,12 @@ async function googleRoutes(app, opts) {
50116
50179
  { reason: err.reason, body: err.body }
50117
50180
  );
50118
50181
  }
50119
- if (err.status === 401) return authRequired();
50182
+ if (err.status === 401) {
50183
+ return forbidden(
50184
+ `${context}: the Google connection has expired or was revoked. Reconnect Google Business Profile.`,
50185
+ { reason: "gbp-reconnect", upstreamStatus: 401 }
50186
+ );
50187
+ }
50120
50188
  return providerError(`${context}: ${err.message}`, { reason: err.reason ?? void 0, status: err.status });
50121
50189
  }
50122
50190
  function rowToDto2(row) {
@@ -50613,7 +50681,7 @@ function parseReviewLimit(value) {
50613
50681
 
50614
50682
  // ../api-routes/src/google-marketing.ts
50615
50683
  import crypto36 from "crypto";
50616
- import { and as and48, desc as desc29, eq as eq58, isNull as isNull10, lt as lt9, ne as ne10, or as or14, sql as sql21 } from "drizzle-orm";
50684
+ import { and as and48, desc as desc29, eq as eq58, isNull as isNull10, lt as lt9, ne as ne11, or as or14, sql as sql21 } from "drizzle-orm";
50617
50685
  import { z as z4 } from "zod";
50618
50686
  var snapshotPageQuerySchema = z4.object({
50619
50687
  limit: z4.coerce.number().int().min(1).max(GOOGLE_MARKETING_STORED_SNAPSHOT_PAGE_MAX).optional(),
@@ -52114,7 +52182,7 @@ async function googleMarketingRoutes(app, opts) {
52114
52182
  const duplicate = app.db.select({ id: conversionTrackingContracts.id }).from(conversionTrackingContracts).where(and48(
52115
52183
  eq58(conversionTrackingContracts.projectId, project.id),
52116
52184
  eq58(conversionTrackingContracts.name, body.name),
52117
- ne10(conversionTrackingContracts.id, existing.id)
52185
+ ne11(conversionTrackingContracts.id, existing.id)
52118
52186
  )).get();
52119
52187
  if (duplicate) throw alreadyExists("Conversion tracking contract", body.name);
52120
52188
  const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -52203,7 +52271,7 @@ async function googleMarketingRoutes(app, opts) {
52203
52271
 
52204
52272
  // ../api-routes/src/ads.ts
52205
52273
  import crypto39 from "crypto";
52206
- import { eq as eq60, and as and50, asc as asc12, desc as desc30, gt as gt6, gte as gte14, lt as lt10, lte as lte14, ne as ne11, inArray as inArray27, or as or16, isNull as isNull12, isNotNull as isNotNull7, sql as sql23 } from "drizzle-orm";
52274
+ import { eq as eq60, and as and50, asc as asc12, desc as desc30, gt as gt6, gte as gte14, lt as lt10, lte as lte14, ne as ne12, inArray as inArray27, or as or16, isNull as isNull12, isNotNull as isNotNull7, sql as sql23 } from "drizzle-orm";
52207
52275
 
52208
52276
  // ../api-routes/src/ads-activation.ts
52209
52277
  import crypto37 from "crypto";
@@ -55901,7 +55969,7 @@ async function sweepAdsOperationsOnce(app, opts, config) {
55901
55969
  );
55902
55970
  const staleReconcileWhere = and50(
55903
55971
  lt10(adsOperations.reconcileAttempts, config.policy.maxAttempts),
55904
- ne11(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
55972
+ ne12(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
55905
55973
  or16(
55906
55974
  eq60(adsOperations.reconcileStrategy, AdsReconcileStrategies.known_entity),
55907
55975
  and50(
@@ -55918,7 +55986,7 @@ async function sweepAdsOperationsOnce(app, opts, config) {
55918
55986
  retryableStateWhere
55919
55987
  );
55920
55988
  const exhaustedRows = app.db.select().from(adsOperations).where(and50(
55921
- ne11(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
55989
+ ne12(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
55922
55990
  gte14(adsOperations.reconcileAttempts, config.policy.maxAttempts),
55923
55991
  or16(
55924
55992
  and50(
@@ -55933,7 +56001,7 @@ async function sweepAdsOperationsOnce(app, opts, config) {
55933
56001
  ),
55934
56002
  or16(
55935
56003
  isNull12(adsOperations.errorCode),
55936
- ne11(adsOperations.errorCode, ADS_RECONCILIATION_QUARANTINED)
56004
+ ne12(adsOperations.errorCode, ADS_RECONCILIATION_QUARANTINED)
55937
56005
  )
55938
56006
  )).orderBy(asc12(adsOperations.updatedAt)).limit(config.batchSize).all();
55939
56007
  for (const row of exhaustedRows) {
@@ -61432,7 +61500,7 @@ function pruneCachedRelease(release, opts = {}) {
61432
61500
  }
61433
61501
 
61434
61502
  // ../api-routes/src/backlinks-filter.ts
61435
- import { and as and54, ne as ne12, notLike } from "drizzle-orm";
61503
+ import { and as and54, ne as ne13, notLike } from "drizzle-orm";
61436
61504
  var BACKLINK_FILTER_PATTERNS = [
61437
61505
  "*.google.com",
61438
61506
  "*.googleusercontent.com",
@@ -61449,10 +61517,10 @@ function backlinkCrawlerExclusionClause() {
61449
61517
  for (const pattern of BACKLINK_FILTER_PATTERNS) {
61450
61518
  if (pattern.startsWith("*.")) {
61451
61519
  const suffix = pattern.slice(2);
61452
- conditions.push(ne12(backlinkDomains.linkingDomain, suffix));
61520
+ conditions.push(ne13(backlinkDomains.linkingDomain, suffix));
61453
61521
  conditions.push(notLike(backlinkDomains.linkingDomain, `%.${suffix}`));
61454
61522
  } else {
61455
- conditions.push(ne12(backlinkDomains.linkingDomain, pattern));
61523
+ conditions.push(ne13(backlinkDomains.linkingDomain, pattern));
61456
61524
  }
61457
61525
  }
61458
61526
  const combined = and54(...conditions);
@@ -82314,7 +82382,7 @@ var reportDailyDataCheck = {
82314
82382
  };
82315
82383
 
82316
82384
  // ../api-routes/src/doctor/checks/referral-assessment.ts
82317
- import { and as and62, eq as eq72, ne as ne13 } from "drizzle-orm";
82385
+ import { and as and62, eq as eq72, ne as ne14 } from "drizzle-orm";
82318
82386
  var REFERRAL_ASSESSMENT_CHECKS = [{
82319
82387
  id: "report.ai-referral-bursts",
82320
82388
  category: CheckCategories.integrations,
@@ -82323,7 +82391,7 @@ var REFERRAL_ASSESSMENT_CHECKS = [{
82323
82391
  notificationPolicy: CheckNotificationPolicies.silent,
82324
82392
  run: (ctx) => {
82325
82393
  if (!ctx.project) return { status: CheckStatuses.skipped, code: "report.ai-referral-bursts.no-project", summary: "Select a project to inspect referral evidence." };
82326
- const configured = ctx.db.select({ id: trafficSources.id }).from(trafficSources).where(and62(eq72(trafficSources.projectId, ctx.project.id), ne13(trafficSources.status, TrafficSourceStatuses.archived))).get();
82394
+ const configured = ctx.db.select({ id: trafficSources.id }).from(trafficSources).where(and62(eq72(trafficSources.projectId, ctx.project.id), ne14(trafficSources.status, TrafficSourceStatuses.archived))).get();
82327
82395
  if (!configured) {
82328
82396
  return { status: CheckStatuses.skipped, code: "report.ai-referral-bursts.not-configured", summary: "No non-archived server traffic source is configured for this project." };
82329
82397
  }
@@ -84632,7 +84700,7 @@ var RUNTIME_STATE_CHECKS = [
84632
84700
  ];
84633
84701
 
84634
84702
  // ../api-routes/src/doctor/checks/traffic-source.ts
84635
- import { and as and67, eq as eq80, gte as gte19, inArray as inArray31, ne as ne14, sql as sql29 } from "drizzle-orm";
84703
+ import { and as and67, eq as eq80, gte as gte19, inArray as inArray31, ne as ne15, sql as sql29 } from "drizzle-orm";
84636
84704
  var RECENT_DATA_WARN_DAYS = 7;
84637
84705
  var RECENT_DATA_FAIL_DAYS = 30;
84638
84706
  function isCloudflareDirectPush(source) {
@@ -84673,7 +84741,7 @@ function loadProbes(ctx) {
84673
84741
  const rows = ctx.db.select().from(trafficSources).where(
84674
84742
  and67(
84675
84743
  eq80(trafficSources.projectId, ctx.project.id),
84676
- ne14(trafficSources.status, TrafficSourceStatuses.archived)
84744
+ ne15(trafficSources.status, TrafficSourceStatuses.archived)
84677
84745
  )
84678
84746
  ).all();
84679
84747
  return rows.map((r) => ({
@@ -90270,7 +90338,7 @@ function analyzableRunPredicates(projectId) {
90270
90338
  // Defensive: RunCoordinator already skips probes before analyzeAndPersist
90271
90339
  // is called, but a future call site invoking it directly for a probe must
90272
90340
  // still not pollute the intelligence window.
90273
- ne15(runs.trigger, RunTriggers.probe)
90341
+ ne16(runs.trigger, RunTriggers.probe)
90274
90342
  ];
90275
90343
  }
90276
90344
  function readStoredGroundingSources2(rawResponse) {
@@ -90865,7 +90933,7 @@ var IntelligenceService = class {
90865
90933
  eq87(runs.kind, RunKinds["answer-visibility"]),
90866
90934
  or21(eq87(runs.status, "completed"), eq87(runs.status, "partial")),
90867
90935
  // Defensive — see top of file.
90868
- ne15(runs.trigger, RunTriggers.probe)
90936
+ ne16(runs.trigger, RunTriggers.probe)
90869
90937
  )
90870
90938
  ).orderBy(desc39(runs.createdAt), desc39(runs.id)).limit((RECURRENCE_LOOKBACK_RUNS + 1) * ROWS_PER_GROUP_BUDGET).all();
90871
90939
  const recentGroups = groupRunsByCreatedAt(recentRunRows);
@@ -90992,6 +91060,7 @@ export {
90992
91060
  auditLog,
90993
91061
  dataRepairCompletions,
90994
91062
  apiKeys,
91063
+ dashboardSessions,
90995
91064
  schedules,
90996
91065
  insightNotifyState,
90997
91066
  doctorHealthState,
@@ -91084,9 +91153,11 @@ export {
91084
91153
  computeCitedCompetitorDomains,
91085
91154
  resolveTrustProxy,
91086
91155
  USER_SESSION_COOKIE_NAME,
91156
+ cookieIsSecure,
91087
91157
  anyUsersExist,
91088
91158
  parseCookieHeader,
91089
91159
  serializeUserSessionCookie,
91160
+ hashSessionToken,
91090
91161
  createUserSession,
91091
91162
  resolveUserSession,
91092
91163
  createCredentialChecker,
@@ -5,7 +5,7 @@ import {
5
5
  PACKAGE_VERSION,
6
6
  canonryMcpTools,
7
7
  isMachineFormat
8
- } from "./chunk-7G7YVTDA.js";
8
+ } from "./chunk-BTHEFJCI.js";
9
9
  import {
10
10
  AGENT_PROVIDER_IDS,
11
11
  CANONRY_NPM_PACKAGE_URL,
@@ -50,7 +50,7 @@ import {
50
50
  skillsClientSchema,
51
51
  upgradeCaveatFor,
52
52
  upgradeCommandFor
53
- } from "./chunk-CF7G2HGX.js";
53
+ } from "./chunk-AU7OUDWR.js";
54
54
 
55
55
  // src/config.ts
56
56
  import fs from "fs";
@@ -7460,13 +7460,13 @@ var ApiClient = class {
7460
7460
  })
7461
7461
  );
7462
7462
  }
7463
- async listRuns(project, limit, kind, status) {
7463
+ async listRuns(project, limit, kind, status, excludeKind, excludeKinds) {
7464
7464
  return this.invoke(
7465
7465
  () => getApiV1ProjectsByNameRuns({
7466
7466
  client: this.heyClient,
7467
7467
  path: { name: project },
7468
- // kind/status arrive as free CLI strings; the server validates them against the enums.
7469
- query: { limit, kind, status }
7468
+ // Filters arrive as free CLI strings; the server validates them against the enums.
7469
+ query: { limit, kind, status, excludeKind, excludeKinds }
7470
7470
  })
7471
7471
  );
7472
7472
  }
package/dist/cli.js CHANGED
@@ -33,11 +33,11 @@ import {
33
33
  trackCliCommandFinished,
34
34
  trackEvent,
35
35
  waitForServerRuntimeStartup
36
- } from "./chunk-S46DWUNR.js";
36
+ } from "./chunk-GBH7N3VZ.js";
37
37
  import {
38
38
  autoSyncSkills,
39
39
  formatAutoSyncNotice
40
- } from "./chunk-JRBQFRNT.js";
40
+ } from "./chunk-EPSHIH63.js";
41
41
  import {
42
42
  configExists,
43
43
  createApiClient,
@@ -54,13 +54,13 @@ import {
54
54
  saveConfig,
55
55
  saveConfigPatch,
56
56
  shouldPrintServeSkillsNudge
57
- } from "./chunk-GQAESNJT.js";
57
+ } from "./chunk-SM6ZZAPK.js";
58
58
  import {
59
59
  DEFAULT_DEMO_TRUSTED_PROXIES,
60
60
  trustedProxiesProblem,
61
61
  trustedProxyProblem
62
62
  } from "./chunk-RB2F4D3Z.js";
63
- import "./chunk-OH7E7TJD.js";
63
+ import "./chunk-7I64KEGM.js";
64
64
  import {
65
65
  CliError,
66
66
  EXIT_SYSTEM_ERROR,
@@ -72,7 +72,7 @@ import {
72
72
  runMeasurementDraftAction,
73
73
  systemError,
74
74
  usageError
75
- } from "./chunk-7G7YVTDA.js";
75
+ } from "./chunk-BTHEFJCI.js";
76
76
  import {
77
77
  CLOUDFLARE_WORKER_BINDINGS,
78
78
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -89,6 +89,7 @@ import {
89
89
  crawlerEventsHourly,
90
90
  createClient,
91
91
  createRunCompetitorResolver,
92
+ dashboardSessions,
92
93
  dataRepairCompletions,
93
94
  determineCitationState,
94
95
  gaAiReferrals,
@@ -103,7 +104,7 @@ import {
103
104
  rawEventSamples,
104
105
  readMarketCompetitorNames,
105
106
  runs
106
- } from "./chunk-HA2BAJ6Z.js";
107
+ } from "./chunk-GQMSJEMZ.js";
107
108
  import {
108
109
  AdsDeliverySnapshotStatuses,
109
110
  AdsHistoricalCampaignRollupStatuses,
@@ -233,7 +234,7 @@ import {
233
234
  visibilityCompareSelectionSchema,
234
235
  visibilityReportRequestSchema,
235
236
  winnabilityClassSchema
236
- } from "./chunk-CF7G2HGX.js";
237
+ } from "./chunk-AU7OUDWR.js";
237
238
 
238
239
  // src/cli.ts
239
240
  import { pathToFileURL } from "url";
@@ -783,7 +784,7 @@ function parseDemoTrustedProxies(values) {
783
784
  async function demoCommand(options) {
784
785
  const { host, port } = parseDemoListenOptions(options);
785
786
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
786
- const { createDemoServer } = await import("./demo-server-BYLZFDKI.js");
787
+ const { createDemoServer } = await import("./demo-server-WJUA4EIG.js");
787
788
  const app = await createDemoServer({ trustProxy });
788
789
  let stopping = false;
789
790
  const stop = () => {
@@ -1197,7 +1198,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
1197
1198
  }
1198
1199
  }
1199
1200
  async function backfillInsightsCommand(project2, opts) {
1200
- const { IntelligenceService } = await import("./intelligence-service-TIM4XTYH.js");
1201
+ const { IntelligenceService } = await import("./intelligence-service-DJRH4Z3D.js");
1201
1202
  const config = loadConfig();
1202
1203
  const db = createClient(config.database);
1203
1204
  migrate(db);
@@ -14663,7 +14664,7 @@ async function showRun(id, format) {
14663
14664
  }
14664
14665
  async function listRuns(project2, opts) {
14665
14666
  const client = getClient23();
14666
- const runs2 = await client.listRuns(project2, opts?.limit, opts?.kind, opts?.status);
14667
+ const runs2 = await client.listRuns(project2, opts?.limit, opts?.kind, opts?.status, opts?.excludeKind, opts?.excludeKinds);
14667
14668
  if (opts?.format === "json") {
14668
14669
  console.log(JSON.stringify(runs2, null, 2));
14669
14670
  return;
@@ -14874,7 +14875,7 @@ async function fillRun(runId, opts = {}) {
14874
14875
  var RUN_TRIGGER_USAGE = "canonry run trigger <project> [--group <key>]... [--target <key>]... [--provider <name>] [--query <q>...] [--location <label>] [--all-locations] [--no-location] [--probe] [--dispatch-mode sync|batch] [--force] [--wait] [--format json]";
14875
14876
  var RUN_USAGE2 = "canonry run <project|--all> [--group <key>]... [--target <key>]... [--provider <name>] [--query <q>...] [--location <label>] [--all-locations] [--no-location] [--probe] [--dispatch-mode sync|batch] [--force] [--wait] [--format json]";
14876
14877
  var RUN_FILL_USAGE = "canonry run fill <run-id> [--provider <name>[,<name>]] [--dry-run] [--wait] [--format json]";
14877
- var RUNS_USAGE = "canonry runs <project> [--limit <n>] [--kind <kind>] [--status <status>] [--format json]";
14878
+ var RUNS_USAGE = "canonry runs <project> [--limit <n>] [--kind <kind>] [--exclude-kind <kind>] [--exclude-kinds <kind>[,<kind>]]... [--status <status>] [--format json]";
14878
14879
  var RUN_TRIGGER_OPTIONS = {
14879
14880
  provider: stringOption(),
14880
14881
  query: multiStringOption(),
@@ -15061,6 +15062,8 @@ var RUN_CLI_COMMANDS = [
15061
15062
  options: {
15062
15063
  limit: stringOption(),
15063
15064
  kind: stringOption(),
15065
+ "exclude-kind": stringOption(),
15066
+ "exclude-kinds": multiStringOption(),
15064
15067
  status: stringOption()
15065
15068
  },
15066
15069
  run: async (input) => {
@@ -15073,6 +15076,8 @@ var RUN_CLI_COMMANDS = [
15073
15076
  message: "--limit must be an integer"
15074
15077
  }),
15075
15078
  kind: getString(input.values, "kind"),
15079
+ excludeKind: getString(input.values, "exclude-kind"),
15080
+ excludeKinds: getStringArray(input.values, "exclude-kinds"),
15076
15081
  status: getString(input.values, "status")
15077
15082
  });
15078
15083
  }
@@ -17120,6 +17125,9 @@ async function bootstrapCommand(opts) {
17120
17125
  const rotating = existingDefault.keyHash !== keyHash;
17121
17126
  const needsUpdate = rotating || existingDefault.keyPrefix !== keyPrefix || !isDeepStrictEqual(existingDefault.scopes, ["*"]) || existingDefault.projectId !== null || existingDefault.revokedAt !== null;
17122
17127
  if (needsUpdate) {
17128
+ if (rotating || existingDefault.revokedAt !== null) {
17129
+ tx.delete(dashboardSessions).where(eq2(dashboardSessions.apiKeyId, existingDefault.id)).run();
17130
+ }
17123
17131
  tx.update(apiKeys).set({
17124
17132
  keyHash,
17125
17133
  keyPrefix,
@@ -17132,6 +17140,7 @@ async function bootstrapCommand(opts) {
17132
17140
  }
17133
17141
  for (const duplicate of existingDefaults) {
17134
17142
  if (duplicate.id === existingDefault.id) continue;
17143
+ tx.delete(dashboardSessions).where(eq2(dashboardSessions.apiKeyId, duplicate.id)).run();
17135
17144
  if (duplicate.revokedAt === null) {
17136
17145
  tx.update(apiKeys).set({ revokedAt: rotatedAt }).where(eq2(apiKeys.id, duplicate.id)).run();
17137
17146
  changed = true;
@@ -5,11 +5,11 @@ import {
5
5
  computeFactorAverages,
6
6
  layoutSiteCrawlGraphInput,
7
7
  persistSiteCrawlGraphLayout
8
- } from "./chunk-OH7E7TJD.js";
8
+ } from "./chunk-7I64KEGM.js";
9
9
  import {
10
10
  PACKAGE_VERSION,
11
11
  canonryMcpTools
12
- } from "./chunk-7G7YVTDA.js";
12
+ } from "./chunk-BTHEFJCI.js";
13
13
  import {
14
14
  HISTORY_WINDOW_RUNS,
15
15
  IntelligenceService,
@@ -83,7 +83,7 @@ import {
83
83
  siteCrawlPages,
84
84
  siteCrawlSnapshots,
85
85
  trafficSources
86
- } from "./chunk-HA2BAJ6Z.js";
86
+ } from "./chunk-GQMSJEMZ.js";
87
87
  import {
88
88
  RunKinds,
89
89
  SiteCrawlFetchStates,
@@ -112,7 +112,7 @@ import {
112
112
  roundPreservingTotal,
113
113
  siteAuditPageFactorSchema,
114
114
  wilsonInterval
115
- } from "./chunk-CF7G2HGX.js";
115
+ } from "./chunk-AU7OUDWR.js";
116
116
 
117
117
  // src/demo-server.ts
118
118
  import { dirname, join as join2 } from "path";