@canonry/canonry 7.44.0 → 7.45.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 +10 -0
- package/assets/assets/{AuditHistoryPanel-CvANw3ds.js → AuditHistoryPanel-CZOskgSK.js} +1 -1
- package/assets/assets/{BacklinksPage-C6x9ELjk.js → BacklinksPage-CgyR9Ydh.js} +1 -1
- package/assets/assets/{HistoryPage-r36HX1cC.js → HistoryPage-luN-L4CJ.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-ComoCzR4.js → MeasurementPropertyPage-B0vFWHFx.js} +1 -1
- package/assets/assets/{ProjectPage-CIeU-buk.js → ProjectPage-BkLOWkz9.js} +1 -1
- package/assets/assets/{RunsPage-DI6eJATZ.js → RunsPage-CeW09PIZ.js} +1 -1
- package/assets/assets/{SettingsPage-Bw7GdXxs.js → SettingsPage-C4jww6eS.js} +1 -1
- package/assets/assets/{SiteHealthSection-C-JaNKpS.js → SiteHealthSection-DXxY03vO.js} +3 -3
- package/assets/assets/{TrafficPage-Cnsp276v.js → TrafficPage-DvUYZXne.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CBIaJk7e.js → TrafficSourceDetailPage-DG-6Vn9Z.js} +1 -1
- package/assets/assets/{extract-error-message-C_Owa0n9.js → extract-error-message-1VWodBea.js} +1 -1
- package/assets/assets/{index-ZS9sSTMS.js → index-rBPSQ6B7.js} +3 -3
- package/assets/assets/{react-sigma_core.esm.min-ugyh-ooq.js → react-sigma_core.esm.min-Cm1_Mhdd.js} +1 -1
- package/assets/assets/{v2-overview-adapter-BLSrVQ7K.js → v2-overview-adapter-DSOkIdKV.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-GZ2KIONF.js → chunk-3MGCNSUL.js} +21 -0
- package/dist/{chunk-5LY7EG4U.js → chunk-3T3JQB2J.js} +2 -2
- package/dist/{chunk-G47XJVAS.js → chunk-6HUFI2G5.js} +80 -12
- package/dist/{chunk-MPWBFKGK.js → chunk-73YOKKHS.js} +1 -1
- package/dist/{chunk-QQYQDTFZ.js → chunk-A2RC2Y24.js} +246 -45
- package/dist/{chunk-JVBI6VBT.js → chunk-D42JVABX.js} +2 -2
- package/dist/{chunk-45Z5RJZW.js → chunk-H4YKQAY7.js} +3 -3
- package/dist/{chunk-3JVF3ULD.js → chunk-JAQNHQGR.js} +1 -1
- package/dist/{chunk-SW54HXSZ.js → chunk-K3AEOEB7.js} +3 -3
- package/dist/{chunk-EJRV7RBZ.js → chunk-TH7WZBUX.js} +11 -9
- package/dist/{cli-catalog-PNJEQV5H.js → cli-catalog-6ABJHAAK.js} +3 -3
- package/dist/cli-commands-OLFYNI6A.js +15 -0
- package/dist/cli.js +10 -10
- package/dist/{demo-server-WRHR25HF.js → demo-server-ZNK2UDRJ.js} +4 -4
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-QXXRUB2G.js → intelligence-service-MDXUBZPJ.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +8 -8
- package/dist/cli-commands-4UT2CRXX.js +0 -15
|
@@ -21,19 +21,19 @@ import {
|
|
|
21
21
|
saveConfig,
|
|
22
22
|
saveConfigPatch,
|
|
23
23
|
sentimentInstallReadiness
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-3T3JQB2J.js";
|
|
25
25
|
import {
|
|
26
26
|
computeFactorAverages,
|
|
27
27
|
deleteSiteCrawlGraphLayout,
|
|
28
28
|
persistSiteCrawlGraphLayout,
|
|
29
29
|
prepareSiteCrawlGraphLayout,
|
|
30
30
|
recordedSharePct
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-D42JVABX.js";
|
|
32
32
|
import {
|
|
33
33
|
CanonryMcpToolNames,
|
|
34
34
|
PACKAGE_VERSION,
|
|
35
35
|
canonryMcpTools
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-73YOKKHS.js";
|
|
37
37
|
import {
|
|
38
38
|
CliError
|
|
39
39
|
} from "./chunk-HGMYEDAQ.js";
|
|
@@ -240,7 +240,7 @@ import {
|
|
|
240
240
|
webhookResponseReason,
|
|
241
241
|
webhookTargetRefusalReason,
|
|
242
242
|
writeAuditLog
|
|
243
|
-
} from "./chunk-
|
|
243
|
+
} from "./chunk-6HUFI2G5.js";
|
|
244
244
|
import {
|
|
245
245
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
246
246
|
AGENT_NONE,
|
|
@@ -255,6 +255,7 @@ import {
|
|
|
255
255
|
AppError,
|
|
256
256
|
BacklinkSources,
|
|
257
257
|
CANONRY_NPM_PACKAGE_URL,
|
|
258
|
+
CHECK_RUNTIME_ERROR_SUFFIX,
|
|
258
259
|
CITED_URL_CAPTURE_VERSION,
|
|
259
260
|
CcReleaseSyncStatuses,
|
|
260
261
|
CheckNotificationPolicies,
|
|
@@ -487,7 +488,7 @@ import {
|
|
|
487
488
|
winnabilityClassLabel,
|
|
488
489
|
withRetry,
|
|
489
490
|
withSkippedProviders
|
|
490
|
-
} from "./chunk-
|
|
491
|
+
} from "./chunk-3MGCNSUL.js";
|
|
491
492
|
|
|
492
493
|
// src/runtime-context.ts
|
|
493
494
|
function cliRuntimeContext(env = process.env, stdio = process) {
|
|
@@ -4812,6 +4813,7 @@ function listModels(config, signal) {
|
|
|
4812
4813
|
}
|
|
4813
4814
|
|
|
4814
4815
|
// ../provider-gemini/src/adapter.ts
|
|
4816
|
+
import { FinishReason } from "@google/genai";
|
|
4815
4817
|
function toGeminiConfig(config) {
|
|
4816
4818
|
return {
|
|
4817
4819
|
apiKey: config.apiKey ?? "",
|
|
@@ -4840,17 +4842,27 @@ function toRawQueryResult(raw) {
|
|
|
4840
4842
|
servedModel: raw.servedModel,
|
|
4841
4843
|
groundingSources: raw.groundingSources,
|
|
4842
4844
|
searchQueries: raw.searchQueries,
|
|
4843
|
-
|
|
4844
|
-
// marker is present on 100% of stored rows, so it has never been shown to
|
|
4845
|
-
// discriminate a non-retrieving answer and wiring it up would hardcode
|
|
4846
|
-
// `used`. `unknown` states what we actually know. The contract is a
|
|
4847
|
-
// declaration about how we build the request, so it is always knowable.
|
|
4848
|
-
retrievalStatus: "unknown",
|
|
4845
|
+
retrievalStatus: readRetrievalStatus(raw.rawResponse),
|
|
4849
4846
|
retrievalContract: "native-auto-v1",
|
|
4850
4847
|
usage: raw.usage,
|
|
4851
4848
|
stopReason: raw.stopReason
|
|
4852
4849
|
};
|
|
4853
4850
|
}
|
|
4851
|
+
function readRetrievalStatus(rawResponse) {
|
|
4852
|
+
const response = rawResponse;
|
|
4853
|
+
if (usageCount(response.usageMetadata?.toolUsePromptTokenCount) > 0) return RetrievalStatuses.used;
|
|
4854
|
+
const candidate = Array.isArray(response.candidates) ? response.candidates[0] : void 0;
|
|
4855
|
+
if (!candidate) return RetrievalStatuses.unknown;
|
|
4856
|
+
const metadata2 = candidate.groundingMetadata;
|
|
4857
|
+
const queries2 = metadata2?.webSearchQueries;
|
|
4858
|
+
const chunks = metadata2?.groundingChunks;
|
|
4859
|
+
if (Array.isArray(queries2) && queries2.some((query) => typeof query === "string" && query.trim().length > 0) || Array.isArray(chunks) && chunks.some((chunk) => typeof chunk.web?.uri === "string" && chunk.web.uri.trim().length > 0)) {
|
|
4860
|
+
return RetrievalStatuses.used;
|
|
4861
|
+
}
|
|
4862
|
+
const parts = candidate.content?.parts;
|
|
4863
|
+
const hasAnswer = Array.isArray(parts) && parts.some((part) => !part.thought && typeof part.text === "string" && part.text.trim().length > 0);
|
|
4864
|
+
return candidate.finishReason === FinishReason.STOP && hasAnswer ? RetrievalStatuses["not-used"] : RetrievalStatuses.unknown;
|
|
4865
|
+
}
|
|
4854
4866
|
var geminiAdapter = {
|
|
4855
4867
|
name: "gemini",
|
|
4856
4868
|
displayName: "Gemini",
|
|
@@ -4923,7 +4935,7 @@ var geminiAdapter = {
|
|
|
4923
4935
|
citedDomains: normalized.citedDomains,
|
|
4924
4936
|
groundingSources: normalized.groundingSources,
|
|
4925
4937
|
searchQueries: normalized.searchQueries,
|
|
4926
|
-
retrievalStatus:
|
|
4938
|
+
retrievalStatus: readRetrievalStatus(raw.rawResponse)
|
|
4927
4939
|
};
|
|
4928
4940
|
},
|
|
4929
4941
|
async generateText(prompt, config) {
|
|
@@ -8357,7 +8369,7 @@ function nonBlank(value) {
|
|
|
8357
8369
|
return trimmed ? trimmed : void 0;
|
|
8358
8370
|
}
|
|
8359
8371
|
function resolveBuildCommit(env = process.env) {
|
|
8360
|
-
const embedded = true ? "
|
|
8372
|
+
const embedded = true ? "1c908111fca7364bee14ee96e5018bc3a5ef740b" : void 0;
|
|
8361
8373
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
8362
8374
|
}
|
|
8363
8375
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -15534,6 +15546,10 @@ function resolveModelForCapability(provider, capability, modelIdOverride) {
|
|
|
15534
15546
|
const baseUrl = base.endsWith("/v1beta") ? base : `${base}/v1beta`;
|
|
15535
15547
|
return { ...model, baseUrl };
|
|
15536
15548
|
}
|
|
15549
|
+
if (provider === AgentProviderIds.zai && process.env.ZAI_BASE_URL) {
|
|
15550
|
+
const baseUrl = process.env.ZAI_BASE_URL.replace(/\/+$/, "");
|
|
15551
|
+
return { ...model, baseUrl };
|
|
15552
|
+
}
|
|
15537
15553
|
return model;
|
|
15538
15554
|
}
|
|
15539
15555
|
function isAgentModelAvailable(provider, id) {
|
|
@@ -18930,6 +18946,39 @@ function isNews(now, was) {
|
|
|
18930
18946
|
return was === void 0 || now.rank > was.rank || now.rank === was.rank && now.code !== was.code;
|
|
18931
18947
|
}
|
|
18932
18948
|
var breachCodes = (breaches) => [...new Set([...breaches].map((b) => b.code))];
|
|
18949
|
+
var URGENT_HEALTH_EVENTS = /* @__PURE__ */ new Set(["health.urgent", "health.urgent-resolved"]);
|
|
18950
|
+
function urgentReceiptGroups(subscribers, receipts) {
|
|
18951
|
+
const groups = /* @__PURE__ */ new Map();
|
|
18952
|
+
for (const subscriber of subscribers) {
|
|
18953
|
+
const { url } = subscriber.config;
|
|
18954
|
+
const receipt = receipts?.[subscriber.id];
|
|
18955
|
+
const notified = receipt?.url === url ? receipt.notified : null;
|
|
18956
|
+
const group = groups.get(notified);
|
|
18957
|
+
if (group) group.subscribers.push(subscriber);
|
|
18958
|
+
else groups.set(notified, { notified, subscribers: [subscriber] });
|
|
18959
|
+
}
|
|
18960
|
+
return [...groups.values()];
|
|
18961
|
+
}
|
|
18962
|
+
function recordUrgentReceipts(receipts, subscribers, notified) {
|
|
18963
|
+
for (const subscriber of subscribers) receipts[subscriber.id] = { url: subscriber.config.url, notified };
|
|
18964
|
+
}
|
|
18965
|
+
function doctorUrgentSummary(receipts) {
|
|
18966
|
+
const breaches = Object.values(receipts).flatMap((receipt) => receipt.notified ? parseBreaches(receipt.notified) : []);
|
|
18967
|
+
const summary = formatUrgentBreaches(indexBreaches(breaches, "id").values());
|
|
18968
|
+
return summary || null;
|
|
18969
|
+
}
|
|
18970
|
+
function formatUrgentBreaches(breaches) {
|
|
18971
|
+
return formatBreaches([...breaches].sort((a, b) => b.rank - a.rank || compareText(a.id ?? a.code, b.id ?? b.code) || compareText(a.code, b.code)));
|
|
18972
|
+
}
|
|
18973
|
+
function siteUrgentSummary(receipts) {
|
|
18974
|
+
return Object.values(receipts).find((receipt) => receipt.notified !== null)?.notified ?? null;
|
|
18975
|
+
}
|
|
18976
|
+
var HEALTH_URGENT_RESOLVED_CODE = "health.urgent.resolved";
|
|
18977
|
+
function receivesHealthEvent(listed, event) {
|
|
18978
|
+
const events = listed ?? [];
|
|
18979
|
+
if (URGENT_HEALTH_EVENTS.has(event)) return events.includes(event);
|
|
18980
|
+
return events.includes(event) || !events.some((e) => URGENT_HEALTH_EVENTS.has(e));
|
|
18981
|
+
}
|
|
18933
18982
|
var SITE_LIVENESS_FAILURES_TO_PAGE = 2;
|
|
18934
18983
|
var SITE_LIVENESS_MIN_PASS_GAP_MS = 4 * 6e4;
|
|
18935
18984
|
var SITE_LIVENESS_MAX_PASS_GAP_MS = 45 * 6e4;
|
|
@@ -19026,9 +19075,19 @@ var Notifier = class {
|
|
|
19026
19075
|
* the next pass, because a quiet clearing pass would otherwise never
|
|
19027
19076
|
* surface it.
|
|
19028
19077
|
*
|
|
19029
|
-
*
|
|
19030
|
-
*
|
|
19031
|
-
*
|
|
19078
|
+
* Urgent problems are tracked beside all of this, not inside it. A check
|
|
19079
|
+
* marks its own output `urgent`; when one starts, `health.urgent` goes to
|
|
19080
|
+
* the webhooks that list it, and `health.urgent-resolved` when the ones it
|
|
19081
|
+
* reported have cleared and none is known to be open. They keep their own
|
|
19082
|
+
* record of what a subscriber was told (`urgent_notified`) because they go
|
|
19083
|
+
* to different webhooks than the alert above and are accepted
|
|
19084
|
+
* independently: a refused urgent alert is owed again whether or not the
|
|
19085
|
+
* general one was delivered. The record exists only while a webhook
|
|
19086
|
+
* subscribes to urgent events.
|
|
19087
|
+
*
|
|
19088
|
+
* Returns the general event emitted, or null when none was sent. The return
|
|
19089
|
+
* value is what tests assert against, so "stayed quiet" is observable rather
|
|
19090
|
+
* than inferred from the absence of a webhook.
|
|
19032
19091
|
*/
|
|
19033
19092
|
async onHealthChecked(projectId, report) {
|
|
19034
19093
|
const project = this.db.select().from(projects).where(eq21(projects.id, projectId)).get();
|
|
@@ -19117,7 +19176,46 @@ var Notifier = class {
|
|
|
19117
19176
|
event = "health.degraded";
|
|
19118
19177
|
}
|
|
19119
19178
|
const cleared = before === null || noSignal ? null : goneFrom(new Map([...before, ...notified ?? []]));
|
|
19120
|
-
const
|
|
19179
|
+
const urgentItems = [];
|
|
19180
|
+
for (const c of failing) {
|
|
19181
|
+
if (c.urgent !== true) continue;
|
|
19182
|
+
urgentItems.push({ id: c.id, code: c.code, rank: rank(c.status), status: c.status, summary: c.summary, remediation: c.remediation ?? null });
|
|
19183
|
+
}
|
|
19184
|
+
for (const c of healthChecks) {
|
|
19185
|
+
const supersededCode = c.details?.supersededCode;
|
|
19186
|
+
if (c.status !== CheckStatuses.skipped || c.urgent !== true || typeof supersededCode !== "string") continue;
|
|
19187
|
+
const causeIds = Array.isArray(c.details?.supersededBy) ? c.details.supersededBy : [];
|
|
19188
|
+
const cause = failing.find((f) => causeIds.includes(f.id));
|
|
19189
|
+
const status2 = c.details?.supersededStatus === "warn" ? "warn" : "fail";
|
|
19190
|
+
urgentItems.push({ id: c.id, code: supersededCode, rank: rank(status2), status: status2, summary: cause?.summary ?? c.summary, remediation: cause?.remediation ?? null });
|
|
19191
|
+
}
|
|
19192
|
+
urgentItems.sort((a, b) => b.rank - a.rank);
|
|
19193
|
+
const urgentById = new Map(urgentItems.map((item) => [item.id, item]));
|
|
19194
|
+
const generalEvent = event;
|
|
19195
|
+
const urgentRecord = previous?.urgentNotified ?? null;
|
|
19196
|
+
const enabled = generalEvent || urgentItems.length > 0 || urgentRecord ? this.db.select().from(notifications).where(eq21(notifications.projectId, projectId)).all().filter((n) => n.enabled) : [];
|
|
19197
|
+
const listedEvents = (notif) => notif.config.events;
|
|
19198
|
+
const hasUrl = (notif) => Boolean(notif.config.url);
|
|
19199
|
+
const urgentSubscribers = enabled.filter((n) => hasUrl(n) && (listedEvents(n) ?? []).some((e) => URGENT_HEALTH_EVENTS.has(e)));
|
|
19200
|
+
const conclusive = new Set(graded.filter((c) => !c.code.endsWith(CHECK_RUNTIME_ERROR_SUFFIX)).map((c) => c.id));
|
|
19201
|
+
const urgentReceipts = {};
|
|
19202
|
+
const recordedReceipts = previous?.urgentReceipts;
|
|
19203
|
+
const validReceipts = recordedReceipts && doctorUrgentSummary(recordedReceipts) === urgentRecord ? recordedReceipts : null;
|
|
19204
|
+
const urgentGroups = urgentReceiptGroups(urgentSubscribers, validReceipts).map((group) => {
|
|
19205
|
+
const urgentTold = group.notified ? parseBreaches(group.notified).filter((b) => b.id !== null) : [];
|
|
19206
|
+
const urgentCleared = urgentTold.filter((b) => conclusive.has(b.id) && !urgentById.has(b.id));
|
|
19207
|
+
const urgentUnknown = urgentTold.filter((b) => !conclusive.has(b.id) && !urgentById.has(b.id));
|
|
19208
|
+
const urgentOpen = urgentTold.filter((b) => urgentById.has(b.id) || hidden.has(b.id));
|
|
19209
|
+
const toldIds = new Set(urgentTold.map((b) => b.id));
|
|
19210
|
+
const urgentNew = urgentItems.filter((item) => !toldIds.has(item.id));
|
|
19211
|
+
const urgentEvent = urgentNew.length > 0 ? "health.urgent" : urgentCleared.length > 0 && urgentOpen.length === 0 ? "health.urgent-resolved" : null;
|
|
19212
|
+
const urgentNotifs = urgentEvent ? group.subscribers.filter((n) => receivesHealthEvent(listedEvents(n), urgentEvent)) : [];
|
|
19213
|
+
const urgentSettled = urgentEvent === "health.urgent-resolved" ? [] : [...urgentUnknown, ...urgentItems];
|
|
19214
|
+
const urgentBefore = urgentEvent === null ? [...urgentUnknown, ...urgentItems.filter((item) => toldIds.has(item.id))] : urgentNotifs.length > 0 ? urgentTold : urgentSettled;
|
|
19215
|
+
recordUrgentReceipts(urgentReceipts, group.subscribers, formatUrgentBreaches(urgentBefore) || null);
|
|
19216
|
+
return { ...group, urgentTold, urgentCleared, urgentNew, urgentEvent, urgentNotifs, urgentSettled };
|
|
19217
|
+
});
|
|
19218
|
+
const notifs = generalEvent ? enabled.filter((n) => receivesHealthEvent(listedEvents(n), generalEvent)) : [];
|
|
19121
19219
|
const willAttempt = notifs.some((n) => Boolean(n.config.url));
|
|
19122
19220
|
const triggerIds = new Set([
|
|
19123
19221
|
...newBreaches ?? failingNow.filter((b) => noSignal || b.id === worst?.id),
|
|
@@ -19134,6 +19232,8 @@ var Notifier = class {
|
|
|
19134
19232
|
failingSignature,
|
|
19135
19233
|
failingChecks: formatBreaches(failingNow),
|
|
19136
19234
|
pendingSignature: pendingNow === null ? null : formatBreaches(pendingNow),
|
|
19235
|
+
urgentNotified: doctorUrgentSummary(urgentReceipts),
|
|
19236
|
+
urgentReceipts,
|
|
19137
19237
|
// A delivered set an older release has written past is no longer what
|
|
19138
19238
|
// the operator was last told.
|
|
19139
19239
|
...idsKnown ? {} : { notifiedSignature: null }
|
|
@@ -19143,6 +19243,60 @@ var Notifier = class {
|
|
|
19143
19243
|
} else {
|
|
19144
19244
|
this.db.update(doctorHealthState).set(observation).where(eq21(doctorHealthState.projectId, projectId)).run();
|
|
19145
19245
|
}
|
|
19246
|
+
for (const group of urgentGroups) {
|
|
19247
|
+
const { urgentEvent, urgentTold, urgentNew, urgentCleared, urgentNotifs, urgentSettled } = group;
|
|
19248
|
+
if (!urgentEvent) continue;
|
|
19249
|
+
const toldRank = urgentTold.reduce((worstRank, b) => Math.max(worstRank, b.rank), 0);
|
|
19250
|
+
const previousUrgent = toldRank === 0 ? "ok" : toldRank === 2 ? "fail" : "warn";
|
|
19251
|
+
const lead = urgentNew[0];
|
|
19252
|
+
const onlyCleared = urgentCleared.length === 1 ? graded.find((c) => c.id === urgentCleared[0].id) : void 0;
|
|
19253
|
+
const resolvedSummary = onlyCleared ? onlyCleared.status === "ok" ? onlyCleared.summary : `No longer urgent. ${onlyCleared.summary}` : `${urgentCleared.length} urgent problems are resolved.`;
|
|
19254
|
+
const urgentHealth = urgentEvent === "health.urgent" && lead ? {
|
|
19255
|
+
status: lead.status,
|
|
19256
|
+
code: lead.code,
|
|
19257
|
+
summary: lead.summary,
|
|
19258
|
+
remediation: lead.remediation,
|
|
19259
|
+
checkedAt: now,
|
|
19260
|
+
previousStatus: previousUrgent,
|
|
19261
|
+
failing: urgentItems.map((item) => ({ id: item.id, status: item.status, code: item.code, summary: item.summary })),
|
|
19262
|
+
added: breachCodes(urgentNew),
|
|
19263
|
+
...urgentCleared.length > 0 ? { cleared: breachCodes(urgentCleared) } : {}
|
|
19264
|
+
} : {
|
|
19265
|
+
status: "ok",
|
|
19266
|
+
code: HEALTH_URGENT_RESOLVED_CODE,
|
|
19267
|
+
summary: resolvedSummary,
|
|
19268
|
+
remediation: null,
|
|
19269
|
+
checkedAt: now,
|
|
19270
|
+
previousStatus: previousUrgent,
|
|
19271
|
+
failing: [],
|
|
19272
|
+
cleared: breachCodes(urgentCleared)
|
|
19273
|
+
};
|
|
19274
|
+
const urgentPayload = {
|
|
19275
|
+
source: "canonry",
|
|
19276
|
+
event: urgentEvent,
|
|
19277
|
+
project: { name: project.name, canonicalDomain: project.canonicalDomain },
|
|
19278
|
+
health: urgentHealth,
|
|
19279
|
+
dashboardUrl: `${this.serverUrl}/projects/${project.name}`
|
|
19280
|
+
};
|
|
19281
|
+
let accepted = 0;
|
|
19282
|
+
for (const notif of urgentNotifs) {
|
|
19283
|
+
const config = notif.config;
|
|
19284
|
+
if (await this.sendWebhook(config.url, urgentPayload, notif.id, projectId, notif.webhookSecret ?? null)) accepted += 1;
|
|
19285
|
+
}
|
|
19286
|
+
if (accepted > 0) {
|
|
19287
|
+
recordUrgentReceipts(urgentReceipts, group.subscribers, formatUrgentBreaches(urgentSettled) || null);
|
|
19288
|
+
this.db.update(doctorHealthState).set({ urgentNotified: doctorUrgentSummary(urgentReceipts), urgentReceipts }).where(eq21(doctorHealthState.projectId, projectId)).run();
|
|
19289
|
+
}
|
|
19290
|
+
if (urgentNotifs.length > 0) {
|
|
19291
|
+
log21.info("health.urgent-notified", {
|
|
19292
|
+
projectId,
|
|
19293
|
+
event: urgentEvent,
|
|
19294
|
+
codes: urgentEvent === "health.urgent" ? breachCodes(urgentNew) : breachCodes(urgentCleared),
|
|
19295
|
+
subscribers: urgentNotifs.length,
|
|
19296
|
+
delivered: accepted
|
|
19297
|
+
});
|
|
19298
|
+
}
|
|
19299
|
+
}
|
|
19146
19300
|
if (!event) {
|
|
19147
19301
|
if (clearedThisPass.length > 0 || easedThisPass.length > 0) {
|
|
19148
19302
|
log21.info("health.improved", { projectId, status, code, cleared: clearedThisPass, eased: easedThisPass });
|
|
@@ -19218,6 +19372,27 @@ var Notifier = class {
|
|
|
19218
19372
|
let event = null;
|
|
19219
19373
|
if (check.status === "fail" && failures >= SITE_LIVENESS_FAILURES_TO_PAGE && !outagePaged) event = "health.degraded";
|
|
19220
19374
|
if (check.status === "ok" && outagePaged) event = "health.recovered";
|
|
19375
|
+
const confirmed = check.status === "fail" && failures >= SITE_LIVENESS_FAILURES_TO_PAGE;
|
|
19376
|
+
const urgentRecord = previous?.urgentNotifiedAt ?? null;
|
|
19377
|
+
const enabled = event !== null || confirmed || urgentRecord !== null ? this.db.select().from(notifications).where(eq21(notifications.projectId, projectId)).all().filter((n) => n.enabled) : [];
|
|
19378
|
+
const listedEvents = (notif) => notif.config.events;
|
|
19379
|
+
const hasUrl = (notif) => Boolean(notif.config.url);
|
|
19380
|
+
const scoped = enabled.filter((n) => hasUrl(n) && (listedEvents(n) ?? []).some((e) => URGENT_HEALTH_EVENTS.has(e)));
|
|
19381
|
+
const urgentReceipts = {};
|
|
19382
|
+
const recordedReceipts = previous?.urgentReceipts;
|
|
19383
|
+
const validReceipts = recordedReceipts && siteUrgentSummary(recordedReceipts) === urgentRecord ? recordedReceipts : null;
|
|
19384
|
+
const urgentGroups = urgentReceiptGroups(scoped, validReceipts).map((group) => {
|
|
19385
|
+
const urgentEvent = confirmed && group.notified === null ? "health.urgent" : check.status === "ok" && group.notified !== null ? "health.urgent-resolved" : null;
|
|
19386
|
+
const recipients = urgentEvent ? group.subscribers.filter((n) => receivesHealthEvent(listedEvents(n), urgentEvent)) : [];
|
|
19387
|
+
const generalName = urgentEvent === "health.urgent-resolved" ? "health.recovered" : "health.degraded";
|
|
19388
|
+
const generalRecipients = recipients.filter((n) => event === generalName && receivesHealthEvent(listedEvents(n), generalName));
|
|
19389
|
+
const generalIds = new Set(generalRecipients.map((n) => n.id));
|
|
19390
|
+
const urgentNotifs = recipients.filter((n) => !generalIds.has(n.id));
|
|
19391
|
+
const settled = urgentEvent === "health.urgent" ? checkedAt : null;
|
|
19392
|
+
const before = urgentEvent && recipients.length === 0 ? settled : group.notified;
|
|
19393
|
+
recordUrgentReceipts(urgentReceipts, group.subscribers, before);
|
|
19394
|
+
return { ...group, urgentEvent, urgentNotifs, generalIds, settled };
|
|
19395
|
+
});
|
|
19221
19396
|
const observation = {
|
|
19222
19397
|
projectId,
|
|
19223
19398
|
status: check.status,
|
|
@@ -19225,43 +19400,69 @@ var Notifier = class {
|
|
|
19225
19400
|
summary: check.summary,
|
|
19226
19401
|
consecutiveFailures: failures,
|
|
19227
19402
|
checkedAt,
|
|
19228
|
-
notifiedAt: check.status === "ok" ? null : previous?.notifiedAt ?? null
|
|
19403
|
+
notifiedAt: check.status === "ok" ? null : previous?.notifiedAt ?? null,
|
|
19404
|
+
urgentNotifiedAt: siteUrgentSummary(urgentReceipts),
|
|
19405
|
+
urgentReceipts
|
|
19229
19406
|
};
|
|
19230
19407
|
if (previous === void 0) {
|
|
19231
19408
|
this.db.insert(siteLivenessState).values(observation).run();
|
|
19232
19409
|
} else {
|
|
19233
19410
|
this.db.update(siteLivenessState).set(observation).where(eq21(siteLivenessState.projectId, projectId)).run();
|
|
19234
19411
|
}
|
|
19235
|
-
if (!event) return null;
|
|
19236
|
-
const
|
|
19237
|
-
|
|
19238
|
-
|
|
19239
|
-
|
|
19240
|
-
|
|
19241
|
-
|
|
19242
|
-
|
|
19243
|
-
|
|
19244
|
-
|
|
19245
|
-
|
|
19246
|
-
|
|
19247
|
-
|
|
19248
|
-
|
|
19249
|
-
|
|
19250
|
-
|
|
19251
|
-
|
|
19252
|
-
|
|
19253
|
-
|
|
19412
|
+
if (!event && urgentGroups.every((group) => group.urgentEvent === null)) return null;
|
|
19413
|
+
const payloadFor = (name) => {
|
|
19414
|
+
const outage = name === "health.degraded" || name === "health.urgent";
|
|
19415
|
+
return {
|
|
19416
|
+
source: "canonry",
|
|
19417
|
+
event: name,
|
|
19418
|
+
project: { name: project.name, canonicalDomain: project.canonicalDomain },
|
|
19419
|
+
health: {
|
|
19420
|
+
status: check.status,
|
|
19421
|
+
code: check.code,
|
|
19422
|
+
summary: check.summary,
|
|
19423
|
+
remediation: check.remediation ?? null,
|
|
19424
|
+
checkedAt,
|
|
19425
|
+
// The counter resets on every ok, so a page always follows a site that was
|
|
19426
|
+
// answering. Echoing the row the first failed pass wrote would report
|
|
19427
|
+
// "fail -> fail" and show the operator no transition at all.
|
|
19428
|
+
previousStatus: outage ? "ok" : "fail",
|
|
19429
|
+
failing: outage ? [{ id: check.id, status: check.status, code: check.code, summary: check.summary }] : []
|
|
19430
|
+
},
|
|
19431
|
+
dashboardUrl: `${this.serverUrl}/projects/${project.name}`
|
|
19432
|
+
};
|
|
19254
19433
|
};
|
|
19255
|
-
|
|
19256
|
-
|
|
19257
|
-
const
|
|
19258
|
-
|
|
19259
|
-
|
|
19434
|
+
const generalAccepted = /* @__PURE__ */ new Set();
|
|
19435
|
+
if (event) {
|
|
19436
|
+
const notifs = enabled.filter((n) => hasUrl(n) && receivesHealthEvent(listedEvents(n), event));
|
|
19437
|
+
let delivered = 0;
|
|
19438
|
+
for (const notif of notifs) {
|
|
19439
|
+
const config = notif.config;
|
|
19440
|
+
if (await this.sendWebhook(config.url, payloadFor(event), notif.id, projectId, notif.webhookSecret ?? null)) {
|
|
19441
|
+
delivered += 1;
|
|
19442
|
+
generalAccepted.add(notif.id);
|
|
19443
|
+
}
|
|
19444
|
+
}
|
|
19445
|
+
if (event === "health.degraded" && delivered > 0) {
|
|
19446
|
+
this.db.update(siteLivenessState).set({ notifiedAt: checkedAt }).where(eq21(siteLivenessState.projectId, projectId)).run();
|
|
19447
|
+
}
|
|
19448
|
+
log21.info("site-liveness.notified", { projectId, event, code: check.code, subscribers: notifs.length, delivered });
|
|
19260
19449
|
}
|
|
19261
|
-
|
|
19262
|
-
|
|
19450
|
+
for (const group of urgentGroups) {
|
|
19451
|
+
const { urgentEvent, urgentNotifs, generalIds } = group;
|
|
19452
|
+
if (!urgentEvent) continue;
|
|
19453
|
+
let accepted = [...generalIds].filter((id) => generalAccepted.has(id)).length;
|
|
19454
|
+
for (const notif of urgentNotifs) {
|
|
19455
|
+
const config = notif.config;
|
|
19456
|
+
if (await this.sendWebhook(config.url, payloadFor(urgentEvent), notif.id, projectId, notif.webhookSecret ?? null)) accepted += 1;
|
|
19457
|
+
}
|
|
19458
|
+
if (accepted > 0) {
|
|
19459
|
+
recordUrgentReceipts(urgentReceipts, group.subscribers, group.settled);
|
|
19460
|
+
this.db.update(siteLivenessState).set({ urgentNotifiedAt: siteUrgentSummary(urgentReceipts), urgentReceipts }).where(eq21(siteLivenessState.projectId, projectId)).run();
|
|
19461
|
+
}
|
|
19462
|
+
if (urgentNotifs.length > 0 || generalIds.size > 0) {
|
|
19463
|
+
log21.info("site-liveness.urgent-notified", { projectId, event: urgentEvent, code: check.code, subscribers: urgentNotifs.length + generalIds.size, delivered: accepted });
|
|
19464
|
+
}
|
|
19263
19465
|
}
|
|
19264
|
-
log21.info("site-liveness.notified", { projectId, event, code: check.code, subscribers: notifs.length, delivered });
|
|
19265
19466
|
return event;
|
|
19266
19467
|
}
|
|
19267
19468
|
/**
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
siteCrawlGraphNodes,
|
|
6
6
|
siteCrawlPages,
|
|
7
7
|
siteCrawlSnapshots
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6HUFI2G5.js";
|
|
9
9
|
import {
|
|
10
10
|
SITE_CRAWL_GRAPH_MAX_EDGES,
|
|
11
11
|
SITE_CRAWL_GRAPH_MAX_NODES,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
factorStatusFromDistribution,
|
|
16
16
|
factorStatusFromScore,
|
|
17
17
|
roundPreservingTotal
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-3MGCNSUL.js";
|
|
19
19
|
|
|
20
20
|
// src/site-audit-factors.ts
|
|
21
21
|
function recordedSharePct(factor) {
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
resolveCliAccess,
|
|
5
5
|
resolveCliAccessForInvocation,
|
|
6
6
|
windowOptions
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-JAQNHQGR.js";
|
|
8
8
|
import {
|
|
9
9
|
CliError,
|
|
10
10
|
EXIT_USER_ERROR,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "./chunk-HGMYEDAQ.js";
|
|
13
13
|
import {
|
|
14
14
|
OperationAccesses
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-3MGCNSUL.js";
|
|
16
16
|
|
|
17
17
|
// src/cli-catalog.ts
|
|
18
18
|
import { createRequire } from "module";
|
|
@@ -109,7 +109,7 @@ async function dispatchRegisteredCommand(args, fallbackFormat, specs) {
|
|
|
109
109
|
const accessValue = accessFilterFromArgs(argsWithoutHelp);
|
|
110
110
|
validateAccessFilter(accessValue);
|
|
111
111
|
if (fallbackFormat !== "text") {
|
|
112
|
-
const { printCliCatalog: printCliCatalog2 } = await import("./cli-catalog-
|
|
112
|
+
const { printCliCatalog: printCliCatalog2 } = await import("./cli-catalog-6ABJHAAK.js");
|
|
113
113
|
const group2 = argsWithoutHelp.find((arg) => !arg.startsWith("--"));
|
|
114
114
|
printCliCatalog2(specs, fallbackFormat, group2, accessValue);
|
|
115
115
|
return true;
|
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
installSkills,
|
|
5
5
|
loadConfigRaw,
|
|
6
6
|
saveConfigPatch
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3T3JQB2J.js";
|
|
8
8
|
import {
|
|
9
9
|
PACKAGE_VERSION
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-73YOKKHS.js";
|
|
11
11
|
import {
|
|
12
12
|
SKILL_MANIFEST_FILENAME,
|
|
13
13
|
SkillsClients
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-3MGCNSUL.js";
|
|
15
15
|
|
|
16
16
|
// src/skills-autosync.ts
|
|
17
17
|
import fs from "fs";
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
trackFeatureCompleted,
|
|
32
32
|
trackIntegrationConnection,
|
|
33
33
|
waitForServerRuntimeStartup
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-A2RC2Y24.js";
|
|
35
35
|
import {
|
|
36
36
|
clearDashboardPassword,
|
|
37
37
|
configExists,
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
saveConfigPatch,
|
|
51
51
|
shouldPrintServeSkillsNudge,
|
|
52
52
|
withBootstrapProviderDefaults
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-3T3JQB2J.js";
|
|
54
54
|
import {
|
|
55
55
|
emitJsonl,
|
|
56
56
|
getBoolean,
|
|
@@ -64,7 +64,7 @@ import {
|
|
|
64
64
|
resolveCliAccess,
|
|
65
65
|
stringOption,
|
|
66
66
|
unknownSubcommand
|
|
67
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-JAQNHQGR.js";
|
|
68
68
|
import {
|
|
69
69
|
DEFAULT_DEMO_TRUSTED_PROXIES,
|
|
70
70
|
trustedProxiesProblem,
|
|
@@ -73,7 +73,7 @@ import {
|
|
|
73
73
|
import {
|
|
74
74
|
measurementDraftOperationSchema,
|
|
75
75
|
runMeasurementDraftAction
|
|
76
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-73YOKKHS.js";
|
|
77
77
|
import {
|
|
78
78
|
CANONRY_DISCORD_URL,
|
|
79
79
|
CANONRY_ISSUES_URL,
|
|
@@ -118,7 +118,7 @@ import {
|
|
|
118
118
|
rawEventSamples,
|
|
119
119
|
readMarketCompetitorNames,
|
|
120
120
|
runs
|
|
121
|
-
} from "./chunk-
|
|
121
|
+
} from "./chunk-6HUFI2G5.js";
|
|
122
122
|
import {
|
|
123
123
|
AdsDeliverySnapshotStatuses,
|
|
124
124
|
AdsHistoricalCampaignRollupStatuses,
|
|
@@ -274,7 +274,7 @@ import {
|
|
|
274
274
|
visibilityCompareSelectionSchema,
|
|
275
275
|
visibilityReportRequestSchema,
|
|
276
276
|
winnabilityClassSchema
|
|
277
|
-
} from "./chunk-
|
|
277
|
+
} from "./chunk-3MGCNSUL.js";
|
|
278
278
|
|
|
279
279
|
// src/cli-commands/sentiment.ts
|
|
280
280
|
import "zod";
|
|
@@ -582,7 +582,7 @@ function parseDemoTrustedProxies(values) {
|
|
|
582
582
|
async function demoCommand(options) {
|
|
583
583
|
const { host, port } = parseDemoListenOptions(options);
|
|
584
584
|
const trustProxy = parseDemoTrustedProxies(options.trustProxy);
|
|
585
|
-
const { createDemoServer } = await import("./demo-server-
|
|
585
|
+
const { createDemoServer } = await import("./demo-server-ZNK2UDRJ.js");
|
|
586
586
|
const app = await createDemoServer({ trustProxy });
|
|
587
587
|
let stopping = false;
|
|
588
588
|
const stop = () => {
|
|
@@ -996,7 +996,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
|
|
|
996
996
|
}
|
|
997
997
|
}
|
|
998
998
|
async function backfillInsightsCommand(project2, opts) {
|
|
999
|
-
const { IntelligenceService } = await import("./intelligence-service-
|
|
999
|
+
const { IntelligenceService } = await import("./intelligence-service-MDXUBZPJ.js");
|
|
1000
1000
|
const config = loadConfig();
|
|
1001
1001
|
const db = createClient(config.database);
|
|
1002
1002
|
migrate(db);
|
|
@@ -13215,6 +13215,8 @@ var EVENT_DESCRIPTIONS = {
|
|
|
13215
13215
|
"insight.high": "A high-severity insight was generated",
|
|
13216
13216
|
"health.degraded": "A scheduled health check found the measurement degraded, daily data stopped arriving, or the website stopped answering (sent when a check starts failing or gets worse, not every pass)",
|
|
13217
13217
|
"health.recovered": "A degraded health check is passing again, or a website that was paged as down answers again",
|
|
13218
|
+
"health.urgent": "An urgent problem started: the website is down, GA4 or Search Console data is stale, or a sync keeps failing. A webhook that lists this receives only the health events it lists, so it is not sent routine warnings",
|
|
13219
|
+
"health.urgent-resolved": "The urgent problems that were reported have cleared (the website check and the scheduled health pass each send their own)",
|
|
13218
13220
|
"review.negative": "A new or edited Google review at or below the project's negative-review threshold, 3 stars by default (Business Profile reviews, or the public listing when Google has not enabled review access)",
|
|
13219
13221
|
"review.rating-dropped": "A location's public Google rating fell (sent for locations without Business Profile review access)"
|
|
13220
13222
|
};
|
|
@@ -19436,7 +19438,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
19436
19438
|
options: { access: stringOption() },
|
|
19437
19439
|
run: async (input) => {
|
|
19438
19440
|
if (input.positionals.length > 1) throw usageError("Expected at most one command group");
|
|
19439
|
-
const [{ printCliCatalog }, { REGISTERED_CLI_COMMANDS: REGISTERED_CLI_COMMANDS2 }] = await Promise.all([import("./cli-catalog-
|
|
19441
|
+
const [{ printCliCatalog }, { REGISTERED_CLI_COMMANDS: REGISTERED_CLI_COMMANDS2 }] = await Promise.all([import("./cli-catalog-6ABJHAAK.js"), import("./cli-commands-OLFYNI6A.js")]);
|
|
19440
19442
|
printCliCatalog(REGISTERED_CLI_COMMANDS2, input.format, input.positionals[0], getString(input.values, "access"));
|
|
19441
19443
|
}
|
|
19442
19444
|
},
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
buildCliCatalog,
|
|
4
4
|
printCliCatalog,
|
|
5
5
|
renderTopLevelHelp
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-H4YKQAY7.js";
|
|
7
|
+
import "./chunk-JAQNHQGR.js";
|
|
8
8
|
import "./chunk-HGMYEDAQ.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-3MGCNSUL.js";
|
|
10
10
|
export {
|
|
11
11
|
CLI_ROOT_SUMMARIES,
|
|
12
12
|
buildCliCatalog,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
REGISTERED_CLI_COMMANDS
|
|
3
|
+
} from "./chunk-TH7WZBUX.js";
|
|
4
|
+
import "./chunk-A2RC2Y24.js";
|
|
5
|
+
import "./chunk-3T3JQB2J.js";
|
|
6
|
+
import "./chunk-JAQNHQGR.js";
|
|
7
|
+
import "./chunk-RB2F4D3Z.js";
|
|
8
|
+
import "./chunk-D42JVABX.js";
|
|
9
|
+
import "./chunk-73YOKKHS.js";
|
|
10
|
+
import "./chunk-HGMYEDAQ.js";
|
|
11
|
+
import "./chunk-6HUFI2G5.js";
|
|
12
|
+
import "./chunk-3MGCNSUL.js";
|
|
13
|
+
export {
|
|
14
|
+
REGISTERED_CLI_COMMANDS
|
|
15
|
+
};
|