@canonry/canonry 4.178.0 → 4.178.2
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 +1 -1
- package/assets/assets/{AuditHistoryPanel-BmKdyRrR.js → AuditHistoryPanel-BCtMrEMF.js} +1 -1
- package/assets/assets/{BacklinksPage-DtqKPVLV.js → BacklinksPage-CIo2hA0i.js} +1 -1
- package/assets/assets/{HistoryPage-yyJsQiJQ.js → HistoryPage-D24U8cUG.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-hjW4iYtr.js → MeasurementPropertyPage-BKDdv-jJ.js} +1 -1
- package/assets/assets/{ProjectPage-DZKpnPvq.js → ProjectPage-DrIs_ZuR.js} +1 -1
- package/assets/assets/{RunRow-BZeBFb22.js → RunRow-B39IK7hK.js} +1 -1
- package/assets/assets/{RunsPage-tnXHsK_R.js → RunsPage-R4W1kD0B.js} +1 -1
- package/assets/assets/{SettingsPage-Djj5k8vv.js → SettingsPage-BAOGyt1F.js} +1 -1
- package/assets/assets/{SiteHealthSection-BkDncS11.js → SiteHealthSection-Bx7yVNMQ.js} +3 -3
- package/assets/assets/{TrafficPage-nJggXVh-.js → TrafficPage-DL8RvM2R.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-zxONbSn-.js → TrafficSourceDetailPage-COzjakZa.js} +1 -1
- package/assets/assets/{extract-error-message-NpQu2vBm.js → extract-error-message-B3TERCQ-.js} +1 -1
- package/assets/assets/index-xtxQtZRo.js +83 -0
- package/assets/assets/{react-sigma_core.esm.min-CxMdd8U8.js → react-sigma_core.esm.min-CbXAujub.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-YIQMTOEZ.js → chunk-5KI5WVWS.js} +86 -7
- package/dist/{chunk-A7FB2BVW.js → chunk-EAUE2RRY.js} +2 -2
- package/dist/{chunk-O7J5WFVZ.js → chunk-LMI72WZP.js} +101 -41
- package/dist/{chunk-T4JH77O6.js → chunk-OYDIND7Q.js} +71 -1
- package/dist/{chunk-SXKA4OXZ.js → chunk-P6JVZJO5.js} +3 -3
- package/dist/cli.js +6 -6
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-YISV327N.js → intelligence-service-KYIQZBEO.js} +2 -2
- package/dist/mcp.js +3 -3
- package/package.json +11 -11
- package/assets/assets/index-Dwwnkd_V.js +0 -83
package/assets/assets/{react-sigma_core.esm.min-CxMdd8U8.js → react-sigma_core.esm.min-CbXAujub.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{g as Bt,t as S,r as C}from"./vendor-tanstack-Dock0eH1.js";import{e as Ht,G as Wt}from"./SiteHealthSection-
|
|
1
|
+
import{g as Bt,t as S,r as C}from"./vendor-tanstack-Dock0eH1.js";import{e as Ht,G as Wt}from"./SiteHealthSection-Bx7yVNMQ.js";import"./index-xtxQtZRo.js";import"./vendor-radix-4UOaVWza.js";import"./vendor-recharts-D8rt5lKn.js";import"./vendor-lucide-nYKv-BB7.js";import"./vendor-markdown-Bn7fRwrx.js";import"./vendor-yaml-GWJz5H2G.js";import"./ChartPrimitives-B_LDVvTy.js";function Vt(r,n){if(typeof r!="object"||!r)return r;var i=r[Symbol.toPrimitive];if(i!==void 0){var e=i.call(r,n);if(typeof e!="object")return e;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(r)}function ae(r){var n=Vt(r,"string");return typeof n=="symbol"?n:n+""}function A(r,n){if(!(r instanceof n))throw new TypeError("Cannot call a class as a function")}function He(r,n){for(var i=0;i<n.length;i++){var e=n[i];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(r,ae(e.key),e)}}function L(r,n,i){return n&&He(r.prototype,n),i&&He(r,i),Object.defineProperty(r,"prototype",{writable:!1}),r}function Q(r){return Q=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(n){return n.__proto__||Object.getPrototypeOf(n)},Q(r)}function yt(){try{var r=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(yt=function(){return!!r})()}function jt(r){if(r===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return r}function Zt(r,n){if(n&&(typeof n=="object"||typeof n=="function"))return n;if(n!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return jt(r)}function I(r,n,i){return n=Q(n),Zt(r,yt()?Reflect.construct(n,i||[],Q(r).constructor):n.apply(r,i))}function xe(r,n){return xe=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(i,e){return i.__proto__=e,i},xe(r,n)}function G(r,n){if(typeof n!="function"&&n!==null)throw new TypeError("Super expression must either be null or a function");r.prototype=Object.create(n&&n.prototype,{constructor:{value:r,writable:!0,configurable:!0}}),Object.defineProperty(r,"prototype",{writable:!1}),n&&xe(r,n)}function qt(r){if(Array.isArray(r))return r}function Yt(r,n){var i=r==null?null:typeof Symbol<"u"&&r[Symbol.iterator]||r["@@iterator"];if(i!=null){var e,t,a,o,s=[],l=!0,h=!1;try{if(a=(i=i.call(r)).next,n===0){if(Object(i)!==i)return;l=!1}else for(;!(l=(e=a.call(i)).done)&&(s.push(e.value),s.length!==n);l=!0);}catch(u){h=!0,t=u}finally{try{if(!l&&i.return!=null&&(o=i.return(),Object(o)!==o))return}finally{if(h)throw t}}return s}}function Se(r,n){(n==null||n>r.length)&&(n=r.length);for(var i=0,e=Array(n);i<n;i++)e[i]=r[i];return e}function Tt(r,n){if(r){if(typeof r=="string")return Se(r,n);var i={}.toString.call(r).slice(8,-1);return i==="Object"&&r.constructor&&(i=r.constructor.name),i==="Map"||i==="Set"?Array.from(r):i==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?Se(r,n):void 0}}function Xt(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
|
|
2
2
|
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function ee(r,n){return qt(r)||Yt(r,n)||Tt(r,n)||Xt()}var pe={black:"#000000",silver:"#C0C0C0",gray:"#808080",grey:"#808080",white:"#FFFFFF",maroon:"#800000",red:"#FF0000",purple:"#800080",fuchsia:"#FF00FF",green:"#008000",lime:"#00FF00",olive:"#808000",yellow:"#FFFF00",navy:"#000080",blue:"#0000FF",teal:"#008080",aqua:"#00FFFF",darkblue:"#00008B",mediumblue:"#0000CD",darkgreen:"#006400",darkcyan:"#008B8B",deepskyblue:"#00BFFF",darkturquoise:"#00CED1",mediumspringgreen:"#00FA9A",springgreen:"#00FF7F",cyan:"#00FFFF",midnightblue:"#191970",dodgerblue:"#1E90FF",lightseagreen:"#20B2AA",forestgreen:"#228B22",seagreen:"#2E8B57",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",limegreen:"#32CD32",mediumseagreen:"#3CB371",turquoise:"#40E0D0",royalblue:"#4169E1",steelblue:"#4682B4",darkslateblue:"#483D8B",mediumturquoise:"#48D1CC",indigo:"#4B0082",darkolivegreen:"#556B2F",cadetblue:"#5F9EA0",cornflowerblue:"#6495ED",rebeccapurple:"#663399",mediumaquamarine:"#66CDAA",dimgray:"#696969",dimgrey:"#696969",slateblue:"#6A5ACD",olivedrab:"#6B8E23",slategray:"#708090",slategrey:"#708090",lightslategray:"#778899",lightslategrey:"#778899",mediumslateblue:"#7B68EE",lawngreen:"#7CFC00",chartreuse:"#7FFF00",aquamarine:"#7FFFD4",skyblue:"#87CEEB",lightskyblue:"#87CEFA",blueviolet:"#8A2BE2",darkred:"#8B0000",darkmagenta:"#8B008B",saddlebrown:"#8B4513",darkseagreen:"#8FBC8F",lightgreen:"#90EE90",mediumpurple:"#9370DB",darkviolet:"#9400D3",palegreen:"#98FB98",darkorchid:"#9932CC",yellowgreen:"#9ACD32",sienna:"#A0522D",brown:"#A52A2A",darkgray:"#A9A9A9",darkgrey:"#A9A9A9",lightblue:"#ADD8E6",greenyellow:"#ADFF2F",paleturquoise:"#AFEEEE",lightsteelblue:"#B0C4DE",powderblue:"#B0E0E6",firebrick:"#B22222",darkgoldenrod:"#B8860B",mediumorchid:"#BA55D3",rosybrown:"#BC8F8F",darkkhaki:"#BDB76B",mediumvioletred:"#C71585",indianred:"#CD5C5C",peru:"#CD853F",chocolate:"#D2691E",tan:"#D2B48C",lightgray:"#D3D3D3",lightgrey:"#D3D3D3",thistle:"#D8BFD8",orchid:"#DA70D6",goldenrod:"#DAA520",palevioletred:"#DB7093",crimson:"#DC143C",gainsboro:"#DCDCDC",plum:"#DDA0DD",burlywood:"#DEB887",lightcyan:"#E0FFFF",lavender:"#E6E6FA",darksalmon:"#E9967A",violet:"#EE82EE",palegoldenrod:"#EEE8AA",lightcoral:"#F08080",khaki:"#F0E68C",aliceblue:"#F0F8FF",honeydew:"#F0FFF0",azure:"#F0FFFF",sandybrown:"#F4A460",wheat:"#F5DEB3",beige:"#F5F5DC",whitesmoke:"#F5F5F5",mintcream:"#F5FFFA",ghostwhite:"#F8F8FF",salmon:"#FA8072",antiquewhite:"#FAEBD7",linen:"#FAF0E6",lightgoldenrodyellow:"#FAFAD2",oldlace:"#FDF5E6",magenta:"#FF00FF",deeppink:"#FF1493",orangered:"#FF4500",tomato:"#FF6347",hotpink:"#FF69B4",coral:"#FF7F50",darkorange:"#FF8C00",lightsalmon:"#FFA07A",orange:"#FFA500",lightpink:"#FFB6C1",pink:"#FFC0CB",gold:"#FFD700",peachpuff:"#FFDAB9",navajowhite:"#FFDEAD",moccasin:"#FFE4B5",bisque:"#FFE4C4",mistyrose:"#FFE4E1",blanchedalmond:"#FFEBCD",papayawhip:"#FFEFD5",lavenderblush:"#FFF0F5",seashell:"#FFF5EE",cornsilk:"#FFF8DC",lemonchiffon:"#FFFACD",floralwhite:"#FFFAF0",snow:"#FFFAFA",lightyellow:"#FFFFE0",ivory:"#FFFFF0"},Ct=new Int8Array(4),be=new Int32Array(Ct.buffer,0,1),$t=new Float32Array(Ct.buffer,0,1),Kt=/^\s*rgba?\s*\(/,Jt=/^\s*rgba?\s*\(\s*([0-9]*)\s*,\s*([0-9]*)\s*,\s*([0-9]*)(?:\s*,\s*(.*)?)?\)\s*$/;function Qt(r){var n=0,i=0,e=0,t=1;if(r[0]==="#")r.length===4?(n=parseInt(r.charAt(1)+r.charAt(1),16),i=parseInt(r.charAt(2)+r.charAt(2),16),e=parseInt(r.charAt(3)+r.charAt(3),16)):(n=parseInt(r.charAt(1)+r.charAt(2),16),i=parseInt(r.charAt(3)+r.charAt(4),16),e=parseInt(r.charAt(5)+r.charAt(6),16)),r.length===9&&(t=parseInt(r.charAt(7)+r.charAt(8),16)/255);else if(Kt.test(r)){var a=r.match(Jt);a&&(n=+a[1],i=+a[2],e=+a[3],a[4]&&(t=+a[4]))}return{r:n,g:i,b:e,a:t}}var J={};for(var le in pe)J[le]=oe(pe[le]),J[pe[le]]=J[le];function Rt(r,n,i,e,t){return be[0]=e<<24|i<<16|n<<8|r,be[0]=be[0]&4278190079,$t[0]}function oe(r){if(r=r.toLowerCase(),typeof J[r]<"u")return J[r];var n=Qt(r),i=n.r,e=n.g,t=n.b,a=n.a;a=a*255|0;var o=Rt(i,e,t,a);return J[r]=o,o}var Ee={};function _t(r){if(typeof Ee[r]<"u")return Ee[r];var n=(r&16711680)>>>16,i=(r&65280)>>>8,e=r&255,t=255,a=Rt(n,i,e,t);return Ee[r]=a,a}function We(r,n,i,e){return i+(n<<8)+(r<<16)}function Ve(r,n,i,e,t,a){var o=Math.floor(i/a*t),s=Math.floor(r.drawingBufferHeight/a-e/a*t),l=new Uint8Array(4);r.bindFramebuffer(r.FRAMEBUFFER,n),r.readPixels(o,s,1,1,r.RGBA,r.UNSIGNED_BYTE,l);var h=ee(l,4),u=h[0],d=h[1],f=h[2],g=h[3];return[u,d,f,g]}function c(r,n,i){return(n=ae(n))in r?Object.defineProperty(r,n,{value:i,enumerable:!0,configurable:!0,writable:!0}):r[n]=i,r}function je(r,n){var i=Object.keys(r);if(Object.getOwnPropertySymbols){var e=Object.getOwnPropertySymbols(r);n&&(e=e.filter(function(t){return Object.getOwnPropertyDescriptor(r,t).enumerable})),i.push.apply(i,e)}return i}function b(r){for(var n=1;n<arguments.length;n++){var i=arguments[n]!=null?arguments[n]:{};n%2?je(Object(i),!0).forEach(function(e){c(r,e,i[e])}):Object.getOwnPropertyDescriptors?Object.defineProperties(r,Object.getOwnPropertyDescriptors(i)):je(Object(i)).forEach(function(e){Object.defineProperty(r,e,Object.getOwnPropertyDescriptor(i,e))})}return r}function ei(r,n){for(;!{}.hasOwnProperty.call(r,n)&&(r=Q(r))!==null;);return r}function Ae(){return Ae=typeof Reflect<"u"&&Reflect.get?Reflect.get.bind():function(r,n,i){var e=ei(r,n);if(e){var t=Object.getOwnPropertyDescriptor(e,n);return t.get?t.get.call(arguments.length<3?r:i):t.value}},Ae.apply(null,arguments)}function wt(r,n,i,e){var t=Ae(Q(r.prototype),n,i);return typeof t=="function"?function(a){return t.apply(i,a)}:t}function ti(r){return r.normalized?1:r.size}function ye(r){var n=0;return r.forEach(function(i){return n+=ti(i)}),n}function xt(r,n,i){var e=r==="VERTEX"?n.VERTEX_SHADER:n.FRAGMENT_SHADER,t=n.createShader(e);if(t===null)throw new Error("loadShader: error while creating the shader");n.shaderSource(t,i),n.compileShader(t);var a=n.getShaderParameter(t,n.COMPILE_STATUS);if(!a){var o=n.getShaderInfoLog(t);throw n.deleteShader(t),new Error(`loadShader: error while compiling the shader:
|
|
3
3
|
`.concat(o,`
|
|
4
4
|
`).concat(i))}return t}function ii(r,n){return xt("VERTEX",r,n)}function ri(r,n){return xt("FRAGMENT",r,n)}function ni(r,n){var i=r.createProgram();if(i===null)throw new Error("loadProgram: error while creating the program.");var e,t;for(e=0,t=n.length;e<t;e++)r.attachShader(i,n[e]);r.linkProgram(i);var a=r.getProgramParameter(i,r.LINK_STATUS);if(!a){var o=r.getProgramInfoLog(i);throw r.deleteProgram(i),new Error("loadProgram: error while linking the program: ".concat(o))}return i}function Ze(r){var n=r.gl,i=r.buffer,e=r.program,t=r.vertexShader,a=r.fragmentShader;n.deleteShader(t),n.deleteShader(a),n.deleteProgram(e),n.deleteBuffer(i)}var qe=`#define PICKING_MODE
|
package/assets/index.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
|
|
13
13
|
<link rel="apple-touch-icon" href="./apple-touch-icon.png" />
|
|
14
14
|
<title>Canonry</title>
|
|
15
|
-
<script type="module" crossorigin src="./assets/index-
|
|
15
|
+
<script type="module" crossorigin src="./assets/index-xtxQtZRo.js"></script>
|
|
16
16
|
<link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Dock0eH1.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="./assets/vendor-radix-4UOaVWza.js">
|
|
18
18
|
<link rel="modulepreload" crossorigin href="./assets/vendor-recharts-D8rt5lKn.js">
|
|
@@ -219,6 +219,7 @@ import {
|
|
|
219
219
|
citationVisibilityResponseSchema,
|
|
220
220
|
classifyAiReferralTrafficClass,
|
|
221
221
|
classifyCitedSurface,
|
|
222
|
+
classifyProviderErrorMessage,
|
|
222
223
|
classifySkillFile,
|
|
223
224
|
classifySurfaceFromCategory,
|
|
224
225
|
classifyTrafficPath,
|
|
@@ -481,6 +482,7 @@ import {
|
|
|
481
482
|
normalizeMeasurementHost,
|
|
482
483
|
normalizeMeasurementPathPrefix,
|
|
483
484
|
normalizeModelId,
|
|
485
|
+
normalizeOnboardingEventForCollection,
|
|
484
486
|
normalizeProjectAliases,
|
|
485
487
|
normalizeProjectDomain,
|
|
486
488
|
normalizeProjectName,
|
|
@@ -513,6 +515,7 @@ import {
|
|
|
513
515
|
projectReportDtoSchema,
|
|
514
516
|
projectSearchResponseSchema,
|
|
515
517
|
projectUpsertRequestSchema,
|
|
518
|
+
providerAuthError,
|
|
516
519
|
providerError,
|
|
517
520
|
queryDtoSchema,
|
|
518
521
|
queryGenerateRequestSchema,
|
|
@@ -634,7 +637,7 @@ import {
|
|
|
634
637
|
wordpressSchemaDeployResultDtoSchema,
|
|
635
638
|
wordpressSchemaStatusResultDtoSchema,
|
|
636
639
|
wordpressStatusDtoSchema
|
|
637
|
-
} from "./chunk-
|
|
640
|
+
} from "./chunk-OYDIND7Q.js";
|
|
638
641
|
|
|
639
642
|
// src/intelligence-service.ts
|
|
640
643
|
import { eq as eq62, desc as desc27, asc as asc11, and as and51, ne as ne8, or as or14, inArray as inArray21, gte as gte14, lte as lte12, isNull as isNull9, sql as sql25, exists } from "drizzle-orm";
|
|
@@ -710,6 +713,7 @@ __export(schema_exports, {
|
|
|
710
713
|
gtmConnections: () => gtmConnections,
|
|
711
714
|
gtmRawSnapshots: () => gtmRawSnapshots,
|
|
712
715
|
healthSnapshots: () => healthSnapshots,
|
|
716
|
+
insightNotifyState: () => insightNotifyState,
|
|
713
717
|
insights: () => insights,
|
|
714
718
|
llmUsageEvents: () => llmUsageEvents,
|
|
715
719
|
measurementDiscoveryConfigs: () => measurementDiscoveryConfigs,
|
|
@@ -1219,6 +1223,25 @@ var schedules = sqliteTable("schedules", {
|
|
|
1219
1223
|
}, (table) => [
|
|
1220
1224
|
uniqueIndex("idx_schedules_project_kind").on(table.projectId, table.kind)
|
|
1221
1225
|
]);
|
|
1226
|
+
var insightNotifyState = sqliteTable("insight_notify_state", {
|
|
1227
|
+
/** `${projectId}:${type}:${subject}:${window}` - stable across runs. */
|
|
1228
|
+
key: text("key").primaryKey(),
|
|
1229
|
+
projectId: text("project_id").notNull(),
|
|
1230
|
+
/** Insight type, e.g. `gbp-keyword-drop`. */
|
|
1231
|
+
type: text("type").notNull(),
|
|
1232
|
+
/** What the finding is about: the keyword, query, or url. */
|
|
1233
|
+
subject: text("subject").notNull(),
|
|
1234
|
+
/**
|
|
1235
|
+
* The finding's identity as text: its title with the magnitude number
|
|
1236
|
+
* neutralised, so 79% and 80% of the same drop over the same window are one
|
|
1237
|
+
* piece of news, while a window that advances is a different one.
|
|
1238
|
+
*/
|
|
1239
|
+
fingerprint: text("fingerprint").notNull(),
|
|
1240
|
+
severity: text("severity").notNull(),
|
|
1241
|
+
/** Rounded magnitude, so a drift from 79% to 80% is not treated as new news. */
|
|
1242
|
+
magnitude: integer("magnitude"),
|
|
1243
|
+
notifiedAt: text("notified_at").notNull()
|
|
1244
|
+
});
|
|
1222
1245
|
var doctorHealthState = sqliteTable("doctor_health_state", {
|
|
1223
1246
|
projectId: text("project_id").primaryKey(),
|
|
1224
1247
|
/** Worst check status observed on the last pass: ok | warn | fail. */
|
|
@@ -6990,6 +7013,32 @@ var MIGRATION_VERSIONS = [
|
|
|
6990
7013
|
statements: [
|
|
6991
7014
|
`ALTER TABLE oauth_clients ADD COLUMN registration TEXT NOT NULL DEFAULT 'operator'`
|
|
6992
7015
|
]
|
|
7016
|
+
},
|
|
7017
|
+
{
|
|
7018
|
+
version: 148,
|
|
7019
|
+
name: "insight-notify-state",
|
|
7020
|
+
// Insight dispatch had no memory. Health is edge-triggered and citations are
|
|
7021
|
+
// transition-based, but every run recomputed its insights and sent whatever
|
|
7022
|
+
// was high or critical, so a finding that persists alerted on every run,
|
|
7023
|
+
// forever. Measured on one client: a GBP keyword drop notified daily for over
|
|
7024
|
+
// a month with byte-identical text, because Google publishes GBP keyword data
|
|
7025
|
+
// about a month behind and the comparison window sat still while the calendar
|
|
7026
|
+
// moved on. The insights table held ONE row for it, so nothing in the stored
|
|
7027
|
+
// data revealed the repetition.
|
|
7028
|
+
statements: [
|
|
7029
|
+
`CREATE TABLE IF NOT EXISTS insight_notify_state (
|
|
7030
|
+
key TEXT PRIMARY KEY,
|
|
7031
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
7032
|
+
type TEXT NOT NULL,
|
|
7033
|
+
subject TEXT NOT NULL,
|
|
7034
|
+
fingerprint TEXT NOT NULL,
|
|
7035
|
+
severity TEXT NOT NULL,
|
|
7036
|
+
magnitude INTEGER,
|
|
7037
|
+
notified_at TEXT NOT NULL
|
|
7038
|
+
)`,
|
|
7039
|
+
`CREATE INDEX IF NOT EXISTS insight_notify_state_project
|
|
7040
|
+
ON insight_notify_state(project_id)`
|
|
7041
|
+
]
|
|
6993
7042
|
}
|
|
6994
7043
|
];
|
|
6995
7044
|
function addRunsMeasurementPlanVersionForeignKey(tx) {
|
|
@@ -9242,6 +9291,7 @@ var GBP_METRIC_MIN_BASELINE = 10;
|
|
|
9242
9291
|
var GBP_KEYWORD_DROP_PCT = 40;
|
|
9243
9292
|
var GBP_KEYWORD_SEVERE_PCT = 70;
|
|
9244
9293
|
var GBP_KEYWORD_MIN_BASELINE = 20;
|
|
9294
|
+
var GBP_ACTIONS_HELD_PCT = -10;
|
|
9245
9295
|
var GBP_INSIGHT_PROVIDER = "gbp";
|
|
9246
9296
|
var METRIC_LABELS = {
|
|
9247
9297
|
BUSINESS_DIRECTION_REQUESTS: "Direction requests",
|
|
@@ -9330,7 +9380,7 @@ function analyzeGbp(signals) {
|
|
|
9330
9380
|
drafts.push({
|
|
9331
9381
|
...base,
|
|
9332
9382
|
type: "gbp-keyword-drop",
|
|
9333
|
-
severity: worstKeyword.dropPct >= GBP_KEYWORD_SEVERE_PCT ? "high" : "medium",
|
|
9383
|
+
severity: worstKeyword.dropPct >= GBP_KEYWORD_SEVERE_PCT && !actionsHeldUp(loc) ? "high" : "medium",
|
|
9334
9384
|
title: `${loc.displayName}: "${worstKeyword.keyword}" impressions down ${worstKeyword.dropPct}% month-over-month${window}`,
|
|
9335
9385
|
recommendation: {
|
|
9336
9386
|
action: "Check whether the property still ranks for this local search term and refresh the profile",
|
|
@@ -9341,6 +9391,11 @@ function analyzeGbp(signals) {
|
|
|
9341
9391
|
}
|
|
9342
9392
|
return drafts;
|
|
9343
9393
|
}
|
|
9394
|
+
function actionsHeldUp(loc) {
|
|
9395
|
+
const deltas = GBP_HEADLINE_METRICS.map((metric2) => loc.metricDeltaPct[metric2]).filter((d) => typeof d === "number");
|
|
9396
|
+
if (deltas.length === 0) return false;
|
|
9397
|
+
return deltas.every((d) => d >= GBP_ACTIONS_HELD_PCT);
|
|
9398
|
+
}
|
|
9344
9399
|
function pickWorstMetricDrop(loc) {
|
|
9345
9400
|
let worst = null;
|
|
9346
9401
|
for (const metric2 of GBP_HEADLINE_METRICS) {
|
|
@@ -11782,6 +11837,24 @@ function replaceProjectQueries(tx, projectId, incomingTexts, now) {
|
|
|
11782
11837
|
}
|
|
11783
11838
|
|
|
11784
11839
|
// ../api-routes/src/queries.ts
|
|
11840
|
+
function generationFailure(err, fallbackMessage) {
|
|
11841
|
+
if (err instanceof AppError) return err;
|
|
11842
|
+
const message = err instanceof Error ? describeError(err) : fallbackMessage;
|
|
11843
|
+
switch (classifyProviderErrorMessage(message)) {
|
|
11844
|
+
case "PROVIDER_AUTH":
|
|
11845
|
+
return providerAuthError(message);
|
|
11846
|
+
case "RATE_LIMITED":
|
|
11847
|
+
return quotaExceeded("provider requests", { message });
|
|
11848
|
+
case "NETWORK":
|
|
11849
|
+
case "TIMEOUT":
|
|
11850
|
+
return providerError(message);
|
|
11851
|
+
// A malformed provider response and an unrecognized failure are both ours
|
|
11852
|
+
// to explain, not the caller's to retry.
|
|
11853
|
+
case "PARSE_ERROR":
|
|
11854
|
+
case "UNKNOWN":
|
|
11855
|
+
return internalError(message);
|
|
11856
|
+
}
|
|
11857
|
+
}
|
|
11785
11858
|
async function queryRoutes(app, opts) {
|
|
11786
11859
|
app.get("/projects/:name/queries", async (request, reply) => {
|
|
11787
11860
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -11959,7 +12032,7 @@ async function queryRoutes(app, opts) {
|
|
|
11959
12032
|
return reply.send({ queries: generated, provider });
|
|
11960
12033
|
} catch (err) {
|
|
11961
12034
|
request.log.error({ err }, "Query generation failed");
|
|
11962
|
-
throw
|
|
12035
|
+
throw generationFailure(err, "Failed to generate queries");
|
|
11963
12036
|
}
|
|
11964
12037
|
});
|
|
11965
12038
|
app.get("/projects/:name/keywords", async (request, reply) => {
|
|
@@ -12086,7 +12159,7 @@ async function queryRoutes(app, opts) {
|
|
|
12086
12159
|
return reply.send({ keywords: generated, provider });
|
|
12087
12160
|
} catch (err) {
|
|
12088
12161
|
request.log.error({ err }, "Keyword generation failed");
|
|
12089
|
-
throw
|
|
12162
|
+
throw generationFailure(err, "Failed to generate keywords");
|
|
12090
12163
|
}
|
|
12091
12164
|
});
|
|
12092
12165
|
}
|
|
@@ -14454,6 +14527,11 @@ async function measurementPlanRoutes(app, opts) {
|
|
|
14454
14527
|
if (activePlan && !activeVersion) {
|
|
14455
14528
|
throw new Error(`Measurement plan ${project.id} points to missing version ${activePlan.activeVersionId}`);
|
|
14456
14529
|
}
|
|
14530
|
+
if (activeVersion && activeVersion.schemaVersion !== 1) {
|
|
14531
|
+
throw validationError(
|
|
14532
|
+
`The active measurement plan is schema v${activeVersion.schemaVersion}; this legacy endpoint publishes only schema v1 and would downgrade it. Publish through the draft flow instead: POST /projects/:name/measurement-plan/draft/actions/publish (CLI: canonry measurement-plan advanced <project> draft-action; MCP: canonry_measurement_draft_action).`
|
|
14533
|
+
);
|
|
14534
|
+
}
|
|
14457
14535
|
if (activeVersion && activeVersion.checksum === checksum) return { kind: "existing", version: activeVersion };
|
|
14458
14536
|
const actualActiveRevision = activeVersion?.revision ?? null;
|
|
14459
14537
|
if (actualActiveRevision !== expectedActiveRevision) {
|
|
@@ -30776,8 +30854,8 @@ var routeCatalog = [
|
|
|
30776
30854
|
{
|
|
30777
30855
|
method: "put",
|
|
30778
30856
|
path: "/api/v1/projects/{name}/measurement-plan",
|
|
30779
|
-
summary: "Publish a measurement-plan revision",
|
|
30780
|
-
description: "Compares the caller-observed active revision, then validates and canonicalizes the plan against current project domains, locations, and tracked queries. Identical active content is idempotent; restoring older content creates a new immutable revision.",
|
|
30857
|
+
summary: "Publish a measurement-plan revision (legacy schema v1)",
|
|
30858
|
+
description: "Legacy schema-v1 publish. Compares the caller-observed active revision, then validates and canonicalizes the plan against current project domains, locations, and tracked queries. Identical active content is idempotent; restoring older content creates a new immutable revision. Refuses when the active revision is schema v2 rather than downgrade it; publish v2 plans through the draft flow (POST .../measurement-plan/draft/actions/publish).",
|
|
30781
30859
|
tags: ["measurement-plans"],
|
|
30782
30860
|
parameters: [nameParameter],
|
|
30783
30861
|
requestBody: {
|
|
@@ -37358,7 +37436,7 @@ async function telemetryRoutes(app, opts) {
|
|
|
37358
37436
|
}))
|
|
37359
37437
|
});
|
|
37360
37438
|
}
|
|
37361
|
-
opts.recordOnboardingEvent?.(parsed.data);
|
|
37439
|
+
opts.recordOnboardingEvent?.(normalizeOnboardingEventForCollection(parsed.data));
|
|
37362
37440
|
return reply.status(202).send({ accepted: Boolean(opts.recordOnboardingEvent) });
|
|
37363
37441
|
});
|
|
37364
37442
|
}
|
|
@@ -79426,6 +79504,7 @@ export {
|
|
|
79426
79504
|
auditLog,
|
|
79427
79505
|
apiKeys,
|
|
79428
79506
|
schedules,
|
|
79507
|
+
insightNotifyState,
|
|
79429
79508
|
doctorHealthState,
|
|
79430
79509
|
notifications,
|
|
79431
79510
|
gscSearchData,
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
installSkills,
|
|
6
6
|
loadConfigRaw,
|
|
7
7
|
saveConfigPatch
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-P6JVZJO5.js";
|
|
9
9
|
import {
|
|
10
10
|
SKILL_MANIFEST_FILENAME,
|
|
11
11
|
SkillsClients
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OYDIND7Q.js";
|
|
13
13
|
|
|
14
14
|
// src/skills-autosync.ts
|
|
15
15
|
import fs from "fs";
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
loadConfig,
|
|
12
12
|
loadConfigRaw,
|
|
13
13
|
saveConfigPatch
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-P6JVZJO5.js";
|
|
15
15
|
import {
|
|
16
16
|
CC_CACHE_DIR,
|
|
17
17
|
DUCKDB_SPEC,
|
|
@@ -99,6 +99,7 @@ import {
|
|
|
99
99
|
hashAttributes,
|
|
100
100
|
hashLodging,
|
|
101
101
|
hashPlaceDetails,
|
|
102
|
+
insightNotifyState,
|
|
102
103
|
insights,
|
|
103
104
|
inspectUrl,
|
|
104
105
|
installDuckdb,
|
|
@@ -156,7 +157,7 @@ import {
|
|
|
156
157
|
siteCrawlSnapshots,
|
|
157
158
|
toAlertView,
|
|
158
159
|
usageCounters
|
|
159
|
-
} from "./chunk-
|
|
160
|
+
} from "./chunk-5KI5WVWS.js";
|
|
160
161
|
import {
|
|
161
162
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
162
163
|
AGENT_PROVIDER_IDS,
|
|
@@ -219,6 +220,7 @@ import {
|
|
|
219
220
|
canonicalizeGtmAccountId,
|
|
220
221
|
canonicalizeGtmContainerId,
|
|
221
222
|
canonicalizeGtmResourceSelection,
|
|
223
|
+
classifyProviderErrorMessages,
|
|
222
224
|
classifyTemplateLinkEdge,
|
|
223
225
|
cloudflareQueueNameSchema,
|
|
224
226
|
computeBacklinkSummaryMetrics,
|
|
@@ -294,7 +296,7 @@ import {
|
|
|
294
296
|
validationError,
|
|
295
297
|
winnabilityClassLabel,
|
|
296
298
|
withRetry
|
|
297
|
-
} from "./chunk-
|
|
299
|
+
} from "./chunk-OYDIND7Q.js";
|
|
298
300
|
|
|
299
301
|
// src/telemetry.ts
|
|
300
302
|
import crypto from "crypto";
|
|
@@ -6410,40 +6412,7 @@ function classifyRunAbortReason(message) {
|
|
|
6410
6412
|
return void 0;
|
|
6411
6413
|
}
|
|
6412
6414
|
function classifyProviderErrors(errors) {
|
|
6413
|
-
|
|
6414
|
-
for (const message of errors.values()) {
|
|
6415
|
-
codes.add(classifyOneProviderError(message));
|
|
6416
|
-
}
|
|
6417
|
-
const priority = [
|
|
6418
|
-
"PROVIDER_AUTH",
|
|
6419
|
-
"RATE_LIMITED",
|
|
6420
|
-
"TIMEOUT",
|
|
6421
|
-
"NETWORK",
|
|
6422
|
-
"PARSE_ERROR",
|
|
6423
|
-
"UNKNOWN"
|
|
6424
|
-
];
|
|
6425
|
-
for (const code of priority) {
|
|
6426
|
-
if (codes.has(code)) return code;
|
|
6427
|
-
}
|
|
6428
|
-
return "UNKNOWN";
|
|
6429
|
-
}
|
|
6430
|
-
function classifyOneProviderError(message) {
|
|
6431
|
-
if (/\b401\b|\b403\b|unauthorized|forbidden|invalid[_ -]?api[_ -]?key|missing[_ -]?api[_ -]?key|authentication/i.test(message)) {
|
|
6432
|
-
return "PROVIDER_AUTH";
|
|
6433
|
-
}
|
|
6434
|
-
if (/\b429\b|rate[_ -]?limit|too many requests|quota[_ -]?exceeded/i.test(message)) {
|
|
6435
|
-
return "RATE_LIMITED";
|
|
6436
|
-
}
|
|
6437
|
-
if (/timeout|timed out|ETIMEDOUT/i.test(message)) {
|
|
6438
|
-
return "TIMEOUT";
|
|
6439
|
-
}
|
|
6440
|
-
if (/ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|network|fetch failed|socket hang up/i.test(message)) {
|
|
6441
|
-
return "NETWORK";
|
|
6442
|
-
}
|
|
6443
|
-
if (/parse|unexpected token|invalid json|malformed|JSON\.parse/i.test(message)) {
|
|
6444
|
-
return "PARSE_ERROR";
|
|
6445
|
-
}
|
|
6446
|
-
return "UNKNOWN";
|
|
6415
|
+
return classifyProviderErrorMessages(errors.values());
|
|
6447
6416
|
}
|
|
6448
6417
|
var JobRunner = class {
|
|
6449
6418
|
/**
|
|
@@ -10916,6 +10885,31 @@ function partitionDeadLinks(deadLinks) {
|
|
|
10916
10885
|
];
|
|
10917
10886
|
return { dead, unverified };
|
|
10918
10887
|
}
|
|
10888
|
+
function reportedTermination(summary) {
|
|
10889
|
+
const limits = summary.limits;
|
|
10890
|
+
if (limits) {
|
|
10891
|
+
if (limits.maxBytes && (summary.bytesRead ?? 0) >= limits.maxBytes) return "max-bytes";
|
|
10892
|
+
if (limits.maxDurationMs && (summary.elapsedMs ?? 0) >= limits.maxDurationMs) return "max-duration";
|
|
10893
|
+
if (limits.maxFetches && (summary.fetchesStarted ?? 0) >= limits.maxFetches) return "max-fetches";
|
|
10894
|
+
}
|
|
10895
|
+
return summary.terminationReason ?? "complete";
|
|
10896
|
+
}
|
|
10897
|
+
function crawlBudgetsFor(maxPages) {
|
|
10898
|
+
return {
|
|
10899
|
+
// 2x the ~745 KB/page measured on a media-heavy production site, so the
|
|
10900
|
+
// byte cap is headroom rather than the thing that stops the crawl.
|
|
10901
|
+
maxBytes: maxPages * 15e5,
|
|
10902
|
+
// ~5 pages/s at the engine's default concurrency of 5, doubled for slow
|
|
10903
|
+
// origins, and never below the engine's own 2-minute floor.
|
|
10904
|
+
maxDurationMs: Math.max(12e4, Math.ceil(maxPages / 5 * 1e3 * 2)),
|
|
10905
|
+
// Redirects and dead-link probes cost fetches without producing pages.
|
|
10906
|
+
maxFetches: Math.ceil(maxPages * 1.5),
|
|
10907
|
+
// Raised off the default of 10 because it is an ADMISSION limit: a sitemap
|
|
10908
|
+
// with many query-parameter variants trips it during seeding and latches a
|
|
10909
|
+
// soft reason that then masks whatever really stops the crawl.
|
|
10910
|
+
maxQueryVariants: Math.max(50, Math.ceil(maxPages / 10))
|
|
10911
|
+
};
|
|
10912
|
+
}
|
|
10919
10913
|
async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
10920
10914
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10921
10915
|
const claim = db.update(runs).set({ status: "running", startedAt }).where(and12(eq15(runs.id, runId), eq15(runs.projectId, projectId), eq15(runs.status, "queued"))).run();
|
|
@@ -11216,6 +11210,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
11216
11210
|
maxEdges,
|
|
11217
11211
|
maxDepth: opts.maxDepth ?? null
|
|
11218
11212
|
});
|
|
11213
|
+
const budgets = crawlBudgetsFor(maxPages);
|
|
11219
11214
|
const report = await runSiteCrawl(crawlRootUrl, {
|
|
11220
11215
|
mode: "summary",
|
|
11221
11216
|
sitemapUrl: opts.sitemapUrl,
|
|
@@ -11223,6 +11218,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
11223
11218
|
maxEdges,
|
|
11224
11219
|
maxDepth: opts.maxDepth,
|
|
11225
11220
|
checkDeadLinks: opts.checkDeadLinks ?? false,
|
|
11221
|
+
...budgets,
|
|
11226
11222
|
signal: opts.signal,
|
|
11227
11223
|
onEvent: persistEvent
|
|
11228
11224
|
});
|
|
@@ -11349,7 +11345,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
11349
11345
|
maxDepth: opts.maxDepth ?? null,
|
|
11350
11346
|
checkDeadLinks: opts.checkDeadLinks ?? false,
|
|
11351
11347
|
complete: crawlSummary.complete,
|
|
11352
|
-
termination: crawlSummary
|
|
11348
|
+
termination: reportedTermination(crawlSummary),
|
|
11353
11349
|
detailsAvailable: true,
|
|
11354
11350
|
pagesDiscovered: crawlSummary.pagesDiscovered,
|
|
11355
11351
|
pagesFetched: crawlSummary.pagesFetched,
|
|
@@ -11900,7 +11896,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
11900
11896
|
return result;
|
|
11901
11897
|
}
|
|
11902
11898
|
async function backfillInsightsCommand(project, opts) {
|
|
11903
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
11899
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-KYIQZBEO.js");
|
|
11904
11900
|
const config = loadConfig();
|
|
11905
11901
|
const db = createClient(config.database);
|
|
11906
11902
|
migrate(db);
|
|
@@ -12890,6 +12886,21 @@ async function refreshAllIntegrations(client, projectName) {
|
|
|
12890
12886
|
import { eq as eq18, desc as desc7, and as and15, inArray as inArray7, or as or2 } from "drizzle-orm";
|
|
12891
12887
|
import crypto21 from "crypto";
|
|
12892
12888
|
var log16 = createLogger("Notifier");
|
|
12889
|
+
var MATERIAL_MAGNITUDE_DELTA = 20;
|
|
12890
|
+
var SEVERITY_RANK = { low: 0, medium: 1, high: 2, critical: 3 };
|
|
12891
|
+
function severityRank(severity) {
|
|
12892
|
+
return SEVERITY_RANK[severity] ?? 0;
|
|
12893
|
+
}
|
|
12894
|
+
function insightFingerprint(insight) {
|
|
12895
|
+
return insight.title.replace(/\b\d+(?:\.\d+)?%/g, "N%");
|
|
12896
|
+
}
|
|
12897
|
+
function insightMagnitude(insight) {
|
|
12898
|
+
const match = /\b(\d+(?:\.\d+)?)%/.exec(insight.title);
|
|
12899
|
+
return match?.[1] === void 0 ? null : Math.round(Number(match[1]));
|
|
12900
|
+
}
|
|
12901
|
+
function insightNotifyKey(projectId, insight) {
|
|
12902
|
+
return `${projectId}:${insight.type}:${insight.query}:${insightFingerprint(insight)}`;
|
|
12903
|
+
}
|
|
12893
12904
|
var Notifier = class {
|
|
12894
12905
|
db;
|
|
12895
12906
|
serverUrl;
|
|
@@ -13045,11 +13056,59 @@ var Notifier = class {
|
|
|
13045
13056
|
log16.info("health.notified", { projectId, event, status, code, subscribers: notifs.length, delivered });
|
|
13046
13057
|
return event;
|
|
13047
13058
|
}
|
|
13059
|
+
/**
|
|
13060
|
+
* Has this finding already been said?
|
|
13061
|
+
*
|
|
13062
|
+
* The identity of a finding is its type, its subject and its wording with the
|
|
13063
|
+
* magnitude neutralised. So the same drop reported at 79% and at 80% over the
|
|
13064
|
+
* same window is one piece of news, while the same drop over an ADVANCED
|
|
13065
|
+
* window is a new one.
|
|
13066
|
+
*
|
|
13067
|
+
* Re-notifies on three things and nothing else:
|
|
13068
|
+
* - never sent before
|
|
13069
|
+
* - severity got worse (medium -> high -> critical)
|
|
13070
|
+
* - magnitude moved by more than MATERIAL_MAGNITUDE_DELTA points
|
|
13071
|
+
*
|
|
13072
|
+
* Deliberately NOT time-based. A daily repeat is exactly the behaviour this
|
|
13073
|
+
* exists to stop, and "re-alert after N days" is that behaviour with a delay.
|
|
13074
|
+
*/
|
|
13075
|
+
isNewInsightNews(projectId, insight) {
|
|
13076
|
+
const key = insightNotifyKey(projectId, insight);
|
|
13077
|
+
const previous = this.db.select().from(insightNotifyState).where(eq18(insightNotifyState.key, key)).get();
|
|
13078
|
+
if (!previous) return true;
|
|
13079
|
+
if (severityRank(insight.severity) > severityRank(previous.severity)) return true;
|
|
13080
|
+
const magnitude = insightMagnitude(insight);
|
|
13081
|
+
if (magnitude !== null && previous.magnitude !== null && previous.magnitude !== void 0) {
|
|
13082
|
+
if (Math.abs(magnitude - previous.magnitude) > MATERIAL_MAGNITUDE_DELTA) return true;
|
|
13083
|
+
}
|
|
13084
|
+
return false;
|
|
13085
|
+
}
|
|
13086
|
+
/** Record a delivered insight so it stops being news. */
|
|
13087
|
+
rememberInsightNotified(projectId, insight) {
|
|
13088
|
+
const key = insightNotifyKey(projectId, insight);
|
|
13089
|
+
const row = {
|
|
13090
|
+
key,
|
|
13091
|
+
projectId,
|
|
13092
|
+
type: insight.type,
|
|
13093
|
+
subject: insight.query,
|
|
13094
|
+
fingerprint: insightFingerprint(insight),
|
|
13095
|
+
severity: insight.severity,
|
|
13096
|
+
magnitude: insightMagnitude(insight),
|
|
13097
|
+
notifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13098
|
+
};
|
|
13099
|
+
const existing = this.db.select().from(insightNotifyState).where(eq18(insightNotifyState.key, key)).get();
|
|
13100
|
+
if (existing) {
|
|
13101
|
+
this.db.update(insightNotifyState).set(row).where(eq18(insightNotifyState.key, key)).run();
|
|
13102
|
+
} else {
|
|
13103
|
+
this.db.insert(insightNotifyState).values(row).run();
|
|
13104
|
+
}
|
|
13105
|
+
}
|
|
13048
13106
|
/** Dispatch insight webhooks for critical/high severity insights after a run. */
|
|
13049
13107
|
async dispatchInsightWebhooks(runId, projectId, result) {
|
|
13050
13108
|
const insightEvents = [];
|
|
13051
|
-
const
|
|
13052
|
-
const
|
|
13109
|
+
const unsent = result.insights.filter((i) => this.isNewInsightNews(projectId, i));
|
|
13110
|
+
const criticalInsights = unsent.filter((i) => i.severity === "critical");
|
|
13111
|
+
const highInsights = unsent.filter((i) => i.severity === "high");
|
|
13053
13112
|
if (criticalInsights.length > 0) insightEvents.push("insight.critical");
|
|
13054
13113
|
if (highInsights.length > 0) insightEvents.push("insight.high");
|
|
13055
13114
|
if (insightEvents.length === 0) return;
|
|
@@ -13083,6 +13142,7 @@ var Notifier = class {
|
|
|
13083
13142
|
dashboardUrl: `${this.serverUrl}/projects/${project.name}`
|
|
13084
13143
|
};
|
|
13085
13144
|
await this.sendWebhook(config.url, payload, notif.id, projectId, notif.webhookSecret ?? null);
|
|
13145
|
+
for (const insight of relevantInsights) this.rememberInsightNotified(projectId, insight);
|
|
13086
13146
|
}
|
|
13087
13147
|
}
|
|
13088
13148
|
}
|
|
@@ -53,6 +53,9 @@ function quotaExceeded(metric, details) {
|
|
|
53
53
|
function providerError(message, details) {
|
|
54
54
|
return new AppError("PROVIDER_ERROR", message, 502, details);
|
|
55
55
|
}
|
|
56
|
+
function providerAuthError(message, details) {
|
|
57
|
+
return new AppError("PROVIDER_AUTH", message, 502, details);
|
|
58
|
+
}
|
|
56
59
|
function noProvider(projectName, details) {
|
|
57
60
|
return new AppError(
|
|
58
61
|
"NO_PROVIDER",
|
|
@@ -16157,6 +16160,44 @@ function serializeForInlineScript(value) {
|
|
|
16157
16160
|
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
16158
16161
|
}
|
|
16159
16162
|
|
|
16163
|
+
// ../contracts/src/provider-errors.ts
|
|
16164
|
+
var PROVIDER_ERROR_PRIORITY = [
|
|
16165
|
+
"PROVIDER_AUTH",
|
|
16166
|
+
"RATE_LIMITED",
|
|
16167
|
+
"TIMEOUT",
|
|
16168
|
+
"NETWORK",
|
|
16169
|
+
"PARSE_ERROR",
|
|
16170
|
+
"UNKNOWN"
|
|
16171
|
+
];
|
|
16172
|
+
function classifyProviderErrorMessage(message) {
|
|
16173
|
+
if (/\b401\b|\b403\b|unauthorized|forbidden|invalid[_ -]?api[_ -]?key|missing[_ -]?api[_ -]?key|authentication/i.test(message)) {
|
|
16174
|
+
return "PROVIDER_AUTH";
|
|
16175
|
+
}
|
|
16176
|
+
if (/\b429\b|rate[_ -]?limit|too many requests|quota[_ -]?exceeded/i.test(message)) {
|
|
16177
|
+
return "RATE_LIMITED";
|
|
16178
|
+
}
|
|
16179
|
+
if (/timeout|timed out|ETIMEDOUT/i.test(message)) {
|
|
16180
|
+
return "TIMEOUT";
|
|
16181
|
+
}
|
|
16182
|
+
if (/ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|network|fetch failed|socket hang up/i.test(message)) {
|
|
16183
|
+
return "NETWORK";
|
|
16184
|
+
}
|
|
16185
|
+
if (/parse|unexpected token|invalid json|malformed|JSON\.parse/i.test(message)) {
|
|
16186
|
+
return "PARSE_ERROR";
|
|
16187
|
+
}
|
|
16188
|
+
return "UNKNOWN";
|
|
16189
|
+
}
|
|
16190
|
+
function classifyProviderErrorMessages(messages) {
|
|
16191
|
+
const codes = /* @__PURE__ */ new Set();
|
|
16192
|
+
for (const message of messages) {
|
|
16193
|
+
codes.add(classifyProviderErrorMessage(message));
|
|
16194
|
+
}
|
|
16195
|
+
for (const code of PROVIDER_ERROR_PRIORITY) {
|
|
16196
|
+
if (codes.has(code)) return code;
|
|
16197
|
+
}
|
|
16198
|
+
return "UNKNOWN";
|
|
16199
|
+
}
|
|
16200
|
+
|
|
16160
16201
|
// ../contracts/src/telemetry.ts
|
|
16161
16202
|
import { z as z54 } from "zod";
|
|
16162
16203
|
var ONBOARDING_FLOW_VERSION = 1;
|
|
@@ -16195,12 +16236,24 @@ var onboardingBlockReasonSchema = z54.enum([
|
|
|
16195
16236
|
"run_rejected",
|
|
16196
16237
|
"run_failed",
|
|
16197
16238
|
"run_cancelled",
|
|
16239
|
+
// Provider-side failures the query-generation step actually hits. Without
|
|
16240
|
+
// these every one of them reported `unknown`, which is why the queries step
|
|
16241
|
+
// was the only blocker nobody could diagnose.
|
|
16242
|
+
"rate_limited",
|
|
16243
|
+
"provider_auth",
|
|
16244
|
+
"network",
|
|
16198
16245
|
"unknown"
|
|
16199
16246
|
]);
|
|
16247
|
+
var onboardingSurfaceSchema = z54.enum([
|
|
16248
|
+
"wizard",
|
|
16249
|
+
"platform",
|
|
16250
|
+
"site_health"
|
|
16251
|
+
]);
|
|
16200
16252
|
var onboardingEventBaseSchema = z54.object({
|
|
16201
16253
|
eventId: z54.string().uuid(),
|
|
16202
16254
|
flowVersion: z54.literal(ONBOARDING_FLOW_VERSION),
|
|
16203
|
-
onboardingSessionId: z54.string().uuid()
|
|
16255
|
+
onboardingSessionId: z54.string().uuid(),
|
|
16256
|
+
surface: onboardingSurfaceSchema.optional()
|
|
16204
16257
|
});
|
|
16205
16258
|
var onboardingTelemetryEventSchema = z54.discriminatedUnion("event", [
|
|
16206
16259
|
onboardingEventBaseSchema.extend({
|
|
@@ -16224,11 +16277,24 @@ var onboardingTelemetryEventSchema = z54.discriminatedUnion("event", [
|
|
|
16224
16277
|
event: z54.literal("run.requested"),
|
|
16225
16278
|
origin: z54.literal("dashboard_setup"),
|
|
16226
16279
|
result: z54.enum(["queued", "rejected"]),
|
|
16280
|
+
/**
|
|
16281
|
+
* What kind of run was asked for. A site-health crawl has no providers and
|
|
16282
|
+
* no tracked queries, so its buckets are legitimately `0`; without this
|
|
16283
|
+
* field that is indistinguishable from a misconfigured visibility sweep.
|
|
16284
|
+
* Optional on the wire and defaulted at collection for the same reason as
|
|
16285
|
+
* `surface`: a documented fallback that nothing applies becomes a null
|
|
16286
|
+
* bucket downstream.
|
|
16287
|
+
*/
|
|
16288
|
+
kind: z54.enum(["answer_visibility", "site_health"]).optional(),
|
|
16227
16289
|
providerCountBucket: onboardingCountBucketSchema,
|
|
16228
16290
|
queryCountBucket: onboardingCountBucketSchema,
|
|
16229
16291
|
reasonCode: onboardingBlockReasonSchema.optional()
|
|
16230
16292
|
}).strict()
|
|
16231
16293
|
]);
|
|
16294
|
+
function normalizeOnboardingEventForCollection(event) {
|
|
16295
|
+
const surfaced = { ...event, surface: event.surface ?? "wizard" };
|
|
16296
|
+
return surfaced.event === "run.requested" ? { ...surfaced, kind: surfaced.kind ?? "answer_visibility" } : surfaced;
|
|
16297
|
+
}
|
|
16232
16298
|
var telemetryEventAcceptedDtoSchema = z54.object({
|
|
16233
16299
|
accepted: z54.boolean()
|
|
16234
16300
|
});
|
|
@@ -16273,6 +16339,7 @@ export {
|
|
|
16273
16339
|
forbidden,
|
|
16274
16340
|
quotaExceeded,
|
|
16275
16341
|
providerError,
|
|
16342
|
+
providerAuthError,
|
|
16276
16343
|
noProvider,
|
|
16277
16344
|
noQueries,
|
|
16278
16345
|
runInProgress,
|
|
@@ -16956,9 +17023,12 @@ export {
|
|
|
16956
17023
|
normalizeIdTokens,
|
|
16957
17024
|
embedClientConfigForRequest,
|
|
16958
17025
|
serializeForInlineScript,
|
|
17026
|
+
classifyProviderErrorMessage,
|
|
17027
|
+
classifyProviderErrorMessages,
|
|
16959
17028
|
ONBOARDING_FLOW_VERSION,
|
|
16960
17029
|
bucketOnboardingCount,
|
|
16961
17030
|
onboardingTelemetryEventSchema,
|
|
17031
|
+
normalizeOnboardingEventForCollection,
|
|
16962
17032
|
telemetryEventAcceptedDtoSchema,
|
|
16963
17033
|
isGhostTelemetryEvent,
|
|
16964
17034
|
MIN_TREND_POINTS,
|
|
@@ -92,7 +92,7 @@ import {
|
|
|
92
92
|
trafficConnectWordpressRequestSchema,
|
|
93
93
|
trafficEventKindSchema,
|
|
94
94
|
trafficSeriesGranularitySchema
|
|
95
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-OYDIND7Q.js";
|
|
96
96
|
|
|
97
97
|
// src/config.ts
|
|
98
98
|
import fs from "fs";
|
|
@@ -10528,8 +10528,8 @@ var canonryMcpTools = [
|
|
|
10528
10528
|
}),
|
|
10529
10529
|
defineTool({
|
|
10530
10530
|
name: "canonry_measurement_plan_publish",
|
|
10531
|
-
title: "Publish measurement plan",
|
|
10532
|
-
description: "Publish only when the active revision still matches the revision you reviewed. Use null only when you reviewed a planless project.",
|
|
10531
|
+
title: "Publish measurement plan (legacy v1)",
|
|
10532
|
+
description: "Legacy schema-v1 publish. For Advanced Measurement use canonry_measurement_draft_action instead: this tool compiles only schema v1 and refuses when the active revision is schema v2 rather than downgrade it. Publish only when the active revision still matches the revision you reviewed. Use null only when you reviewed a planless project.",
|
|
10533
10533
|
access: "write",
|
|
10534
10534
|
tier: "setup",
|
|
10535
10535
|
inputSchema: measurementPlanPublishInputSchema,
|