@ainyc/canonry 5.0.0 → 5.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +1 -1
  2. package/assets/agent-workspace/skills/canonry/SKILL.md +24 -5
  3. package/assets/assets/{AuditHistoryPanel-vWUtVXOI.js → AuditHistoryPanel-9F_74jKS.js} +1 -1
  4. package/assets/assets/{BacklinksPage-CSzuMEuv.js → BacklinksPage-DmF2rbwy.js} +1 -1
  5. package/assets/assets/{HistoryPage-DtCTQ7CQ.js → HistoryPage-DKopwAjo.js} +1 -1
  6. package/assets/assets/{MeasurementPropertyPage-B7gTMWNs.js → MeasurementPropertyPage-Dpd_DYQv.js} +1 -1
  7. package/assets/assets/{ProjectPage-Bsm0_jUY.js → ProjectPage-b1-57SzP.js} +1 -1
  8. package/assets/assets/{RunRow-D5k4Rdo4.js → RunRow-BjrSRp6X.js} +1 -1
  9. package/assets/assets/{RunsPage-DIXBB2Om.js → RunsPage-BFrymZ7D.js} +1 -1
  10. package/assets/assets/{SettingsPage-wYhBm6Az.js → SettingsPage-DdVa6pLK.js} +1 -1
  11. package/assets/assets/{SiteHealthSection-YebzsgDh.js → SiteHealthSection-CizmiBjA.js} +3 -3
  12. package/assets/assets/{TrafficPage-DtnSRFO8.js → TrafficPage-N7sZgb9x.js} +1 -1
  13. package/assets/assets/{TrafficSourceDetailPage-DYQ6mpYq.js → TrafficSourceDetailPage-BNQmMJ5D.js} +1 -1
  14. package/assets/assets/{extract-error-message-BdChrkmo.js → extract-error-message-Bh9mRIz9.js} +1 -1
  15. package/assets/assets/{index-C_E2NRYY.js → index-BnR1Tu9j.js} +3 -3
  16. package/assets/assets/{react-sigma_core.esm.min-sKWUk_tG.js → react-sigma_core.esm.min-DGaYwjLZ.js} +1 -1
  17. package/assets/assets/{v2-overview-adapter-CkPt-sRK.js → v2-overview-adapter-eqN-8OK5.js} +1 -1
  18. package/assets/index.html +1 -1
  19. package/dist/{chunk-3WODA4JF.js → chunk-3DL3KV6C.js} +2 -0
  20. package/dist/{chunk-LJKAB4CX.js → chunk-66KGNC4U.js} +2 -2
  21. package/dist/{chunk-UMU3IBXM.js → chunk-LHHJVGH7.js} +39 -26
  22. package/dist/{chunk-B2XM6AQ6.js → chunk-MEQGGODM.js} +21 -9
  23. package/dist/{chunk-67NX4KVE.js → chunk-ZWVXUPLP.js} +128 -8
  24. package/dist/cli.js +6 -5
  25. package/dist/index.js +4 -4
  26. package/dist/{intelligence-service-MGQRJW4V.js → intelligence-service-7XX5ZRYE.js} +2 -2
  27. package/dist/mcp.js +6 -7
  28. package/package.json +26 -11
@@ -1,4 +1,4 @@
1
- import{g as Bt,t as S,r as C}from"./vendor-tanstack-ZXXkndVj.js";import{e as Ht,G as Wt}from"./SiteHealthSection-YebzsgDh.js";import"./index-C_E2NRYY.js";import"./vendor-radix-twS1M3FF.js";import"./vendor-recharts-D-cUuLZo.js";import"./vendor-lucide-CW2qe0uH.js";import"./vendor-markdown-B-Oq2NYq.js";import"./vendor-yaml-GWJz5H2G.js";import"./ChartPrimitives-Bxa_vFmq.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.
1
+ import{g as Bt,t as S,r as C}from"./vendor-tanstack-ZXXkndVj.js";import{e as Ht,G as Wt}from"./SiteHealthSection-CizmiBjA.js";import"./index-BnR1Tu9j.js";import"./vendor-radix-twS1M3FF.js";import"./vendor-recharts-D-cUuLZo.js";import"./vendor-lucide-CW2qe0uH.js";import"./vendor-markdown-B-Oq2NYq.js";import"./vendor-yaml-GWJz5H2G.js";import"./ChartPrimitives-Bxa_vFmq.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
@@ -1 +1 @@
1
- import{j as o,r as C}from"./vendor-tanstack-ZXXkndVj.js";import{y as M,B as A}from"./index-C_E2NRYY.js";import{M as $}from"./vendor-markdown-B-Oq2NYq.js";const H="Sources",y={copy:"Copy answer",copying:"Copying…",copied:"Copied",failed:"Could not copy. Select and copy the answer above."};function N({headingLevel:e}){return n=>{const r=[],s=i=>{i.type==="heading"&&i.depth&&r.push(i),i.children?.forEach(s)};s(n);const l=r.reduce((i,u)=>Math.min(i,u.depth),6);for(const i of r){const u=i.depth-l;i.depth=Math.min(6,e+u),i.data={...i.data,hProperties:{...i.data?.hProperties,"data-answer-heading-level":u+1}}}}}function R({answer:e}){const[n,r]=C.useState("idle"),s=C.useRef(!1),l=async()=>{if(!s.current){s.current=!0,r("copying");try{await navigator.clipboard.writeText(e),r("copied")}catch{r("failed")}finally{s.current=!1}}};return o.jsxs("div",{className:"mt-3 flex flex-wrap items-center gap-x-3",children:[o.jsx(A,{type:"button",variant:"ghost",className:"min-h-11 px-2","aria-disabled":n==="copying","aria-busy":n==="copying",onClick:()=>{l()},children:n==="copying"?y.copying:y.copy}),o.jsx("span",{role:"status",className:"text-sm text-secondary",children:n==="copied"?y.copied:n==="failed"?y.failed:""})]})}function L({children:e,headingLevel:n=4,copyable:r=!1}){return o.jsxs("div",{className:"answer-markdown",children:[o.jsx($,{remarkPlugins:[[N,{headingLevel:n}]],components:{p:({children:s})=>o.jsx("p",{className:"mb-3 whitespace-pre-wrap last:mb-0",children:s}),ul:({children:s})=>o.jsx("ul",{className:"mb-3 ml-5 list-disc space-y-1",children:s}),ol:({children:s,start:l})=>o.jsx("ol",{start:l,className:"mb-3 ml-5 list-decimal space-y-1",children:s}),pre:({children:s})=>o.jsx("pre",{className:"mb-3 overflow-x-auto whitespace-pre-wrap",children:s}),a:({children:s,href:l})=>{const i=M(l);return i?o.jsx("a",{href:i,target:"_blank",rel:"noopener noreferrer",className:"text-link underline",children:s}):o.jsx("span",{children:s})},img:({alt:s})=>o.jsx("span",{children:s})},children:e}),r&&e.trim()?o.jsx(R,{answer:e},e):null]})}function F(e){const n=e[0];return n?e.every(r=>r.mode===n.mode&&r.scope.kind===n.scope.kind&&r.scope.key===n.scope.key&&r.queryClass===n.queryClass&&r.measurement.displayedRunId===n.measurement.displayedRunId):!0}function p(e){return e.state==="unavailable"?{numerator:null,denominator:null,reason:e.reason}:e.numerator===void 0||e.denominator===void 0||e.denominator<=0?{numerator:null,denominator:null,reason:"not_applicable"}:{numerator:e.numerator,denominator:e.denominator}}function I(e){return e.kind==="exact"?e.url:e.kind==="prefix"?`https://${e.host}${e.pathPrefix}/*`:`https://${e.host}/*`}function T(e){return e==="assigned"?"this-property":e==="sibling"?"another-property":e==="ownedUnmapped"?"owned-unassigned":e==="external"?"external":e==="ambiguous"?"multiple-properties":"invalid-url"}function _(e){return e==="assigned"?"positive":e==="external"?"neutral":e==="invalid"?"negative":"caution"}function k(e){return`target:${e.targetKey}:${e.queryId}:${e.executionNodeKey}`}function j(e,n,r){const s=new Map;if(!n)return s;const l=new Map(e.usageEdges.map(a=>[k(a),a.targetKey])),i=new Map(e.assignments.map(a=>[`${a.targetKey}:${a.queryId}`,a.queryClass])),u=new Map(e.usageEdges.map(a=>[k(a),i.get(`${a.targetKey}:${a.queryId}`)]));for(const a of n.evidence){if(r&&u.get(a.usageEdgeId)!==r)continue;const c=l.get(a.usageEdgeId);if(!c)continue;const m=s.get(c)??[];m.push({id:`${a.observationId}:${a.expectedSlotId}:${a.usageEdgeId}:${a.sourceUrl}`,kind:T(a.classification),query:a.queryText,provider:a.provider,location:a.location,url:a.sourceUrl,tone:_(a.classification),historical:a.historical||a.bridged}),s.set(c,m)}return s}function B(e){if(e.flags>0)return{label:"Review",tone:"caution"};const n=[e.mentionCoverage,e.citationCoverage].filter(r=>r.state==="unavailable").map(r=>r.reason);return n.includes("no_completed_run")?{label:"Not measured",tone:"neutral"}:n.includes("plan_v1")?{label:"Update setup",tone:"caution"}:n.includes("evidence_incomplete")?{label:"Evidence incomplete",tone:"caution"}:n.includes("no_population")?{label:"No queries",tone:"neutral"}:n.includes("not_applicable")?{label:"Not applicable",tone:"neutral"}:{label:"Complete",tone:"positive"}}function O(e){return e==="complete"?{label:"Complete",tone:"positive"}:e==="partial"?{label:"Partial",tone:"caution"}:e==="queued"?{label:"Queued",tone:"neutral"}:e==="running"?{label:"Running",tone:"neutral"}:e==="failed"?{label:"Failed",tone:"negative"}:{label:"Not measured",tone:"neutral"}}function U(e){if(!e)return"";const n=new Date(e);return Number.isFinite(n.getTime())?n.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric",timeZone:"UTC"}):""}function q(e){const n=e.nextAction.count??e.flags.total;if(e.nextAction.kind==="review_flags")return`${n} flagged ${n===1?"result needs":"results need"} review.`;if(e.nextAction.kind==="complete_setup")return"Finish setup.";if(e.nextAction.kind==="republish_setup")return"Setup update required.";if(e.nextAction.kind==="run_measurement")return"Ready to measure."}function W({overview:e,activePlan:n,report:r,reportState:s=r?"ready":"loading",sort:l}){if(n.plan.schemaVersion!==2||e.mode!=="active-v2")throw new Error("The current overview requires an active version-two setup.");if(e.scope.kind!=="all"&&e.scope.kind!=="group")throw new Error("The current overview requires an All Properties or group scope.");const i=n.plan,u=new Map(i.targets.map(t=>[t.stableKey,t])),a=new Map(i.querySnapshots.map(t=>[t.queryId,t.queryText])),c=new Map;for(const t of i.assignments){if(e.queryClass!=="all"&&t.queryClass!==e.queryClass)continue;const d=a.get(t.queryId);if(!d)continue;const g=c.get(t.targetKey)??new Set;g.add(d),c.set(t.targetKey,g)}const m=r?.revision===n.revision&&r.run?.id===e.measurement.displayedRunId?r:null,K=e.measurement.displayedRunId===void 0||m?"ready":s==="error"||r!=null?"error":"loading",S=j(i,m,e.queryClass==="all"?void 0:e.queryClass),h=new Map;for(const t of i.groups)for(const d of t.targetKeys)h.has(d)||h.set(d,t.label);const x=e.properties.items.map(t=>{const d=u.get(t.targetKey),g=S.get(t.targetKey)??[];return{id:t.targetKey,name:t.label,mentionCoverage:p(t.mentionCoverage),citationCoverage:p(t.citationCoverage),status:B(t),providers:t.providers?.map(f=>({provider:f.provider,mentionCoverage:p(f.mentionCoverage),citationCoverage:p(f.citationCoverage)})),market:h.get(t.targetKey),assignedQueries:[...c.get(t.targetKey)??[]],urls:d?.urlMatchers.map(I)??[],evidence:g,evidenceState:K,historical:g.some(f=>f.historical)}}),b=e.namedShareOfVoice?.entries.map(t=>({name:t.label,coverage:{numerator:t.credits,denominator:e.namedShareOfVoice.denominator}})),E=e.properties.items.flatMap(t=>t.flags>0?[{id:`property:${t.targetKey}`,property:t.label,summary:`${t.flags} ${t.flags===1?"result needs":"results need"} review.`,tone:"caution",count:t.flags}]:[]);return{classReporting:"available",latestMeasurement:{status:O(e.measurement.state),completedSlots:e.measurement.completed,totalSlots:e.measurement.expected,date:U(e.measurement.completedAt),includesBridgedHistory:e.measurement.includesHistoricalData===!0},currentView:{scope:e.scope.kind==="group"?{kind:"group",...e.scope.key?{key:e.scope.key}:{}}:{kind:"all"},queryClass:e.queryClass,aggregate:{metrics:{propertiesMentioned:p(e.metrics.propertiesMentioned),mentionCoverage:p(e.metrics.mentionCoverage),citationCoverage:p(e.metrics.citationCoverage)},properties:x,...b?{shareOfVoice:b}:{}},propertyTotal:e.properties.totalEstimate??x.length,outcomes:e.outcomes,nextCursor:e.properties.nextCursor,...l?{sort:l}:{}},availableGroups:i.groups.map(t=>({id:t.stableKey,label:t.label})),...q(e)?{nextActionText:q(e)}:{},flaggedResultsTotal:e.flags.total,flaggedResults:E}}export{L as A,H as a,F as b,W as c,I as m};
1
+ import{j as o,r as C}from"./vendor-tanstack-ZXXkndVj.js";import{y as M,B as A}from"./index-BnR1Tu9j.js";import{M as $}from"./vendor-markdown-B-Oq2NYq.js";const H="Sources",y={copy:"Copy answer",copying:"Copying…",copied:"Copied",failed:"Could not copy. Select and copy the answer above."};function N({headingLevel:e}){return n=>{const r=[],s=i=>{i.type==="heading"&&i.depth&&r.push(i),i.children?.forEach(s)};s(n);const l=r.reduce((i,u)=>Math.min(i,u.depth),6);for(const i of r){const u=i.depth-l;i.depth=Math.min(6,e+u),i.data={...i.data,hProperties:{...i.data?.hProperties,"data-answer-heading-level":u+1}}}}}function R({answer:e}){const[n,r]=C.useState("idle"),s=C.useRef(!1),l=async()=>{if(!s.current){s.current=!0,r("copying");try{await navigator.clipboard.writeText(e),r("copied")}catch{r("failed")}finally{s.current=!1}}};return o.jsxs("div",{className:"mt-3 flex flex-wrap items-center gap-x-3",children:[o.jsx(A,{type:"button",variant:"ghost",className:"min-h-11 px-2","aria-disabled":n==="copying","aria-busy":n==="copying",onClick:()=>{l()},children:n==="copying"?y.copying:y.copy}),o.jsx("span",{role:"status",className:"text-sm text-secondary",children:n==="copied"?y.copied:n==="failed"?y.failed:""})]})}function L({children:e,headingLevel:n=4,copyable:r=!1}){return o.jsxs("div",{className:"answer-markdown",children:[o.jsx($,{remarkPlugins:[[N,{headingLevel:n}]],components:{p:({children:s})=>o.jsx("p",{className:"mb-3 whitespace-pre-wrap last:mb-0",children:s}),ul:({children:s})=>o.jsx("ul",{className:"mb-3 ml-5 list-disc space-y-1",children:s}),ol:({children:s,start:l})=>o.jsx("ol",{start:l,className:"mb-3 ml-5 list-decimal space-y-1",children:s}),pre:({children:s})=>o.jsx("pre",{className:"mb-3 overflow-x-auto whitespace-pre-wrap",children:s}),a:({children:s,href:l})=>{const i=M(l);return i?o.jsx("a",{href:i,target:"_blank",rel:"noopener noreferrer",className:"text-link underline",children:s}):o.jsx("span",{children:s})},img:({alt:s})=>o.jsx("span",{children:s})},children:e}),r&&e.trim()?o.jsx(R,{answer:e},e):null]})}function F(e){const n=e[0];return n?e.every(r=>r.mode===n.mode&&r.scope.kind===n.scope.kind&&r.scope.key===n.scope.key&&r.queryClass===n.queryClass&&r.measurement.displayedRunId===n.measurement.displayedRunId):!0}function p(e){return e.state==="unavailable"?{numerator:null,denominator:null,reason:e.reason}:e.numerator===void 0||e.denominator===void 0||e.denominator<=0?{numerator:null,denominator:null,reason:"not_applicable"}:{numerator:e.numerator,denominator:e.denominator}}function I(e){return e.kind==="exact"?e.url:e.kind==="prefix"?`https://${e.host}${e.pathPrefix}/*`:`https://${e.host}/*`}function T(e){return e==="assigned"?"this-property":e==="sibling"?"another-property":e==="ownedUnmapped"?"owned-unassigned":e==="external"?"external":e==="ambiguous"?"multiple-properties":"invalid-url"}function _(e){return e==="assigned"?"positive":e==="external"?"neutral":e==="invalid"?"negative":"caution"}function k(e){return`target:${e.targetKey}:${e.queryId}:${e.executionNodeKey}`}function j(e,n,r){const s=new Map;if(!n)return s;const l=new Map(e.usageEdges.map(a=>[k(a),a.targetKey])),i=new Map(e.assignments.map(a=>[`${a.targetKey}:${a.queryId}`,a.queryClass])),u=new Map(e.usageEdges.map(a=>[k(a),i.get(`${a.targetKey}:${a.queryId}`)]));for(const a of n.evidence){if(r&&u.get(a.usageEdgeId)!==r)continue;const c=l.get(a.usageEdgeId);if(!c)continue;const m=s.get(c)??[];m.push({id:`${a.observationId}:${a.expectedSlotId}:${a.usageEdgeId}:${a.sourceUrl}`,kind:T(a.classification),query:a.queryText,provider:a.provider,location:a.location,url:a.sourceUrl,tone:_(a.classification),historical:a.historical||a.bridged}),s.set(c,m)}return s}function B(e){if(e.flags>0)return{label:"Review",tone:"caution"};const n=[e.mentionCoverage,e.citationCoverage].filter(r=>r.state==="unavailable").map(r=>r.reason);return n.includes("no_completed_run")?{label:"Not measured",tone:"neutral"}:n.includes("plan_v1")?{label:"Update setup",tone:"caution"}:n.includes("evidence_incomplete")?{label:"Evidence incomplete",tone:"caution"}:n.includes("no_population")?{label:"No queries",tone:"neutral"}:n.includes("not_applicable")?{label:"Not applicable",tone:"neutral"}:{label:"Complete",tone:"positive"}}function O(e){return e==="complete"?{label:"Complete",tone:"positive"}:e==="partial"?{label:"Partial",tone:"caution"}:e==="queued"?{label:"Queued",tone:"neutral"}:e==="running"?{label:"Running",tone:"neutral"}:e==="failed"?{label:"Failed",tone:"negative"}:{label:"Not measured",tone:"neutral"}}function U(e){if(!e)return"";const n=new Date(e);return Number.isFinite(n.getTime())?n.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric",timeZone:"UTC"}):""}function q(e){const n=e.nextAction.count??e.flags.total;if(e.nextAction.kind==="review_flags")return`${n} flagged ${n===1?"result needs":"results need"} review.`;if(e.nextAction.kind==="complete_setup")return"Finish setup.";if(e.nextAction.kind==="republish_setup")return"Setup update required.";if(e.nextAction.kind==="run_measurement")return"Ready to measure."}function W({overview:e,activePlan:n,report:r,reportState:s=r?"ready":"loading",sort:l}){if(n.plan.schemaVersion!==2||e.mode!=="active-v2")throw new Error("The current overview requires an active version-two setup.");if(e.scope.kind!=="all"&&e.scope.kind!=="group")throw new Error("The current overview requires an All Properties or group scope.");const i=n.plan,u=new Map(i.targets.map(t=>[t.stableKey,t])),a=new Map(i.querySnapshots.map(t=>[t.queryId,t.queryText])),c=new Map;for(const t of i.assignments){if(e.queryClass!=="all"&&t.queryClass!==e.queryClass)continue;const d=a.get(t.queryId);if(!d)continue;const g=c.get(t.targetKey)??new Set;g.add(d),c.set(t.targetKey,g)}const m=r?.revision===n.revision&&r.run?.id===e.measurement.displayedRunId?r:null,K=e.measurement.displayedRunId===void 0||m?"ready":s==="error"||r!=null?"error":"loading",S=j(i,m,e.queryClass==="all"?void 0:e.queryClass),h=new Map;for(const t of i.groups)for(const d of t.targetKeys)h.has(d)||h.set(d,t.label);const x=e.properties.items.map(t=>{const d=u.get(t.targetKey),g=S.get(t.targetKey)??[];return{id:t.targetKey,name:t.label,mentionCoverage:p(t.mentionCoverage),citationCoverage:p(t.citationCoverage),status:B(t),providers:t.providers?.map(f=>({provider:f.provider,mentionCoverage:p(f.mentionCoverage),citationCoverage:p(f.citationCoverage)})),market:h.get(t.targetKey),assignedQueries:[...c.get(t.targetKey)??[]],urls:d?.urlMatchers.map(I)??[],evidence:g,evidenceState:K,historical:g.some(f=>f.historical)}}),b=e.namedShareOfVoice?.entries.map(t=>({name:t.label,coverage:{numerator:t.credits,denominator:e.namedShareOfVoice.denominator}})),E=e.properties.items.flatMap(t=>t.flags>0?[{id:`property:${t.targetKey}`,property:t.label,summary:`${t.flags} ${t.flags===1?"result needs":"results need"} review.`,tone:"caution",count:t.flags}]:[]);return{classReporting:"available",latestMeasurement:{status:O(e.measurement.state),completedSlots:e.measurement.completed,totalSlots:e.measurement.expected,date:U(e.measurement.completedAt),includesBridgedHistory:e.measurement.includesHistoricalData===!0},currentView:{scope:e.scope.kind==="group"?{kind:"group",...e.scope.key?{key:e.scope.key}:{}}:{kind:"all"},queryClass:e.queryClass,aggregate:{metrics:{propertiesMentioned:p(e.metrics.propertiesMentioned),mentionCoverage:p(e.metrics.mentionCoverage),citationCoverage:p(e.metrics.citationCoverage)},properties:x,...b?{shareOfVoice:b}:{}},propertyTotal:e.properties.totalEstimate??x.length,outcomes:e.outcomes,nextCursor:e.properties.nextCursor,...l?{sort:l}:{}},availableGroups:i.groups.map(t=>({id:t.stableKey,label:t.label})),...q(e)?{nextActionText:q(e)}:{},flaggedResultsTotal:e.flags.total,flaggedResults:E}}export{L as A,H as a,F as b,W as c,I as m};
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-C_E2NRYY.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-BnR1Tu9j.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-ZXXkndVj.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/vendor-radix-twS1M3FF.js">
18
18
  <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-D-cUuLZo.js">
@@ -5716,6 +5716,8 @@ var apiKeyDtoSchema = z10.object({
5716
5716
  * — surfaces don't recompute it (see the UI/CLI parity rule). Additive field.
5717
5717
  */
5718
5718
  readOnly: z10.boolean(),
5719
+ /** Present on /keys/self: host-derived direct-bearer authority, never a mintable scope. */
5720
+ operator: z10.boolean().optional(),
5719
5721
  createdAt: z10.string(),
5720
5722
  lastUsedAt: z10.string().nullable(),
5721
5723
  revokedAt: z10.string().nullable()
@@ -5,11 +5,11 @@ import {
5
5
  installSkills,
6
6
  loadConfigRaw,
7
7
  saveConfigPatch
8
- } from "./chunk-B2XM6AQ6.js";
8
+ } from "./chunk-MEQGGODM.js";
9
9
  import {
10
10
  SKILL_MANIFEST_FILENAME,
11
11
  SkillsClients
12
- } from "./chunk-3WODA4JF.js";
12
+ } from "./chunk-3DL3KV6C.js";
13
13
 
14
14
  // src/skills-autosync.ts
15
15
  import fs from "fs";
@@ -702,10 +702,10 @@ import {
702
702
  wordpressSchemaDeployResultDtoSchema,
703
703
  wordpressSchemaStatusResultDtoSchema,
704
704
  wordpressStatusDtoSchema
705
- } from "./chunk-3WODA4JF.js";
705
+ } from "./chunk-3DL3KV6C.js";
706
706
 
707
707
  // src/intelligence-service.ts
708
- import { eq as eq67, desc as desc29, asc as asc12, and as and57, ne as ne10, or as or16, inArray as inArray25, gte as gte18, lte as lte15, isNull as isNull12, sql as sql27, exists } from "drizzle-orm";
708
+ import { eq as eq67, desc as desc29, asc as asc12, and as and57, ne as ne11, or as or16, inArray as inArray25, gte as gte18, lte as lte15, isNull as isNull12, sql as sql27, exists } from "drizzle-orm";
709
709
 
710
710
  // ../db/src/client.ts
711
711
  import { mkdirSync } from "fs";
@@ -11226,6 +11226,11 @@ function requireAdminSession(request) {
11226
11226
  if (!role || role === UserRoles.admin) return;
11227
11227
  throw forbidden(ADMIN_ONLY_MESSAGE);
11228
11228
  }
11229
+ function requireOperator(request) {
11230
+ const principal = request.principal;
11231
+ if (request.operatorAccess === true && principal?.kind === "api-key" && !principal.viaCookie && !principal.projectId && !principal.delegatedUser) return;
11232
+ throw forbidden("Internal diagnostics require a host-approved operator API key. Customer accounts and scopes cannot grant operator access.");
11233
+ }
11229
11234
  function requirePaidReadScope(request) {
11230
11235
  const principal = request.principal;
11231
11236
  if (!principal || principal.kind !== "api-key") return;
@@ -11500,6 +11505,7 @@ async function authPlugin(app, opts = {}) {
11500
11505
  viaCookie: keyArrivedInCookie,
11501
11506
  ...delegatedUser ? { delegatedUser: { id: delegatedUser.id, name: delegatedUser.name, role: delegatedUser.role } } : {}
11502
11507
  };
11508
+ request.operatorAccess = !keyArrivedInCookie && !key.projectId && !key.delegatedUserId && opts.operatorApiKeyIds?.includes(key.id) === true;
11503
11509
  assertSameOriginWrite(request);
11504
11510
  applyRoleGates(request);
11505
11511
  applyScopedWriteGates(request);
@@ -26092,7 +26098,7 @@ function normalizeCompetitorList2(domains) {
26092
26098
  }
26093
26099
 
26094
26100
  // ../api-routes/src/history.ts
26095
- import { and as and22, asc as asc3, count, desc as desc9, eq as eq27, gte as gte5, inArray as inArray11, like, or as or4 } from "drizzle-orm";
26101
+ import { and as and22, asc as asc3, count, desc as desc9, eq as eq27, gte as gte5, inArray as inArray11, like, ne as ne5, or as or4 } from "drizzle-orm";
26096
26102
 
26097
26103
  // ../api-routes/src/notification-redaction.ts
26098
26104
  var REDACTED_URL = {
@@ -26171,6 +26177,7 @@ async function historyRoutes(app) {
26171
26177
  app.get("/projects/:name/history", async (request, reply) => {
26172
26178
  const project = resolveProject(app.db, request.params.name);
26173
26179
  const filters = [eq27(auditLog.projectId, project.id)];
26180
+ if (request.operatorAccess !== true) filters.push(ne5(auditLog.entityType, "telemetry"));
26174
26181
  addAuditHistoryFilters(filters, request.query);
26175
26182
  const rows = app.db.select().from(auditLog).where(and22(...filters)).orderBy(desc9(auditLog.createdAt), desc9(auditLog.id)).limit(parseBoundedInt(request.query.limit, 100, 500)).offset(parseBoundedInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER)).all();
26176
26183
  return reply.send(rows.map(formatAuditEntry));
@@ -26178,6 +26185,7 @@ async function historyRoutes(app) {
26178
26185
  app.get("/history", async (request, reply) => {
26179
26186
  const scopedProjectId = request.apiKey?.projectId;
26180
26187
  const filters = scopedProjectId ? [eq27(auditLog.projectId, scopedProjectId)] : [];
26188
+ if (request.operatorAccess !== true) filters.push(ne5(auditLog.entityType, "telemetry"));
26181
26189
  addAuditHistoryFilters(filters, request.query);
26182
26190
  const rows = app.db.select().from(auditLog).where(filters.length > 0 ? and22(...filters) : void 0).orderBy(desc9(auditLog.createdAt), desc9(auditLog.id)).limit(parseBoundedInt(request.query.limit, 100, 500)).offset(parseBoundedInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER)).all();
26183
26191
  return reply.send(rows.map(formatAuditEntry));
@@ -27525,7 +27533,7 @@ function nonSubresourceReferralPathCondition() {
27525
27533
  }
27526
27534
 
27527
27535
  // ../api-routes/src/report.ts
27528
- import { and as and29, desc as desc14, eq as eq33, gte as gte8, inArray as inArray15, lt as lt5, lte as lte6, ne as ne5, or as or5, sql as sql11 } from "drizzle-orm";
27536
+ import { and as and29, desc as desc14, eq as eq33, gte as gte8, inArray as inArray15, lt as lt5, lte as lte6, ne as ne6, or as or5, sql as sql11 } from "drizzle-orm";
27529
27537
 
27530
27538
  // ../api-routes/src/content.ts
27531
27539
  import crypto23 from "crypto";
@@ -31308,7 +31316,7 @@ function buildServerActivity(db, projectId, windowDays2) {
31308
31316
  const sourceRows = db.select({ id: trafficSources.id }).from(trafficSources).where(
31309
31317
  and29(
31310
31318
  eq33(trafficSources.projectId, projectId),
31311
- ne5(trafficSources.status, TrafficSourceStatuses.archived)
31319
+ ne6(trafficSources.status, TrafficSourceStatuses.archived)
31312
31320
  )
31313
31321
  ).all();
31314
31322
  if (sourceRows.length === 0) return null;
@@ -31334,7 +31342,7 @@ function buildServerActivity(db, projectId, windowDays2) {
31334
31342
  db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
31335
31343
  and29(
31336
31344
  eq33(crawlerEventsHourly.projectId, projectId),
31337
- ne5(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
31345
+ ne6(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
31338
31346
  gte8(crawlerEventsHourly.tsHour, windowStartIso),
31339
31347
  exclusiveEnd ? lt5(crawlerEventsHourly.tsHour, windowEndIso) : lte6(crawlerEventsHourly.tsHour, windowEndIso)
31340
31348
  )
@@ -38346,7 +38354,7 @@ var routeCatalog = [
38346
38354
  method: "get",
38347
38355
  path: "/api/v1/operations/logs",
38348
38356
  summary: "Read bounded, redacted runtime logs",
38349
- description: "Requires logs.read (or wildcard), an admin role for user sessions, and an instance-wide credential. Includes application and HTTP diagnostics, separate from audit history. File-backed hosts retain logs across restarts; inspect retention, retentionPolicy, dropped, and captureErrors. Filters apply before pagination and must remain unchanged when resuming a cursor. Cursors expire on retention eviction.",
38357
+ description: "Requires a direct bearer API key approved in the host CANONRY_OPERATOR_KEY_IDS allowlist, plus logs.read (or wildcard). Customer admin roles, scopes alone, browser sessions, OAuth/delegated keys, and project-scoped keys cannot grant access. Includes application and HTTP diagnostics, separate from audit history. File-backed hosts retain logs across restarts; inspect retention, retentionPolicy, dropped, and captureErrors. Filters apply before pagination and must remain unchanged when resuming a cursor. Cursors expire on retention eviction.",
38350
38358
  tags: ["operations"],
38351
38359
  parameters: [
38352
38360
  { name: "limit", in: "query", required: false, description: "Maximum entries (default 100).", schema: { type: "integer", minimum: 1, maximum: 200, default: 100 } },
@@ -38359,7 +38367,7 @@ var routeCatalog = [
38359
38367
  200: jsonResponse("Runtime log page with retention and capture-loss metadata.", "OperationalLogListDto"),
38360
38368
  400: errorResponse("Invalid filters or expired cursor."),
38361
38369
  401: errorResponse("Authentication required."),
38362
- 403: errorResponse("Instance logs.read permission required."),
38370
+ 403: errorResponse("Host-approved operator authority and instance logs.read permission required."),
38363
38371
  501: errorResponse("This deployment does not provide operational logs.")
38364
38372
  }
38365
38373
  },
@@ -38367,9 +38375,11 @@ var routeCatalog = [
38367
38375
  method: "get",
38368
38376
  path: "/api/v1/telemetry",
38369
38377
  summary: "Get telemetry status",
38378
+ description: "Internal surface. Requires a direct bearer API key approved in the host CANONRY_OPERATOR_KEY_IDS allowlist; customer admin roles and wildcard scopes alone are insufficient.",
38370
38379
  tags: ["telemetry"],
38371
38380
  responses: {
38372
38381
  200: jsonResponse("Telemetry status returned.", "TelemetryStatusDto"),
38382
+ 403: errorResponse("Host-approved operator authority required."),
38373
38383
  501: errorResponse("Telemetry status is not available.")
38374
38384
  }
38375
38385
  },
@@ -38377,7 +38387,7 @@ var routeCatalog = [
38377
38387
  method: "put",
38378
38388
  path: "/api/v1/telemetry",
38379
38389
  summary: "Update telemetry status",
38380
- description: "Requires settings.write. The response reports the configured preference and effective state; deployment/environment opt-outs take precedence.",
38390
+ description: "Requires host-approved direct-bearer operator authority and settings.write. The response reports the configured preference and effective state; deployment/environment opt-outs take precedence.",
38381
38391
  tags: ["telemetry"],
38382
38392
  requestBody: {
38383
38393
  required: true,
@@ -38396,7 +38406,7 @@ var routeCatalog = [
38396
38406
  responses: {
38397
38407
  200: jsonResponse("Telemetry updated.", "TelemetryStatusDto"),
38398
38408
  400: errorResponse("Invalid telemetry request."),
38399
- 403: errorResponse("The credential lacks settings.write."),
38409
+ 403: errorResponse("The credential lacks host-approved operator authority or settings.write."),
38400
38410
  501: errorResponse("Telemetry configuration is not available.")
38401
38411
  }
38402
38412
  },
@@ -42777,7 +42787,7 @@ async function keysRoutes(app) {
42777
42787
  if (!row) {
42778
42788
  throw notFound("API key", id);
42779
42789
  }
42780
- return toApiKeyDto({ ...row, scopes: request.apiKey?.scopes ?? row.scopes }, projectNameById(app.db));
42790
+ return { ...toApiKeyDto({ ...row, scopes: request.apiKey?.scopes ?? row.scopes }, projectNameById(app.db)), operator: request.operatorAccess === true };
42781
42791
  });
42782
42792
  app.post("/keys", async (request) => {
42783
42793
  requireAdminSession(request);
@@ -43011,13 +43021,15 @@ async function snapshotRoutes(app, opts) {
43011
43021
 
43012
43022
  // ../api-routes/src/telemetry.ts
43013
43023
  async function telemetryRoutes(app, opts) {
43014
- app.get("/telemetry", async () => {
43024
+ app.get("/telemetry", async (request) => {
43025
+ requireOperator(request);
43015
43026
  if (!opts.getTelemetryStatus) {
43016
43027
  throw notImplemented("Telemetry status is not available in this deployment");
43017
43028
  }
43018
43029
  return normalizeTelemetryStatus(opts.getTelemetryStatus());
43019
43030
  });
43020
43031
  app.put("/telemetry", async (request) => {
43032
+ requireOperator(request);
43021
43033
  requireScope(request, SETTINGS_WRITE_SCOPE);
43022
43034
  if (!opts.setTelemetryEnabled) {
43023
43035
  throw notImplemented("Telemetry configuration is not available in this deployment");
@@ -43065,7 +43077,7 @@ async function telemetryRoutes(app, opts) {
43065
43077
  // ../api-routes/src/operational-logs.ts
43066
43078
  async function operationalLogsRoutes(app, opts = {}) {
43067
43079
  app.get("/operations/logs", async (request) => {
43068
- requireAdminSession(request);
43080
+ requireOperator(request);
43069
43081
  requireScope(request, "logs.read");
43070
43082
  if (request.apiKey?.projectId) {
43071
43083
  throw forbidden("This API key is scoped to one project and cannot read instance operational diagnostics.");
@@ -47198,7 +47210,7 @@ async function googleRoutes(app, opts) {
47198
47210
 
47199
47211
  // ../api-routes/src/google-marketing.ts
47200
47212
  import crypto33 from "crypto";
47201
- import { and as and38, desc as desc20, eq as eq45, isNull as isNull6, lt as lt6, ne as ne6, or as or9, sql as sql16 } from "drizzle-orm";
47213
+ import { and as and38, desc as desc20, eq as eq45, isNull as isNull6, lt as lt6, ne as ne7, or as or9, sql as sql16 } from "drizzle-orm";
47202
47214
  import { z as z4 } from "zod";
47203
47215
  var snapshotPageQuerySchema = z4.object({
47204
47216
  limit: z4.coerce.number().int().min(1).max(GOOGLE_MARKETING_STORED_SNAPSHOT_PAGE_MAX).optional(),
@@ -48699,7 +48711,7 @@ async function googleMarketingRoutes(app, opts) {
48699
48711
  const duplicate = app.db.select({ id: conversionTrackingContracts.id }).from(conversionTrackingContracts).where(and38(
48700
48712
  eq45(conversionTrackingContracts.projectId, project.id),
48701
48713
  eq45(conversionTrackingContracts.name, body.name),
48702
- ne6(conversionTrackingContracts.id, existing.id)
48714
+ ne7(conversionTrackingContracts.id, existing.id)
48703
48715
  )).get();
48704
48716
  if (duplicate) throw alreadyExists("Conversion tracking contract", body.name);
48705
48717
  const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -48788,7 +48800,7 @@ async function googleMarketingRoutes(app, opts) {
48788
48800
 
48789
48801
  // ../api-routes/src/ads.ts
48790
48802
  import crypto36 from "crypto";
48791
- import { eq as eq47, and as and40, asc as asc9, desc as desc21, gt as gt3, gte as gte12, lt as lt7, lte as lte11, ne as ne7, inArray as inArray20, or as or11, isNull as isNull8, isNotNull as isNotNull4, sql as sql18 } from "drizzle-orm";
48803
+ import { eq as eq47, and as and40, asc as asc9, desc as desc21, gt as gt3, gte as gte12, lt as lt7, lte as lte11, ne as ne8, inArray as inArray20, or as or11, isNull as isNull8, isNotNull as isNotNull4, sql as sql18 } from "drizzle-orm";
48792
48804
 
48793
48805
  // ../api-routes/src/ads-activation.ts
48794
48806
  import crypto34 from "crypto";
@@ -52475,7 +52487,7 @@ async function sweepAdsOperationsOnce(app, opts, config) {
52475
52487
  );
52476
52488
  const staleReconcileWhere = and40(
52477
52489
  lt7(adsOperations.reconcileAttempts, config.policy.maxAttempts),
52478
- ne7(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
52490
+ ne8(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
52479
52491
  or11(
52480
52492
  eq47(adsOperations.reconcileStrategy, AdsReconcileStrategies.known_entity),
52481
52493
  and40(
@@ -52492,7 +52504,7 @@ async function sweepAdsOperationsOnce(app, opts, config) {
52492
52504
  retryableStateWhere
52493
52505
  );
52494
52506
  const exhaustedRows = app.db.select().from(adsOperations).where(and40(
52495
- ne7(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
52507
+ ne8(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
52496
52508
  gte12(adsOperations.reconcileAttempts, config.policy.maxAttempts),
52497
52509
  or11(
52498
52510
  and40(
@@ -52507,7 +52519,7 @@ async function sweepAdsOperationsOnce(app, opts, config) {
52507
52519
  ),
52508
52520
  or11(
52509
52521
  isNull8(adsOperations.errorCode),
52510
- ne7(adsOperations.errorCode, ADS_RECONCILIATION_QUARANTINED)
52522
+ ne8(adsOperations.errorCode, ADS_RECONCILIATION_QUARANTINED)
52511
52523
  )
52512
52524
  )).orderBy(asc9(adsOperations.updatedAt)).limit(config.batchSize).all();
52513
52525
  for (const row of exhaustedRows) {
@@ -57985,7 +57997,7 @@ function pruneCachedRelease(release, opts = {}) {
57985
57997
  }
57986
57998
 
57987
57999
  // ../api-routes/src/backlinks-filter.ts
57988
- import { and as and44, ne as ne8, notLike } from "drizzle-orm";
58000
+ import { and as and44, ne as ne9, notLike } from "drizzle-orm";
57989
58001
  var BACKLINK_FILTER_PATTERNS = [
57990
58002
  "*.google.com",
57991
58003
  "*.googleusercontent.com",
@@ -58002,10 +58014,10 @@ function backlinkCrawlerExclusionClause() {
58002
58014
  for (const pattern of BACKLINK_FILTER_PATTERNS) {
58003
58015
  if (pattern.startsWith("*.")) {
58004
58016
  const suffix = pattern.slice(2);
58005
- conditions.push(ne8(backlinkDomains.linkingDomain, suffix));
58017
+ conditions.push(ne9(backlinkDomains.linkingDomain, suffix));
58006
58018
  conditions.push(notLike(backlinkDomains.linkingDomain, `%.${suffix}`));
58007
58019
  } else {
58008
- conditions.push(ne8(backlinkDomains.linkingDomain, pattern));
58020
+ conditions.push(ne9(backlinkDomains.linkingDomain, pattern));
58009
58021
  }
58010
58022
  }
58011
58023
  const combined = and44(...conditions);
@@ -80283,7 +80295,7 @@ var RUNTIME_STATE_CHECKS = [
80283
80295
  ];
80284
80296
 
80285
80297
  // ../api-routes/src/doctor/checks/traffic-source.ts
80286
- import { and as and52, eq as eq61, gte as gte15, inArray as inArray22, ne as ne9, sql as sql24 } from "drizzle-orm";
80298
+ import { and as and52, eq as eq61, gte as gte15, inArray as inArray22, ne as ne10, sql as sql24 } from "drizzle-orm";
80287
80299
  var RECENT_DATA_WARN_DAYS = 7;
80288
80300
  var RECENT_DATA_FAIL_DAYS = 30;
80289
80301
  function isCloudflareDirectPush(source) {
@@ -80324,7 +80336,7 @@ function loadProbes(ctx) {
80324
80336
  const rows = ctx.db.select().from(trafficSources).where(
80325
80337
  and52(
80326
80338
  eq61(trafficSources.projectId, ctx.project.id),
80327
- ne9(trafficSources.status, TrafficSourceStatuses.archived)
80339
+ ne10(trafficSources.status, TrafficSourceStatuses.archived)
80328
80340
  )
80329
80341
  ).all();
80330
80342
  return rows.map((r) => ({
@@ -84465,6 +84477,7 @@ async function apiRoutes(app, opts) {
84465
84477
  });
84466
84478
  if (!opts.skipAuth) {
84467
84479
  await authPlugin(api, {
84480
+ operatorApiKeyIds: opts.operatorApiKeyIds,
84468
84481
  researchAllowViewers: opts.researchAllowViewers,
84469
84482
  // A bearer that is not an api key is tried as an OAuth access token.
84470
84483
  // Wired unconditionally: the table is empty until an operator registers
@@ -85348,7 +85361,7 @@ function analyzableRunPredicates(projectId) {
85348
85361
  // Defensive: RunCoordinator already skips probes before analyzeAndPersist
85349
85362
  // is called, but a future call site invoking it directly for a probe must
85350
85363
  // still not pollute the intelligence window.
85351
- ne10(runs.trigger, RunTriggers.probe)
85364
+ ne11(runs.trigger, RunTriggers.probe)
85352
85365
  ];
85353
85366
  }
85354
85367
  function readStoredGroundingSources(rawResponse) {
@@ -85936,7 +85949,7 @@ var IntelligenceService = class {
85936
85949
  eq67(runs.kind, RunKinds["answer-visibility"]),
85937
85950
  or16(eq67(runs.status, "completed"), eq67(runs.status, "partial")),
85938
85951
  // Defensive — see top of file.
85939
- ne10(runs.trigger, RunTriggers.probe)
85952
+ ne11(runs.trigger, RunTriggers.probe)
85940
85953
  )
85941
85954
  ).orderBy(desc29(runs.createdAt), desc29(runs.id)).limit((RECURRENCE_LOOKBACK_RUNS + 1) * ROWS_PER_GROUP_BUDGET).all();
85942
85955
  const recentGroups = groupRunsByCreatedAt(recentRunRows);
@@ -115,7 +115,7 @@ import {
115
115
  trafficEventKindSchema,
116
116
  trafficSeriesGranularitySchema,
117
117
  visibilityReportRequestSchema
118
- } from "./chunk-3WODA4JF.js";
118
+ } from "./chunk-3DL3KV6C.js";
119
119
 
120
120
  // src/cli-error.ts
121
121
  function isMachineFormat(format) {
@@ -190,6 +190,13 @@ var dashboardManagedSweepsSchema = z.boolean().nullish();
190
190
  var researchAllowViewersSchema = z.boolean().nullish();
191
191
  var researchViewerDailyRunLimitSchema = z.number().int().positive().nullish();
192
192
  var dashboardManagedRunKindsSchema = z.array(schedulableRunKindSchema).nullish();
193
+ function resolveOperatorApiKeyIds(source) {
194
+ const raw = source.CANONRY_OPERATOR_KEY_IDS?.trim();
195
+ if (!raw) return [];
196
+ const parsed = z.array(z.string().regex(/^[\w-]{1,256}$/)).max(100).safeParse(raw.split(",").map((id) => id.trim()));
197
+ if (!parsed.success) throw new Error("CANONRY_OPERATOR_KEY_IDS must contain at most 100 comma-separated API key IDs (letters, digits, underscores, hyphens; no wildcards).");
198
+ return [...new Set(parsed.data)];
199
+ }
193
200
  var envSchema = z.object({
194
201
  DATABASE_URL: z.string().default("postgresql://aeo:aeo@postgres:5432/aeo_platform"),
195
202
  API_PORT: z.coerce.number().int().positive().default(3e3),
@@ -10492,7 +10499,7 @@ var canonryMcpTools = [
10492
10499
  defineTool({
10493
10500
  name: "canonry_key_self",
10494
10501
  title: "Get current API key identity",
10495
- description: "Read safe metadata for the credential backing this MCP connection, including its scopes, project reach, and derived read-only status. It never exposes the bearer token or stored hash and does not grant mutation access.",
10502
+ description: "Read safe metadata for the credential backing this MCP connection, including scopes, project reach, read-only status, and host-derived operator authority. It never exposes the bearer token or stored hash and cannot grant authority.",
10496
10503
  access: "read",
10497
10504
  tier: "core",
10498
10505
  inputSchema: emptyInputSchema,
@@ -10503,8 +10510,9 @@ var canonryMcpTools = [
10503
10510
  }),
10504
10511
  defineTool({
10505
10512
  name: "canonry_logs_list",
10513
+ requiresOperator: true,
10506
10514
  title: "Read runtime logs",
10507
- description: "Read bounded, redacted runtime logs, including application and HTTP failures. Filter by actor, request, run, project, level, module, or time. Inspect retention, retentionPolicy, dropped, and captureErrors for coverage; file-backed hosts retain logs across restarts. Separate from audit history. Requires instance-wide logs.read (or wildcard), and an admin role for user sessions; project-scoped keys are refused even with a project filter. Cursors expire on retention eviction; reuse with the same filters. No provider calls.",
10515
+ description: "Read bounded, redacted runtime logs, including application and HTTP failures. Filter by actor, request, run, project, level, module, or time. Inspect retention, retentionPolicy, dropped, and captureErrors for coverage; file-backed hosts retain logs across restarts. Separate from audit history. Requires a host-approved operator bearer and instance-wide logs.read (or wildcard). Customer admins, browser/OAuth/delegated sessions, and project-scoped keys cannot grant access. Cursors expire on retention eviction; reuse with the same filters. No provider calls.",
10508
10516
  access: "read",
10509
10517
  tier: "setup",
10510
10518
  inputSchema: logQuerySchema,
@@ -10515,8 +10523,9 @@ var canonryMcpTools = [
10515
10523
  }),
10516
10524
  defineTool({
10517
10525
  name: "canonry_telemetry_update",
10526
+ requiresOperator: true,
10518
10527
  title: "Update server telemetry preference",
10519
- description: "Set the connected server telemetry preference with explicit approval and settings.write. Returns effective state including environment overrides. Does not change the agent machine configuration.",
10528
+ description: "Set the connected server telemetry preference with explicit approval, host-approved operator authority, and settings.write. Customer roles and scopes alone cannot grant operator access. Returns effective state including environment overrides. Does not change the agent machine configuration.",
10520
10529
  access: "write",
10521
10530
  requiredScope: "settings.write",
10522
10531
  tier: "setup",
@@ -10541,8 +10550,9 @@ var canonryMcpTools = [
10541
10550
  }),
10542
10551
  defineTool({
10543
10552
  name: "canonry_telemetry_get",
10553
+ requiresOperator: true,
10544
10554
  title: "Get telemetry status",
10545
- description: "Read the server telemetry status and effective reason. This safe settings read does not change telemetry collection.",
10555
+ description: "Read internal server telemetry status and effective reason. Requires a host-approved operator bearer, not a customer admin role or scopes alone. Does not change telemetry collection.",
10546
10556
  access: "read",
10547
10557
  tier: "setup",
10548
10558
  inputSchema: emptyInputSchema,
@@ -13063,7 +13073,7 @@ var OPERATIONS_GUIDE = {
13063
13073
  "guidance": "Read the stored report for the selected project and period. Retain evidence dates, scope, sample sizes, missing data, and separate mention/citation signals; do not generate fresh runs implicitly."
13064
13074
  }
13065
13075
  },
13066
- "markdown": "# Canonry Operations Guide v1\n\nCanonry is an agent-first AI visibility platform. MCP is the universal entry\npoint for connected agents. Host-native skills are optional upgrades, not a\nprerequisite or a permission mechanism.\n\n## Connect and choose a route\n\nRead the initialization guidance, then call `canonry_help` with an `intent`:\n`status`, `diagnose`, `operations`, `prospecting`, `measurement`, `integrations`,\n`reports`, or a short task\ndescription. Select an accessible project with `canonry_projects_list` before\nusing its exact name in project tools. Inspect each listed tool's input schema;\nhelp suggests tool names, not invented arguments or authorization.\n\nHelp returns a versioned, compact route: connection `mode`, available `next`\ntools, workflow guidance, approval boundaries, and this guide's URL. It performs\nno provider calls, reads no project data, and changes no permissions.\n`next` lists stored reads. Optional `actions` lists loaded tools for work that\nrequires approval; listing an action does not authorize or execute it.\n`includeCatalog: true` additionally returns toolkit details when needed.\n\nHosted connections use a fixed catalog. Help only suggests tools offered by that\nconnection; it never tells a hosted agent to dynamically load a toolkit. A\nprogressive local stdio connection may return `loadToolkits`: call\n`canonry_load_toolkit` with one returned name, await it, then call help again.\nLoading only changes local tool discovery, never server authority.\n\nThe optional `canonry://agent-operations/v1` MCP resource contains this same\nguide. If the host cannot read resources or open links, continue through help.\nDo not install a plugin, local runtime, or skill merely to use connected MCP.\nAn installed Codex or Claude Canonry skill contains a generated copy of this\nguide plus links to host-native references. It does not replace runtime help.\n\n## Vocabulary and evidence\n\n- **Mentioned** means the brand appears in answer text. **Cited** means its\n domain appears in source links. Either, both, or neither can occur; never\n compute one signal from the other.\n- `answerMentioned: null` means not checked, not false. Missing runs and empty\n populations mean no measurement, not zero visibility.\n- Preserve project, time window, provider, requested/served model, location,\n sample size, and query class when comparing evidence. Use server-returned\n metrics; do not invent a score from incompatible populations.\n- Simple projects and Advanced portfolios share the workflow. For Advanced\n results, preserve Property, Target, market, plan revision, and class scope.\n Groups organize navigation; do not infer an unrequested fan-out.\n- Research is isolated evidence, not tracked measurement. A probe still spends\n quota and persists evidence but is excluded from normal tracking metrics.\n\n## Workflows\n\n**Status:** read the stored overview and freshness first. Say when evidence is\nmissing instead of silently creating it.\n\n**Diagnose:** inspect stored history and comparable evidence. Explain what\nchanged separately from why it might have changed. A hypothesis is not a\nmeasured cause. Propose bounded verification if stored evidence is insufficient.\n\n**Prospecting:** generate a one-shot company snapshot without creating a project.\nInspect stored provider settings first. Agree on the company, domain, selected\nproviders, and queries before starting the quota-spending snapshot action.\nBrowser-only selection requires manual queries. Progressive stdio help offers\nthe discovery toolkit when this connection permits snapshots; load it, then\ncall help again. Fixed catalogs offer only already available actions. Read-only\nand restricted connections must not bypass missing snapshot access.\n\n**Measurement:** inspect the existing setup and results before proposing edits.\nKeep research, query tracking, plan publication, and sweep execution separate.\nFor direct research, submit the final editable query text in one context. For a\nreviewed batch, submit each explicit destination with its final text and one\nidempotency key. Pattern substitution happens in the client before either\nrequest; choosing a market or Property records a destination only and never\nrewrites a query or creates an automatic fan-out. `research.run` does not\nauthorize saving patterns, changing tracking, publishing plans, or settings.\nUse a supported preview where available, inspect its exact destination and\nrevision, then seek approval for the actual change. A preview may itself require\nwrite permission; never treat a dry-run flag as a universal safety guarantee.\n\n**Integrations:** inspect stored connection state and snapshot freshness first.\nProvider configuration evidence does not prove a browser event fired or a\nconversion was recorded. Connection, resource selection, refresh/sync, and live\nreads are separate actions. Credentials belong in the operator's secure setup\nflow, never in chat, tool arguments, reports, or public guidance.\n\n**Reports:** use saved evidence for the requested period and scope. Keep mention\nand citation signals separate, include dates and sample sizes, and state missing\nor stale inputs. For Advanced Property mention rankings, use\n`canonry_measurement_portfolio_summary` and its `mentionRanking.strongest`,\n`.weakest`, and `.excluded` lists. It defaults to non-brand questions; state the\nreturned class and report branded results separately. An unavailable portfolio\naggregate does not invalidate available Property mention rates. Flag excluded\nProperties individually; do not silently replace mention ranking with citation\nranking. Keep sample sizes and ties visible. Preparing a report does not\nauthorize new measurement.\n\n## Authority and approval\n\n### Agent operations\n\nUse `canonry_key_self` to inspect the current credential's scopes and project\nboundary without exposing its token. `canonry_settings_get` and\n`canonry_telemetry_get` describe the connected server, not the agent's local\nmachine. Telemetry reports configured preference, effective state, and any\nenvironment override; inspecting status never creates an anonymous identifier.\nAfter approval, `canonry_telemetry_update` changes that preference and\n`canonry_provider_settings_update` changes an already-configured provider's\nmodel/quota. Both require `settings.write`; neither accepts credentials.\n\n`canonry_logs_list` reads bounded, redacted runtime events from both the\napplication logger and Fastify request/error logging. It requires an\ninstance-wide `logs.read` grant (or wildcard), and\nadmin role for signed-in users. Project-scoped keys cannot use it, even with a\nproject filter. A `logs.read`-only key is read-only automatically, without a\nsecond `read` marker. Named `*.read` scopes cannot grant mutations; an explicit\nwrite grant is needed and remains subject to its route gates. Returned messages\nare sanitized and bounded; raw request or response bodies, headers, cookies,\nprovider payloads, and stacks are not\npart of the queryable surface. The same secret-redaction policy runs before\nconsole output and storage. Do not deliberately log secrets: redaction is a\ndefense in depth, not permission to put credentials into diagnostic strings.\nOpaque escaped payloads containing secret assignments are omitted when safe\npartial masking cannot be guaranteed; correlate their retained error codes and IDs.\n\nFile-backed hosts retain runtime logs in SQLite across restarts, bounded to\n10,000 events and seven days. In-memory hosts report `retention: \"process\"`.\nFilter by `actor`, `requestId`, `runId`, `projectId`, `module`, `level`, or an\ninclusive `since`/`until` interval. Keep filters unchanged when resuming an\nopaque cursor; retention eviction can invalidate it. Inspect `retentionPolicy`,\n`captureErrors`, `dropped`, `truncated`, and `retention` before drawing\nconclusions. Missing logs are not proof that an action did not happen. Use\n`canonry_project_history` or `canonry_history_global` for persistent audit events;\noffset pages have deterministic ordering but are not snapshots of concurrent writes.\n\nAudit `actor` comes from authenticated identity (`user:<id>` or `api-key:<id>`),\nnot a caller-supplied header. A delegated MCP credential records its originating\nuser as actor and the actual credential in `credentialId`. `requestId` correlates\nHTTP events; `userAgent` and `actorSession` are bounded, untrusted client hints,\nnever identity or permission grants. Older audit rows are not backfilled with\nidentities the server cannot prove.\n\nBoth shipped HTTP hosts issue restart-safe UUID request IDs and return them in\n`x-request-id`. Use that value to correlate a CLI/API failure with log entries;\nHTTP diagnostics retain the method and route template, not raw URL parameters.\nRequest-bound loggers retain completion attribution, while generic background\ncontinuations stop inheriting caller identity after the response completes.\nCapture covers the owning server process after initialization, not arbitrary\nconsole output, other worker processes, or host/container logs. Run one server\ninstance per process and database, as required by the single-tenant deployment\nmodel; this is not a cross-tenant or distributed log collector.\n\nFor CLI use, settings reads are remote. Google setup and telemetry retain their\nlocal defaults: pass `--target server` explicitly to configure the connected\nserver. `schedule list <project>` lists all schedule kinds, and\n`notify events --target server` discovers the server's event catalog.\n\nMCP returns legacy text JSON plus structured results. Objects keep their shape;\narrays use `{items: [...]}` in `structuredContent`, and scalars use `{value: ...}`.\nErrors preserve the existing envelope and CLI exit codes: HTTP 4xx (including\n429 policy limits) use exit 1; HTTP 5xx use exit 2. Server-provided `Retry-After`\nand request IDs are exposed as `retryAfterMs` and `requestId` when available;\nclients do not infer retryability from HTTP 429 or retry automatically.\nFor a write with an ambiguous outcome, inspect saved state or its receipt before\nretrying; a retry hint is not proof that repeating a write is safe.\n\n### Action boundaries\n\nStart with stored evidence. Before a live provider read, sweep, probe, research\nrun, sync, write, or externally visible action, obtain approval covering its\nexact target, action, and bounded work. Approval already given for that exact\noperation need not be asked for again, but does not extend to more projects,\nlarger batches, retries with new identities, or recurring work.\n\nHTTP GET and MCP `readOnlyHint` describe aspects of an operation, not its cost\nor permission. Provider discovery, account reads, and live diagnostics may\nconsume quota even when labeled read-only. If the tool's effect is unclear,\ninspect its description and request direction before calling it.\n\nAuthentication, role/scope checks, project restrictions, quotas, and guarded\napproval receipts are enforced by the server. Help, skills, resources, and tool\nvisibility cannot grant authority. Never change credentials, endpoints, or\nproject identifiers to work around a missing tool or a `403` response.\n\nFor guarded ads writes, inspect unresolved operation receipts before retrying.\nUse the receipt's supported recovery action; do not replay a mutation under a\nnew identity. An executor cannot create or widen its own human approval grant.\nOn ambiguous results, exhausted bounds, or refusal, stop and report what is\nknown and what permission or operator action is needed.\n\n## Version and source\n\nThis public, versioned document is the source for initialization guidance,\nintent routes, the optional resource, and generated Canonry `SKILL.md` files.\nGuide v1 may receive compatible clarifications; incompatible routing contracts\nrequire a new guide version. The running server's help describes its actual\ncatalog and remains usable without fetching this document.\n"
13076
+ "markdown": "# Canonry Operations Guide v1\n\nCanonry is an agent-first AI visibility platform. MCP is the universal entry\npoint for connected agents. Host-native skills are optional upgrades, not a\nprerequisite or a permission mechanism.\n\n## Connect and choose a route\n\nRead the initialization guidance, then call `canonry_help` with an `intent`:\n`status`, `diagnose`, `operations`, `prospecting`, `measurement`, `integrations`,\n`reports`, or a short task\ndescription. Select an accessible project with `canonry_projects_list` before\nusing its exact name in project tools. Inspect each listed tool's input schema;\nhelp suggests tool names, not invented arguments or authorization.\n\nHelp returns a versioned, compact route: connection `mode`, available `next`\ntools, workflow guidance, approval boundaries, and this guide's URL. It performs\nno provider calls, reads no project data, and changes no permissions.\n`next` lists stored reads. Optional `actions` lists loaded tools for work that\nrequires approval; listing an action does not authorize or execute it.\n`includeCatalog: true` additionally returns toolkit details when needed.\n\nHosted connections use a fixed catalog. Help only suggests tools offered by that\nconnection; it never tells a hosted agent to dynamically load a toolkit. A\nprogressive local stdio connection may return `loadToolkits`: call\n`canonry_load_toolkit` with one returned name, await it, then call help again.\nLoading only changes local tool discovery, never server authority.\n\nThe optional `canonry://agent-operations/v1` MCP resource contains this same\nguide. If the host cannot read resources or open links, continue through help.\nDo not install a plugin, local runtime, or skill merely to use connected MCP.\nAn installed Codex or Claude Canonry skill contains a generated copy of this\nguide plus links to host-native references. It does not replace runtime help.\n\n## Vocabulary and evidence\n\n- **Mentioned** means the brand appears in answer text. **Cited** means its\n domain appears in source links. Either, both, or neither can occur; never\n compute one signal from the other.\n- `answerMentioned: null` means not checked, not false. Missing runs and empty\n populations mean no measurement, not zero visibility.\n- Preserve project, time window, provider, requested/served model, location,\n sample size, and query class when comparing evidence. Use server-returned\n metrics; do not invent a score from incompatible populations.\n- Simple projects and Advanced portfolios share the workflow. For Advanced\n results, preserve Property, Target, market, plan revision, and class scope.\n Groups organize navigation; do not infer an unrequested fan-out.\n- Research is isolated evidence, not tracked measurement. A probe still spends\n quota and persists evidence but is excluded from normal tracking metrics.\n\n## Workflows\n\n**Status:** read the stored overview and freshness first. Say when evidence is\nmissing instead of silently creating it.\n\n**Diagnose:** inspect stored history and comparable evidence. Explain what\nchanged separately from why it might have changed. A hypothesis is not a\nmeasured cause. Propose bounded verification if stored evidence is insufficient.\n\n**Prospecting:** generate a one-shot company snapshot without creating a project.\nInspect stored provider settings first. Agree on the company, domain, selected\nproviders, and queries before starting the quota-spending snapshot action.\nBrowser-only selection requires manual queries. Progressive stdio help offers\nthe discovery toolkit when this connection permits snapshots; load it, then\ncall help again. Fixed catalogs offer only already available actions. Read-only\nand restricted connections must not bypass missing snapshot access.\n\n**Measurement:** inspect the existing setup and results before proposing edits.\nKeep research, query tracking, plan publication, and sweep execution separate.\nFor direct research, submit the final editable query text in one context. For a\nreviewed batch, submit each explicit destination with its final text and one\nidempotency key. Pattern substitution happens in the client before either\nrequest; choosing a market or Property records a destination only and never\nrewrites a query or creates an automatic fan-out. `research.run` does not\nauthorize saving patterns, changing tracking, publishing plans, or settings.\nUse a supported preview where available, inspect its exact destination and\nrevision, then seek approval for the actual change. A preview may itself require\nwrite permission; never treat a dry-run flag as a universal safety guarantee.\n\n**Integrations:** inspect stored connection state and snapshot freshness first.\nProvider configuration evidence does not prove a browser event fired or a\nconversion was recorded. Connection, resource selection, refresh/sync, and live\nreads are separate actions. Credentials belong in the operator's secure setup\nflow, never in chat, tool arguments, reports, or public guidance.\n\n**Reports:** use saved evidence for the requested period and scope. Keep mention\nand citation signals separate, include dates and sample sizes, and state missing\nor stale inputs. For Advanced Property mention rankings, use\n`canonry_measurement_portfolio_summary` and its `mentionRanking.strongest`,\n`.weakest`, and `.excluded` lists. It defaults to non-brand questions; state the\nreturned class and report branded results separately. An unavailable portfolio\naggregate does not invalidate available Property mention rates. Flag excluded\nProperties individually; do not silently replace mention ranking with citation\nranking. Keep sample sizes and ties visible. Preparing a report does not\nauthorize new measurement.\n\n## Authority and approval\n\n### Agent operations\n\nUse `canonry_key_self` (CLI `canonry key whoami --format json`) to inspect the\ncurrent credential's scopes, project boundary, and host-derived `operator`\nauthority without exposing its token. Missing `operator` means unapproved.\n`canonry_settings_get` and\n`canonry_telemetry_get` describe the connected server, not the agent's local\nmachine. Telemetry reports configured preference, effective state, and any\nenvironment override; inspecting status never creates an anonymous identifier.\nAfter approval, `canonry_telemetry_update` changes that preference and\n`canonry_provider_settings_update` changes an already-configured provider's\nmodel/quota. Both require `settings.write`; neither accepts credentials.\nServer telemetry reads and updates additionally require operator authority.\nOrdinary audit-history reads omit internal telemetry events and their state.\n\nOperator authority is deny-by-default and separate from customer admin roles.\nThe deployment owner must approve a dedicated, instance-wide API key's ID in\nthe server environment variable `CANONRY_OPERATOR_KEY_IDS` (comma-separated IDs),\nthen restart the server. Empty/unset approves nobody; wildcards are invalid.\nKeep the bearer private to internal operators; never approve a customer-held or\nshared proxy/bootstrap key. Use `logs.read` for read-only diagnostics, adding\n`settings.write` only when telemetry control is required. Ordinary key creation,\naccount roles, OAuth consent, and caller headers cannot grant operator status.\nRevoking an approved key invalidates it immediately. Host enrollment is a trust\nbootstrap step, intentionally unavailable through customer-facing APIs.\nAPI, CLI, and MCP enforce the same boundary; MCP hides internal tools unless\nthe server confirms operator authority, including in explicit read-only mode.\nProject analytics, research, and normal project permissions are unchanged.\n\n`canonry_logs_list` reads bounded, redacted runtime events from both the\napplication logger and Fastify request/error logging. It requires an\ninstance-wide `logs.read` grant (or wildcard) and a host-approved direct bearer.\nBrowser sessions, OAuth/delegated credentials, customer admins, and project-scoped\nkeys cannot use it, even with a project filter or a matching allowlist ID.\nA `logs.read`-only key is read-only automatically, without a\nsecond `read` marker. Named `*.read` scopes cannot grant mutations; an explicit\nwrite grant is needed and remains subject to its route gates. Returned messages\nare sanitized and bounded; raw request or response bodies, headers, cookies,\nprovider payloads, and stacks are not\npart of the queryable surface. The same secret-redaction policy runs before\nconsole output and storage. Do not deliberately log secrets: redaction is a\ndefense in depth, not permission to put credentials into diagnostic strings.\nOpaque escaped payloads containing secret assignments are omitted when safe\npartial masking cannot be guaranteed; correlate their retained error codes and IDs.\n\nFile-backed hosts retain runtime logs in SQLite across restarts, bounded to\n10,000 events and seven days. In-memory hosts report `retention: \"process\"`.\nFilter by `actor`, `requestId`, `runId`, `projectId`, `module`, `level`, or an\ninclusive `since`/`until` interval. Keep filters unchanged when resuming an\nopaque cursor; retention eviction can invalidate it. Inspect `retentionPolicy`,\n`captureErrors`, `dropped`, `truncated`, and `retention` before drawing\nconclusions. Missing logs are not proof that an action did not happen. Use\n`canonry_project_history` or `canonry_history_global` for persistent audit events;\noffset pages have deterministic ordering but are not snapshots of concurrent writes.\n\nAudit `actor` comes from authenticated identity (`user:<id>` or `api-key:<id>`),\nnot a caller-supplied header. A delegated MCP credential records its originating\nuser as actor and the actual credential in `credentialId`. `requestId` correlates\nHTTP events; `userAgent` and `actorSession` are bounded, untrusted client hints,\nnever identity or permission grants. Older audit rows are not backfilled with\nidentities the server cannot prove.\n\nBoth shipped HTTP hosts issue restart-safe UUID request IDs and return them in\n`x-request-id`. Use that value to correlate a CLI/API failure with log entries;\nHTTP diagnostics retain the method and route template, not raw URL parameters.\nRequest-bound loggers retain completion attribution, while generic background\ncontinuations stop inheriting caller identity after the response completes.\nCapture covers the owning server process after initialization, not arbitrary\nconsole output, other worker processes, or host/container logs. Run one server\ninstance per process and database, as required by the single-tenant deployment\nmodel; this is not a cross-tenant or distributed log collector.\n\nFor CLI use, settings reads are remote. Google setup and telemetry retain their\nlocal defaults: pass `--target server` explicitly to configure the connected\nserver. `schedule list <project>` lists all schedule kinds, and\n`notify events --target server` discovers the server's event catalog.\n\nMCP returns legacy text JSON plus structured results. Objects keep their shape;\narrays use `{items: [...]}` in `structuredContent`, and scalars use `{value: ...}`.\nErrors preserve the existing envelope and CLI exit codes: HTTP 4xx (including\n429 policy limits) use exit 1; HTTP 5xx use exit 2. Server-provided `Retry-After`\nand request IDs are exposed as `retryAfterMs` and `requestId` when available;\nclients do not infer retryability from HTTP 429 or retry automatically.\nFor a write with an ambiguous outcome, inspect saved state or its receipt before\nretrying; a retry hint is not proof that repeating a write is safe.\n\n### Action boundaries\n\nStart with stored evidence. Before a live provider read, sweep, probe, research\nrun, sync, write, or externally visible action, obtain approval covering its\nexact target, action, and bounded work. Approval already given for that exact\noperation need not be asked for again, but does not extend to more projects,\nlarger batches, retries with new identities, or recurring work.\n\nHTTP GET and MCP `readOnlyHint` describe aspects of an operation, not its cost\nor permission. Provider discovery, account reads, and live diagnostics may\nconsume quota even when labeled read-only. If the tool's effect is unclear,\ninspect its description and request direction before calling it.\n\nAuthentication, role/scope checks, project restrictions, quotas, and guarded\napproval receipts are enforced by the server. Help, skills, resources, and tool\nvisibility cannot grant authority. Never change credentials, endpoints, or\nproject identifiers to work around a missing tool or a `403` response.\n\nFor guarded ads writes, inspect unresolved operation receipts before retrying.\nUse the receipt's supported recovery action; do not replay a mutation under a\nnew identity. An executor cannot create or widen its own human approval grant.\nOn ambiguous results, exhausted bounds, or refusal, stop and report what is\nknown and what permission or operator action is needed.\n\n## Version and source\n\nThis public, versioned document is the source for initialization guidance,\nintent routes, the optional resource, and generated Canonry `SKILL.md` files.\nGuide v1 may receive compatible clarifications; incompatible routing contracts\nrequire a new guide version. The running server's help describes its actual\ncatalog and remains usable without fetching this document.\n"
13067
13077
  };
13068
13078
 
13069
13079
  // src/mcp/operations-guide.ts
@@ -13117,7 +13127,7 @@ function createCanonryMcpServerWithCatalog(options = {}) {
13117
13127
  });
13118
13128
  server.validateToolInput = async (_tool, args) => args;
13119
13129
  const entries = [];
13120
- for (const registryTool of getCanonryMcpTools(scope, options.tiers, options.credentialScopes)) {
13130
+ for (const registryTool of getCanonryMcpTools(scope, options.tiers, options.credentialScopes, options.operator)) {
13121
13131
  const tool = registryTool;
13122
13132
  const handler = tool.handler;
13123
13133
  const registered = server.registerTool(
@@ -13189,10 +13199,11 @@ function registerMetaTools(server, catalog, opts) {
13189
13199
  })
13190
13200
  );
13191
13201
  }
13192
- function getCanonryMcpTools(scope = "all", tiers, credentialScopes) {
13202
+ function getCanonryMcpTools(scope = "all", tiers, credentialScopes, operator = false) {
13193
13203
  const readOnly = scope === "read-only" || credentialScopes !== void 0 && isReadOnlyKey(credentialScopes);
13194
13204
  const restricted = credentialScopes && restrictedWriteScopes(credentialScopes);
13195
- const byScope = readOnly ? canonryMcpTools.filter((tool) => tool.access === "read") : canonryMcpTools.filter((tool) => {
13205
+ const authorized = canonryMcpTools.filter((tool) => !tool.requiresOperator || operator);
13206
+ const byScope = readOnly ? authorized.filter((tool) => tool.access === "read") : authorized.filter((tool) => {
13196
13207
  if (!restricted || tool.access === "read") return true;
13197
13208
  if (tool.requiredScope && restricted.includes(tool.requiredScope)) return true;
13198
13209
  return restricted.some((grant) => grant !== RESEARCH_RUN_SCOPE) && tool.tier === "ads";
@@ -13582,6 +13593,7 @@ function parseSkillsClient(value) {
13582
13593
 
13583
13594
  export {
13584
13595
  dashboardManagedRunKindsSchema,
13596
+ resolveOperatorApiKeyIds,
13585
13597
  getBootstrapEnv,
13586
13598
  isMachineFormat,
13587
13599
  EXIT_USER_ERROR,