@canonry/canonry 4.164.0 → 4.165.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +120 -66
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +4 -1
  3. package/assets/assets/{AuditHistoryPanel-CD1GVkS9.js → AuditHistoryPanel-IYMElyK5.js} +1 -1
  4. package/assets/assets/{BacklinksPage-B0Ijhntp.js → BacklinksPage-BMqIPRvZ.js} +1 -1
  5. package/assets/assets/{HistoryPage-DIKtpT_z.js → HistoryPage-iuETLmwO.js} +1 -1
  6. package/assets/assets/{MeasurementPropertyPage-BfPwwIsT.js → MeasurementPropertyPage-BzNAaB8X.js} +1 -1
  7. package/assets/assets/{ProjectPage-3czWArKE.js → ProjectPage-DIzi4Ggo.js} +1 -1
  8. package/assets/assets/{RunRow-CHyd0gQH.js → RunRow-BkkYxEjl.js} +1 -1
  9. package/assets/assets/{RunsPage-Dujt0SQ5.js → RunsPage-DGTrqrTA.js} +1 -1
  10. package/assets/assets/{SettingsPage-CJE9Oeg9.js → SettingsPage-Cwb_Ft9u.js} +1 -1
  11. package/assets/assets/{SiteHealthSection-DvuvziPJ.js → SiteHealthSection-k4I71wfJ.js} +3 -3
  12. package/assets/assets/{TrafficPage-B0tru9ek.js → TrafficPage-C8nyb8l1.js} +1 -1
  13. package/assets/assets/{TrafficSourceDetailPage-DshEUtQX.js → TrafficSourceDetailPage-ry7Da0pV.js} +1 -1
  14. package/assets/assets/{extract-error-message-O2dirpiH.js → extract-error-message-BYyBW07F.js} +1 -1
  15. package/assets/assets/{index-C_y0aGQL.js → index-rqOXhYwp.js} +13 -13
  16. package/assets/assets/{react-sigma_core.esm.min-sVSSzmzq.js → react-sigma_core.esm.min-BtG8TWTe.js} +1 -1
  17. package/assets/index.html +1 -1
  18. package/dist/{chunk-BFGHZ2GU.js → chunk-BU6C2DKF.js} +23 -0
  19. package/dist/{chunk-RULHNUBH.js → chunk-L5SMSDZB.js} +4 -4
  20. package/dist/{chunk-C4WKYU5C.js → chunk-QDOZEMDK.js} +29 -1
  21. package/dist/{chunk-GCQPFCP2.js → chunk-QKLNTSQ2.js} +161 -14
  22. package/dist/cli.js +46 -8
  23. package/dist/index.js +4 -4
  24. package/dist/{intelligence-service-Z3XS2UXQ.js → intelligence-service-RDGUEEZQ.js} +2 -2
  25. package/dist/mcp.js +2 -2
  26. package/package.json +10 -10
@@ -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-DvuvziPJ.js";import"./index-C_y0aGQL.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-BoYe2M-i.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-Dock0eH1.js";import{e as Ht,G as Wt}from"./SiteHealthSection-k4I71wfJ.js";import"./index-rqOXhYwp.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-BoYe2M-i.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-C_y0aGQL.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-rqOXhYwp.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">
@@ -11189,12 +11189,34 @@ var ga4StatusDtoSchema = z34.object({
11189
11189
  createdAt: z34.string().nullable().optional(),
11190
11190
  updatedAt: z34.string().nullable().optional()
11191
11191
  });
11192
+ var ga4PropertySummaryDtoSchema = z34.object({
11193
+ propertyId: z34.string(),
11194
+ displayName: z34.string(),
11195
+ /** GA4 account that owns the property. Two accounts can hold like-named properties. */
11196
+ accountName: z34.string()
11197
+ });
11198
+ var ga4PropertiesDtoSchema = z34.object({
11199
+ properties: z34.array(ga4PropertySummaryDtoSchema)
11200
+ });
11192
11201
  var ga4SyncResponseDtoSchema = z34.object({
11193
11202
  synced: z34.boolean(),
11194
11203
  rowCount: z34.number().int().nonnegative(),
11195
11204
  aiReferralCount: z34.number().int().nonnegative(),
11196
11205
  socialReferralCount: z34.number().int().nonnegative(),
11206
+ /**
11207
+ * The window that was ACTUALLY fetched and written, in days — the request's
11208
+ * `days` bounded to GA4's supported sync range (1–90). Read this, not
11209
+ * `requestedDays`, when reporting or reasoning about coverage: a
11210
+ * `--days 500` sync writes 90 days, and this field says 90.
11211
+ */
11197
11212
  days: z34.number().int().nonnegative(),
11213
+ /** The window the caller asked for, before bounding. Equals `days` unless `clamped`. */
11214
+ requestedDays: z34.number().int().nonnegative(),
11215
+ /**
11216
+ * True when `requestedDays` fell outside GA4's supported range and `days`
11217
+ * differs from it — i.e. the synced history is not the range requested.
11218
+ */
11219
+ clamped: z34.boolean(),
11198
11220
  syncedAt: z34.string(),
11199
11221
  measurement: z34.object({
11200
11222
  acquisition: z34.object({ days: z34.number().int().nonnegative(), status: z34.enum(["ready", "error"]), rowCount: z34.number().int().nonnegative(), error: z34.string().optional() }),
@@ -15380,6 +15402,7 @@ export {
15380
15402
  aiReferralClassCounts,
15381
15403
  formatAiReferralClassSummary,
15382
15404
  ga4StatusDtoSchema,
15405
+ ga4PropertiesDtoSchema,
15383
15406
  ga4SyncResponseDtoSchema,
15384
15407
  ga4AiReferralHistoryEntrySchema,
15385
15408
  ga4AiReferralDailyDtoSchema,
@@ -10,7 +10,7 @@ import {
10
10
  loadConfig,
11
11
  loadConfigRaw,
12
12
  saveConfigPatch
13
- } from "./chunk-C4WKYU5C.js";
13
+ } from "./chunk-QDOZEMDK.js";
14
14
  import {
15
15
  CC_CACHE_DIR,
16
16
  DUCKDB_SPEC,
@@ -139,7 +139,7 @@ import {
139
139
  siteCrawlSnapshots,
140
140
  toAlertView,
141
141
  usageCounters
142
- } from "./chunk-GCQPFCP2.js";
142
+ } from "./chunk-QKLNTSQ2.js";
143
143
  import {
144
144
  AGENT_MEMORY_VALUE_MAX_BYTES,
145
145
  AGENT_PROVIDER_IDS,
@@ -253,7 +253,7 @@ import {
253
253
  validationError,
254
254
  winnabilityClassLabel,
255
255
  withRetry
256
- } from "./chunk-BFGHZ2GU.js";
256
+ } from "./chunk-BU6C2DKF.js";
257
257
 
258
258
  // src/telemetry.ts
259
259
  import crypto from "crypto";
@@ -9084,7 +9084,7 @@ function readStoredGroundingSources(rawResponse) {
9084
9084
  return result;
9085
9085
  }
9086
9086
  async function backfillInsightsCommand(project, opts) {
9087
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-Z3XS2UXQ.js");
9087
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-RDGUEEZQ.js");
9088
9088
  const config = loadConfig();
9089
9089
  const db = createClient(config.database);
9090
9090
  migrate(db);
@@ -81,7 +81,7 @@ import {
81
81
  trafficConnectWordpressRequestSchema,
82
82
  trafficEventKindSchema,
83
83
  trafficSeriesGranularitySchema
84
- } from "./chunk-BFGHZ2GU.js";
84
+ } from "./chunk-BU6C2DKF.js";
85
85
 
86
86
  // src/config.ts
87
87
  import fs from "fs";
@@ -4148,6 +4148,18 @@ var getApiV1ProjectsByNameGaStatus = (options) => {
4148
4148
  ...options
4149
4149
  });
4150
4150
  };
4151
+ var getApiV1ProjectsByNameGaProperties = (options) => {
4152
+ return (options.client ?? client).get({
4153
+ security: [
4154
+ {
4155
+ scheme: "bearer",
4156
+ type: "http"
4157
+ }
4158
+ ],
4159
+ url: "/api/v1/projects/{name}/ga/properties",
4160
+ ...options
4161
+ });
4162
+ };
4151
4163
  var getApiV1ProjectsByNameGaMeasurementAnalysis = (options) => {
4152
4164
  return (options.client ?? client).get({
4153
4165
  security: [
@@ -6925,6 +6937,11 @@ var ApiClient = class {
6925
6937
  () => getApiV1ProjectsByNameGaStatus({ client: this.heyClient, path: { name: project } })
6926
6938
  );
6927
6939
  }
6940
+ async gaProperties(project) {
6941
+ return this.invoke(
6942
+ () => getApiV1ProjectsByNameGaProperties({ client: this.heyClient, path: { name: project } })
6943
+ );
6944
+ }
6928
6945
  async gaMeasurementAnalysis(project, params) {
6929
6946
  return this.invoke(
6930
6947
  () => getApiV1ProjectsByNameGaMeasurementAnalysis({
@@ -9299,6 +9316,17 @@ var canonryMcpTools = [
9299
9316
  openApiOperations: ["GET /api/v1/projects/{name}/ga/status"],
9300
9317
  handler: (client2, input) => client2.gaStatus(input.project)
9301
9318
  }),
9319
+ defineTool({
9320
+ name: "canonry_ga_properties",
9321
+ title: "List GA4 properties",
9322
+ description: `List the GA4 properties the project's connected Google account can read, with their numeric property ids. Use this to find the id that "canonry ga connect --property-id" needs; it cannot be derived from the domain or the OAuth grant.`,
9323
+ access: "read",
9324
+ tier: "ga",
9325
+ inputSchema: projectInputSchema,
9326
+ annotations: readAnnotations(),
9327
+ openApiOperations: ["GET /api/v1/projects/{name}/ga/properties"],
9328
+ handler: (client2, input) => client2.gaProperties(input.project)
9329
+ }),
9302
9330
  defineTool({
9303
9331
  name: "canonry_ga_measurement_analysis",
9304
9332
  title: "Analyze GA acquisition and search demand",
@@ -271,6 +271,7 @@ import {
271
271
  formatWindowCountDelta,
272
272
  ga4AiReferralDailyDtoSchema,
273
273
  ga4AiReferralHistoryEntrySchema,
274
+ ga4PropertiesDtoSchema,
274
275
  ga4SessionHistoryEntrySchema,
275
276
  ga4SocialReferralHistoryEntrySchema,
276
277
  ga4StatusDtoSchema,
@@ -581,7 +582,7 @@ import {
581
582
  wordpressSchemaDeployResultDtoSchema,
582
583
  wordpressSchemaStatusResultDtoSchema,
583
584
  wordpressStatusDtoSchema
584
- } from "./chunk-BFGHZ2GU.js";
585
+ } from "./chunk-BU6C2DKF.js";
585
586
 
586
587
  // src/intelligence-service.ts
587
588
  import { eq as eq59, desc as desc26, asc as asc11, and as and47, ne as ne7, or as or13, inArray as inArray21, gte as gte14, lte as lte12 } from "drizzle-orm";
@@ -9225,6 +9226,13 @@ function assertProjectScope(request, projectId) {
9225
9226
  throw forbidden("This API key is scoped to a different project.");
9226
9227
  }
9227
9228
  }
9229
+ function assertNotProjectScoped(request, what) {
9230
+ if (request.apiKey?.projectId) {
9231
+ throw forbidden(
9232
+ `This API key is scoped to a single project, and ${what} covers every account the connected Google principal can see. Use a full-instance key.`
9233
+ );
9234
+ }
9235
+ }
9228
9236
  function hashApiKey(key) {
9229
9237
  return crypto4.createHash("sha256").update(key).digest("hex");
9230
9238
  }
@@ -28064,6 +28072,7 @@ var SCHEMA_TABLE = {
28064
28072
  GA4MeasurementAnalysisDto: gaMeasurementAnalysisDtoSchema,
28065
28073
  GA4SessionHistoryEntry: ga4SessionHistoryEntrySchema,
28066
28074
  GA4SocialReferralHistoryEntry: ga4SocialReferralHistoryEntrySchema,
28075
+ GA4PropertiesDto: ga4PropertiesDtoSchema,
28067
28076
  GA4StatusDto: ga4StatusDtoSchema,
28068
28077
  GA4SyncResponseDto: ga4SyncResponseDtoSchema,
28069
28078
  GbpAccountListResponse: gbpAccountListResponseSchema,
@@ -32893,7 +32902,7 @@ var routeCatalog = [
32893
32902
  {
32894
32903
  method: "post",
32895
32904
  path: "/api/v1/projects/{name}/ga/connect",
32896
- summary: "Connect Google Analytics 4 via service account",
32905
+ summary: "Connect Google Analytics 4 (service account or existing OAuth connection)",
32897
32906
  tags: ["ga4"],
32898
32907
  parameters: [nameParameter],
32899
32908
  requestBody: {
@@ -32902,9 +32911,12 @@ var routeCatalog = [
32902
32911
  "application/json": {
32903
32912
  schema: {
32904
32913
  type: "object",
32905
- required: ["propertyId", "keyJson"],
32914
+ required: ["propertyId"],
32906
32915
  properties: {
32907
32916
  propertyId: stringSchema,
32917
+ // Optional: omit it to select a property on an existing OAuth
32918
+ // connection (canonry google connect --type ga4). Required only
32919
+ // for service-account auth.
32908
32920
  keyJson: stringSchema
32909
32921
  }
32910
32922
  }
@@ -32940,6 +32952,19 @@ var routeCatalog = [
32940
32952
  404: errorResponse("Project not found.")
32941
32953
  }
32942
32954
  },
32955
+ {
32956
+ method: "get",
32957
+ path: "/api/v1/projects/{name}/ga/properties",
32958
+ summary: "List GA4 properties the connected account can read",
32959
+ description: "Lists every GA4 property visible to the project's OAuth connection, so the numeric property id needed by ga/connect can be discovered without leaving canonry. Requires an OAuth GA4 connection; service-account connections already carry their property id.",
32960
+ tags: ["ga4"],
32961
+ parameters: [nameParameter],
32962
+ responses: {
32963
+ 200: jsonResponse("GA4 properties returned.", "GA4PropertiesDto"),
32964
+ 400: errorResponse("No OAuth GA4 connection for this project."),
32965
+ 404: errorResponse("Project not found.")
32966
+ }
32967
+ },
32943
32968
  {
32944
32969
  method: "get",
32945
32970
  path: "/api/v1/projects/{name}/ga/measurement-analysis",
@@ -32985,6 +33010,7 @@ var routeCatalog = [
32985
33010
  method: "post",
32986
33011
  path: "/api/v1/projects/{name}/ga/sync",
32987
33012
  summary: "Sync GA4 traffic and AI referral data",
33013
+ description: "Syncs a window of GA4 history. `days` is bounded to GA4's supported sync range (1-90); a request outside it is clamped rather than rejected. The response reports the window ACTUALLY written as `days`, the unbounded request as `requestedDays`, and sets `clamped` when the two differ.",
32988
33014
  tags: ["ga4"],
32989
33015
  parameters: [nameParameter],
32990
33016
  requestBody: {
@@ -32994,7 +33020,7 @@ var routeCatalog = [
32994
33020
  schema: {
32995
33021
  type: "object",
32996
33022
  properties: {
32997
- days: integerSchema
33023
+ days: { ...integerSchema, description: "Days of history to sync. Clamped to 1-90; check `clamped` in the response to detect truncation. Defaults to 30." }
32998
33024
  }
32999
33025
  }
33000
33026
  }
@@ -36113,10 +36139,16 @@ import crypto26 from "crypto";
36113
36139
 
36114
36140
  // ../integration-google-analytics/src/constants.ts
36115
36141
  var GA4_DATA_API_BASE = "https://analyticsdata.googleapis.com/v1beta";
36142
+ var GA4_ADMIN_API_BASE = "https://analyticsadmin.googleapis.com/v1beta";
36116
36143
  var GA4_SCOPE = "https://www.googleapis.com/auth/analytics.readonly";
36117
36144
  var GOOGLE_TOKEN_URL2 = "https://oauth2.googleapis.com/token";
36118
36145
  var GA4_DEFAULT_SYNC_DAYS = 30;
36119
36146
  var GA4_MAX_SYNC_DAYS = 90;
36147
+ function resolveGa4SyncDays(days) {
36148
+ const requestedDays = days != null && Number.isFinite(days) ? Math.trunc(days) : GA4_DEFAULT_SYNC_DAYS;
36149
+ const effectiveDays = Math.min(Math.max(1, requestedDays), GA4_MAX_SYNC_DAYS);
36150
+ return { requestedDays, effectiveDays, clamped: effectiveDays !== requestedDays };
36151
+ }
36120
36152
  var GA4_REQUEST_TIMEOUT_MS = 3e4;
36121
36153
  var GA4_MAX_PAGES = 50;
36122
36154
  var GA4_MAX_CONCURRENT_REQUESTS = 4;
@@ -36299,6 +36331,9 @@ function isRetryableGa4Error(err) {
36299
36331
  if (err instanceof GA4ApiError) {
36300
36332
  return err.status === 429 || err.status >= 500;
36301
36333
  }
36334
+ if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
36335
+ return true;
36336
+ }
36302
36337
  return false;
36303
36338
  }
36304
36339
  async function withGa4Retry(fn, errLabel) {
@@ -36435,7 +36470,7 @@ function formatDate2(d) {
36435
36470
  return d.toISOString().slice(0, 10);
36436
36471
  }
36437
36472
  function measurementDateRange(days) {
36438
- const syncDays = Math.min(Math.max(1, days ?? GA4_DEFAULT_SYNC_DAYS), GA4_MAX_SYNC_DAYS);
36473
+ const { effectiveDays: syncDays } = resolveGa4SyncDays(days);
36439
36474
  const endDate = /* @__PURE__ */ new Date();
36440
36475
  const startDate = /* @__PURE__ */ new Date();
36441
36476
  startDate.setUTCDate(startDate.getUTCDate() - syncDays);
@@ -36543,7 +36578,7 @@ var AI_REFERRAL_SOURCE_FILTERS = [
36543
36578
  async function fetchTrafficByLandingPage(accessToken, propertyId, days) {
36544
36579
  validateAccessToken2(accessToken);
36545
36580
  validatePropertyId(propertyId);
36546
- const syncDays = Math.min(Math.max(1, days ?? GA4_DEFAULT_SYNC_DAYS), GA4_MAX_SYNC_DAYS);
36581
+ const { effectiveDays: syncDays } = resolveGa4SyncDays(days);
36547
36582
  const endDate = /* @__PURE__ */ new Date();
36548
36583
  const startDate = /* @__PURE__ */ new Date();
36549
36584
  startDate.setDate(startDate.getDate() - syncDays);
@@ -36672,10 +36707,73 @@ async function verifyConnectionWithToken(accessToken, propertyId) {
36672
36707
  });
36673
36708
  return true;
36674
36709
  }
36710
+ async function listProperties(accessToken) {
36711
+ validateAccessToken2(accessToken);
36712
+ const properties = [];
36713
+ let pageToken;
36714
+ do {
36715
+ const url = new URL(`${GA4_ADMIN_API_BASE}/accountSummaries`);
36716
+ url.searchParams.set("pageSize", "200");
36717
+ if (pageToken) url.searchParams.set("pageToken", pageToken);
36718
+ const body = await withGa4Limit(() => withGa4Retry(async () => {
36719
+ const res = await fetch(url, {
36720
+ headers: { "Authorization": `Bearer ${accessToken}` },
36721
+ signal: AbortSignal.timeout(GA4_REQUEST_TIMEOUT_MS)
36722
+ });
36723
+ if (!res.ok) {
36724
+ const raw = await res.text().catch(() => "");
36725
+ let detail = "";
36726
+ let quotaReason = false;
36727
+ try {
36728
+ const parsed = JSON.parse(raw);
36729
+ if (parsed.error?.status === "SERVICE_DISABLED") {
36730
+ detail = " The Google Analytics Admin API is not enabled for this GCP project. Enable it at: https://console.developers.google.com/apis/api/analyticsadmin.googleapis.com/overview";
36731
+ } else if (parsed.error?.message) {
36732
+ detail = ` ${parsed.error.message}`;
36733
+ }
36734
+ quotaReason = (parsed.error?.errors ?? []).some(
36735
+ (e) => e.reason === "rateLimitExceeded" || e.reason === "quotaExceeded" || e.reason === "userRateLimitExceeded"
36736
+ ) || parsed.error?.status === "RESOURCE_EXHAUSTED";
36737
+ } catch {
36738
+ if (raw.length < 200) detail = ` ${raw}`;
36739
+ }
36740
+ const sanitized = detail.replace(new RegExp(escapeRegExp3(accessToken), "g"), "***");
36741
+ const retryAfterSeconds = res.status === 429 || res.status >= 500 || res.status === 403 && quotaReason ? parseRetryAfter(res.headers.get("retry-after")) : void 0;
36742
+ ga4Log("error", "admin.account-summaries-failed", {
36743
+ httpStatus: res.status,
36744
+ retryAfterSeconds,
36745
+ quotaReason
36746
+ });
36747
+ throw new GA4ApiError(
36748
+ `Failed to list GA4 properties.${sanitized}`,
36749
+ // A quota 403 is surfaced as 429 so every downstream consumer — the
36750
+ // retry predicate and the route's error mapper — reads it as rate
36751
+ // limiting rather than as a permission denial.
36752
+ res.status === 403 && quotaReason ? 429 : res.status,
36753
+ retryAfterSeconds
36754
+ );
36755
+ }
36756
+ return await res.json();
36757
+ }, "GA4 accountSummaries"));
36758
+ for (const account of body.accountSummaries ?? []) {
36759
+ for (const summary of account.propertySummaries ?? []) {
36760
+ const propertyId = (summary.property ?? "").split("/").pop() ?? "";
36761
+ if (!propertyId) continue;
36762
+ properties.push({
36763
+ propertyId,
36764
+ displayName: summary.displayName ?? "",
36765
+ accountName: account.displayName ?? ""
36766
+ });
36767
+ }
36768
+ }
36769
+ pageToken = body.nextPageToken;
36770
+ } while (pageToken);
36771
+ return properties;
36772
+ }
36675
36773
  async function fetchAggregateSummary(accessToken, propertyId, days) {
36676
36774
  validateAccessToken2(accessToken);
36677
36775
  validatePropertyId(propertyId);
36678
- const syncDays = Math.min(Math.max(1, days ?? GA4_DEFAULT_SYNC_DAYS), GA4_MAX_SYNC_DAYS);
36776
+ const { effectiveDays: syncDays } = resolveGa4SyncDays(days);
36679
36777
  const endDate = /* @__PURE__ */ new Date();
36680
36778
  const startDate = /* @__PURE__ */ new Date();
36681
36779
  startDate.setDate(startDate.getDate() - syncDays);
@@ -36725,7 +36823,7 @@ function parseOptionalCountMetric(value) {
36725
36823
  async function fetchDailyTotals(accessToken, propertyId, days) {
36726
36824
  validateAccessToken2(accessToken);
36727
36825
  validatePropertyId(propertyId);
36728
- const syncDays = Math.min(Math.max(1, days ?? GA4_DEFAULT_SYNC_DAYS), GA4_MAX_SYNC_DAYS);
36826
+ const { effectiveDays: syncDays } = resolveGa4SyncDays(days);
36729
36827
  const endDate = /* @__PURE__ */ new Date();
36730
36828
  const startDate = /* @__PURE__ */ new Date();
36731
36829
  startDate.setDate(startDate.getDate() - syncDays);
@@ -36818,7 +36916,7 @@ async function fetchWindowSummary(accessToken, propertyId, windowKey) {
36818
36916
  async function fetchAiReferrals(accessToken, propertyId, days) {
36819
36917
  validateAccessToken2(accessToken);
36820
36918
  validatePropertyId(propertyId);
36821
- const syncDays = Math.min(Math.max(1, days ?? GA4_DEFAULT_SYNC_DAYS), GA4_MAX_SYNC_DAYS);
36919
+ const { effectiveDays: syncDays } = resolveGa4SyncDays(days);
36822
36920
  const endDate = /* @__PURE__ */ new Date();
36823
36921
  const startDate = /* @__PURE__ */ new Date();
36824
36922
  startDate.setDate(startDate.getDate() - syncDays);
@@ -36911,7 +37009,7 @@ var SOCIAL_CHANNEL_GROUPS = ["Organic Social", "Paid Social"];
36911
37009
  async function fetchSocialReferrals(accessToken, propertyId, days) {
36912
37010
  validateAccessToken2(accessToken);
36913
37011
  validatePropertyId(propertyId);
36914
- const syncDays = Math.min(Math.max(1, days ?? GA4_DEFAULT_SYNC_DAYS), GA4_MAX_SYNC_DAYS);
37012
+ const { effectiveDays: syncDays } = resolveGa4SyncDays(days);
36915
37013
  const endDate = /* @__PURE__ */ new Date();
36916
37014
  const startDate = /* @__PURE__ */ new Date();
36917
37015
  startDate.setDate(startDate.getDate() - syncDays);
@@ -37736,6 +37834,7 @@ async function googleRoutes(app, opts) {
37736
37834
  return reply.status(204).send();
37737
37835
  });
37738
37836
  app.get("/projects/:name/google/properties", async (request) => {
37837
+ assertNotProjectScoped(request, "listing Search Console properties");
37739
37838
  const { clientId: googleClientId, clientSecret: googleClientSecret } = getAuthConfig();
37740
37839
  if (!googleClientId || !googleClientSecret) {
37741
37840
  throw validationError("Google OAuth is not configured");
@@ -38652,6 +38751,7 @@ async function googleRoutes(app, opts) {
38652
38751
  return listSelectionResponse(project.id);
38653
38752
  });
38654
38753
  app.get("/projects/:name/gbp/accounts", async (request) => {
38754
+ assertNotProjectScoped(request, "listing Business Profile accounts");
38655
38755
  const { clientId: googleClientId, clientSecret: googleClientSecret } = getAuthConfig();
38656
38756
  if (!googleClientId || !googleClientSecret) {
38657
38757
  throw validationError("Google OAuth is not configured");
@@ -44172,6 +44272,7 @@ async function bingRoutes(app, opts) {
44172
44272
  };
44173
44273
  });
44174
44274
  app.get("/projects/:name/bing/sites", async (request) => {
44275
+ assertNotProjectScoped(request, "listing Bing Webmaster sites");
44175
44276
  const store = requireConnectionStore();
44176
44277
  const project = resolveProject(app.db, request.params.name);
44177
44278
  const conn = requireConnection(store, project.canonicalDomain);
@@ -45038,6 +45139,22 @@ async function refreshOAuthTokenIfNeeded(googleStore, authConfig, canonicalDomai
45038
45139
  }
45039
45140
  return oauthConn.accessToken;
45040
45141
  }
45142
+ function ga4ErrorToAppError(err, context) {
45143
+ if (err instanceof AppError) return err;
45144
+ if (err instanceof GA4ApiError) {
45145
+ if (err.status === 429) {
45146
+ return quotaExceeded("Google Analytics Admin API (rate limited; retries exhausted)");
45147
+ }
45148
+ if (err.status === 401 || err.status === 403) {
45149
+ return forbidden(
45150
+ `${context}: the connected Google account was rejected by Google (${err.status}). Reconnect with "canonry google connect <project> --type ga4".`,
45151
+ { reason: "ga4-upstream-auth", upstreamStatus: err.status }
45152
+ );
45153
+ }
45154
+ return providerError(`${context}: ${err.message}`);
45155
+ }
45156
+ return providerError(`${context}: ${err instanceof Error ? err.message : String(err)}`);
45157
+ }
45041
45158
  async function resolveGa4AccessToken(opts, projectName, canonicalDomain) {
45042
45159
  const saConn = opts.ga4CredentialStore?.getConnection(projectName);
45043
45160
  if (saConn?.clientEmail && saConn?.privateKey && saConn?.propertyId) {
@@ -45059,7 +45176,7 @@ async function resolveGa4AccessToken(opts, projectName, canonicalDomain) {
45059
45176
  }
45060
45177
  if (!oauthConn.propertyId) {
45061
45178
  throw validationError(
45062
- 'GA4 property ID not set. Run "canonry ga set-property <project> <propertyId>" to configure it.'
45179
+ 'GA4 property ID not set. Run "canonry ga properties <project>" to list the readable properties, then "canonry ga connect <project> --property-id <id>" to select one.'
45063
45180
  );
45064
45181
  }
45065
45182
  const accessToken = await refreshOAuthTokenIfNeeded(googleStore, authConfig, canonicalDomain, {
@@ -45188,6 +45305,33 @@ async function ga4Routes(app, opts) {
45188
45305
  });
45189
45306
  return reply.status(204).send();
45190
45307
  });
45308
+ app.get("/projects/:name/ga/properties", async (request) => {
45309
+ assertNotProjectScoped(request, "listing GA4 properties");
45310
+ const project = resolveProject(app.db, request.params.name);
45311
+ const googleStore = opts.googleConnectionStore;
45312
+ const authConfig = opts.getGoogleAuthConfig?.();
45313
+ if (!googleStore || !authConfig) {
45314
+ throw validationError(
45315
+ 'Google OAuth is not configured. Run "canonry google connect <project> --type ga4" first.'
45316
+ );
45317
+ }
45318
+ const oauthConn = googleStore.getConnection(project.canonicalDomain, "ga4");
45319
+ if (!oauthConn?.accessToken || !oauthConn?.refreshToken) {
45320
+ throw validationError(
45321
+ 'No GA4 OAuth connection for this project. Run "canonry google connect <project> --type ga4" first. Service-account connections already carry their property id and do not need this listing.'
45322
+ );
45323
+ }
45324
+ try {
45325
+ const accessToken = await refreshOAuthTokenIfNeeded(googleStore, authConfig, project.canonicalDomain, {
45326
+ accessToken: oauthConn.accessToken,
45327
+ refreshToken: oauthConn.refreshToken,
45328
+ tokenExpiresAt: oauthConn.tokenExpiresAt
45329
+ });
45330
+ return { properties: await listProperties(accessToken) };
45331
+ } catch (err) {
45332
+ throw ga4ErrorToAppError(err, "Failed to list GA4 properties");
45333
+ }
45334
+ });
45191
45335
  app.get("/projects/:name/ga/status", async (request, _reply) => {
45192
45336
  const project = resolveProject(app.db, request.params.name);
45193
45337
  const saConn = opts.ga4CredentialStore?.getConnection(project.name);
@@ -45209,7 +45353,7 @@ async function ga4Routes(app, opts) {
45209
45353
  });
45210
45354
  app.post("/projects/:name/ga/sync", async (request, _reply) => {
45211
45355
  const project = resolveProject(app.db, request.params.name);
45212
- const days = request.body?.days ?? 30;
45356
+ const { requestedDays, effectiveDays: days, clamped } = resolveGa4SyncDays(request.body?.days);
45213
45357
  const only = request.body?.only;
45214
45358
  const validOnlyValues = ["traffic", "ai", "social"];
45215
45359
  if (only !== void 0 && !validOnlyValues.includes(only)) {
@@ -45220,8 +45364,8 @@ async function ga4Routes(app, opts) {
45220
45364
  const syncAi = !only || only === "ai";
45221
45365
  const syncSocial = !only || only === "social";
45222
45366
  const measurementState = app.db.select().from(gaMeasurementSyncStates).where(eq44(gaMeasurementSyncStates.projectId, project.id)).get();
45223
- const acquisitionDays = measurementState?.acquisitionSyncedAt != null ? Math.min(Math.max(1, days), 90) : 90;
45224
- const leadDays = measurementState?.leadSyncedAt != null ? Math.min(Math.max(1, days), 90) : 90;
45367
+ const acquisitionDays = measurementState?.acquisitionSyncedAt != null ? days : GA4_MAX_SYNC_DAYS;
45368
+ const leadDays = measurementState?.leadSyncedAt != null ? days : GA4_MAX_SYNC_DAYS;
45225
45369
  const leadEventNames = project.measurement.leadEventNames;
45226
45370
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
45227
45371
  const runId = crypto35.randomUUID();
@@ -45484,6 +45628,7 @@ async function ga4Routes(app, opts) {
45484
45628
  aiReferralCount: aiReferrals.length,
45485
45629
  socialReferralCount: socialReferrals.length,
45486
45630
  days,
45631
+ ...clamped ? { requestedDays, clamped } : {},
45487
45632
  totalUsers: summary.totalUsers,
45488
45633
  ...only ? { only } : {}
45489
45634
  });
@@ -45493,6 +45638,8 @@ async function ga4Routes(app, opts) {
45493
45638
  aiReferralCount: aiReferrals.length,
45494
45639
  socialReferralCount: socialReferrals.length,
45495
45640
  days,
45641
+ requestedDays,
45642
+ clamped,
45496
45643
  syncedAt: now,
45497
45644
  measurement: {
45498
45645
  acquisition: acquisitionResult,