@canonry/canonry 4.169.0 → 4.171.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/assets/assets/{AuditHistoryPanel-B3APGWB4.js → AuditHistoryPanel-BpIn6E7p.js} +1 -1
  2. package/assets/assets/{BacklinksPage-C-7KAx5g.js → BacklinksPage-BvOWgx1l.js} +1 -1
  3. package/assets/assets/{HistoryPage-Cf-dJuPA.js → HistoryPage-BXaQyur8.js} +1 -1
  4. package/assets/assets/{MeasurementPropertyPage-C1W-BG81.js → MeasurementPropertyPage-Buj708lc.js} +1 -1
  5. package/assets/assets/{ProjectPage-CnZC8AA7.js → ProjectPage-BwC29w2_.js} +1 -1
  6. package/assets/assets/{RunRow-DJQ4Up9f.js → RunRow-C9hp5yEp.js} +1 -1
  7. package/assets/assets/{RunsPage-BbtbYLgP.js → RunsPage-DmkRH1iu.js} +1 -1
  8. package/assets/assets/{SettingsPage-DG-6IuO1.js → SettingsPage-BIyuj-gL.js} +1 -1
  9. package/assets/assets/{SiteHealthSection-rCrAk98W.js → SiteHealthSection-ZlYR3JtG.js} +3 -3
  10. package/assets/assets/{TrafficPage-CpS4K8eT.js → TrafficPage-PxIetTWH.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-D_zPmdUz.js → TrafficSourceDetailPage-BhF_leby.js} +1 -1
  12. package/assets/assets/{extract-error-message-40mUXcbq.js → extract-error-message-Dd2su-BK.js} +1 -1
  13. package/assets/assets/{index-Nk2MzD2d.js → index-DyjfMyum.js} +21 -21
  14. package/assets/assets/{react-sigma_core.esm.min-BOZ1RPZp.js → react-sigma_core.esm.min-CsNemdas.js} +1 -1
  15. package/assets/index.html +1 -1
  16. package/dist/{chunk-T2UJAYTX.js → chunk-5KGN224W.js} +3407 -137
  17. package/dist/{chunk-K7XXDKYY.js → chunk-HJ7W2LXI.js} +165 -1
  18. package/dist/{chunk-HVBAGSQC.js → chunk-IIFDS3K2.js} +807 -13
  19. package/dist/{chunk-U2X42SKN.js → chunk-O226BLC7.js} +3893 -1157
  20. package/dist/{chunk-ANA3GJSC.js → chunk-RR4KSDOD.js} +2 -2
  21. package/dist/cli.js +690 -111
  22. package/dist/index.d.ts +132 -2
  23. package/dist/index.js +10 -4
  24. package/dist/{intelligence-service-6ZYTEFXX.js → intelligence-service-KT6K4ODY.js} +2 -2
  25. package/dist/mcp.js +37 -4
  26. package/package.json +8 -8
@@ -4,6 +4,7 @@ import {
4
4
  CodingAgents,
5
5
  DISCOVERY_MAX_PROBES_CAP,
6
6
  DISCOVERY_PROBE_CONCURRENCY_CAP,
7
+ GOOGLE_MARKETING_STORED_SNAPSHOT_PAGE_MAX,
7
8
  SKILL_MANIFEST_FILENAME,
8
9
  SkillsClients,
9
10
  adsActivateTreeRequestSchema,
@@ -19,6 +20,8 @@ import {
19
20
  adsPauseRequestSchema,
20
21
  adsUnresolvedOperationListQuerySchema,
21
22
  backlinkSourceSchema,
23
+ canonicalizeGtmAccountId,
24
+ canonicalizeGtmResourceSelection,
22
25
  classifySkillFile,
23
26
  coerceSkillManifest,
24
27
  competitorBatchRequestSchema,
@@ -88,12 +91,13 @@ import {
88
91
  trafficConnectWordpressRequestSchema,
89
92
  trafficEventKindSchema,
90
93
  trafficSeriesGranularitySchema
91
- } from "./chunk-K7XXDKYY.js";
94
+ } from "./chunk-HJ7W2LXI.js";
92
95
 
93
96
  // src/config.ts
94
97
  import fs from "fs";
95
98
  import path from "path";
96
99
  import os from "os";
100
+ import crypto from "crypto";
97
101
  import { parse, stringify } from "yaml";
98
102
  function normalizeGoogleConfig(config) {
99
103
  if (!config.google) return;
@@ -105,6 +109,24 @@ function normalizeGoogleConfig(config) {
105
109
  scopes: connection.scopes ?? []
106
110
  }));
107
111
  }
112
+ function normalizeGoogleMarketingConfig(config) {
113
+ if (config.googleAds) {
114
+ config.googleAds.connections = (config.googleAds.connections ?? []).map((connection) => ({
115
+ ...connection,
116
+ refreshToken: connection.refreshToken ?? null,
117
+ tokenExpiresAt: connection.tokenExpiresAt ?? null,
118
+ scopes: connection.scopes ?? []
119
+ }));
120
+ }
121
+ if (config.gtm) {
122
+ config.gtm.connections = (config.gtm.connections ?? []).map((connection) => ({
123
+ ...connection,
124
+ refreshToken: connection.refreshToken ?? null,
125
+ tokenExpiresAt: connection.tokenExpiresAt ?? null,
126
+ scopes: connection.scopes ?? []
127
+ }));
128
+ }
129
+ }
108
130
  function normalizeWordpressConfig(config) {
109
131
  if (!config.wordpress) return;
110
132
  config.wordpress.connections = (config.wordpress.connections ?? []).map((connection) => ({
@@ -166,6 +188,7 @@ Do not write config.yaml by hand; use "canonry init", "canonry settings", or "ca
166
188
  };
167
189
  }
168
190
  normalizeGoogleConfig(parsed);
191
+ normalizeGoogleMarketingConfig(parsed);
169
192
  normalizeWordpressConfig(parsed);
170
193
  normalizeCloudflareTrafficConfig(parsed);
171
194
  const portOverride = process.env.CANONRY_PORT?.trim();
@@ -228,6 +251,35 @@ function loadConfigRaw() {
228
251
  return null;
229
252
  }
230
253
  }
254
+ function writeConfigAtomically(configPath, contents) {
255
+ const directory = path.dirname(configPath);
256
+ const temporaryPath = path.join(
257
+ directory,
258
+ `.${path.basename(configPath)}.${process.pid}.${crypto.randomUUID()}.tmp`
259
+ );
260
+ let descriptor;
261
+ try {
262
+ descriptor = fs.openSync(temporaryPath, "wx", 384);
263
+ fs.fchmodSync(descriptor, 384);
264
+ fs.writeFileSync(descriptor, contents, { encoding: "utf-8" });
265
+ fs.fsyncSync(descriptor);
266
+ fs.closeSync(descriptor);
267
+ descriptor = void 0;
268
+ fs.renameSync(temporaryPath, configPath);
269
+ } catch (error) {
270
+ if (descriptor !== void 0) {
271
+ try {
272
+ fs.closeSync(descriptor);
273
+ } catch {
274
+ }
275
+ }
276
+ try {
277
+ fs.unlinkSync(temporaryPath);
278
+ } catch {
279
+ }
280
+ throw error;
281
+ }
282
+ }
231
283
  function saveConfig(config) {
232
284
  const configDir = getConfigDir();
233
285
  if (!fs.existsSync(configDir)) {
@@ -254,7 +306,7 @@ function saveConfig(config) {
254
306
  }
255
307
  }
256
308
  const yaml = stringify(merged);
257
- fs.writeFileSync(configPath, yaml, { encoding: "utf-8", mode: 384 });
309
+ writeConfigAtomically(configPath, yaml);
258
310
  }
259
311
  function saveConfigPatch(patch) {
260
312
  const configDir = getConfigDir();
@@ -284,7 +336,7 @@ function saveConfigPatch(patch) {
284
336
  }
285
337
  }
286
338
  const yaml = stringify(merged);
287
- fs.writeFileSync(configPath, yaml, { encoding: "utf-8", mode: 384 });
339
+ writeConfigAtomically(configPath, yaml);
288
340
  }
289
341
  function configExists() {
290
342
  return fs.existsSync(getConfigPath());
@@ -2799,27 +2851,339 @@ var putApiV1Telemetry = (options) => {
2799
2851
  }
2800
2852
  });
2801
2853
  };
2802
- var getApiV1CdpStatus = (options) => {
2803
- return (options?.client ?? client).get({
2854
+ var getApiV1CdpStatus = (options) => {
2855
+ return (options?.client ?? client).get({
2856
+ security: [
2857
+ {
2858
+ scheme: "bearer",
2859
+ type: "http"
2860
+ }
2861
+ ],
2862
+ url: "/api/v1/cdp/status",
2863
+ ...options
2864
+ });
2865
+ };
2866
+ var postApiV1CdpScreenshot = (options) => {
2867
+ return (options.client ?? client).post({
2868
+ security: [
2869
+ {
2870
+ scheme: "bearer",
2871
+ type: "http"
2872
+ }
2873
+ ],
2874
+ url: "/api/v1/cdp/screenshot",
2875
+ ...options,
2876
+ headers: {
2877
+ "Content-Type": "application/json",
2878
+ ...options.headers
2879
+ }
2880
+ });
2881
+ };
2882
+ var getApiV1ProjectsByNameRunsByRunIdBrowserDiff = (options) => {
2883
+ return (options.client ?? client).get({
2884
+ security: [
2885
+ {
2886
+ scheme: "bearer",
2887
+ type: "http"
2888
+ }
2889
+ ],
2890
+ url: "/api/v1/projects/{name}/runs/{runId}/browser-diff",
2891
+ ...options
2892
+ });
2893
+ };
2894
+ var getApiV1ProjectsByNameGoogleAdsStatus = (options) => {
2895
+ return (options.client ?? client).get({
2896
+ security: [
2897
+ {
2898
+ scheme: "bearer",
2899
+ type: "http"
2900
+ }
2901
+ ],
2902
+ url: "/api/v1/projects/{name}/google-ads/status",
2903
+ ...options
2904
+ });
2905
+ };
2906
+ var postApiV1ProjectsByNameGoogleAdsOauthConnect = (options) => {
2907
+ return (options.client ?? client).post({
2908
+ security: [
2909
+ {
2910
+ scheme: "bearer",
2911
+ type: "http"
2912
+ }
2913
+ ],
2914
+ url: "/api/v1/projects/{name}/google-ads/oauth/connect",
2915
+ ...options,
2916
+ headers: {
2917
+ "Content-Type": "application/json",
2918
+ ...options.headers
2919
+ }
2920
+ });
2921
+ };
2922
+ var getApiV1ProjectsByNameGoogleAdsCustomers = (options) => {
2923
+ return (options.client ?? client).get({
2924
+ security: [
2925
+ {
2926
+ scheme: "bearer",
2927
+ type: "http"
2928
+ }
2929
+ ],
2930
+ url: "/api/v1/projects/{name}/google-ads/customers",
2931
+ ...options
2932
+ });
2933
+ };
2934
+ var putApiV1ProjectsByNameGoogleAdsSelection = (options) => {
2935
+ return (options.client ?? client).put({
2936
+ security: [
2937
+ {
2938
+ scheme: "bearer",
2939
+ type: "http"
2940
+ }
2941
+ ],
2942
+ url: "/api/v1/projects/{name}/google-ads/selection",
2943
+ ...options,
2944
+ headers: {
2945
+ "Content-Type": "application/json",
2946
+ ...options.headers
2947
+ }
2948
+ });
2949
+ };
2950
+ var postApiV1ProjectsByNameGoogleAdsSync = (options) => {
2951
+ return (options.client ?? client).post({
2952
+ security: [
2953
+ {
2954
+ scheme: "bearer",
2955
+ type: "http"
2956
+ }
2957
+ ],
2958
+ url: "/api/v1/projects/{name}/google-ads/sync",
2959
+ ...options
2960
+ });
2961
+ };
2962
+ var getApiV1ProjectsByNameGoogleAdsSnapshots = (options) => {
2963
+ return (options.client ?? client).get({
2964
+ security: [
2965
+ {
2966
+ scheme: "bearer",
2967
+ type: "http"
2968
+ }
2969
+ ],
2970
+ url: "/api/v1/projects/{name}/google-ads/snapshots",
2971
+ ...options
2972
+ });
2973
+ };
2974
+ var getApiV1ProjectsByNameGoogleAdsSnapshotsBySnapshotId = (options) => {
2975
+ return (options.client ?? client).get({
2976
+ security: [
2977
+ {
2978
+ scheme: "bearer",
2979
+ type: "http"
2980
+ }
2981
+ ],
2982
+ url: "/api/v1/projects/{name}/google-ads/snapshots/{snapshotId}",
2983
+ ...options
2984
+ });
2985
+ };
2986
+ var deleteApiV1ProjectsByNameGoogleAdsConnection = (options) => {
2987
+ return (options.client ?? client).delete({
2988
+ security: [
2989
+ {
2990
+ scheme: "bearer",
2991
+ type: "http"
2992
+ }
2993
+ ],
2994
+ url: "/api/v1/projects/{name}/google-ads/connection",
2995
+ ...options
2996
+ });
2997
+ };
2998
+ var getApiV1ProjectsByNameGtmStatus = (options) => {
2999
+ return (options.client ?? client).get({
3000
+ security: [
3001
+ {
3002
+ scheme: "bearer",
3003
+ type: "http"
3004
+ }
3005
+ ],
3006
+ url: "/api/v1/projects/{name}/gtm/status",
3007
+ ...options
3008
+ });
3009
+ };
3010
+ var postApiV1ProjectsByNameGtmOauthConnect = (options) => {
3011
+ return (options.client ?? client).post({
3012
+ security: [
3013
+ {
3014
+ scheme: "bearer",
3015
+ type: "http"
3016
+ }
3017
+ ],
3018
+ url: "/api/v1/projects/{name}/gtm/oauth/connect",
3019
+ ...options,
3020
+ headers: {
3021
+ "Content-Type": "application/json",
3022
+ ...options.headers
3023
+ }
3024
+ });
3025
+ };
3026
+ var getApiV1ProjectsByNameGtmAccounts = (options) => {
3027
+ return (options.client ?? client).get({
3028
+ security: [
3029
+ {
3030
+ scheme: "bearer",
3031
+ type: "http"
3032
+ }
3033
+ ],
3034
+ url: "/api/v1/projects/{name}/gtm/accounts",
3035
+ ...options
3036
+ });
3037
+ };
3038
+ var getApiV1ProjectsByNameGtmAccountsByAccountIdContainers = (options) => {
3039
+ return (options.client ?? client).get({
3040
+ security: [
3041
+ {
3042
+ scheme: "bearer",
3043
+ type: "http"
3044
+ }
3045
+ ],
3046
+ url: "/api/v1/projects/{name}/gtm/accounts/{accountId}/containers",
3047
+ ...options
3048
+ });
3049
+ };
3050
+ var getApiV1ProjectsByNameGtmAccountsByAccountIdContainersByContainerIdWorkspaces = (options) => {
3051
+ return (options.client ?? client).get({
3052
+ security: [
3053
+ {
3054
+ scheme: "bearer",
3055
+ type: "http"
3056
+ }
3057
+ ],
3058
+ url: "/api/v1/projects/{name}/gtm/accounts/{accountId}/containers/{containerId}/workspaces",
3059
+ ...options
3060
+ });
3061
+ };
3062
+ var putApiV1ProjectsByNameGtmSelection = (options) => {
3063
+ return (options.client ?? client).put({
3064
+ security: [
3065
+ {
3066
+ scheme: "bearer",
3067
+ type: "http"
3068
+ }
3069
+ ],
3070
+ url: "/api/v1/projects/{name}/gtm/selection",
3071
+ ...options,
3072
+ headers: {
3073
+ "Content-Type": "application/json",
3074
+ ...options.headers
3075
+ }
3076
+ });
3077
+ };
3078
+ var postApiV1ProjectsByNameGtmSync = (options) => {
3079
+ return (options.client ?? client).post({
3080
+ security: [
3081
+ {
3082
+ scheme: "bearer",
3083
+ type: "http"
3084
+ }
3085
+ ],
3086
+ url: "/api/v1/projects/{name}/gtm/sync",
3087
+ ...options
3088
+ });
3089
+ };
3090
+ var getApiV1ProjectsByNameGtmSnapshots = (options) => {
3091
+ return (options.client ?? client).get({
3092
+ security: [
3093
+ {
3094
+ scheme: "bearer",
3095
+ type: "http"
3096
+ }
3097
+ ],
3098
+ url: "/api/v1/projects/{name}/gtm/snapshots",
3099
+ ...options
3100
+ });
3101
+ };
3102
+ var getApiV1ProjectsByNameGtmSnapshotsBySnapshotId = (options) => {
3103
+ return (options.client ?? client).get({
3104
+ security: [
3105
+ {
3106
+ scheme: "bearer",
3107
+ type: "http"
3108
+ }
3109
+ ],
3110
+ url: "/api/v1/projects/{name}/gtm/snapshots/{snapshotId}",
3111
+ ...options
3112
+ });
3113
+ };
3114
+ var deleteApiV1ProjectsByNameGtmConnection = (options) => {
3115
+ return (options.client ?? client).delete({
3116
+ security: [
3117
+ {
3118
+ scheme: "bearer",
3119
+ type: "http"
3120
+ }
3121
+ ],
3122
+ url: "/api/v1/projects/{name}/gtm/connection",
3123
+ ...options
3124
+ });
3125
+ };
3126
+ var getApiV1ProjectsByNameConversionTrackingContracts = (options) => {
3127
+ return (options.client ?? client).get({
3128
+ security: [
3129
+ {
3130
+ scheme: "bearer",
3131
+ type: "http"
3132
+ }
3133
+ ],
3134
+ url: "/api/v1/projects/{name}/conversion-tracking/contracts",
3135
+ ...options
3136
+ });
3137
+ };
3138
+ var postApiV1ProjectsByNameConversionTrackingContracts = (options) => {
3139
+ return (options.client ?? client).post({
3140
+ security: [
3141
+ {
3142
+ scheme: "bearer",
3143
+ type: "http"
3144
+ }
3145
+ ],
3146
+ url: "/api/v1/projects/{name}/conversion-tracking/contracts",
3147
+ ...options,
3148
+ headers: {
3149
+ "Content-Type": "application/json",
3150
+ ...options.headers
3151
+ }
3152
+ });
3153
+ };
3154
+ var deleteApiV1ProjectsByNameConversionTrackingContractsByContractId = (options) => {
3155
+ return (options.client ?? client).delete({
3156
+ security: [
3157
+ {
3158
+ scheme: "bearer",
3159
+ type: "http"
3160
+ }
3161
+ ],
3162
+ url: "/api/v1/projects/{name}/conversion-tracking/contracts/{contractId}",
3163
+ ...options
3164
+ });
3165
+ };
3166
+ var getApiV1ProjectsByNameConversionTrackingContractsByContractId = (options) => {
3167
+ return (options.client ?? client).get({
2804
3168
  security: [
2805
3169
  {
2806
3170
  scheme: "bearer",
2807
3171
  type: "http"
2808
3172
  }
2809
3173
  ],
2810
- url: "/api/v1/cdp/status",
3174
+ url: "/api/v1/projects/{name}/conversion-tracking/contracts/{contractId}",
2811
3175
  ...options
2812
3176
  });
2813
3177
  };
2814
- var postApiV1CdpScreenshot = (options) => {
2815
- return (options.client ?? client).post({
3178
+ var putApiV1ProjectsByNameConversionTrackingContractsByContractId = (options) => {
3179
+ return (options.client ?? client).put({
2816
3180
  security: [
2817
3181
  {
2818
3182
  scheme: "bearer",
2819
3183
  type: "http"
2820
3184
  }
2821
3185
  ],
2822
- url: "/api/v1/cdp/screenshot",
3186
+ url: "/api/v1/projects/{name}/conversion-tracking/contracts/{contractId}",
2823
3187
  ...options,
2824
3188
  headers: {
2825
3189
  "Content-Type": "application/json",
@@ -2827,7 +3191,7 @@ var postApiV1CdpScreenshot = (options) => {
2827
3191
  }
2828
3192
  });
2829
3193
  };
2830
- var getApiV1ProjectsByNameRunsByRunIdBrowserDiff = (options) => {
3194
+ var getApiV1ProjectsByNameConversionTrackingContractsByContractIdIntegrity = (options) => {
2831
3195
  return (options.client ?? client).get({
2832
3196
  security: [
2833
3197
  {
@@ -2835,7 +3199,7 @@ var getApiV1ProjectsByNameRunsByRunIdBrowserDiff = (options) => {
2835
3199
  type: "http"
2836
3200
  }
2837
3201
  ],
2838
- url: "/api/v1/projects/{name}/runs/{runId}/browser-diff",
3202
+ url: "/api/v1/projects/{name}/conversion-tracking/contracts/{contractId}/integrity",
2839
3203
  ...options
2840
3204
  });
2841
3205
  };
@@ -6381,6 +6745,207 @@ var ApiClient = class {
6381
6745
  })
6382
6746
  );
6383
6747
  }
6748
+ // ── Google Marketing: Google Ads + Google Tag Manager ──────────────────
6749
+ async connectGoogleAds(project, request) {
6750
+ return this.invoke(
6751
+ () => postApiV1ProjectsByNameGoogleAdsOauthConnect({
6752
+ client: this.heyClient,
6753
+ path: { name: project },
6754
+ body: request
6755
+ })
6756
+ );
6757
+ }
6758
+ async connectGtm(project, request) {
6759
+ return this.invoke(
6760
+ () => postApiV1ProjectsByNameGtmOauthConnect({
6761
+ client: this.heyClient,
6762
+ path: { name: project },
6763
+ body: request
6764
+ })
6765
+ );
6766
+ }
6767
+ async disconnectGoogleAds(project) {
6768
+ return this.invoke(
6769
+ () => deleteApiV1ProjectsByNameGoogleAdsConnection({
6770
+ client: this.heyClient,
6771
+ path: { name: project }
6772
+ })
6773
+ );
6774
+ }
6775
+ async disconnectGtm(project) {
6776
+ return this.invoke(
6777
+ () => deleteApiV1ProjectsByNameGtmConnection({
6778
+ client: this.heyClient,
6779
+ path: { name: project }
6780
+ })
6781
+ );
6782
+ }
6783
+ async getGoogleAdsStatus(project) {
6784
+ return this.invoke(
6785
+ () => getApiV1ProjectsByNameGoogleAdsStatus({
6786
+ client: this.heyClient,
6787
+ path: { name: project }
6788
+ })
6789
+ );
6790
+ }
6791
+ async listGoogleAdsCustomers(project) {
6792
+ return this.invoke(
6793
+ () => getApiV1ProjectsByNameGoogleAdsCustomers({
6794
+ client: this.heyClient,
6795
+ path: { name: project }
6796
+ })
6797
+ );
6798
+ }
6799
+ async setGoogleAdsSelection(project, request) {
6800
+ return this.invoke(
6801
+ () => putApiV1ProjectsByNameGoogleAdsSelection({
6802
+ client: this.heyClient,
6803
+ path: { name: project },
6804
+ body: request
6805
+ })
6806
+ );
6807
+ }
6808
+ async triggerGoogleAdsSync(project) {
6809
+ return this.invoke(
6810
+ () => postApiV1ProjectsByNameGoogleAdsSync({
6811
+ client: this.heyClient,
6812
+ path: { name: project }
6813
+ })
6814
+ );
6815
+ }
6816
+ async listGoogleAdsSnapshots(project, query) {
6817
+ return this.invoke(
6818
+ () => getApiV1ProjectsByNameGoogleAdsSnapshots({
6819
+ client: this.heyClient,
6820
+ path: { name: project },
6821
+ query
6822
+ })
6823
+ );
6824
+ }
6825
+ async getGoogleAdsSnapshot(project, snapshotId) {
6826
+ return this.invoke(
6827
+ () => getApiV1ProjectsByNameGoogleAdsSnapshotsBySnapshotId({
6828
+ client: this.heyClient,
6829
+ path: { name: project, snapshotId }
6830
+ })
6831
+ );
6832
+ }
6833
+ async getGtmStatus(project) {
6834
+ return this.invoke(
6835
+ () => getApiV1ProjectsByNameGtmStatus({
6836
+ client: this.heyClient,
6837
+ path: { name: project }
6838
+ })
6839
+ );
6840
+ }
6841
+ async listGtmAccounts(project) {
6842
+ return this.invoke(
6843
+ () => getApiV1ProjectsByNameGtmAccounts({
6844
+ client: this.heyClient,
6845
+ path: { name: project }
6846
+ })
6847
+ );
6848
+ }
6849
+ async listGtmContainers(project, accountId) {
6850
+ return this.invoke(
6851
+ () => getApiV1ProjectsByNameGtmAccountsByAccountIdContainers({
6852
+ client: this.heyClient,
6853
+ path: { name: project, accountId }
6854
+ })
6855
+ );
6856
+ }
6857
+ async listGtmWorkspaces(project, accountId, containerId) {
6858
+ return this.invoke(
6859
+ () => getApiV1ProjectsByNameGtmAccountsByAccountIdContainersByContainerIdWorkspaces({
6860
+ client: this.heyClient,
6861
+ path: { name: project, accountId, containerId }
6862
+ })
6863
+ );
6864
+ }
6865
+ async setGtmSelection(project, request) {
6866
+ return this.invoke(
6867
+ () => putApiV1ProjectsByNameGtmSelection({
6868
+ client: this.heyClient,
6869
+ path: { name: project },
6870
+ body: request
6871
+ })
6872
+ );
6873
+ }
6874
+ async triggerGtmSync(project) {
6875
+ return this.invoke(
6876
+ () => postApiV1ProjectsByNameGtmSync({
6877
+ client: this.heyClient,
6878
+ path: { name: project }
6879
+ })
6880
+ );
6881
+ }
6882
+ async listGtmSnapshots(project, query) {
6883
+ return this.invoke(
6884
+ () => getApiV1ProjectsByNameGtmSnapshots({
6885
+ client: this.heyClient,
6886
+ path: { name: project },
6887
+ query
6888
+ })
6889
+ );
6890
+ }
6891
+ async getGtmSnapshot(project, snapshotId) {
6892
+ return this.invoke(
6893
+ () => getApiV1ProjectsByNameGtmSnapshotsBySnapshotId({
6894
+ client: this.heyClient,
6895
+ path: { name: project, snapshotId }
6896
+ })
6897
+ );
6898
+ }
6899
+ async listConversionTrackingContracts(project) {
6900
+ return this.invoke(
6901
+ () => getApiV1ProjectsByNameConversionTrackingContracts({
6902
+ client: this.heyClient,
6903
+ path: { name: project }
6904
+ })
6905
+ );
6906
+ }
6907
+ async getConversionTrackingContract(project, contractId) {
6908
+ return this.invoke(
6909
+ () => getApiV1ProjectsByNameConversionTrackingContractsByContractId({
6910
+ client: this.heyClient,
6911
+ path: { name: project, contractId }
6912
+ })
6913
+ );
6914
+ }
6915
+ async createConversionTrackingContract(project, request) {
6916
+ return this.invoke(
6917
+ () => postApiV1ProjectsByNameConversionTrackingContracts({
6918
+ client: this.heyClient,
6919
+ path: { name: project },
6920
+ body: request
6921
+ })
6922
+ );
6923
+ }
6924
+ async updateConversionTrackingContract(project, contractId, request) {
6925
+ return this.invoke(
6926
+ () => putApiV1ProjectsByNameConversionTrackingContractsByContractId({
6927
+ client: this.heyClient,
6928
+ path: { name: project, contractId },
6929
+ body: request
6930
+ })
6931
+ );
6932
+ }
6933
+ async deleteConversionTrackingContract(project, contractId) {
6934
+ await this.invoke(
6935
+ () => deleteApiV1ProjectsByNameConversionTrackingContractsByContractId({
6936
+ client: this.heyClient,
6937
+ path: { name: project, contractId }
6938
+ })
6939
+ );
6940
+ }
6941
+ async getConversionTrackingIntegrity(project, contractId) {
6942
+ return this.invoke(
6943
+ () => getApiV1ProjectsByNameConversionTrackingContractsByContractIdIntegrity({
6944
+ client: this.heyClient,
6945
+ path: { name: project, contractId }
6946
+ })
6947
+ );
6948
+ }
6384
6949
  // Google Business Profile (Phase 1: auth + discovery)
6385
6950
  async listGbpAccounts(project) {
6386
6951
  return this.invoke(
@@ -7816,7 +8381,7 @@ var _require = createRequire(import.meta.url);
7816
8381
  var PACKAGE_VERSION = _require("../package.json").version;
7817
8382
 
7818
8383
  // src/commands/skills.ts
7819
- import crypto from "crypto";
8384
+ import crypto2 from "crypto";
7820
8385
  import fs2 from "fs";
7821
8386
  import os2 from "os";
7822
8387
  import path2 from "path";
@@ -7871,7 +8436,7 @@ function walkRelative(dir, prefix = "") {
7871
8436
  return out.sort();
7872
8437
  }
7873
8438
  function sha256File(filePath) {
7874
- return crypto.createHash("sha256").update(fs2.readFileSync(filePath)).digest("hex");
8439
+ return crypto2.createHash("sha256").update(fs2.readFileSync(filePath)).digest("hex");
7875
8440
  }
7876
8441
  function readSkillManifest(skillDir) {
7877
8442
  try {
@@ -8788,6 +9353,54 @@ var adsAdPauseInputSchema = z3.object({
8788
9353
  adId: z3.string().min(1),
8789
9354
  request: adsPauseRequestSchema
8790
9355
  });
9356
+ var googleMarketingSnapshotPageInputSchema = z3.object({
9357
+ project: projectNameSchema,
9358
+ limit: z3.number().int().min(1).max(GOOGLE_MARKETING_STORED_SNAPSHOT_PAGE_MAX).optional(),
9359
+ cursor: z3.string().trim().min(1).optional()
9360
+ }).strict();
9361
+ var googleMarketingSnapshotInputSchema = z3.object({
9362
+ project: projectNameSchema,
9363
+ snapshotId: z3.string().trim().min(1)
9364
+ }).strict();
9365
+ var gtmAccountInputSchema = z3.object({
9366
+ project: projectNameSchema,
9367
+ accountId: z3.string().trim().min(1)
9368
+ }).strict().superRefine((input, context) => {
9369
+ if (!canonicalizeGtmAccountId(input.accountId)) {
9370
+ context.addIssue({
9371
+ code: z3.ZodIssueCode.custom,
9372
+ path: ["accountId"],
9373
+ message: "Expected a safe GTM account ID or accounts/{id} resource path."
9374
+ });
9375
+ }
9376
+ });
9377
+ var gtmContainerInputSchema = z3.object({
9378
+ project: projectNameSchema,
9379
+ accountId: z3.string().trim().min(1),
9380
+ containerId: z3.string().trim().min(1)
9381
+ }).strict().superRefine((input, context) => {
9382
+ if (!canonicalizeGtmResourceSelection(input)) {
9383
+ context.addIssue({
9384
+ code: z3.ZodIssueCode.custom,
9385
+ path: ["containerId"],
9386
+ message: "Expected matching safe GTM account/container IDs or resource paths."
9387
+ });
9388
+ }
9389
+ });
9390
+ function canonicalGtmMcpAccountId(accountId) {
9391
+ const canonical = canonicalizeGtmAccountId(accountId);
9392
+ if (!canonical) throw new Error("Invalid GTM account input.");
9393
+ return canonical;
9394
+ }
9395
+ function canonicalGtmMcpSelection(accountId, containerId) {
9396
+ const canonical = canonicalizeGtmResourceSelection({ accountId, containerId });
9397
+ if (!canonical) throw new Error("Invalid GTM account/container input.");
9398
+ return canonical;
9399
+ }
9400
+ var conversionTrackingContractInputSchema = z3.object({
9401
+ project: projectNameSchema,
9402
+ contractId: z3.string().trim().min(1)
9403
+ }).strict();
8791
9404
  var keywordsInputSchema = z3.object({
8792
9405
  project: projectNameSchema,
8793
9406
  request: keywordBatchRequestSchema
@@ -11130,6 +11743,187 @@ var canonryMcpTools = [
11130
11743
  checkDeadLinks: input.checkDeadLinks
11131
11744
  })
11132
11745
  }),
11746
+ // ----- Google Marketing: Google Ads + Google Tag Manager -----
11747
+ // OAuth, selection, disconnect, and contract writes deliberately stay on
11748
+ // the operator CLI/dashboard. The agent surface can only inspect bounded
11749
+ // evidence, trigger bounded read-only syncs, and assess stored integrity.
11750
+ defineTool({
11751
+ name: "canonry_google_ads_status",
11752
+ title: "Get Google Ads connection status",
11753
+ description: "Read the project-local Google Ads connection, selected customer, and stored-evidence freshness. This never exposes credentials or changes a Google Ads account.",
11754
+ access: "read",
11755
+ tier: "google-ads",
11756
+ inputSchema: projectInputSchema,
11757
+ annotations: readAnnotations(),
11758
+ openApiOperations: ["GET /api/v1/projects/{name}/google-ads/status"],
11759
+ handler: (client2, input) => client2.getGoogleAdsStatus(input.project)
11760
+ }),
11761
+ defineTool({
11762
+ name: "canonry_google_ads_customers",
11763
+ title: "List live Google Ads customers",
11764
+ description: "Read the bounded set of Google Ads customers available to the project OAuth connection. This makes live provider GET requests and requires google-marketing.read-live; customer selection remains an explicit operator action.",
11765
+ access: "read",
11766
+ tier: "google-ads",
11767
+ inputSchema: projectInputSchema,
11768
+ annotations: readAnnotations(true),
11769
+ openApiOperations: ["GET /api/v1/projects/{name}/google-ads/customers"],
11770
+ handler: (client2, input) => client2.listGoogleAdsCustomers(input.project)
11771
+ }),
11772
+ defineTool({
11773
+ name: "canonry_google_ads_snapshots",
11774
+ title: "List stored Google Ads evidence",
11775
+ description: "List a bounded page of redacted, append-only Google Ads snapshots. Stored reads are quota-free and do not call Google.",
11776
+ access: "read",
11777
+ tier: "google-ads",
11778
+ inputSchema: googleMarketingSnapshotPageInputSchema,
11779
+ annotations: readAnnotations(),
11780
+ openApiOperations: ["GET /api/v1/projects/{name}/google-ads/snapshots"],
11781
+ handler: (client2, input) => client2.listGoogleAdsSnapshots(input.project, {
11782
+ limit: input.limit,
11783
+ cursor: input.cursor
11784
+ })
11785
+ }),
11786
+ defineTool({
11787
+ name: "canonry_google_ads_snapshot_get",
11788
+ title: "Get stored Google Ads evidence",
11789
+ description: "Read one redacted Google Ads evidence snapshot by ID. It can show conversion actions and effective campaign goals, but cannot prove a browser conversion fired.",
11790
+ access: "read",
11791
+ tier: "google-ads",
11792
+ inputSchema: googleMarketingSnapshotInputSchema,
11793
+ annotations: readAnnotations(),
11794
+ openApiOperations: ["GET /api/v1/projects/{name}/google-ads/snapshots/{snapshotId}"],
11795
+ handler: (client2, input) => client2.getGoogleAdsSnapshot(input.project, input.snapshotId)
11796
+ }),
11797
+ defineTool({
11798
+ name: "canonry_google_ads_sync",
11799
+ title: "Sync Google Ads conversion evidence",
11800
+ description: "Queue a bounded read-only Google Ads evidence sync using GETs and SearchStream POSTs. It creates a local run and sanitized snapshots, but never changes campaigns, conversion actions, goals, bids, or budgets. Requires google-marketing.read-live and write authority.",
11801
+ access: "write",
11802
+ tier: "google-ads",
11803
+ inputSchema: projectInputSchema,
11804
+ annotations: writeAnnotations({ idempotentHint: false, openWorldHint: true }),
11805
+ openApiOperations: ["POST /api/v1/projects/{name}/google-ads/sync"],
11806
+ handler: (client2, input) => client2.triggerGoogleAdsSync(input.project)
11807
+ }),
11808
+ defineTool({
11809
+ name: "canonry_gtm_status",
11810
+ title: "Get Google Tag Manager connection status",
11811
+ description: "Read the project-local GTM connection, selected account/container/workspace, and stored-evidence freshness. This never exposes credentials or edits GTM.",
11812
+ access: "read",
11813
+ tier: "gtm",
11814
+ inputSchema: projectInputSchema,
11815
+ annotations: readAnnotations(),
11816
+ openApiOperations: ["GET /api/v1/projects/{name}/gtm/status"],
11817
+ handler: (client2, input) => client2.getGtmStatus(input.project)
11818
+ }),
11819
+ defineTool({
11820
+ name: "canonry_gtm_accounts",
11821
+ title: "List live GTM accounts",
11822
+ description: "Read the bounded set of GTM accounts available to the project OAuth connection. This makes live provider GET requests and requires google-marketing.read-live; resource selection remains operator-only.",
11823
+ access: "read",
11824
+ tier: "gtm",
11825
+ inputSchema: projectInputSchema,
11826
+ annotations: readAnnotations(true),
11827
+ openApiOperations: ["GET /api/v1/projects/{name}/gtm/accounts"],
11828
+ handler: (client2, input) => client2.listGtmAccounts(input.project)
11829
+ }),
11830
+ defineTool({
11831
+ name: "canonry_gtm_containers",
11832
+ title: "List live GTM containers",
11833
+ description: "Read the bounded set of containers in one GTM account. This is a live GET-only provider read; it cannot edit or publish a container.",
11834
+ access: "read",
11835
+ tier: "gtm",
11836
+ inputSchema: gtmAccountInputSchema,
11837
+ annotations: readAnnotations(true),
11838
+ openApiOperations: ["GET /api/v1/projects/{name}/gtm/accounts/{accountId}/containers"],
11839
+ handler: (client2, input) => client2.listGtmContainers(
11840
+ input.project,
11841
+ canonicalGtmMcpAccountId(input.accountId)
11842
+ )
11843
+ }),
11844
+ defineTool({
11845
+ name: "canonry_gtm_workspaces",
11846
+ title: "List live GTM workspaces",
11847
+ description: "Read the bounded set of workspaces in one GTM container. This is a live GET-only provider read; it cannot edit a workspace or publish a version.",
11848
+ access: "read",
11849
+ tier: "gtm",
11850
+ inputSchema: gtmContainerInputSchema,
11851
+ annotations: readAnnotations(true),
11852
+ openApiOperations: ["GET /api/v1/projects/{name}/gtm/accounts/{accountId}/containers/{containerId}/workspaces"],
11853
+ handler: (client2, input) => {
11854
+ const selection = canonicalGtmMcpSelection(input.accountId, input.containerId);
11855
+ return client2.listGtmWorkspaces(input.project, selection.accountId, selection.containerId);
11856
+ }
11857
+ }),
11858
+ defineTool({
11859
+ name: "canonry_gtm_snapshots",
11860
+ title: "List stored GTM evidence",
11861
+ description: "List a bounded page of redacted, append-only GTM live/draft graph snapshots. Stored reads are quota-free and do not call Google.",
11862
+ access: "read",
11863
+ tier: "gtm",
11864
+ inputSchema: googleMarketingSnapshotPageInputSchema,
11865
+ annotations: readAnnotations(),
11866
+ openApiOperations: ["GET /api/v1/projects/{name}/gtm/snapshots"],
11867
+ handler: (client2, input) => client2.listGtmSnapshots(input.project, {
11868
+ limit: input.limit,
11869
+ cursor: input.cursor
11870
+ })
11871
+ }),
11872
+ defineTool({
11873
+ name: "canonry_gtm_snapshot_get",
11874
+ title: "Get stored GTM evidence",
11875
+ description: "Read one redacted GTM live/draft graph snapshot by ID. It can prove static tag and trigger configuration only; it cannot prove a browser event fired.",
11876
+ access: "read",
11877
+ tier: "gtm",
11878
+ inputSchema: googleMarketingSnapshotInputSchema,
11879
+ annotations: readAnnotations(),
11880
+ openApiOperations: ["GET /api/v1/projects/{name}/gtm/snapshots/{snapshotId}"],
11881
+ handler: (client2, input) => client2.getGtmSnapshot(input.project, input.snapshotId)
11882
+ }),
11883
+ defineTool({
11884
+ name: "canonry_gtm_sync",
11885
+ title: "Sync GTM conversion evidence",
11886
+ description: "Queue a bounded GTM GET-only evidence sync. It creates a local run and sanitized live/draft snapshots, but never edits a workspace or publishes a container version. Requires google-marketing.read-live and write authority.",
11887
+ access: "write",
11888
+ tier: "gtm",
11889
+ inputSchema: projectInputSchema,
11890
+ annotations: writeAnnotations({ idempotentHint: false, openWorldHint: true }),
11891
+ openApiOperations: ["POST /api/v1/projects/{name}/gtm/sync"],
11892
+ handler: (client2, input) => client2.triggerGtmSync(input.project)
11893
+ }),
11894
+ defineTool({
11895
+ name: "canonry_conversion_tracking_contracts",
11896
+ title: "List conversion-tracking contracts",
11897
+ description: "List the project\u2019s declared business contracts linking an application event to Google Ads and GTM identifiers. Contract creation and changes remain operator-only.",
11898
+ access: "read",
11899
+ tier: "conversion-tracking",
11900
+ inputSchema: projectInputSchema,
11901
+ annotations: readAnnotations(),
11902
+ openApiOperations: ["GET /api/v1/projects/{name}/conversion-tracking/contracts"],
11903
+ handler: (client2, input) => client2.listConversionTrackingContracts(input.project)
11904
+ }),
11905
+ defineTool({
11906
+ name: "canonry_conversion_tracking_contract_get",
11907
+ title: "Get conversion-tracking contract",
11908
+ description: "Read one declared conversion-tracking contract. It declares intended semantics; use integrity assessment to evaluate stored evidence.",
11909
+ access: "read",
11910
+ tier: "conversion-tracking",
11911
+ inputSchema: conversionTrackingContractInputSchema,
11912
+ annotations: readAnnotations(),
11913
+ openApiOperations: ["GET /api/v1/projects/{name}/conversion-tracking/contracts/{contractId}"],
11914
+ handler: (client2, input) => client2.getConversionTrackingContract(input.project, input.contractId)
11915
+ }),
11916
+ defineTool({
11917
+ name: "canonry_conversion_tracking_integrity",
11918
+ title: "Assess stored conversion integrity",
11919
+ description: "Evaluate a declared contract against stored redacted Google Ads and GTM evidence. This does not call providers and cannot treat static configuration as proof of a browser event or observed Google Ads conversion.",
11920
+ access: "read",
11921
+ tier: "conversion-tracking",
11922
+ inputSchema: conversionTrackingContractInputSchema,
11923
+ annotations: readAnnotations(),
11924
+ openApiOperations: ["GET /api/v1/projects/{name}/conversion-tracking/contracts/{contractId}/integrity"],
11925
+ handler: (client2, input) => client2.getConversionTrackingIntegrity(input.project, input.contractId)
11926
+ }),
11133
11927
  // ----- OpenAI ads (ChatGPT ads) -----
11134
11928
  defineTool({
11135
11929
  name: "canonry_ads_status",