@canonry/canonry 6.1.5 → 6.3.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.
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +13 -1
- package/assets/assets/{AuditHistoryPanel-D-W_kwEY.js → AuditHistoryPanel-BUYWIjlG.js} +1 -1
- package/assets/assets/{BacklinksPage-BeqCFRO3.js → BacklinksPage-aBcxpN_9.js} +1 -1
- package/assets/assets/{HistoryPage-wAPbY0z4.js → HistoryPage-PoBuxiY_.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-BSOrkHCB.js → MeasurementPropertyPage-DyjyqHac.js} +1 -1
- package/assets/assets/{ProjectPage-v0C7CwyA.js → ProjectPage-Mmv5YW-K.js} +1 -1
- package/assets/assets/{RunsPage-CxolaIps.js → RunsPage-CHHxOpzo.js} +1 -1
- package/assets/assets/{SettingsPage-BQqJz0Ms.js → SettingsPage-CNKnrLXC.js} +1 -1
- package/assets/assets/{SiteHealthSection-C56kNcQQ.js → SiteHealthSection-C9RivT7G.js} +3 -3
- package/assets/assets/{TrafficPage-BDmWdYWf.js → TrafficPage-CY-ibXnb.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-DZl3YNBs.js → TrafficSourceDetailPage-BgiPXubs.js} +1 -1
- package/assets/assets/{extract-error-message-D0mBVEGn.js → extract-error-message-H1dzzfBP.js} +1 -1
- package/assets/assets/{index-DZ__RzGa.js → index-DSCun3I9.js} +22 -22
- package/assets/assets/{react-sigma_core.esm.min-BQNp33-g.js → react-sigma_core.esm.min-uiYtR_DU.js} +1 -1
- package/assets/assets/{v2-overview-adapter-XZbD7swG.js → v2-overview-adapter-BQokN2eb.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-PAYC6TQ2.js → chunk-5GY5WBAF.js} +3 -3
- package/dist/{chunk-5QH72CTN.js → chunk-DERWXLHX.js} +15 -1
- package/dist/{chunk-J7LV4CS2.js → chunk-EV5G5QRR.js} +335 -280
- package/dist/{chunk-MSFBNFTI.js → chunk-FSRVG2H7.js} +2 -2
- package/dist/{chunk-4UEX44C3.js → chunk-OTROA7CN.js} +395 -173
- package/dist/{chunk-MTEVU6KG.js → chunk-QVGG76UW.js} +79 -24
- package/dist/{chunk-FRBKYQN7.js → chunk-ZZEPF3A5.js} +24 -4
- package/dist/cli.js +55 -10
- package/dist/{demo-server-UGILQVEC.js → demo-server-5MNLI6GC.js} +4 -4
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-JYGP6LME.js → intelligence-service-5J36RBEM.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +11 -11
|
@@ -19,21 +19,21 @@ import {
|
|
|
19
19
|
runWithUsageTags,
|
|
20
20
|
saveConfigPatch,
|
|
21
21
|
sentimentInstallReadiness
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-ZZEPF3A5.js";
|
|
23
23
|
import {
|
|
24
24
|
computeFactorAverages,
|
|
25
25
|
deleteSiteCrawlGraphLayout,
|
|
26
26
|
persistSiteCrawlGraphLayout,
|
|
27
27
|
prepareSiteCrawlGraphLayout,
|
|
28
28
|
recordedSharePct
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-FSRVG2H7.js";
|
|
30
30
|
import {
|
|
31
31
|
CanonryMcpToolNames,
|
|
32
32
|
CliError,
|
|
33
33
|
PACKAGE_VERSION,
|
|
34
34
|
canonryMcpTools,
|
|
35
35
|
isMachineFormat
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-DERWXLHX.js";
|
|
37
37
|
import {
|
|
38
38
|
CC_CACHE_DIR,
|
|
39
39
|
DUCKDB_SPEC,
|
|
@@ -214,7 +214,7 @@ import {
|
|
|
214
214
|
siteLivenessState,
|
|
215
215
|
toAlertView,
|
|
216
216
|
usageCounters
|
|
217
|
-
} from "./chunk-
|
|
217
|
+
} from "./chunk-QVGG76UW.js";
|
|
218
218
|
import {
|
|
219
219
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
220
220
|
AGENT_NONE,
|
|
@@ -232,6 +232,7 @@ import {
|
|
|
232
232
|
CheckNotificationPolicies,
|
|
233
233
|
DEFAULT_VIEWER_RESEARCH_DAILY_RUN_LIMIT,
|
|
234
234
|
DiscoveryCompetitorTypes,
|
|
235
|
+
FEEDBACK_LIMITS,
|
|
235
236
|
GOOGLE_ADS_CAMPAIGN_METRICS_MAX_CAMPAIGNS,
|
|
236
237
|
GOOGLE_ADS_CAMPAIGN_METRICS_MAX_DAYS,
|
|
237
238
|
GOOGLE_ADS_CAMPAIGN_METRICS_MAX_ROWS,
|
|
@@ -304,6 +305,7 @@ import {
|
|
|
304
305
|
canonicalizeGtmAccountId,
|
|
305
306
|
canonicalizeGtmContainerId,
|
|
306
307
|
canonicalizeGtmResourceSelection,
|
|
308
|
+
classifyProviderErrorMessage,
|
|
307
309
|
classifyProviderErrorMessages,
|
|
308
310
|
classifyTemplateLinkEdge,
|
|
309
311
|
cloudflareQueueNameSchema,
|
|
@@ -315,6 +317,7 @@ import {
|
|
|
315
317
|
contentBriefDtoSchema,
|
|
316
318
|
createTemplateLinkDetectionTally,
|
|
317
319
|
createTemplateLinkPairIndex,
|
|
320
|
+
deliveryFailed,
|
|
318
321
|
deriveCitedUrlCandidates,
|
|
319
322
|
deriveGoogleAdsEffectiveGoalGraph,
|
|
320
323
|
deriveIndexCoverage,
|
|
@@ -327,6 +330,7 @@ import {
|
|
|
327
330
|
effectiveBrandNames,
|
|
328
331
|
effectiveDomains,
|
|
329
332
|
embedClientConfigForRequest,
|
|
333
|
+
extractProviderHttpStatus,
|
|
330
334
|
factorStatusFromScore,
|
|
331
335
|
filterCapturedCitedUrls,
|
|
332
336
|
forbidden,
|
|
@@ -383,7 +387,9 @@ import {
|
|
|
383
387
|
percentOf,
|
|
384
388
|
providerError,
|
|
385
389
|
providerSupportsLocationContext,
|
|
390
|
+
quotaExceeded,
|
|
386
391
|
ratioUnitSchema,
|
|
392
|
+
redactFeedbackText,
|
|
387
393
|
registrableDomain,
|
|
388
394
|
reportMonthsForDoctor,
|
|
389
395
|
resolveNegativeReviewMaxStars,
|
|
@@ -414,7 +420,7 @@ import {
|
|
|
414
420
|
visibilityReportRequestSchema,
|
|
415
421
|
winnabilityClassLabel,
|
|
416
422
|
withRetry
|
|
417
|
-
} from "./chunk-
|
|
423
|
+
} from "./chunk-EV5G5QRR.js";
|
|
418
424
|
|
|
419
425
|
// src/runtime-context.ts
|
|
420
426
|
function cliRuntimeContext(env = process.env, stdio = process) {
|
|
@@ -3629,7 +3635,7 @@ function createGoogleMarketingRuntime(options) {
|
|
|
3629
3635
|
|
|
3630
3636
|
// src/server.ts
|
|
3631
3637
|
import { createRequire as createRequire3 } from "module";
|
|
3632
|
-
import
|
|
3638
|
+
import crypto29 from "crypto";
|
|
3633
3639
|
import fs9 from "fs";
|
|
3634
3640
|
import path8 from "path";
|
|
3635
3641
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -4053,6 +4059,19 @@ import os4 from "os";
|
|
|
4053
4059
|
import { GoogleGenAI } from "@google/genai";
|
|
4054
4060
|
|
|
4055
4061
|
// ../provider-gemini/src/utils.ts
|
|
4062
|
+
var GEMINI_MAX_RATE_LIMIT_WAIT_MS = 65e3;
|
|
4063
|
+
function geminiRetryDelayMs(err) {
|
|
4064
|
+
const match = /"retryDelay"\s*:\s*"(\d+(?:\.\d+)?)s"/.exec(describeError(err));
|
|
4065
|
+
return match ? Math.round(Number(match[1]) * 1e3) : null;
|
|
4066
|
+
}
|
|
4067
|
+
function isGeminiDailyQuotaError(err) {
|
|
4068
|
+
return /"quotaId"\s*:\s*"[^"]*PerDay/i.test(describeError(err));
|
|
4069
|
+
}
|
|
4070
|
+
function isGeminiRetryable(err) {
|
|
4071
|
+
if (!isRetryableHttpError(err) || isGeminiDailyQuotaError(err)) return false;
|
|
4072
|
+
const asked = geminiRetryDelayMs(err);
|
|
4073
|
+
return asked === null || asked <= GEMINI_MAX_RATE_LIMIT_WAIT_MS;
|
|
4074
|
+
}
|
|
4056
4075
|
async function withRetry2(fn, options = {}) {
|
|
4057
4076
|
return withRetry(fn, {
|
|
4058
4077
|
maxRetries: options.maxRetries ?? 3,
|
|
@@ -4061,7 +4080,12 @@ async function withRetry2(fn, options = {}) {
|
|
|
4061
4080
|
// provider has shipped to date (1000, 2000, 4000ms). Enable per-caller
|
|
4062
4081
|
// if you need it.
|
|
4063
4082
|
jitter: false,
|
|
4064
|
-
isRetryable:
|
|
4083
|
+
isRetryable: isGeminiRetryable,
|
|
4084
|
+
computeDelayMs: (_attempt, err, defaultMs) => {
|
|
4085
|
+
const asked = geminiRetryDelayMs(err);
|
|
4086
|
+
return asked === null ? defaultMs : asked + 500;
|
|
4087
|
+
},
|
|
4088
|
+
...options.sleep ? { sleep: options.sleep } : {},
|
|
4065
4089
|
onRetry: ({ attempt, err, delayMs }) => {
|
|
4066
4090
|
console.warn(
|
|
4067
4091
|
`[provider] Attempt ${attempt + 1} failed, retrying in ${delayMs}ms...`,
|
|
@@ -4072,7 +4096,7 @@ async function withRetry2(fn, options = {}) {
|
|
|
4072
4096
|
}
|
|
4073
4097
|
|
|
4074
4098
|
// ../provider-gemini/src/normalize.ts
|
|
4075
|
-
var DEFAULT_MODEL = "gemini-
|
|
4099
|
+
var DEFAULT_MODEL = "gemini-flash-latest";
|
|
4076
4100
|
function isVertexConfig(config) {
|
|
4077
4101
|
return !!config.vertexProject;
|
|
4078
4102
|
}
|
|
@@ -4515,10 +4539,20 @@ var geminiAdapter = {
|
|
|
4515
4539
|
keyUrl: "https://aistudio.google.com/apikey",
|
|
4516
4540
|
// Upstream model list: https://ai.google.dev/gemini-api/docs/models
|
|
4517
4541
|
modelRegistry: {
|
|
4518
|
-
defaultModel: "gemini-
|
|
4542
|
+
defaultModel: "gemini-flash-latest",
|
|
4519
4543
|
validationPattern: /./,
|
|
4520
|
-
validationHint: "any valid Google model name (e.g. gemini-
|
|
4544
|
+
validationHint: "any valid Google model name (e.g. gemini-flash-latest, gemini-3.8-flash)",
|
|
4521
4545
|
knownModels: [
|
|
4546
|
+
// Google's moving aliases: always the newest Flash / Flash-Lite, with
|
|
4547
|
+
// 2 weeks' email notice before a breaking change. The model actually
|
|
4548
|
+
// served is recorded per snapshot (`servedModel`), so trends still show
|
|
4549
|
+
// when the model behind an alias changed.
|
|
4550
|
+
{ id: "gemini-flash-latest", displayName: "Gemini Flash (latest)", tier: "standard" },
|
|
4551
|
+
{ id: "gemini-flash-lite-latest", displayName: "Gemini Flash-Lite (latest)", tier: "economy" },
|
|
4552
|
+
{ id: "gemini-3.8-flash", displayName: "Gemini 3.8 Flash", tier: "standard" },
|
|
4553
|
+
{ id: "gemini-3.5-flash-lite", displayName: "Gemini 3.5 Flash-Lite", tier: "economy" },
|
|
4554
|
+
// Google limits the 2.5 models to projects that already used them, so a
|
|
4555
|
+
// new key may be refused. Listed for installs that still pin them.
|
|
4522
4556
|
{ id: "gemini-2.5-pro", displayName: "Gemini 2.5 Pro", tier: "flagship" },
|
|
4523
4557
|
{ id: "gemini-2.5-flash", displayName: "Gemini 2.5 Flash", tier: "standard" },
|
|
4524
4558
|
{ id: "gemini-2.5-flash-lite", displayName: "Gemini 2.5 Flash-Lite", tier: "economy" },
|
|
@@ -7824,13 +7858,101 @@ function createApiUsageTelemetry(options = {}) {
|
|
|
7824
7858
|
};
|
|
7825
7859
|
}
|
|
7826
7860
|
|
|
7861
|
+
// src/feedback.ts
|
|
7862
|
+
import crypto4 from "crypto";
|
|
7863
|
+
var FEEDBACK_ENDPOINT = "https://canonry.ai/api/feedback";
|
|
7864
|
+
var TIMEOUT_MS2 = 1e4;
|
|
7865
|
+
var FEEDBACK_MAX_BODY_BYTES = 8e3;
|
|
7866
|
+
function feedbackSource(context) {
|
|
7867
|
+
const surface = classifyUsageSurface({
|
|
7868
|
+
userAgent: context.userAgent,
|
|
7869
|
+
usageLabels: { surface: context.surface }
|
|
7870
|
+
});
|
|
7871
|
+
if (surface === "cli" || surface === "dashboard") return surface;
|
|
7872
|
+
if (surface === "mcp-stdio" || surface === "mcp-http") return "mcp";
|
|
7873
|
+
return "api";
|
|
7874
|
+
}
|
|
7875
|
+
async function sendFeedback(submission, context, deps = {}) {
|
|
7876
|
+
const doFetch = deps.fetch ?? fetch;
|
|
7877
|
+
const telemetryOn = (deps.telemetryEnabled ?? isTelemetryEnabled)();
|
|
7878
|
+
const anonymousId = telemetryOn ? (deps.anonymousId ?? getOrCreateAnonymousId)() : void 0;
|
|
7879
|
+
const agent = [context.agent, context.mcpClient].map((label) => normalizeAgentSlug(label)).find((slug) => !!slug && slug !== "none");
|
|
7880
|
+
const payload = {
|
|
7881
|
+
feedbackId: crypto4.randomUUID(),
|
|
7882
|
+
...anonymousId ? { anonymousId } : {},
|
|
7883
|
+
kind: submission.kind,
|
|
7884
|
+
summary: redactFeedbackText(submission.summary, FEEDBACK_LIMITS.summary),
|
|
7885
|
+
...submission.details ? { details: redactFeedbackText(submission.details, FEEDBACK_LIMITS.details) } : {},
|
|
7886
|
+
...submission.area ? { area: redactFeedbackText(submission.area, FEEDBACK_LIMITS.area) } : {},
|
|
7887
|
+
...submission.command ? { command: redactFeedbackText(submission.command, FEEDBACK_LIMITS.command) } : {},
|
|
7888
|
+
...submission.errorCode ? { errorCode: redactFeedbackText(submission.errorCode, FEEDBACK_LIMITS.errorCode) } : {},
|
|
7889
|
+
source: feedbackSource(context),
|
|
7890
|
+
...agent ? { agent } : {},
|
|
7891
|
+
version: PACKAGE_VERSION.slice(0, 20),
|
|
7892
|
+
nodeVersion: process.versions.node.slice(0, 20),
|
|
7893
|
+
os: process.platform.slice(0, 20),
|
|
7894
|
+
arch: process.arch.slice(0, 20),
|
|
7895
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7896
|
+
};
|
|
7897
|
+
const body = fitToByteBudget(payload);
|
|
7898
|
+
let response;
|
|
7899
|
+
try {
|
|
7900
|
+
response = await doFetch(FEEDBACK_ENDPOINT, {
|
|
7901
|
+
method: "POST",
|
|
7902
|
+
headers: { "Content-Type": "application/json" },
|
|
7903
|
+
body,
|
|
7904
|
+
signal: AbortSignal.timeout(TIMEOUT_MS2)
|
|
7905
|
+
});
|
|
7906
|
+
} catch {
|
|
7907
|
+
throw deliveryFailed("Could not reach canonry.ai to send feedback. Check the network and try again.");
|
|
7908
|
+
}
|
|
7909
|
+
if (response.status === 202 || response.ok) {
|
|
7910
|
+
const body2 = await response.json().catch(() => ({}));
|
|
7911
|
+
return { accepted: body2.accepted ?? true, id: typeof body2.id === "string" ? body2.id : payload.feedbackId };
|
|
7912
|
+
}
|
|
7913
|
+
if (response.status === 429) throw quotaExceeded("feedback submissions", { retryLater: true });
|
|
7914
|
+
if (response.status === 400 || response.status === 413) {
|
|
7915
|
+
const body2 = await response.json().catch(() => ({}));
|
|
7916
|
+
throw validationError("canonry.ai rejected the feedback", body2.issues ? { issues: body2.issues } : void 0);
|
|
7917
|
+
}
|
|
7918
|
+
throw deliveryFailed(`canonry.ai could not store the feedback (HTTP ${response.status}). Try again later.`);
|
|
7919
|
+
}
|
|
7920
|
+
function truncateCodePoints(value, maxCodePoints) {
|
|
7921
|
+
return Array.from(value).slice(0, Math.max(0, maxCodePoints)).join("");
|
|
7922
|
+
}
|
|
7923
|
+
function fitToByteBudget(payload) {
|
|
7924
|
+
let current = { ...payload };
|
|
7925
|
+
for (const field of ["details", "summary"]) {
|
|
7926
|
+
let serialized = JSON.stringify(current);
|
|
7927
|
+
let bytes = Buffer.byteLength(serialized, "utf8");
|
|
7928
|
+
while (bytes > FEEDBACK_MAX_BODY_BYTES) {
|
|
7929
|
+
const text = current[field];
|
|
7930
|
+
if (typeof text !== "string" || text.length === 0) break;
|
|
7931
|
+
const points = Array.from(text).length;
|
|
7932
|
+
const target = points - Math.max(1, Math.ceil((bytes - FEEDBACK_MAX_BODY_BYTES) / 4));
|
|
7933
|
+
if (field === "summary" && target < 1) break;
|
|
7934
|
+
const shorter = truncateCodePoints(text, target);
|
|
7935
|
+
if (field === "details" && shorter.length === 0) {
|
|
7936
|
+
const { details: _dropped, ...rest } = current;
|
|
7937
|
+
current = rest;
|
|
7938
|
+
} else {
|
|
7939
|
+
current = { ...current, [field]: shorter };
|
|
7940
|
+
}
|
|
7941
|
+
serialized = JSON.stringify(current);
|
|
7942
|
+
bytes = Buffer.byteLength(serialized, "utf8");
|
|
7943
|
+
}
|
|
7944
|
+
if (bytes <= FEEDBACK_MAX_BODY_BYTES) return serialized;
|
|
7945
|
+
}
|
|
7946
|
+
return JSON.stringify(current);
|
|
7947
|
+
}
|
|
7948
|
+
|
|
7827
7949
|
// src/instance-identity.ts
|
|
7828
7950
|
function nonBlank(value) {
|
|
7829
7951
|
const trimmed = value?.trim();
|
|
7830
7952
|
return trimmed ? trimmed : void 0;
|
|
7831
7953
|
}
|
|
7832
7954
|
function resolveBuildCommit(env = process.env) {
|
|
7833
|
-
const embedded = true ? "
|
|
7955
|
+
const embedded = true ? "a0351a3c7ac5eb8e0e4028ee004ddd5a12874575" : void 0;
|
|
7834
7956
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
7835
7957
|
}
|
|
7836
7958
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -7841,15 +7963,15 @@ function resolveInstanceIdentity(env = process.env) {
|
|
|
7841
7963
|
}
|
|
7842
7964
|
|
|
7843
7965
|
// src/job-runner.ts
|
|
7844
|
-
import
|
|
7966
|
+
import crypto7 from "crypto";
|
|
7845
7967
|
import fs4 from "fs";
|
|
7846
7968
|
import path3 from "path";
|
|
7847
7969
|
import os3 from "os";
|
|
7848
7970
|
import { setImmediate as yieldToEventLoop } from "timers/promises";
|
|
7849
|
-
import { and as and3, asc as asc2, count, eq as eq3, inArray, isNotNull, isNull, ne, sql as sql2 } from "drizzle-orm";
|
|
7971
|
+
import { and as and3, asc as asc2, count, desc, eq as eq3, inArray, isNotNull, isNull, lt, ne, sql as sql2 } from "drizzle-orm";
|
|
7850
7972
|
|
|
7851
7973
|
// src/run-telemetry.ts
|
|
7852
|
-
import
|
|
7974
|
+
import crypto5 from "crypto";
|
|
7853
7975
|
function extractRegistrableHost(input) {
|
|
7854
7976
|
if (!input) return null;
|
|
7855
7977
|
const trimmed = input.trim();
|
|
@@ -7869,7 +7991,7 @@ function extractRegistrableHost(input) {
|
|
|
7869
7991
|
function hashDomain(input) {
|
|
7870
7992
|
const host = extractRegistrableHost(input);
|
|
7871
7993
|
if (!host) return null;
|
|
7872
|
-
return
|
|
7994
|
+
return crypto5.createHash("sha256").update(host).digest("hex");
|
|
7873
7995
|
}
|
|
7874
7996
|
function buildRunCompletedProps(input) {
|
|
7875
7997
|
const totalMs = input.phases?.total_ms ?? Date.now() - input.startTime;
|
|
@@ -7911,6 +8033,51 @@ function buildSiteAuditCompletedProps(input) {
|
|
|
7911
8033
|
if (crawl.checkDeadLinks) props.deadLinksFound = crawl.deadLinksFound;
|
|
7912
8034
|
return props;
|
|
7913
8035
|
}
|
|
8036
|
+
var MAX_NESTED_KEYS = 12;
|
|
8037
|
+
function buildProviderOutcomeProps(providers, providerErrors) {
|
|
8038
|
+
if (providerErrors.size === 0) return {};
|
|
8039
|
+
const names = [.../* @__PURE__ */ new Set([...providers, ...providerErrors.keys()])].slice(0, MAX_NESTED_KEYS);
|
|
8040
|
+
const providerOutcomes = {};
|
|
8041
|
+
const providerHttpStatus = {};
|
|
8042
|
+
for (const name of names) {
|
|
8043
|
+
const message = providerErrors.get(name);
|
|
8044
|
+
if (message === void 0) {
|
|
8045
|
+
providerOutcomes[name] = "ok";
|
|
8046
|
+
continue;
|
|
8047
|
+
}
|
|
8048
|
+
providerOutcomes[name] = classifyProviderErrorMessage(message);
|
|
8049
|
+
const status = extractProviderHttpStatus(message);
|
|
8050
|
+
if (status !== void 0) providerHttpStatus[name] = status;
|
|
8051
|
+
}
|
|
8052
|
+
return Object.keys(providerHttpStatus).length > 0 ? { providerOutcomes, providerHttpStatus } : { providerOutcomes };
|
|
8053
|
+
}
|
|
8054
|
+
function runFailureSite(providerCallStart, providerCallEnd) {
|
|
8055
|
+
if (providerCallStart === void 0) return "setup";
|
|
8056
|
+
return providerCallEnd === void 0 ? "provider_call" : "finalize";
|
|
8057
|
+
}
|
|
8058
|
+
var ERROR_NAME_RE = /^[a-z_$][\w$]{0,39}$/i;
|
|
8059
|
+
var ERROR_SYS_CODE_RE = /^[A-Z][A-Z0-9_]{1,39}$/;
|
|
8060
|
+
function describeRunFailure(err, site) {
|
|
8061
|
+
const props = site ? { errorSite: site } : {};
|
|
8062
|
+
const name = err instanceof Error ? err.name : typeof err;
|
|
8063
|
+
if (ERROR_NAME_RE.test(name)) props.errorName = name;
|
|
8064
|
+
const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
|
|
8065
|
+
if (typeof code === "string" && ERROR_SYS_CODE_RE.test(code)) props.errorSysCode = code;
|
|
8066
|
+
return props;
|
|
8067
|
+
}
|
|
8068
|
+
var FAILURE_STREAK_SAMPLE_AFTER = 5;
|
|
8069
|
+
var FAILURE_STREAK_SAMPLE_RATE = 20;
|
|
8070
|
+
function failureStreakSampling(status, priorStreak, runId) {
|
|
8071
|
+
if (status !== "failed" && status !== "partial") return { report: true, props: {} };
|
|
8072
|
+
if (priorStreak < FAILURE_STREAK_SAMPLE_AFTER) {
|
|
8073
|
+
return { report: true, props: { failureStreak: priorStreak } };
|
|
8074
|
+
}
|
|
8075
|
+
const bucket = crypto5.createHash("sha256").update(runId).digest().readUInt32BE(0) % FAILURE_STREAK_SAMPLE_RATE;
|
|
8076
|
+
return {
|
|
8077
|
+
report: bucket === 0,
|
|
8078
|
+
props: { failureStreak: priorStreak, sampleRate: FAILURE_STREAK_SAMPLE_RATE }
|
|
8079
|
+
};
|
|
8080
|
+
}
|
|
7914
8081
|
|
|
7915
8082
|
// src/provider-execution-gate.ts
|
|
7916
8083
|
var ProviderExecutionGate = class {
|
|
@@ -7978,7 +8145,7 @@ function getSharedProviderExecutionGate(providerName, maxConcurrency, maxPerMinu
|
|
|
7978
8145
|
}
|
|
7979
8146
|
|
|
7980
8147
|
// src/usage-quota.ts
|
|
7981
|
-
import
|
|
8148
|
+
import crypto6 from "crypto";
|
|
7982
8149
|
import { and as and2, eq as eq2, lte, sql } from "drizzle-orm";
|
|
7983
8150
|
function getCurrentUsageDay() {
|
|
7984
8151
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -7995,7 +8162,7 @@ function reserveDailyQueryQuota(db, input) {
|
|
|
7995
8162
|
)).run();
|
|
7996
8163
|
if (reserveExisting().changes === 1) return { reserved: true, used: 0 };
|
|
7997
8164
|
const inserted = tx.insert(usageCounters).values({
|
|
7998
|
-
id:
|
|
8165
|
+
id: crypto6.randomUUID(),
|
|
7999
8166
|
scope: input.scope,
|
|
8000
8167
|
period: input.period,
|
|
8001
8168
|
metric: "queries",
|
|
@@ -8259,6 +8426,7 @@ function classifyRunAbortReason(message) {
|
|
|
8259
8426
|
if (/^Run [^ ]+ is not executable\b/.test(message)) return "run_not_executable";
|
|
8260
8427
|
return void 0;
|
|
8261
8428
|
}
|
|
8429
|
+
var FAILURE_STREAK_LOOKBACK = 50;
|
|
8262
8430
|
function classifyProviderErrors(errors) {
|
|
8263
8431
|
return classifyProviderErrorMessages(errors.values());
|
|
8264
8432
|
}
|
|
@@ -8589,7 +8757,7 @@ var JobRunner = class {
|
|
|
8589
8757
|
const answerLocation = runLocation && isSearchLocationIgnored(providerName, normalized.retrievalStatus) ? { location: null, requestedContext: runLocation, supportedContext: { status: "ignored" } } : { location: runLocation?.label ?? null };
|
|
8590
8758
|
let screenshotRelPath = null;
|
|
8591
8759
|
if (raw.screenshotPath && fs4.existsSync(raw.screenshotPath)) {
|
|
8592
|
-
const snapshotId =
|
|
8760
|
+
const snapshotId = crypto7.randomUUID();
|
|
8593
8761
|
const screenshotDir = path3.join(os3.homedir(), ".canonry", "screenshots", runId);
|
|
8594
8762
|
if (!fs4.existsSync(screenshotDir)) fs4.mkdirSync(screenshotDir, { recursive: true });
|
|
8595
8763
|
const destPath = path3.join(screenshotDir, `${snapshotId}.png`);
|
|
@@ -8633,7 +8801,7 @@ var JobRunner = class {
|
|
|
8633
8801
|
}).run();
|
|
8634
8802
|
} else {
|
|
8635
8803
|
this.db.insert(querySnapshots).values({
|
|
8636
|
-
id:
|
|
8804
|
+
id: crypto7.randomUUID(),
|
|
8637
8805
|
runId,
|
|
8638
8806
|
queryId: q.id,
|
|
8639
8807
|
queryText: q.query,
|
|
@@ -8853,21 +9021,28 @@ var JobRunner = class {
|
|
|
8853
9021
|
...executionContext.location ? { location: executionContext.location } : {}
|
|
8854
9022
|
});
|
|
8855
9023
|
} else {
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
9024
|
+
const sampling = failureStreakSampling("failed", this.priorFailureStreak(projectId, runId), runId);
|
|
9025
|
+
if (sampling.report) {
|
|
9026
|
+
trackEvent(
|
|
9027
|
+
"run.completed",
|
|
9028
|
+
{
|
|
9029
|
+
...buildRunCompletedProps({
|
|
9030
|
+
status: "failed",
|
|
9031
|
+
providerCount: executionContext.providerCount,
|
|
9032
|
+
providers: executionContext.providers,
|
|
9033
|
+
queryCount: executionContext.queryCount,
|
|
9034
|
+
startTime,
|
|
9035
|
+
trigger: executionContext.trigger,
|
|
9036
|
+
canonicalDomain: executionContext.canonicalDomain,
|
|
9037
|
+
phases,
|
|
9038
|
+
location: executionContext.location
|
|
9039
|
+
}),
|
|
9040
|
+
...describeRunFailure(err, runFailureSite(providerCallStart, providerCallEnd)),
|
|
9041
|
+
...sampling.props
|
|
9042
|
+
},
|
|
9043
|
+
{ errorCode: "INTERNAL" }
|
|
9044
|
+
);
|
|
9045
|
+
}
|
|
8871
9046
|
}
|
|
8872
9047
|
if (this.onRunCompleted) {
|
|
8873
9048
|
this.onRunCompleted(runId, projectId).catch((notifErr) => {
|
|
@@ -8931,21 +9106,28 @@ var JobRunner = class {
|
|
|
8931
9106
|
reportFinalizedRun(input, finalStatus) {
|
|
8932
9107
|
const { runId, projectId, providerErrors, executionContext } = input;
|
|
8933
9108
|
const failureCode = providerErrors.size > 0 ? classifyProviderErrors(providerErrors) : void 0;
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
9109
|
+
const sampling = failureStreakSampling(finalStatus, this.priorFailureStreak(projectId, runId), runId);
|
|
9110
|
+
if (sampling.report) {
|
|
9111
|
+
trackEvent(
|
|
9112
|
+
"run.completed",
|
|
9113
|
+
{
|
|
9114
|
+
...buildRunCompletedProps({
|
|
9115
|
+
status: finalStatus,
|
|
9116
|
+
providerCount: executionContext.providerCount,
|
|
9117
|
+
providers: executionContext.providers,
|
|
9118
|
+
queryCount: executionContext.queryCount,
|
|
9119
|
+
startTime: input.startTime,
|
|
9120
|
+
trigger: executionContext.trigger,
|
|
9121
|
+
canonicalDomain: executionContext.canonicalDomain,
|
|
9122
|
+
phases: input.phases,
|
|
9123
|
+
location: executionContext.location
|
|
9124
|
+
}),
|
|
9125
|
+
...buildProviderOutcomeProps(executionContext.providers, providerErrors),
|
|
9126
|
+
...sampling.props
|
|
9127
|
+
},
|
|
9128
|
+
failureCode ? { errorCode: failureCode } : void 0
|
|
9129
|
+
);
|
|
9130
|
+
}
|
|
8949
9131
|
if (input.kind === RunKinds["answer-visibility"] && executionContext.trigger !== RunTriggers.probe && input.inserted > 0 && !this.hasPriorActivation(projectId, runId)) {
|
|
8950
9132
|
trackEvent("activation.completed", {
|
|
8951
9133
|
flowVersion: ONBOARDING_FLOW_VERSION,
|
|
@@ -9159,7 +9341,7 @@ var JobRunner = class {
|
|
|
9159
9341
|
syncUnits.push(unit);
|
|
9160
9342
|
continue;
|
|
9161
9343
|
}
|
|
9162
|
-
const customId =
|
|
9344
|
+
const customId = crypto7.randomUUID().replace(/-/g, "");
|
|
9163
9345
|
const lines = linesByModel.get(model) ?? [];
|
|
9164
9346
|
lines.push({ unit, requestedModel, customId, request, bytes: Buffer.byteLength(JSON.stringify({ custom_id: customId, params: request.body })) });
|
|
9165
9347
|
linesByModel.set(model, lines);
|
|
@@ -9195,7 +9377,7 @@ var JobRunner = class {
|
|
|
9195
9377
|
if (!capability || !reservation) throw new Error(`Provider ${providerName} cannot submit a batch for run ${ctx.runId}`);
|
|
9196
9378
|
const deadlineMs = (config.batch?.deadlineHours ?? capability.defaultDeadlineHours) * HOUR_MS;
|
|
9197
9379
|
const created = /* @__PURE__ */ new Date();
|
|
9198
|
-
const rowId =
|
|
9380
|
+
const rowId = crypto7.randomUUID();
|
|
9199
9381
|
const ledger = lines.map((line) => ({
|
|
9200
9382
|
id: line.customId,
|
|
9201
9383
|
batchId: rowId,
|
|
@@ -9888,7 +10070,7 @@ var JobRunner = class {
|
|
|
9888
10070
|
competitorAliases
|
|
9889
10071
|
);
|
|
9890
10072
|
const answerContext = supportedContext && isSearchLocationIgnored(providerName, normalized.retrievalStatus) ? { status: "ignored" } : supportedContext;
|
|
9891
|
-
const snapshotId =
|
|
10073
|
+
const snapshotId = crypto7.randomUUID();
|
|
9892
10074
|
let screenshotRelPath = null;
|
|
9893
10075
|
if (raw.screenshotPath && fs4.existsSync(raw.screenshotPath)) {
|
|
9894
10076
|
const screenshotDir = path3.join(os3.homedir(), ".canonry", "screenshots", ctx.runId);
|
|
@@ -9990,7 +10172,7 @@ var JobRunner = class {
|
|
|
9990
10172
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9991
10173
|
const period = now.slice(0, 10);
|
|
9992
10174
|
this.db.insert(usageCounters).values({
|
|
9993
|
-
id:
|
|
10175
|
+
id: crypto7.randomUUID(),
|
|
9994
10176
|
scope,
|
|
9995
10177
|
period,
|
|
9996
10178
|
metric,
|
|
@@ -10008,6 +10190,36 @@ var JobRunner = class {
|
|
|
10008
10190
|
}
|
|
10009
10191
|
providerReservations.clear();
|
|
10010
10192
|
}
|
|
10193
|
+
/**
|
|
10194
|
+
* How many runs of the same kind, created before this one, failed or came
|
|
10195
|
+
* back partial in a row. Telemetry only: it feeds `failureStreak` and the
|
|
10196
|
+
* sampling of runs stuck failing the same way. Probes do not count, other run
|
|
10197
|
+
* kinds (syncs, audits) neither break nor extend a streak, and a run aborted
|
|
10198
|
+
* before any provider was called (`run.aborted`, never a failed
|
|
10199
|
+
* `run.completed`) is skipped, so every failure counted was also reported.
|
|
10200
|
+
*/
|
|
10201
|
+
priorFailureStreak(projectId, currentRunId) {
|
|
10202
|
+
try {
|
|
10203
|
+
const current = this.db.select({ kind: runs.kind, createdAt: runs.createdAt }).from(runs).where(eq3(runs.id, currentRunId)).get();
|
|
10204
|
+
if (!current) return 0;
|
|
10205
|
+
const recent = this.db.select({ status: runs.status, error: runs.error }).from(runs).where(and3(
|
|
10206
|
+
eq3(runs.projectId, projectId),
|
|
10207
|
+
eq3(runs.kind, current.kind),
|
|
10208
|
+
lt(runs.createdAt, current.createdAt),
|
|
10209
|
+
ne(runs.trigger, RunTriggers.probe),
|
|
10210
|
+
inArray(runs.status, ["completed", "partial", "failed"])
|
|
10211
|
+
)).orderBy(desc(runs.createdAt)).limit(FAILURE_STREAK_LOOKBACK).all();
|
|
10212
|
+
let streak = 0;
|
|
10213
|
+
for (const row of recent) {
|
|
10214
|
+
if (row.status === "completed") break;
|
|
10215
|
+
if (row.status === "failed" && row.error && classifyRunAbortReason(row.error)) continue;
|
|
10216
|
+
streak++;
|
|
10217
|
+
}
|
|
10218
|
+
return streak;
|
|
10219
|
+
} catch {
|
|
10220
|
+
return 0;
|
|
10221
|
+
}
|
|
10222
|
+
}
|
|
10011
10223
|
hasPriorActivation(projectId, currentRunId) {
|
|
10012
10224
|
return this.db.select({ id: querySnapshots.id }).from(querySnapshots).innerJoin(runs, eq3(querySnapshots.runId, runs.id)).where(and3(
|
|
10013
10225
|
eq3(runs.projectId, projectId),
|
|
@@ -10094,12 +10306,12 @@ function maybeShowActivationNotice(io = {}) {
|
|
|
10094
10306
|
}
|
|
10095
10307
|
|
|
10096
10308
|
// src/gsc-sync.ts
|
|
10097
|
-
import
|
|
10309
|
+
import crypto9 from "crypto";
|
|
10098
10310
|
import { eq as eq5, and as and5, sql as sql3 } from "drizzle-orm";
|
|
10099
10311
|
|
|
10100
10312
|
// src/gsc-coverage-snapshot.ts
|
|
10101
|
-
import
|
|
10102
|
-
import { and as and4, desc, eq as eq4, gte } from "drizzle-orm";
|
|
10313
|
+
import crypto8 from "crypto";
|
|
10314
|
+
import { and as and4, desc as desc2, eq as eq4, gte } from "drizzle-orm";
|
|
10103
10315
|
var COVERAGE_IMPRESSION_WINDOW_DAYS = 30;
|
|
10104
10316
|
function pageKey(raw) {
|
|
10105
10317
|
if (!raw) return null;
|
|
@@ -10123,9 +10335,9 @@ function writeCoverageSnapshot(db, projectId, runId, opts = {}) {
|
|
|
10123
10335
|
eq4(siteCrawlSnapshots.projectId, projectId),
|
|
10124
10336
|
eq4(siteCrawlSnapshots.complete, true),
|
|
10125
10337
|
eq4(runs.status, "completed")
|
|
10126
|
-
)).orderBy(
|
|
10338
|
+
)).orderBy(desc2(siteCrawlSnapshots.createdAt)).limit(1).get();
|
|
10127
10339
|
const hasCrawlHistory = Boolean(db.select({ id: siteCrawlSnapshots.id }).from(siteCrawlSnapshots).where(eq4(siteCrawlSnapshots.projectId, projectId)).limit(1).get());
|
|
10128
|
-
const latestAudit = db.select({ runId: siteAuditSnapshots.runId }).from(siteAuditSnapshots).where(eq4(siteAuditSnapshots.projectId, projectId)).orderBy(
|
|
10340
|
+
const latestAudit = db.select({ runId: siteAuditSnapshots.runId }).from(siteAuditSnapshots).where(eq4(siteAuditSnapshots.projectId, projectId)).orderBy(desc2(siteAuditSnapshots.createdAt)).limit(1).get();
|
|
10129
10341
|
const sitemapUrls = latestCrawl?.attemptId ? db.select({ url: siteCrawlPages.url }).from(siteCrawlPages).where(and4(
|
|
10130
10342
|
eq4(siteCrawlPages.projectId, projectId),
|
|
10131
10343
|
eq4(siteCrawlPages.runId, latestCrawl.runId),
|
|
@@ -10160,7 +10372,7 @@ function writeCoverageSnapshot(db, projectId, runId, opts = {}) {
|
|
|
10160
10372
|
const snapshotDate = now.toISOString().split("T")[0];
|
|
10161
10373
|
db.delete(gscCoverageSnapshots).where(and4(eq4(gscCoverageSnapshots.projectId, projectId), eq4(gscCoverageSnapshots.date, snapshotDate))).run();
|
|
10162
10374
|
db.insert(gscCoverageSnapshots).values({
|
|
10163
|
-
id:
|
|
10375
|
+
id: crypto8.randomUUID(),
|
|
10164
10376
|
projectId,
|
|
10165
10377
|
syncRunId: runId,
|
|
10166
10378
|
date: snapshotDate,
|
|
@@ -10239,7 +10451,7 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
10239
10451
|
for (const row of batch) {
|
|
10240
10452
|
const [query, page, country, device, date] = row.keys;
|
|
10241
10453
|
db.insert(gscSearchData).values({
|
|
10242
|
-
id:
|
|
10454
|
+
id: crypto9.randomUUID(),
|
|
10243
10455
|
projectId,
|
|
10244
10456
|
syncRunId: runId,
|
|
10245
10457
|
date: date ?? "",
|
|
@@ -10271,7 +10483,7 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
10271
10483
|
for (const row of totalRows) {
|
|
10272
10484
|
const [date] = row.keys;
|
|
10273
10485
|
db.insert(gscDailyTotals).values({
|
|
10274
|
-
id:
|
|
10486
|
+
id: crypto9.randomUUID(),
|
|
10275
10487
|
projectId,
|
|
10276
10488
|
date: date ?? "",
|
|
10277
10489
|
clicks: row.clicks,
|
|
@@ -10320,7 +10532,7 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
10320
10532
|
const [date, query] = row.keys;
|
|
10321
10533
|
if (!date || !query) continue;
|
|
10322
10534
|
db.insert(gscQueryDailyTotals).values({
|
|
10323
|
-
id:
|
|
10535
|
+
id: crypto9.randomUUID(),
|
|
10324
10536
|
projectId,
|
|
10325
10537
|
date,
|
|
10326
10538
|
query,
|
|
@@ -10345,12 +10557,12 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
10345
10557
|
}
|
|
10346
10558
|
|
|
10347
10559
|
// src/gbp-sync.ts
|
|
10348
|
-
import
|
|
10349
|
-
import { eq as eq7, and as and7, desc as
|
|
10560
|
+
import crypto11 from "crypto";
|
|
10561
|
+
import { eq as eq7, and as and7, desc as desc4, inArray as inArray3, lt as lt2 } from "drizzle-orm";
|
|
10350
10562
|
|
|
10351
10563
|
// src/gbp-reviews.ts
|
|
10352
|
-
import
|
|
10353
|
-
import { and as and6, desc as
|
|
10564
|
+
import crypto10 from "crypto";
|
|
10565
|
+
import { and as and6, desc as desc3, eq as eq6, inArray as inArray2, sql as sql4 } from "drizzle-orm";
|
|
10354
10566
|
var log4 = createLogger("GbpReviews");
|
|
10355
10567
|
var MS_PER_DAY = 864e5;
|
|
10356
10568
|
var REVIEW_ALERT_MAX_AGE_DAYS = 30;
|
|
@@ -10382,7 +10594,7 @@ function placesReviewsDue(db, projectId, locationName, now) {
|
|
|
10382
10594
|
eq6(gbpReviewRatings.projectId, projectId),
|
|
10383
10595
|
eq6(gbpReviewRatings.locationName, locationName),
|
|
10384
10596
|
eq6(gbpReviewRatings.origin, "places")
|
|
10385
|
-
)).orderBy(
|
|
10597
|
+
)).orderBy(desc3(gbpReviewRatings.observedAt)).limit(1).get();
|
|
10386
10598
|
if (!latest) return true;
|
|
10387
10599
|
return now.getTime() - Date.parse(latest.observedAt) >= PLACES_REVIEW_MIN_INTERVAL_HOURS * 36e5;
|
|
10388
10600
|
}
|
|
@@ -10463,12 +10675,12 @@ function persistReviewObservation(tx, projectId, runId, obs, ctx) {
|
|
|
10463
10675
|
eq6(gbpReviewRatings.projectId, projectId),
|
|
10464
10676
|
eq6(gbpReviewRatings.locationName, obs.locationName),
|
|
10465
10677
|
eq6(gbpReviewRatings.origin, obs.origin)
|
|
10466
|
-
)).orderBy(
|
|
10678
|
+
)).orderBy(desc3(gbpReviewRatings.observedAt)).limit(1).get();
|
|
10467
10679
|
const baseline = latestRating === void 0;
|
|
10468
10680
|
let queuedRatingDrop = false;
|
|
10469
10681
|
if (!latestRating) {
|
|
10470
10682
|
tx.insert(gbpReviewRatings).values({
|
|
10471
|
-
id:
|
|
10683
|
+
id: crypto10.randomUUID(),
|
|
10472
10684
|
projectId,
|
|
10473
10685
|
locationName: obs.locationName,
|
|
10474
10686
|
origin: obs.origin,
|
|
@@ -10488,7 +10700,7 @@ function persistReviewObservation(tx, projectId, runId, obs, ctx) {
|
|
|
10488
10700
|
const alertState = ratingAlertState(obs.origin, latestRating.rating, obs.rating);
|
|
10489
10701
|
queuedRatingDrop = alertState === "pending";
|
|
10490
10702
|
tx.insert(gbpReviewRatings).values({
|
|
10491
|
-
id:
|
|
10703
|
+
id: crypto10.randomUUID(),
|
|
10492
10704
|
projectId,
|
|
10493
10705
|
locationName: obs.locationName,
|
|
10494
10706
|
origin: obs.origin,
|
|
@@ -10538,7 +10750,7 @@ function persistReviewObservation(tx, projectId, runId, obs, ctx) {
|
|
|
10538
10750
|
const alertState = reviewAlertState(review, { baseline, now, negativeMaxStars });
|
|
10539
10751
|
if (alertState === "pending") queuedReviews++;
|
|
10540
10752
|
tx.insert(gbpReviews).values({
|
|
10541
|
-
id:
|
|
10753
|
+
id: crypto10.randomUUID(),
|
|
10542
10754
|
projectId,
|
|
10543
10755
|
origin: obs.origin,
|
|
10544
10756
|
reviewName: review.reviewName,
|
|
@@ -10669,15 +10881,15 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
10669
10881
|
fetchLocationReviews(db, projectId, loc, reviewContext)
|
|
10670
10882
|
]);
|
|
10671
10883
|
const lodgingHash = lodging ? hashLodging(lodging) : null;
|
|
10672
|
-
const latestLodging = lodging ? db.select().from(gbpLodgingSnapshots).where(and7(eq7(gbpLodgingSnapshots.projectId, projectId), eq7(gbpLodgingSnapshots.locationName, loc.locationName))).orderBy(
|
|
10884
|
+
const latestLodging = lodging ? db.select().from(gbpLodgingSnapshots).where(and7(eq7(gbpLodgingSnapshots.projectId, projectId), eq7(gbpLodgingSnapshots.locationName, loc.locationName))).orderBy(desc4(gbpLodgingSnapshots.syncedAt)).limit(1).get() : void 0;
|
|
10673
10885
|
const lodgingChanged = lodging !== null && latestLodging?.contentHash !== lodgingHash;
|
|
10674
10886
|
const attributesHash = hashAttributes(attributes);
|
|
10675
|
-
const latestAttributes = db.select().from(gbpAttributesSnapshots).where(and7(eq7(gbpAttributesSnapshots.projectId, projectId), eq7(gbpAttributesSnapshots.locationName, loc.locationName))).orderBy(
|
|
10887
|
+
const latestAttributes = db.select().from(gbpAttributesSnapshots).where(and7(eq7(gbpAttributesSnapshots.projectId, projectId), eq7(gbpAttributesSnapshots.locationName, loc.locationName))).orderBy(desc4(gbpAttributesSnapshots.syncedAt)).limit(1).get();
|
|
10676
10888
|
const attributesChanged = latestAttributes?.contentHash !== attributesHash;
|
|
10677
10889
|
let placeToWrite = null;
|
|
10678
10890
|
let placeToTouch = null;
|
|
10679
10891
|
if (placesTier !== "off" && placesApiKey && lodging !== null && loc.placeId) {
|
|
10680
|
-
const latestPlace = db.select().from(gbpPlaceDetails).where(and7(eq7(gbpPlaceDetails.projectId, projectId), eq7(gbpPlaceDetails.locationName, loc.locationName))).orderBy(
|
|
10892
|
+
const latestPlace = db.select().from(gbpPlaceDetails).where(and7(eq7(gbpPlaceDetails.projectId, projectId), eq7(gbpPlaceDetails.locationName, loc.locationName))).orderBy(desc4(gbpPlaceDetails.syncedAt)).limit(1).get();
|
|
10681
10893
|
const ageDays = latestPlace ? (Date.now() - new Date(latestPlace.syncedAt).getTime()) / MS_PER_DAY2 : Infinity;
|
|
10682
10894
|
if (ageDays >= placesRefreshDays) {
|
|
10683
10895
|
try {
|
|
@@ -10699,7 +10911,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
10699
10911
|
tx.delete(gbpDailyMetrics).where(and7(eq7(gbpDailyMetrics.projectId, projectId), eq7(gbpDailyMetrics.locationName, loc.locationName))).run();
|
|
10700
10912
|
for (const row of metricRows) {
|
|
10701
10913
|
tx.insert(gbpDailyMetrics).values({
|
|
10702
|
-
id:
|
|
10914
|
+
id: crypto11.randomUUID(),
|
|
10703
10915
|
projectId,
|
|
10704
10916
|
locationName: loc.locationName,
|
|
10705
10917
|
date: row.date,
|
|
@@ -10711,7 +10923,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
10711
10923
|
tx.delete(gbpKeywordImpressions).where(and7(eq7(gbpKeywordImpressions.projectId, projectId), eq7(gbpKeywordImpressions.locationName, loc.locationName))).run();
|
|
10712
10924
|
for (const row of keywordRows) {
|
|
10713
10925
|
tx.insert(gbpKeywordImpressions).values({
|
|
10714
|
-
id:
|
|
10926
|
+
id: crypto11.randomUUID(),
|
|
10715
10927
|
projectId,
|
|
10716
10928
|
locationName: loc.locationName,
|
|
10717
10929
|
periodStart: monthKey(keywordsStart),
|
|
@@ -10732,7 +10944,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
10732
10944
|
for (const { month, rows } of monthlyKeywordResults) {
|
|
10733
10945
|
for (const row of rows) {
|
|
10734
10946
|
tx.insert(gbpKeywordMonthly).values({
|
|
10735
|
-
id:
|
|
10947
|
+
id: crypto11.randomUUID(),
|
|
10736
10948
|
projectId,
|
|
10737
10949
|
locationName: loc.locationName,
|
|
10738
10950
|
month,
|
|
@@ -10747,13 +10959,13 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
10747
10959
|
tx.delete(gbpKeywordMonthly).where(and7(
|
|
10748
10960
|
eq7(gbpKeywordMonthly.projectId, projectId),
|
|
10749
10961
|
eq7(gbpKeywordMonthly.locationName, loc.locationName),
|
|
10750
|
-
|
|
10962
|
+
lt2(gbpKeywordMonthly.month, keywordRetentionCutoff)
|
|
10751
10963
|
)).run();
|
|
10752
10964
|
}
|
|
10753
10965
|
tx.delete(gbpPlaceActions).where(and7(eq7(gbpPlaceActions.projectId, projectId), eq7(gbpPlaceActions.locationName, loc.locationName))).run();
|
|
10754
10966
|
for (const row of placeActionRows) {
|
|
10755
10967
|
tx.insert(gbpPlaceActions).values({
|
|
10756
|
-
id:
|
|
10968
|
+
id: crypto11.randomUUID(),
|
|
10757
10969
|
projectId,
|
|
10758
10970
|
locationName: loc.locationName,
|
|
10759
10971
|
placeActionLinkName: row.placeActionLinkName,
|
|
@@ -10766,7 +10978,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
10766
10978
|
}
|
|
10767
10979
|
if (lodging !== null && lodgingChanged) {
|
|
10768
10980
|
tx.insert(gbpLodgingSnapshots).values({
|
|
10769
|
-
id:
|
|
10981
|
+
id: crypto11.randomUUID(),
|
|
10770
10982
|
projectId,
|
|
10771
10983
|
locationName: loc.locationName,
|
|
10772
10984
|
contentHash: lodgingHash,
|
|
@@ -10780,7 +10992,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
10780
10992
|
}
|
|
10781
10993
|
if (attributesChanged) {
|
|
10782
10994
|
tx.insert(gbpAttributesSnapshots).values({
|
|
10783
|
-
id:
|
|
10995
|
+
id: crypto11.randomUUID(),
|
|
10784
10996
|
projectId,
|
|
10785
10997
|
locationName: loc.locationName,
|
|
10786
10998
|
contentHash: attributesHash,
|
|
@@ -10794,7 +11006,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
10794
11006
|
}
|
|
10795
11007
|
if (placeToWrite) {
|
|
10796
11008
|
tx.insert(gbpPlaceDetails).values({
|
|
10797
|
-
id:
|
|
11009
|
+
id: crypto11.randomUUID(),
|
|
10798
11010
|
projectId,
|
|
10799
11011
|
locationName: loc.locationName,
|
|
10800
11012
|
placeId: placeToWrite.placeId,
|
|
@@ -10899,7 +11111,7 @@ async function refreshSelectedLocationProfiles(db, projectId, accessToken, locat
|
|
|
10899
11111
|
}
|
|
10900
11112
|
|
|
10901
11113
|
// src/ads-sync.ts
|
|
10902
|
-
import
|
|
11114
|
+
import crypto12 from "crypto";
|
|
10903
11115
|
import { and as and8, eq as eq8, inArray as inArray4, notInArray } from "drizzle-orm";
|
|
10904
11116
|
|
|
10905
11117
|
// ../integration-openai-ads/src/constants.ts
|
|
@@ -11674,7 +11886,7 @@ async function executeAdsSync(db, runId, projectId, opts) {
|
|
|
11674
11886
|
}
|
|
11675
11887
|
for (const upsert of insightUpserts) {
|
|
11676
11888
|
tx.insert(adsInsightsDaily).values({
|
|
11677
|
-
id:
|
|
11889
|
+
id: crypto12.randomUUID(),
|
|
11678
11890
|
projectId,
|
|
11679
11891
|
level: upsert.level,
|
|
11680
11892
|
entityId: upsert.entityId,
|
|
@@ -11742,7 +11954,7 @@ async function executeAdsSync(db, runId, projectId, opts) {
|
|
|
11742
11954
|
}
|
|
11743
11955
|
|
|
11744
11956
|
// src/gsc-inspect-sitemap.ts
|
|
11745
|
-
import
|
|
11957
|
+
import crypto14 from "crypto";
|
|
11746
11958
|
import { eq as eq9 } from "drizzle-orm";
|
|
11747
11959
|
|
|
11748
11960
|
// src/sitemap-parser.ts
|
|
@@ -11842,7 +12054,7 @@ async function parseSitemapRecursive(url, urls, visited, depth, isChild) {
|
|
|
11842
12054
|
}
|
|
11843
12055
|
|
|
11844
12056
|
// src/inspect-rate-gate.ts
|
|
11845
|
-
import
|
|
12057
|
+
import crypto13 from "crypto";
|
|
11846
12058
|
var gates = /* @__PURE__ */ new Map();
|
|
11847
12059
|
function sharedRateGate(key) {
|
|
11848
12060
|
let entry = gates.get(key);
|
|
@@ -11861,7 +12073,7 @@ function sharedRateGate(key) {
|
|
|
11861
12073
|
};
|
|
11862
12074
|
}
|
|
11863
12075
|
function credentialGateKey(prefix, secret) {
|
|
11864
|
-
const digest =
|
|
12076
|
+
const digest = crypto13.createHash("sha256").update(secret).digest("hex").slice(0, 12);
|
|
11865
12077
|
return `${prefix}:${digest}`;
|
|
11866
12078
|
}
|
|
11867
12079
|
|
|
@@ -12016,7 +12228,7 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
|
|
|
12016
12228
|
const rich = ir.richResultsResult;
|
|
12017
12229
|
const inspectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12018
12230
|
db.insert(gscUrlInspections).values({
|
|
12019
|
-
id:
|
|
12231
|
+
id: crypto14.randomUUID(),
|
|
12020
12232
|
projectId,
|
|
12021
12233
|
syncRunId: runId,
|
|
12022
12234
|
url: pageUrl,
|
|
@@ -12074,8 +12286,8 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
|
|
|
12074
12286
|
}
|
|
12075
12287
|
|
|
12076
12288
|
// src/bing-inspect-sitemap.ts
|
|
12077
|
-
import
|
|
12078
|
-
import { eq as eq10, desc as
|
|
12289
|
+
import crypto15 from "crypto";
|
|
12290
|
+
import { eq as eq10, desc as desc5 } from "drizzle-orm";
|
|
12079
12291
|
var log9 = createLogger("BingInspectSitemap");
|
|
12080
12292
|
var BING_INSPECT_SPACING_MS = 2e3;
|
|
12081
12293
|
function parseBingDate(value) {
|
|
@@ -12179,7 +12391,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
12179
12391
|
derivedInIndex = false;
|
|
12180
12392
|
}
|
|
12181
12393
|
db.insert(bingUrlInspections).values({
|
|
12182
|
-
id:
|
|
12394
|
+
id: crypto15.randomUUID(),
|
|
12183
12395
|
projectId,
|
|
12184
12396
|
url: pageUrl,
|
|
12185
12397
|
httpCode,
|
|
@@ -12239,7 +12451,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
12239
12451
|
`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}`
|
|
12240
12452
|
);
|
|
12241
12453
|
}
|
|
12242
|
-
const allInspections = db.select().from(bingUrlInspections).where(eq10(bingUrlInspections.projectId, projectId)).orderBy(
|
|
12454
|
+
const allInspections = db.select().from(bingUrlInspections).where(eq10(bingUrlInspections.projectId, projectId)).orderBy(desc5(bingUrlInspections.inspectedAt)).all();
|
|
12243
12455
|
const latestByUrl = /* @__PURE__ */ new Map();
|
|
12244
12456
|
const definitiveByUrl = /* @__PURE__ */ new Map();
|
|
12245
12457
|
for (const row of allInspections) {
|
|
@@ -12263,7 +12475,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
12263
12475
|
const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
12264
12476
|
const snapNow = (/* @__PURE__ */ new Date()).toISOString();
|
|
12265
12477
|
db.insert(bingCoverageSnapshots).values({
|
|
12266
|
-
id:
|
|
12478
|
+
id: crypto15.randomUUID(),
|
|
12267
12479
|
projectId,
|
|
12268
12480
|
syncRunId: runId,
|
|
12269
12481
|
date: snapshotDate,
|
|
@@ -12304,8 +12516,8 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
12304
12516
|
}
|
|
12305
12517
|
|
|
12306
12518
|
// src/coverage-refresh.ts
|
|
12307
|
-
import
|
|
12308
|
-
import { and as and9, desc as
|
|
12519
|
+
import crypto16 from "crypto";
|
|
12520
|
+
import { and as and9, desc as desc6, eq as eq11, inArray as inArray5 } from "drizzle-orm";
|
|
12309
12521
|
var log10 = createLogger("CoverageRefresh");
|
|
12310
12522
|
var COVERAGE_REFRESH_MIN_INTERVAL_MS = 60 * 60 * 1e3;
|
|
12311
12523
|
var COVERAGE_REFRESH_MANUAL_MIN_INTERVAL_MS = 2 * 60 * 1e3;
|
|
@@ -12334,7 +12546,7 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
|
|
|
12334
12546
|
eq11(runs.kind, RunKinds["inspect-sitemap"]),
|
|
12335
12547
|
inArray5(runs.status, IN_FLIGHT_STATUSES)
|
|
12336
12548
|
)
|
|
12337
|
-
).orderBy(
|
|
12549
|
+
).orderBy(desc6(runs.createdAt)).limit(1).get();
|
|
12338
12550
|
if (inFlight2) {
|
|
12339
12551
|
log10.info("skip.in-flight", { projectId });
|
|
12340
12552
|
return null;
|
|
@@ -12346,7 +12558,7 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
|
|
|
12346
12558
|
eq11(runs.kind, RunKinds["inspect-sitemap"]),
|
|
12347
12559
|
inArray5(runs.status, ACTIVE_OR_DONE_STATUSES)
|
|
12348
12560
|
)
|
|
12349
|
-
).orderBy(
|
|
12561
|
+
).orderBy(desc6(runs.createdAt)).limit(1).get();
|
|
12350
12562
|
if (recent) {
|
|
12351
12563
|
const ageMs = nowMs - Date.parse(recent.createdAt);
|
|
12352
12564
|
if (Number.isFinite(ageMs) && ageMs < windowMs) {
|
|
@@ -12354,7 +12566,7 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
|
|
|
12354
12566
|
return null;
|
|
12355
12567
|
}
|
|
12356
12568
|
}
|
|
12357
|
-
const runId =
|
|
12569
|
+
const runId = crypto16.randomUUID();
|
|
12358
12570
|
db.insert(runs).values({
|
|
12359
12571
|
id: runId,
|
|
12360
12572
|
projectId,
|
|
@@ -12377,7 +12589,7 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
|
|
|
12377
12589
|
}
|
|
12378
12590
|
|
|
12379
12591
|
// src/commoncrawl-sync.ts
|
|
12380
|
-
import
|
|
12592
|
+
import crypto17 from "crypto";
|
|
12381
12593
|
import path4 from "path";
|
|
12382
12594
|
import { and as and10, eq as eq12, sql as sql5 } from "drizzle-orm";
|
|
12383
12595
|
var log11 = createLogger("CommonCrawlSync");
|
|
@@ -12452,7 +12664,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
12452
12664
|
if (!projectIds) continue;
|
|
12453
12665
|
for (const projectId of projectIds) {
|
|
12454
12666
|
expanded.push({
|
|
12455
|
-
id:
|
|
12667
|
+
id: crypto17.randomUUID(),
|
|
12456
12668
|
projectId,
|
|
12457
12669
|
releaseSyncId: syncId,
|
|
12458
12670
|
release,
|
|
@@ -12472,7 +12684,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
12472
12684
|
const projectRows = rowsByProject.get(p.id) ?? [];
|
|
12473
12685
|
const summary = computeSummary(projectRows);
|
|
12474
12686
|
tx.insert(backlinkSummaries).values({
|
|
12475
|
-
id:
|
|
12687
|
+
id: crypto17.randomUUID(),
|
|
12476
12688
|
projectId: p.id,
|
|
12477
12689
|
releaseSyncId: syncId,
|
|
12478
12690
|
source: BacklinkSources.commoncrawl,
|
|
@@ -12569,9 +12781,9 @@ function computeSummary(rows) {
|
|
|
12569
12781
|
}
|
|
12570
12782
|
|
|
12571
12783
|
// src/backlink-extract.ts
|
|
12572
|
-
import
|
|
12784
|
+
import crypto18 from "crypto";
|
|
12573
12785
|
import fs5 from "fs";
|
|
12574
|
-
import { and as and11, desc as
|
|
12786
|
+
import { and as and11, desc as desc7, eq as eq13 } from "drizzle-orm";
|
|
12575
12787
|
var log12 = createLogger("BacklinkExtract");
|
|
12576
12788
|
function defaultDeps3() {
|
|
12577
12789
|
return {
|
|
@@ -12589,7 +12801,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
12589
12801
|
if (!project) {
|
|
12590
12802
|
throw new Error(`Project not found: ${projectId}`);
|
|
12591
12803
|
}
|
|
12592
|
-
const sync = opts.release ? db.select().from(ccReleaseSyncs).where(eq13(ccReleaseSyncs.release, opts.release)).get() : db.select().from(ccReleaseSyncs).where(eq13(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).orderBy(
|
|
12804
|
+
const sync = opts.release ? db.select().from(ccReleaseSyncs).where(eq13(ccReleaseSyncs.release, opts.release)).get() : db.select().from(ccReleaseSyncs).where(eq13(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).orderBy(desc7(ccReleaseSyncs.createdAt)).limit(1).get();
|
|
12593
12805
|
if (!sync) {
|
|
12594
12806
|
throw new Error("No ready release sync available \u2014 run `canonry backlinks sync` first");
|
|
12595
12807
|
}
|
|
@@ -12625,7 +12837,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
12625
12837
|
).run();
|
|
12626
12838
|
if (rows.length > 0) {
|
|
12627
12839
|
const values = rows.map((r) => ({
|
|
12628
|
-
id:
|
|
12840
|
+
id: crypto18.randomUUID(),
|
|
12629
12841
|
projectId,
|
|
12630
12842
|
releaseSyncId: syncId,
|
|
12631
12843
|
source: BacklinkSources.commoncrawl,
|
|
@@ -12639,7 +12851,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
12639
12851
|
}
|
|
12640
12852
|
const summary = computeSummary2(rows);
|
|
12641
12853
|
tx.insert(backlinkSummaries).values({
|
|
12642
|
-
id:
|
|
12854
|
+
id: crypto18.randomUUID(),
|
|
12643
12855
|
projectId,
|
|
12644
12856
|
releaseSyncId: syncId,
|
|
12645
12857
|
source: BacklinkSources.commoncrawl,
|
|
@@ -12682,7 +12894,7 @@ function computeSummary2(rows) {
|
|
|
12682
12894
|
}
|
|
12683
12895
|
|
|
12684
12896
|
// src/discovery-run.ts
|
|
12685
|
-
import
|
|
12897
|
+
import crypto19 from "crypto";
|
|
12686
12898
|
import { and as and12, eq as eq14 } from "drizzle-orm";
|
|
12687
12899
|
var log13 = createLogger("DiscoveryRun");
|
|
12688
12900
|
var DEFAULT_SEED_COUNT = 30;
|
|
@@ -13037,7 +13249,7 @@ function writeDiscoveryInsight(db, input) {
|
|
|
13037
13249
|
eq14(insights.dismissed, false)
|
|
13038
13250
|
)).run();
|
|
13039
13251
|
tx.insert(insights).values({
|
|
13040
|
-
id:
|
|
13252
|
+
id: crypto19.randomUUID(),
|
|
13041
13253
|
projectId: input.projectId,
|
|
13042
13254
|
runId: input.runId,
|
|
13043
13255
|
type: "discovery.basket-divergence",
|
|
@@ -13073,7 +13285,7 @@ function buildDiscoveryInsightTitle(input) {
|
|
|
13073
13285
|
}
|
|
13074
13286
|
|
|
13075
13287
|
// src/execute-site-audit.ts
|
|
13076
|
-
import
|
|
13288
|
+
import crypto20 from "crypto";
|
|
13077
13289
|
import { and as and14, eq as eq16, ne as ne2, sql as sql6 } from "drizzle-orm";
|
|
13078
13290
|
import { runSiteCrawl } from "@canonry/aeo-audit";
|
|
13079
13291
|
|
|
@@ -13493,10 +13705,13 @@ function deadLinkCheckedCount(edges, pages) {
|
|
|
13493
13705
|
}
|
|
13494
13706
|
function trackSiteAuditOutcome(db, input) {
|
|
13495
13707
|
try {
|
|
13496
|
-
const errorCode = input.status === "cancelled" ? "RUN_CANCELLED" : input.status === "failed" ? "
|
|
13708
|
+
const errorCode = input.status === "cancelled" ? "RUN_CANCELLED" : input.status === "failed" ? "INTERNAL" : void 0;
|
|
13497
13709
|
trackEvent(
|
|
13498
13710
|
"site_audit.completed",
|
|
13499
|
-
|
|
13711
|
+
{
|
|
13712
|
+
...buildSiteAuditCompletedProps(input),
|
|
13713
|
+
...input.status === "failed" ? describeRunFailure(input.error) : {}
|
|
13714
|
+
},
|
|
13500
13715
|
errorCode ? { errorCode } : void 0
|
|
13501
13716
|
);
|
|
13502
13717
|
const pagesAudited = input.crawl?.pagesAudited ?? 0;
|
|
@@ -13521,7 +13736,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13521
13736
|
const trigger = db.select({ trigger: runs.trigger }).from(runs).where(eq16(runs.id, runId)).get()?.trigger ?? null;
|
|
13522
13737
|
let canonicalDomain = null;
|
|
13523
13738
|
const attemptNumber = (db.select({ value: sql6`coalesce(max(${siteCrawlAttempts.attemptNumber}), 0)` }).from(siteCrawlAttempts).where(eq16(siteCrawlAttempts.runId, runId)).get()?.value ?? 0) + 1;
|
|
13524
|
-
const attemptId =
|
|
13739
|
+
const attemptId = crypto20.randomUUID();
|
|
13525
13740
|
db.insert(siteCrawlAttempts).values({
|
|
13526
13741
|
id: attemptId,
|
|
13527
13742
|
projectId,
|
|
@@ -13599,7 +13814,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13599
13814
|
nodeKey: page.key
|
|
13600
13815
|
});
|
|
13601
13816
|
tx.insert(siteCrawlPages).values({
|
|
13602
|
-
id:
|
|
13817
|
+
id: crypto20.randomUUID(),
|
|
13603
13818
|
projectId,
|
|
13604
13819
|
runId,
|
|
13605
13820
|
attemptId,
|
|
@@ -13692,7 +13907,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13692
13907
|
if (isSelfLink(edge.from, edge.to)) return;
|
|
13693
13908
|
observedEdges.set(edge.key, edge);
|
|
13694
13909
|
tx.insert(siteCrawlEdges).values({
|
|
13695
|
-
id:
|
|
13910
|
+
id: crypto20.randomUUID(),
|
|
13696
13911
|
projectId,
|
|
13697
13912
|
runId,
|
|
13698
13913
|
attemptId,
|
|
@@ -13762,7 +13977,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13762
13977
|
return;
|
|
13763
13978
|
}
|
|
13764
13979
|
tx.insert(siteCrawlEventReceipts).values({
|
|
13765
|
-
id:
|
|
13980
|
+
id: crypto20.randomUUID(),
|
|
13766
13981
|
projectId,
|
|
13767
13982
|
runId,
|
|
13768
13983
|
attemptId,
|
|
@@ -13898,7 +14113,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13898
14113
|
if (claim2.changes === 0) return false;
|
|
13899
14114
|
if (hasAuditedPages) {
|
|
13900
14115
|
tx.insert(siteAuditSnapshots).values({
|
|
13901
|
-
id:
|
|
14116
|
+
id: crypto20.randomUUID(),
|
|
13902
14117
|
projectId,
|
|
13903
14118
|
runId,
|
|
13904
14119
|
sitemapUrl: opts.sitemapUrl ?? new URL("/sitemap.xml", crawlRootUrl).href,
|
|
@@ -13916,7 +14131,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13916
14131
|
for (const page of observedPages.values()) {
|
|
13917
14132
|
if (!isLegacyScorecardPage(page)) continue;
|
|
13918
14133
|
tx.insert(siteAuditPages).values({
|
|
13919
|
-
id:
|
|
14134
|
+
id: crypto20.randomUUID(),
|
|
13920
14135
|
projectId,
|
|
13921
14136
|
runId,
|
|
13922
14137
|
url: page.finalUrl ?? page.requestedUrl,
|
|
@@ -13929,7 +14144,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13929
14144
|
}
|
|
13930
14145
|
}
|
|
13931
14146
|
tx.insert(siteCrawlSnapshots).values({
|
|
13932
|
-
id:
|
|
14147
|
+
id: crypto20.randomUUID(),
|
|
13933
14148
|
projectId,
|
|
13934
14149
|
runId,
|
|
13935
14150
|
attemptId,
|
|
@@ -13975,7 +14190,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
13975
14190
|
if (opts.checkDeadLinks) {
|
|
13976
14191
|
for (const finding of deadLinkFindings) {
|
|
13977
14192
|
tx.insert(siteCrawlFindings).values({
|
|
13978
|
-
id:
|
|
14193
|
+
id: crypto20.randomUUID(),
|
|
13979
14194
|
projectId,
|
|
13980
14195
|
runId,
|
|
13981
14196
|
attemptId,
|
|
@@ -14063,7 +14278,8 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
14063
14278
|
status: cancelled ? "cancelled" : "failed",
|
|
14064
14279
|
startTime,
|
|
14065
14280
|
trigger,
|
|
14066
|
-
canonicalDomain
|
|
14281
|
+
canonicalDomain,
|
|
14282
|
+
error
|
|
14067
14283
|
});
|
|
14068
14284
|
throw error;
|
|
14069
14285
|
}
|
|
@@ -14071,7 +14287,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
14071
14287
|
|
|
14072
14288
|
// src/commands/backfill.ts
|
|
14073
14289
|
import { setImmediate as yieldToEventLoop2 } from "timers/promises";
|
|
14074
|
-
import { and as and15, asc as asc4, desc as
|
|
14290
|
+
import { and as and15, asc as asc4, desc as desc8, eq as eq17, gt as gt3, inArray as inArray7, isNull as isNull2, lte as lte2, sql as sql7 } from "drizzle-orm";
|
|
14075
14291
|
var SNAPSHOT_BATCH_SIZE = 500;
|
|
14076
14292
|
var PATH_REPAIR_PAGE_SIZE = 128;
|
|
14077
14293
|
async function backfillAnswerVisibilityCommand(opts) {
|
|
@@ -14259,7 +14475,7 @@ function backfillNormalizedPaths(db, opts) {
|
|
|
14259
14475
|
async function repairStoredPaths(db, table, opts) {
|
|
14260
14476
|
const pageSize = opts?.pageSize ?? PATH_REPAIR_PAGE_SIZE;
|
|
14261
14477
|
const projectScope = opts?.projectId ? sql7`+${table.projectId} = ${opts.projectId}` : void 0;
|
|
14262
|
-
const lastId = db.select({ id: table.id }).from(table).where(projectScope).orderBy(
|
|
14478
|
+
const lastId = db.select({ id: table.id }).from(table).where(projectScope).orderBy(desc8(table.id)).limit(1).get()?.id;
|
|
14263
14479
|
if (lastId === void 0) return { examined: 0, updated: 0, unchanged: 0 };
|
|
14264
14480
|
let cursor;
|
|
14265
14481
|
let examined = 0;
|
|
@@ -14549,7 +14765,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
14549
14765
|
return result2;
|
|
14550
14766
|
}
|
|
14551
14767
|
async function backfillInsightsCommand(project, opts) {
|
|
14552
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
14768
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-5J36RBEM.js");
|
|
14553
14769
|
const config = loadConfig();
|
|
14554
14770
|
const db = createClient(config.database);
|
|
14555
14771
|
migrate(db);
|
|
@@ -15139,7 +15355,7 @@ var ProviderRegistry = class {
|
|
|
15139
15355
|
import { createHash as createHash4 } from "crypto";
|
|
15140
15356
|
var TTL_MS = 60 * 60 * 1e3;
|
|
15141
15357
|
var RETRY_MS = 60 * 1e3;
|
|
15142
|
-
var
|
|
15358
|
+
var TIMEOUT_MS3 = 3e3;
|
|
15143
15359
|
function createProviderModelCatalog(registry) {
|
|
15144
15360
|
const cache = /* @__PURE__ */ new Map();
|
|
15145
15361
|
const read = async (name) => {
|
|
@@ -15165,7 +15381,7 @@ function createProviderModelCatalog(registry) {
|
|
|
15165
15381
|
timer = setTimeout(() => {
|
|
15166
15382
|
controller.abort();
|
|
15167
15383
|
reject(new Error("Model discovery timed out"));
|
|
15168
|
-
},
|
|
15384
|
+
}, TIMEOUT_MS3);
|
|
15169
15385
|
});
|
|
15170
15386
|
current.pending = Promise.race([
|
|
15171
15387
|
Promise.resolve().then(() => provider.adapter.listModels(provider.config, controller.signal)),
|
|
@@ -15196,14 +15412,14 @@ function createProviderModelCatalog(registry) {
|
|
|
15196
15412
|
}
|
|
15197
15413
|
|
|
15198
15414
|
// src/scheduler.ts
|
|
15199
|
-
import
|
|
15415
|
+
import crypto21 from "crypto";
|
|
15200
15416
|
import cron from "node-cron";
|
|
15201
15417
|
import { and as and16, eq as eq18, inArray as inArray8, notExists, sql as sql8 } from "drizzle-orm";
|
|
15202
15418
|
var log15 = createLogger("Scheduler");
|
|
15203
15419
|
var DEFAULT_HEALTH_CRON = "0 */6 * * *";
|
|
15204
15420
|
function ensureDefaultHealthSchedule(db, projectId, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
15205
15421
|
const result2 = db.insert(schedules).values({
|
|
15206
|
-
id:
|
|
15422
|
+
id: crypto21.randomUUID(),
|
|
15207
15423
|
projectId,
|
|
15208
15424
|
kind: SchedulableRunKinds.doctor,
|
|
15209
15425
|
cronExpr: DEFAULT_HEALTH_CRON,
|
|
@@ -15521,7 +15737,7 @@ var Scheduler = class {
|
|
|
15521
15737
|
log15.warn("gbp-sync.no-callback", { scheduleId, projectId, msg: "host did not register onGbpSyncRequested" });
|
|
15522
15738
|
return;
|
|
15523
15739
|
}
|
|
15524
|
-
const runId2 =
|
|
15740
|
+
const runId2 = crypto21.randomUUID();
|
|
15525
15741
|
this.db.insert(runs).values({
|
|
15526
15742
|
id: runId2,
|
|
15527
15743
|
projectId,
|
|
@@ -15553,7 +15769,7 @@ var Scheduler = class {
|
|
|
15553
15769
|
this.updateScheduleTiming(currentSchedule.id, { nextRunAt });
|
|
15554
15770
|
return;
|
|
15555
15771
|
}
|
|
15556
|
-
const runId2 =
|
|
15772
|
+
const runId2 = crypto21.randomUUID();
|
|
15557
15773
|
this.db.insert(runs).values({
|
|
15558
15774
|
id: runId2,
|
|
15559
15775
|
projectId,
|
|
@@ -15624,7 +15840,7 @@ var Scheduler = class {
|
|
|
15624
15840
|
this.updateScheduleTiming(currentSchedule.id, { nextRunAt });
|
|
15625
15841
|
return;
|
|
15626
15842
|
}
|
|
15627
|
-
const runId2 =
|
|
15843
|
+
const runId2 = crypto21.randomUUID();
|
|
15628
15844
|
const effectiveRequest = normalizeSiteAuditRunRequest({ maxPages: SITE_AUDIT_MAX_PAGE_LIMIT });
|
|
15629
15845
|
this.db.transaction((tx) => {
|
|
15630
15846
|
tx.insert(runs).values({
|
|
@@ -15966,8 +16182,8 @@ async function refreshAllIntegrations(client, projectName, now = /* @__PURE__ */
|
|
|
15966
16182
|
}
|
|
15967
16183
|
|
|
15968
16184
|
// src/notifier.ts
|
|
15969
|
-
import { eq as eq20, desc as
|
|
15970
|
-
import
|
|
16185
|
+
import { eq as eq20, desc as desc9, and as and18, inArray as inArray10, or } from "drizzle-orm";
|
|
16186
|
+
import crypto22 from "crypto";
|
|
15971
16187
|
var log19 = createLogger("Notifier");
|
|
15972
16188
|
var MATERIAL_MAGNITUDE_DELTA = 20;
|
|
15973
16189
|
var SEVERITY_RANK = { low: 0, medium: 1, high: 2, critical: 3 };
|
|
@@ -16507,7 +16723,7 @@ var Notifier = class {
|
|
|
16507
16723
|
eq20(runs.kind, thisRun.kind),
|
|
16508
16724
|
or(eq20(runs.status, "completed"), eq20(runs.status, "partial"))
|
|
16509
16725
|
)
|
|
16510
|
-
).orderBy(
|
|
16726
|
+
).orderBy(desc9(runs.createdAt), desc9(runs.id)).limit(RECENT_FETCH_LIMIT).all();
|
|
16511
16727
|
const groups = groupRunsByCreatedAt(recentRuns);
|
|
16512
16728
|
const currentGroupIdx = groups.findIndex((g) => g[0]?.createdAt === thisRun.createdAt);
|
|
16513
16729
|
if (currentGroupIdx < 0) return [];
|
|
@@ -16594,7 +16810,7 @@ var Notifier = class {
|
|
|
16594
16810
|
}
|
|
16595
16811
|
logDelivery(projectId, notificationId, event, status, error) {
|
|
16596
16812
|
this.db.insert(auditLog).values({
|
|
16597
|
-
id:
|
|
16813
|
+
id: crypto22.randomUUID(),
|
|
16598
16814
|
projectId,
|
|
16599
16815
|
actor: "scheduler",
|
|
16600
16816
|
action: `notification.${status}`,
|
|
@@ -16770,7 +16986,7 @@ function analysisResultFromInsights(insights2) {
|
|
|
16770
16986
|
}
|
|
16771
16987
|
|
|
16772
16988
|
// src/agent/session-registry.ts
|
|
16773
|
-
import
|
|
16989
|
+
import crypto26 from "crypto";
|
|
16774
16990
|
import { eq as eq24 } from "drizzle-orm";
|
|
16775
16991
|
|
|
16776
16992
|
// src/agent/session.ts
|
|
@@ -16828,12 +17044,17 @@ var PROVIDER_MODELS = {
|
|
|
16828
17044
|
[LlmCapabilities.classify]: "gpt-5-nano"
|
|
16829
17045
|
},
|
|
16830
17046
|
[AgentProviderIds.gemini]: {
|
|
16831
|
-
// Gemini's
|
|
17047
|
+
// Gemini's Flash is already cheap + capable; Flash-Lite is the
|
|
16832
17048
|
// dedicated micro tier for classify. Analyze stays on flash because
|
|
16833
17049
|
// flash-lite drops too much quality for structured synthesis.
|
|
16834
|
-
|
|
16835
|
-
|
|
16836
|
-
|
|
17050
|
+
// Exact IDs, never Google's `-latest` aliases: pi-ai derives request
|
|
17051
|
+
// options (thinking level) from the catalog entry for the ID it is given,
|
|
17052
|
+
// and an alias entry can lag the model it serves. `gemini-flash-latest`
|
|
17053
|
+
// allows MINIMAL thinking while the 3.8 Flash it resolves to rejects it,
|
|
17054
|
+
// so every Aero turn failed. Sweeps can use the alias; Aero cannot.
|
|
17055
|
+
[LlmCapabilities.agent]: "gemini-3.8-flash",
|
|
17056
|
+
[LlmCapabilities.analyze]: "gemini-3.8-flash",
|
|
17057
|
+
[LlmCapabilities.classify]: "gemini-3.5-flash-lite"
|
|
16837
17058
|
},
|
|
16838
17059
|
[AgentProviderIds.zai]: {
|
|
16839
17060
|
// GLM lineage: 5.2 replaced 5.1, which pi-ai 0.87 dropped from its
|
|
@@ -18018,7 +18239,7 @@ function buildAeroStateTools(ctx, opts = {}) {
|
|
|
18018
18239
|
}
|
|
18019
18240
|
|
|
18020
18241
|
// src/agent/llm-usage.ts
|
|
18021
|
-
import
|
|
18242
|
+
import crypto23 from "crypto";
|
|
18022
18243
|
var AeroLlmUsageFeatures = {
|
|
18023
18244
|
turn: "aero.turn"
|
|
18024
18245
|
};
|
|
@@ -18037,7 +18258,7 @@ function recordLlmUsageEvent(args) {
|
|
|
18037
18258
|
const usage = args.message.usage;
|
|
18038
18259
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18039
18260
|
args.db.insert(llmUsageEvents).values({
|
|
18040
|
-
id:
|
|
18261
|
+
id: crypto23.randomUUID(),
|
|
18041
18262
|
projectId: args.projectId,
|
|
18042
18263
|
runId: args.runId,
|
|
18043
18264
|
agentSessionId: args.agentSessionId,
|
|
@@ -18407,7 +18628,7 @@ This context applies to this turn. Explicit user scope takes precedence; do not
|
|
|
18407
18628
|
}
|
|
18408
18629
|
|
|
18409
18630
|
// src/agent/tool-usage.ts
|
|
18410
|
-
import
|
|
18631
|
+
import crypto24 from "crypto";
|
|
18411
18632
|
var AeroToolEventStatuses = {
|
|
18412
18633
|
success: "success",
|
|
18413
18634
|
error: "error"
|
|
@@ -18470,7 +18691,7 @@ function createAeroToolUsageHooks(args) {
|
|
|
18470
18691
|
function recordAgentToolEvent(args) {
|
|
18471
18692
|
try {
|
|
18472
18693
|
args.db.insert(agentToolEvents).values({
|
|
18473
|
-
id:
|
|
18694
|
+
id: crypto24.randomUUID(),
|
|
18474
18695
|
projectId: args.projectId,
|
|
18475
18696
|
agentSessionId: args.agentSessionId,
|
|
18476
18697
|
toolCallId: args.toolCallId,
|
|
@@ -18725,8 +18946,8 @@ async function loadExternalMcpTools(servers, opts = {}) {
|
|
|
18725
18946
|
}
|
|
18726
18947
|
|
|
18727
18948
|
// src/agent/memory-store.ts
|
|
18728
|
-
import
|
|
18729
|
-
import { and as and19, desc as
|
|
18949
|
+
import crypto25 from "crypto";
|
|
18950
|
+
import { and as and19, desc as desc10, eq as eq22, like, sql as sql10, or as or2 } from "drizzle-orm";
|
|
18730
18951
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
18731
18952
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
18732
18953
|
function rowToDto(row) {
|
|
@@ -18740,7 +18961,7 @@ function rowToDto(row) {
|
|
|
18740
18961
|
};
|
|
18741
18962
|
}
|
|
18742
18963
|
function listMemoryEntries(db, projectId, opts = {}) {
|
|
18743
|
-
const query = db.select().from(agentMemory).where(eq22(agentMemory.projectId, projectId)).orderBy(
|
|
18964
|
+
const query = db.select().from(agentMemory).where(eq22(agentMemory.projectId, projectId)).orderBy(desc10(agentMemory.updatedAt));
|
|
18744
18965
|
const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
|
|
18745
18966
|
return rows.map(rowToDto);
|
|
18746
18967
|
}
|
|
@@ -18754,7 +18975,7 @@ function upsertMemoryEntry(db, args) {
|
|
|
18754
18975
|
throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
|
|
18755
18976
|
}
|
|
18756
18977
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18757
|
-
const id =
|
|
18978
|
+
const id = crypto25.randomUUID();
|
|
18758
18979
|
db.insert(agentMemory).values({
|
|
18759
18980
|
id,
|
|
18760
18981
|
projectId: args.projectId,
|
|
@@ -18786,7 +19007,7 @@ function loadRecentForHydrate(db, projectId, limit, sessionId) {
|
|
|
18786
19007
|
return db.select().from(agentMemory).where(and19(
|
|
18787
19008
|
eq22(agentMemory.projectId, projectId),
|
|
18788
19009
|
or2(sql10`substr(${agentMemory.key}, 1, ${COMPACTION_KEY_PREFIX.length}) <> ${COMPACTION_KEY_PREFIX}`, sessionId ? sql10`substr(${agentMemory.key}, 1, ${sessionPrefix.length}) = ${sessionPrefix}` : void 0)
|
|
18789
|
-
)).orderBy(
|
|
19010
|
+
)).orderBy(desc10(agentMemory.updatedAt)).limit(limit).all().map(rowToDto);
|
|
18790
19011
|
}
|
|
18791
19012
|
function writeCompactionNote(db, args) {
|
|
18792
19013
|
if (Buffer.byteLength(args.summary, "utf8") > AGENT_MEMORY_VALUE_MAX_BYTES) {
|
|
@@ -18796,7 +19017,7 @@ function writeCompactionNote(db, args) {
|
|
|
18796
19017
|
}
|
|
18797
19018
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18798
19019
|
const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
|
|
18799
|
-
const id =
|
|
19020
|
+
const id = crypto25.randomUUID();
|
|
18800
19021
|
let inserted;
|
|
18801
19022
|
db.transaction((tx) => {
|
|
18802
19023
|
tx.insert(agentMemory).values({
|
|
@@ -18814,7 +19035,7 @@ function writeCompactionNote(db, args) {
|
|
|
18814
19035
|
eq22(agentMemory.projectId, args.projectId),
|
|
18815
19036
|
like(agentMemory.key, `${sessionPrefix}%`)
|
|
18816
19037
|
)
|
|
18817
|
-
).orderBy(
|
|
19038
|
+
).orderBy(desc10(agentMemory.updatedAt)).all();
|
|
18818
19039
|
const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
|
|
18819
19040
|
if (stale.length > 0) {
|
|
18820
19041
|
tx.delete(agentMemory).where(sql10`${agentMemory.id} IN (${sql10.join(stale.map((s) => sql10`${s}`), sql10`, `)})`).run();
|
|
@@ -19168,7 +19389,7 @@ var SessionRegistry = class {
|
|
|
19168
19389
|
return agent2;
|
|
19169
19390
|
}
|
|
19170
19391
|
const { provider, modelId } = resolveSessionProviderAndModel(this.opts.config, preferences);
|
|
19171
|
-
const sessionId =
|
|
19392
|
+
const sessionId = crypto26.randomUUID();
|
|
19172
19393
|
const agent = createAeroSession({
|
|
19173
19394
|
projectName,
|
|
19174
19395
|
client: this.opts.client,
|
|
@@ -19620,7 +19841,7 @@ ${lines.join("\n")}
|
|
|
19620
19841
|
insertRow(params) {
|
|
19621
19842
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
19622
19843
|
this.opts.db.insert(agentSessions).values({
|
|
19623
|
-
id: params.id ??
|
|
19844
|
+
id: params.id ?? crypto26.randomUUID(),
|
|
19624
19845
|
projectId: params.projectId,
|
|
19625
19846
|
systemPrompt: params.systemPrompt,
|
|
19626
19847
|
modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
|
|
@@ -19647,7 +19868,7 @@ ${lines.join("\n")}
|
|
|
19647
19868
|
};
|
|
19648
19869
|
|
|
19649
19870
|
// src/agent/viewer-sessions.ts
|
|
19650
|
-
import
|
|
19871
|
+
import crypto27 from "crypto";
|
|
19651
19872
|
import { and as and21, eq as eq25, isNotNull as isNotNull3, like as like2 } from "drizzle-orm";
|
|
19652
19873
|
var AERO_VIEWER_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([
|
|
19653
19874
|
CanonryMcpToolNames.canonry_ads_account,
|
|
@@ -19701,8 +19922,8 @@ function trimViewerTranscript(messages, max = MAX_VIEWER_MESSAGES) {
|
|
|
19701
19922
|
}
|
|
19702
19923
|
var VIEWER_IDLE_MS = 12 * 60 * 60 * 1e3;
|
|
19703
19924
|
function mintViewerAeroKey(db, userId, now = /* @__PURE__ */ new Date()) {
|
|
19704
|
-
const raw = `cnry_${
|
|
19705
|
-
const id =
|
|
19925
|
+
const raw = `cnry_${crypto27.randomBytes(24).toString("hex")}`;
|
|
19926
|
+
const id = crypto27.randomUUID();
|
|
19706
19927
|
db.insert(apiKeys).values({
|
|
19707
19928
|
id,
|
|
19708
19929
|
name: `${VIEWER_AERO_KEY_PREFIX}${userId}`,
|
|
@@ -19854,15 +20075,15 @@ var ViewerAeroSessions = class {
|
|
|
19854
20075
|
};
|
|
19855
20076
|
|
|
19856
20077
|
// src/mcp-http.ts
|
|
19857
|
-
import
|
|
20078
|
+
import crypto28 from "crypto";
|
|
19858
20079
|
import { eq as eq26 } from "drizzle-orm";
|
|
19859
20080
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
19860
20081
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
19861
20082
|
var SESSION_IDLE_MS = 10 * 60 * 1e3;
|
|
19862
20083
|
var SWEEP_INTERVAL_MS = 60 * 1e3;
|
|
19863
20084
|
function mintSessionKey(db, scopes, userId) {
|
|
19864
|
-
const raw = `cnry_${
|
|
19865
|
-
const id =
|
|
20085
|
+
const raw = `cnry_${crypto28.randomBytes(24).toString("hex")}`;
|
|
20086
|
+
const id = crypto28.randomUUID();
|
|
19866
20087
|
db.insert(apiKeys).values({
|
|
19867
20088
|
id,
|
|
19868
20089
|
name: `mcp-session:${userId}`,
|
|
@@ -19946,7 +20167,7 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
19946
20167
|
clientName: `canonry-mcp/${PACKAGE_VERSION}`,
|
|
19947
20168
|
surface: "mcp-http",
|
|
19948
20169
|
// Correlation only: this is never accepted as caller identity or authority.
|
|
19949
|
-
actorSession:
|
|
20170
|
+
actorSession: crypto28.randomUUID()
|
|
19950
20171
|
});
|
|
19951
20172
|
let server;
|
|
19952
20173
|
try {
|
|
@@ -19963,7 +20184,7 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
19963
20184
|
throw error;
|
|
19964
20185
|
}
|
|
19965
20186
|
const transport = new StreamableHTTPServerTransport({
|
|
19966
|
-
sessionIdGenerator: () =>
|
|
20187
|
+
sessionIdGenerator: () => crypto28.randomUUID(),
|
|
19967
20188
|
onsessioninitialized: (id) => {
|
|
19968
20189
|
sessions.set(id, { transport, close, authorizationId, segmentId: segment.id, lastSeenAt: now() });
|
|
19969
20190
|
}
|
|
@@ -21672,14 +21893,14 @@ function summarizeProviderConfig(config) {
|
|
|
21672
21893
|
};
|
|
21673
21894
|
}
|
|
21674
21895
|
function hashApiKey2(key) {
|
|
21675
|
-
return
|
|
21896
|
+
return crypto29.createHash("sha256").update(key).digest("hex");
|
|
21676
21897
|
}
|
|
21677
21898
|
var DASHBOARD_SCRYPT_KEYLEN = 64;
|
|
21678
21899
|
var DASHBOARD_SCRYPT_COST = 1 << 15;
|
|
21679
21900
|
var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
|
|
21680
21901
|
function hashDashboardPassword(password) {
|
|
21681
|
-
const salt =
|
|
21682
|
-
const derived =
|
|
21902
|
+
const salt = crypto29.randomBytes(16);
|
|
21903
|
+
const derived = crypto29.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
|
|
21683
21904
|
N: DASHBOARD_SCRYPT_COST,
|
|
21684
21905
|
maxmem: DASHBOARD_SCRYPT_MAXMEM
|
|
21685
21906
|
});
|
|
@@ -21700,14 +21921,14 @@ function verifyDashboardPassword(password, storedHash) {
|
|
|
21700
21921
|
} catch {
|
|
21701
21922
|
return { ok: false, needsRehash: false };
|
|
21702
21923
|
}
|
|
21703
|
-
const derived =
|
|
21924
|
+
const derived = crypto29.scryptSync(password, salt, expected.length, {
|
|
21704
21925
|
N: DASHBOARD_SCRYPT_COST,
|
|
21705
21926
|
maxmem: DASHBOARD_SCRYPT_MAXMEM
|
|
21706
21927
|
});
|
|
21707
21928
|
if (derived.length !== expected.length)
|
|
21708
21929
|
return { ok: false, needsRehash: false };
|
|
21709
21930
|
return {
|
|
21710
|
-
ok:
|
|
21931
|
+
ok: crypto29.timingSafeEqual(derived, expected),
|
|
21711
21932
|
needsRehash: false
|
|
21712
21933
|
};
|
|
21713
21934
|
}
|
|
@@ -21716,7 +21937,7 @@ function verifyDashboardPassword(password, storedHash) {
|
|
|
21716
21937
|
const expected = Buffer.from(storedHash, "hex");
|
|
21717
21938
|
if (candidate.length !== expected.length)
|
|
21718
21939
|
return { ok: false, needsRehash: false };
|
|
21719
|
-
const ok =
|
|
21940
|
+
const ok = crypto29.timingSafeEqual(candidate, expected);
|
|
21720
21941
|
return { ok, needsRehash: ok };
|
|
21721
21942
|
}
|
|
21722
21943
|
return { ok: false, needsRehash: false };
|
|
@@ -21852,7 +22073,7 @@ function cloneGtmConfig(config) {
|
|
|
21852
22073
|
function createGoogleMarketingConfigCredentialStore(input) {
|
|
21853
22074
|
const persistConfigPatch = input.saveConfigPatch ?? saveConfigPatch;
|
|
21854
22075
|
const env = input.env ?? process.env;
|
|
21855
|
-
const randomUUID3 = input.randomUUID ??
|
|
22076
|
+
const randomUUID3 = input.randomUUID ?? crypto29.randomUUID;
|
|
21856
22077
|
const snapshot = () => ({
|
|
21857
22078
|
googleAds: cloneGoogleAdsConfig(input.config.googleAds),
|
|
21858
22079
|
gtm: cloneGtmConfig(input.config.gtm)
|
|
@@ -21955,7 +22176,7 @@ async function createServer(opts) {
|
|
|
21955
22176
|
const trustProxy = resolveTrustProxy(process.env.CANONRY_TRUST_PROXY);
|
|
21956
22177
|
const app = Fastify({
|
|
21957
22178
|
loggerInstance: logger,
|
|
21958
|
-
genReqId: () =>
|
|
22179
|
+
genReqId: () => crypto29.randomUUID(),
|
|
21959
22180
|
trustProxy
|
|
21960
22181
|
});
|
|
21961
22182
|
const registry = new ProviderRegistry();
|
|
@@ -22722,7 +22943,7 @@ async function createServer(opts) {
|
|
|
22722
22943
|
return removed;
|
|
22723
22944
|
}
|
|
22724
22945
|
};
|
|
22725
|
-
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ??
|
|
22946
|
+
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto29.randomBytes(32).toString("hex");
|
|
22726
22947
|
const googleConnectionStore = {
|
|
22727
22948
|
listConnections: (domain) => listGoogleConnections(opts.config, domain),
|
|
22728
22949
|
getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
|
|
@@ -22816,7 +23037,7 @@ async function createServer(opts) {
|
|
|
22816
23037
|
if (!existing) {
|
|
22817
23038
|
const prefix = opts.config.apiKey.slice(0, 12);
|
|
22818
23039
|
opts.db.insert(apiKeys).values({
|
|
22819
|
-
id: `key_${
|
|
23040
|
+
id: `key_${crypto29.randomBytes(8).toString("hex")}`,
|
|
22820
23041
|
name: "default",
|
|
22821
23042
|
keyHash,
|
|
22822
23043
|
keyPrefix: prefix,
|
|
@@ -22840,7 +23061,7 @@ async function createServer(opts) {
|
|
|
22840
23061
|
};
|
|
22841
23062
|
const createSession = (apiKeyId) => {
|
|
22842
23063
|
pruneExpiredSessions();
|
|
22843
|
-
const sessionId =
|
|
23064
|
+
const sessionId = crypto29.randomBytes(32).toString("hex");
|
|
22844
23065
|
sessions.set(sessionId, {
|
|
22845
23066
|
apiKeyId,
|
|
22846
23067
|
expiresAt: Date.now() + SESSION_TTL_MS
|
|
@@ -23313,7 +23534,7 @@ async function createServer(opts) {
|
|
|
23313
23534
|
deps: {
|
|
23314
23535
|
enqueueAutoExtract: ({ projectId, release: r }) => {
|
|
23315
23536
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23316
|
-
const runId =
|
|
23537
|
+
const runId = crypto29.randomUUID();
|
|
23317
23538
|
opts.db.insert(runs).values({
|
|
23318
23539
|
id: runId,
|
|
23319
23540
|
projectId,
|
|
@@ -23557,7 +23778,7 @@ async function createServer(opts) {
|
|
|
23557
23778
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23558
23779
|
opts.db.insert(auditLog).values(
|
|
23559
23780
|
targetProjectIds.map((projectId) => ({
|
|
23560
|
-
id:
|
|
23781
|
+
id: crypto29.randomUUID(),
|
|
23561
23782
|
projectId,
|
|
23562
23783
|
actor: auditContext?.actor ?? "api",
|
|
23563
23784
|
userAgent: auditContext?.userAgent ?? null,
|
|
@@ -23654,6 +23875,7 @@ async function createServer(opts) {
|
|
|
23654
23875
|
const { event: eventName, eventId, ...properties } = event;
|
|
23655
23876
|
trackEvent(eventName, properties, { source: "dashboard", eventId });
|
|
23656
23877
|
},
|
|
23878
|
+
submitFeedback: (submission, context) => sendFeedback(submission, context),
|
|
23657
23879
|
onCdpConfigure: async (host, port2) => {
|
|
23658
23880
|
if (!opts.config.cdp) opts.config.cdp = {};
|
|
23659
23881
|
opts.config.cdp.host = host;
|