@canonry/canonry 7.23.0 → 7.24.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 (30) hide show
  1. package/assets/assets/{AuditHistoryPanel-J8AR3x5F.js → AuditHistoryPanel-DKKnX8IT.js} +1 -1
  2. package/assets/assets/{BacklinksPage-CRDPutGA.js → BacklinksPage-DNbur8am.js} +1 -1
  3. package/assets/assets/{HistoryPage-C5taVW3t.js → HistoryPage-DahtHlpM.js} +1 -1
  4. package/assets/assets/{MeasurementPropertyPage-txucmLuz.js → MeasurementPropertyPage-BoNDtLfp.js} +1 -1
  5. package/assets/assets/{ProjectPage-C4FaBoWI.js → ProjectPage--ZlpQduH.js} +1 -1
  6. package/assets/assets/{RunsPage-D-3XKUhh.js → RunsPage-DKB2CHTM.js} +1 -1
  7. package/assets/assets/{SettingsPage-Cq_SJRj_.js → SettingsPage-lnmMZzEj.js} +1 -1
  8. package/assets/assets/{SiteHealthSection-Cn_IZVO1.js → SiteHealthSection-BBjucRed.js} +3 -3
  9. package/assets/assets/{TrafficPage-BMdFZs3X.js → TrafficPage-DNyTmPLd.js} +1 -1
  10. package/assets/assets/{TrafficSourceDetailPage-BmrIOc-2.js → TrafficSourceDetailPage-D1zKZHSh.js} +1 -1
  11. package/assets/assets/{extract-error-message-HlQy5L7O.js → extract-error-message-v0b8P-wc.js} +1 -1
  12. package/assets/assets/index-CfaBRiBG.js +86 -0
  13. package/assets/assets/{react-sigma_core.esm.min-CogAr1i4.js → react-sigma_core.esm.min-BZSJoRrV.js} +1 -1
  14. package/assets/assets/{v2-overview-adapter-tps7TUxq.js → v2-overview-adapter-DXAl-O_h.js} +1 -1
  15. package/assets/index.html +1 -1
  16. package/dist/{chunk-222NAFTY.js → chunk-7Q7QCBJ3.js} +2 -2
  17. package/dist/{chunk-5IXQAOVK.js → chunk-DS2ZKB3T.js} +15 -6
  18. package/dist/{chunk-MRKV43TV.js → chunk-IM2BFAAT.js} +565 -206
  19. package/dist/{chunk-UF7MH6TU.js → chunk-JJBVYL45.js} +2 -2
  20. package/dist/{chunk-46JZR2IV.js → chunk-O5UMRQVN.js} +1303 -504
  21. package/dist/{chunk-3OPV5XBY.js → chunk-SG3MPCDL.js} +5709 -4588
  22. package/dist/{chunk-KWBFKTOA.js → chunk-YCPTP2NC.js} +3 -3
  23. package/dist/cli.js +199 -49
  24. package/dist/{demo-server-32PU7BM6.js → demo-server-C7UGXXN7.js} +4 -4
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.js +6 -6
  27. package/dist/{intelligence-service-256DQBVR.js → intelligence-service-37TX4YV7.js} +2 -2
  28. package/dist/mcp.js +4 -4
  29. package/package.json +12 -12
  30. package/assets/assets/index-BJcjB5XW.js +0 -86
@@ -4,14 +4,14 @@ import {
4
4
  installSkills,
5
5
  loadConfigRaw,
6
6
  saveConfigPatch
7
- } from "./chunk-UF7MH6TU.js";
7
+ } from "./chunk-JJBVYL45.js";
8
8
  import {
9
9
  PACKAGE_VERSION
10
- } from "./chunk-5IXQAOVK.js";
10
+ } from "./chunk-DS2ZKB3T.js";
11
11
  import {
12
12
  SKILL_MANIFEST_FILENAME,
13
13
  SkillsClients
14
- } from "./chunk-MRKV43TV.js";
14
+ } from "./chunk-IM2BFAAT.js";
15
15
 
16
16
  // src/skills-autosync.ts
17
17
  import fs from "fs";
package/dist/cli.js CHANGED
@@ -14,13 +14,16 @@ import {
14
14
  getOrCreateAnonymousId,
15
15
  getPrintedUpdateAvailable,
16
16
  getTelemetryStatus,
17
+ installAttribution,
17
18
  isAeroToolProfile,
18
19
  isFirstRun,
19
20
  isLoopbackBindHost,
20
21
  isTelemetryEnabled,
21
22
  listAgentProviders,
22
23
  notePrintedUpdateAvailable,
24
+ outcomeFailure,
23
25
  readCachedUpdateAvailable,
26
+ recordInstallRef,
24
27
  registeredProviderNames,
25
28
  reparseStoredResult,
26
29
  reparseStoredResult2,
@@ -34,12 +37,14 @@ import {
34
37
  showFirstRunNotice,
35
38
  trackCliCommandFinished,
36
39
  trackEvent,
40
+ trackFeatureCompleted,
41
+ trackIntegrationConnection,
37
42
  waitForServerRuntimeStartup
38
- } from "./chunk-3OPV5XBY.js";
43
+ } from "./chunk-SG3MPCDL.js";
39
44
  import {
40
45
  autoSyncSkills,
41
46
  formatAutoSyncNotice
42
- } from "./chunk-KWBFKTOA.js";
47
+ } from "./chunk-YCPTP2NC.js";
43
48
  import {
44
49
  clearDashboardPassword,
45
50
  configExists,
@@ -58,14 +63,16 @@ import {
58
63
  saveConfigPatch,
59
64
  shouldPrintServeSkillsNudge,
60
65
  withBootstrapProviderDefaults
61
- } from "./chunk-UF7MH6TU.js";
66
+ } from "./chunk-JJBVYL45.js";
62
67
  import {
63
68
  DEFAULT_DEMO_TRUSTED_PROXIES,
64
69
  trustedProxiesProblem,
65
70
  trustedProxyProblem
66
71
  } from "./chunk-RB2F4D3Z.js";
67
- import "./chunk-222NAFTY.js";
72
+ import "./chunk-7Q7QCBJ3.js";
68
73
  import {
74
+ CANONRY_DISCORD_URL,
75
+ CANONRY_ISSUES_URL,
69
76
  CliError,
70
77
  EXIT_SYSTEM_ERROR,
71
78
  EXIT_USER_ERROR,
@@ -76,7 +83,7 @@ import {
76
83
  runMeasurementDraftAction,
77
84
  systemError,
78
85
  usageError
79
- } from "./chunk-5IXQAOVK.js";
86
+ } from "./chunk-DS2ZKB3T.js";
80
87
  import {
81
88
  CLOUDFLARE_WORKER_BINDINGS,
82
89
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -109,7 +116,7 @@ import {
109
116
  rawEventSamples,
110
117
  readMarketCompetitorNames,
111
118
  runs
112
- } from "./chunk-46JZR2IV.js";
119
+ } from "./chunk-O5UMRQVN.js";
113
120
  import {
114
121
  AdsDeliverySnapshotStatuses,
115
122
  AdsHistoricalCampaignRollupStatuses,
@@ -128,6 +135,8 @@ import {
128
135
  GaMoverChangeBases,
129
136
  MeasurementEvidenceShapes,
130
137
  OUTSTANDING_PROVIDER_BATCH_STATUSES,
138
+ OutcomeReasonCodes,
139
+ OutcomeTriggers,
131
140
  ProviderBatchStatuses,
132
141
  ProviderNames,
133
142
  ProviderReloadErrorReasons,
@@ -166,6 +175,7 @@ import {
166
175
  buildModelChangeNotice,
167
176
  canonicalizeGtmAccountId,
168
177
  canonicalizeGtmResourceSelection,
178
+ classifyOutcomeError,
169
179
  competitorAutoAliasModeSchema,
170
180
  competitorLandscapeQuerySchema,
171
181
  computeCompetitorOverlap,
@@ -243,7 +253,7 @@ import {
243
253
  visibilityCompareSelectionSchema,
244
254
  visibilityReportRequestSchema,
245
255
  winnabilityClassSchema
246
- } from "./chunk-MRKV43TV.js";
256
+ } from "./chunk-IM2BFAAT.js";
247
257
 
248
258
  // src/cli.ts
249
259
  import { pathToFileURL } from "url";
@@ -292,9 +302,9 @@ Usage: ${spec.usage}
292
302
  if (spec.help) console.log(`${spec.help}
293
303
  `);
294
304
  }
295
- function matchesPath(args, path12) {
296
- if (args.length < path12.length) return false;
297
- return path12.every((segment, index) => args[index] === segment);
305
+ function matchesPath(args, path13) {
306
+ if (args.length < path13.length) return false;
307
+ return path13.every((segment, index) => args[index] === segment);
298
308
  }
299
309
  function withGlobalOptions(options) {
300
310
  const base = options ? { ...options } : {};
@@ -789,7 +799,7 @@ function parseDemoTrustedProxies(values) {
789
799
  async function demoCommand(options) {
790
800
  const { host, port } = parseDemoListenOptions(options);
791
801
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
792
- const { createDemoServer } = await import("./demo-server-32PU7BM6.js");
802
+ const { createDemoServer } = await import("./demo-server-C7UGXXN7.js");
793
803
  const app = await createDemoServer({ trustProxy });
794
804
  let stopping = false;
795
805
  const stop = () => {
@@ -1203,7 +1213,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
1203
1213
  }
1204
1214
  }
1205
1215
  async function backfillInsightsCommand(project2, opts) {
1206
- const { IntelligenceService } = await import("./intelligence-service-256DQBVR.js");
1216
+ const { IntelligenceService } = await import("./intelligence-service-37TX4YV7.js");
1207
1217
  const config = loadConfig();
1208
1218
  const db = createClient(config.database);
1209
1219
  migrate(db);
@@ -2826,6 +2836,11 @@ var BING_CLI_COMMANDS = [
2826
2836
  }
2827
2837
  ];
2828
2838
 
2839
+ // src/cli-connection-telemetry.ts
2840
+ function trackCliConnection(properties) {
2841
+ trackIntegrationConnection({ ...properties, surface: "cli", agent: cliRuntimeContext().agent });
2842
+ }
2843
+
2829
2844
  // src/commands/cdp.ts
2830
2845
  function getClient3() {
2831
2846
  return createApiClient();
@@ -2839,7 +2854,13 @@ async function cdpConnect(opts) {
2839
2854
  host,
2840
2855
  port
2841
2856
  };
2842
- saveConfigPatch(config);
2857
+ try {
2858
+ saveConfigPatch(config);
2859
+ } catch (err) {
2860
+ trackCliConnection({ integration: "cdp", action: "connect", status: "failed", ...outcomeFailure(err) });
2861
+ throw err;
2862
+ }
2863
+ trackCliConnection({ integration: "cdp", action: "connect", status: "succeeded" });
2843
2864
  if (isMachineFormat(opts.format)) {
2844
2865
  console.log(JSON.stringify({
2845
2866
  host,
@@ -5402,9 +5423,9 @@ async function gaConnect(project2, opts) {
5402
5423
  propertyId: opts.propertyId
5403
5424
  };
5404
5425
  if (opts.keyFile) {
5405
- const fs20 = await import("fs");
5426
+ const fs21 = await import("fs");
5406
5427
  try {
5407
- const content = fs20.readFileSync(opts.keyFile, "utf-8");
5428
+ const content = fs21.readFileSync(opts.keyFile, "utf-8");
5408
5429
  JSON.parse(content);
5409
5430
  body.keyJson = content;
5410
5431
  } catch (e) {
@@ -7685,9 +7706,9 @@ async function getCommand(opts) {
7685
7706
  console.log(JSON.stringify(leaf, null, 2));
7686
7707
  }
7687
7708
  }
7688
- function walkPath(value, path12) {
7689
- if (!path12 || path12 === ".") return value;
7690
- const segments = path12.split(".").flatMap((part) => {
7709
+ function walkPath(value, path13) {
7710
+ if (!path13 || path13 === ".") return value;
7711
+ const segments = path13.split(".").flatMap((part) => {
7691
7712
  const tokens = [];
7692
7713
  let i = 0;
7693
7714
  const bracketStart = part.indexOf("[");
@@ -7735,13 +7756,13 @@ var GET_CLI_COMMANDS = [
7735
7756
  },
7736
7757
  run: async (input) => {
7737
7758
  const project2 = requireProject(input, "get", USAGE2);
7738
- const path12 = requirePositional(input, 1, {
7759
+ const path13 = requirePositional(input, 1, {
7739
7760
  command: "get",
7740
7761
  usage: USAGE2,
7741
7762
  message: 'path is required (e.g. "scores.mentionShare.value")'
7742
7763
  });
7743
7764
  const from = getString(input.values, "from");
7744
- await getCommand({ project: project2, path: path12, from, format: input.format });
7765
+ await getCommand({ project: project2, path: path13, from, format: input.format });
7745
7766
  }
7746
7767
  }
7747
7768
  ];
@@ -8410,9 +8431,9 @@ async function trafficConnectWordpress(project2, opts) {
8410
8431
  }
8411
8432
  let applicationPassword = opts.appPassword?.trim() ?? "";
8412
8433
  if (!applicationPassword && opts.appPasswordFile) {
8413
- const fs20 = await import("fs");
8434
+ const fs21 = await import("fs");
8414
8435
  try {
8415
- applicationPassword = fs20.readFileSync(opts.appPasswordFile, "utf-8").trim();
8436
+ applicationPassword = fs21.readFileSync(opts.appPasswordFile, "utf-8").trim();
8416
8437
  } catch (e) {
8417
8438
  const msg = describeError(e);
8418
8439
  throw new CliError({
@@ -8468,10 +8489,10 @@ async function trafficConnectCloudRun(project2, opts) {
8468
8489
  details: { project: project2 }
8469
8490
  });
8470
8491
  }
8471
- const fs20 = await import("fs");
8492
+ const fs21 = await import("fs");
8472
8493
  let keyJson;
8473
8494
  try {
8474
- keyJson = fs20.readFileSync(opts.serviceAccountKey, "utf-8");
8495
+ keyJson = fs21.readFileSync(opts.serviceAccountKey, "utf-8");
8475
8496
  JSON.parse(keyJson);
8476
8497
  } catch (e) {
8477
8498
  const msg = describeError(e);
@@ -8531,9 +8552,9 @@ async function trafficConnectVercel(project2, opts) {
8531
8552
  }
8532
8553
  let token = opts.token?.trim() ?? "";
8533
8554
  if (!token && opts.tokenFile) {
8534
- const fs20 = await import("fs");
8555
+ const fs21 = await import("fs");
8535
8556
  try {
8536
- token = fs20.readFileSync(opts.tokenFile, "utf-8").trim();
8557
+ token = fs21.readFileSync(opts.tokenFile, "utf-8").trim();
8537
8558
  } catch (e) {
8538
8559
  const msg = describeError(e);
8539
8560
  throw new CliError({
@@ -17548,7 +17569,17 @@ async function bootstrapCommand(opts) {
17548
17569
  };
17549
17570
  const providersChanged = !isDeepStrictEqual(persistedValue(existingRaw?.providers ?? {}), persistedValue(mergedProviders));
17550
17571
  const configChanged = !existingRaw || existingRaw.apiUrl !== apiUrl || existingRaw.database !== databasePath || existingRaw.apiKey !== rawApiKey || providersChanged || !isDeepStrictEqual(persistedValue(existingRaw.google), persistedValue(mergedGoogle));
17551
- if (configChanged) saveConfig(nextConfig);
17572
+ const providerConnections = Object.keys(env.providers).filter((name) => env.providers[name] && !isDeepStrictEqual(persistedValue(existingRaw?.providers?.[name]), persistedValue(mergedProviders[name]))).map((name) => ({ integration: "provider", provider: name, action: existingRaw?.providers?.[name] ? "reauth" : "connect" }));
17573
+ if (configChanged) {
17574
+ try {
17575
+ saveConfig(nextConfig);
17576
+ } catch (err) {
17577
+ for (const connection of providerConnections) trackCliConnection({ ...connection, status: "failed", ...outcomeFailure(err) });
17578
+ throw err;
17579
+ }
17580
+ }
17581
+ if (isTelemetryEnabled()) recordInstallRef(opts?.ref);
17582
+ for (const connection of providerConnections) trackCliConnection({ ...connection, status: "succeeded" });
17552
17583
  const status = !existing ? "created" : configChanged || keyChanged ? "updated" : "unchanged";
17553
17584
  const providerFree = registeredProviderNames(nextConfig).length === 0;
17554
17585
  const serverUrl = loadConfig().apiUrl;
@@ -17643,6 +17674,21 @@ async function bootstrapCommand(opts) {
17643
17674
  if (reloadFailure) throw reloadFailure;
17644
17675
  }
17645
17676
 
17677
+ // src/commands/support.ts
17678
+ var FEEDBACK_COMMAND = 'canonry feedback "<what happened>"';
17679
+ function supportCommand(format) {
17680
+ if (isMachineFormat(format)) {
17681
+ console.log(JSON.stringify({ discord: CANONRY_DISCORD_URL, issues: CANONRY_ISSUES_URL, feedbackCommand: FEEDBACK_COMMAND }, null, 2));
17682
+ return;
17683
+ }
17684
+ console.log([
17685
+ "Get help with Canonry:",
17686
+ ` Discord: ${CANONRY_DISCORD_URL}`,
17687
+ ` Issues: ${CANONRY_ISSUES_URL}`,
17688
+ ` From here: ${FEEDBACK_COMMAND}`
17689
+ ].join("\n"));
17690
+ }
17691
+
17646
17692
  // src/commands/daemon.ts
17647
17693
  import { spawn as spawn2 } from "child_process";
17648
17694
  import fs16 from "fs";
@@ -18180,6 +18226,7 @@ ${mcpTip}`);
18180
18226
  }
18181
18227
  if (isTelemetryEnabled()) {
18182
18228
  const postInitSetupState = buildSetupState();
18229
+ recordInstallRef(opts?.ref);
18183
18230
  trackEvent("cli.init", {
18184
18231
  providerCount: providerNames.length,
18185
18232
  providers: providerNames,
@@ -18202,8 +18249,12 @@ ${mcpTip}`);
18202
18249
  hasAgent: !!agentLLM
18203
18250
  }),
18204
18251
  skillsInstalled: !!skillsSummary,
18252
+ ...installAttribution(),
18205
18253
  ...cliRuntimeContext()
18206
18254
  });
18255
+ for (const provider of Object.keys(providers)) {
18256
+ trackCliConnection({ integration: "provider", provider, action: "connect", status: "succeeded" });
18257
+ }
18207
18258
  }
18208
18259
  if (!isMachineFormat(format) && process.stdin.isTTY && process.stdout.isTTY) {
18209
18260
  const answer = await prompt("\nStart the dashboard now? [Y/n]: ");
@@ -18477,6 +18528,80 @@ function detectCanonryAgentPlugin(opts = {}) {
18477
18528
  return { configuredClients, verifiedClients, verifiedClientVersions };
18478
18529
  }
18479
18530
 
18531
+ // src/server-crash-telemetry.ts
18532
+ import fs19 from "fs";
18533
+ import path12 from "path";
18534
+ var CRASH_MARKER = "crash-telemetry.json";
18535
+ var MAX_MARKER_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
18536
+ function telemetryAllowed() {
18537
+ try {
18538
+ return isTelemetryEnabled();
18539
+ } catch {
18540
+ return false;
18541
+ }
18542
+ }
18543
+ function markerPath(configDir) {
18544
+ return path12.join(configDir, CRASH_MARKER);
18545
+ }
18546
+ function trackServerStartFailure(err, reasonCode) {
18547
+ try {
18548
+ const classified = classifyOutcomeError(err);
18549
+ const code = err?.code;
18550
+ const reason = reasonCode ?? (code === "EADDRINUSE" ? OutcomeReasonCodes.PORT_IN_USE : classified.reasonCode);
18551
+ trackFeatureCompleted({
18552
+ feature: "server",
18553
+ operation: "start",
18554
+ status: "failed",
18555
+ trigger: OutcomeTriggers.startup,
18556
+ reasonCode: reason,
18557
+ ...classified.errorName ? { errorName: classified.errorName } : {}
18558
+ });
18559
+ } catch {
18560
+ }
18561
+ }
18562
+ var watched = /* @__PURE__ */ new WeakSet();
18563
+ function watchServerCrashes(configDir = getConfigDir(), proc = process, now = Date.now) {
18564
+ if (watched.has(proc)) return;
18565
+ watched.add(proc);
18566
+ proc.on("uncaughtExceptionMonitor", (err, origin) => {
18567
+ if (!telemetryAllowed()) return;
18568
+ try {
18569
+ const { errorName } = classifyOutcomeError(err);
18570
+ fs19.writeFileSync(markerPath(configDir), JSON.stringify({
18571
+ origin: origin === "unhandledRejection" ? "unhandledRejection" : "uncaughtException",
18572
+ at: now(),
18573
+ ...errorName ? { errorName } : {}
18574
+ }));
18575
+ } catch {
18576
+ }
18577
+ });
18578
+ }
18579
+ function reportPreviousServerCrash(configDir = getConfigDir(), now = Date.now) {
18580
+ if (!telemetryAllowed()) return;
18581
+ const file = markerPath(configDir);
18582
+ let marker;
18583
+ try {
18584
+ marker = JSON.parse(fs19.readFileSync(file, "utf8"));
18585
+ } catch {
18586
+ return;
18587
+ }
18588
+ try {
18589
+ fs19.rmSync(file, { force: true });
18590
+ } catch {
18591
+ return;
18592
+ }
18593
+ if (typeof marker.at !== "number" || now() - marker.at > MAX_MARKER_AGE_MS) return;
18594
+ const errorName = typeof marker.errorName === "string" && /^[A-Z_$][\w$]{0,39}$/i.test(marker.errorName) ? marker.errorName : void 0;
18595
+ trackFeatureCompleted({
18596
+ feature: "server",
18597
+ operation: "crash",
18598
+ status: "failed",
18599
+ trigger: OutcomeTriggers.startup,
18600
+ reasonCode: marker.origin === "unhandledRejection" ? OutcomeReasonCodes.UNHANDLED_REJECTION : OutcomeReasonCodes.UNCAUGHT_EXCEPTION,
18601
+ ...errorName ? { errorName } : {}
18602
+ });
18603
+ }
18604
+
18480
18605
  // src/commands/serve.ts
18481
18606
  function readServeOpenState(db) {
18482
18607
  try {
@@ -18525,7 +18650,12 @@ async function serveCommand(format = "text") {
18525
18650
  const host = process.env.CANONRY_HOST ?? "127.0.0.1";
18526
18651
  config.port = port;
18527
18652
  const db = createClient(config.database);
18528
- migrate(db);
18653
+ try {
18654
+ migrate(db);
18655
+ } catch (err) {
18656
+ trackServerStartFailure(err, "MIGRATION_FAILED");
18657
+ throw err;
18658
+ }
18529
18659
  try {
18530
18660
  const result = await repairNormalizedPathsOnStartup(db);
18531
18661
  if (result && result.updated > 0 && format === "text") {
@@ -18602,10 +18732,13 @@ Canonry server running at ${url}`);
18602
18732
  }, null, 2));
18603
18733
  }
18604
18734
  setTelemetrySource("cli-server");
18735
+ watchServerCrashes();
18736
+ reportPreviousServerCrash();
18605
18737
  const providerNames = registeredProviderNames(config);
18606
18738
  trackEvent("serve.started", {
18607
18739
  providerCount: providerNames.length,
18608
18740
  providers: providerNames,
18741
+ ...installAttribution(),
18609
18742
  // Who launched the server: an agent-started install is its own funnel.
18610
18743
  ...cliRuntimeContext()
18611
18744
  });
@@ -18616,6 +18749,7 @@ Canonry server running at ${url}`);
18616
18749
  `);
18617
18750
  return;
18618
18751
  }
18752
+ trackServerStartFailure(err);
18619
18753
  try {
18620
18754
  await closeWithIdleSweep(app);
18621
18755
  } catch (closeErr) {
@@ -18808,10 +18942,11 @@ var SYSTEM_CLI_COMMANDS = [
18808
18942
  },
18809
18943
  {
18810
18944
  path: ["init"],
18811
- usage: "canonry init [--force] [--gemini-key <key>] [--openai-key <key>] [--claude-key <key>] [--perplexity-key <key>] [--muse-key <key>] [--local-url <url>] [--local-model <name>] [--local-key <key>] [--google-client-id <id>] [--google-client-secret <key>] [--skip-skills] [--skip-mcp] [--skills-dir <path>] [--format json]",
18945
+ usage: "canonry init [--force] [--ref <tag>] [--gemini-key <key>] [--openai-key <key>] [--claude-key <key>] [--perplexity-key <key>] [--muse-key <key>] [--local-url <url>] [--local-model <name>] [--local-key <key>] [--google-client-id <id>] [--google-client-secret <key>] [--skip-skills] [--skip-mcp] [--skills-dir <path>] [--format json]",
18812
18946
  help: 'Optional interactive provider and OAuth setup. First-run Page Health does not need this: use "canonry bootstrap", then "canonry serve". At least one provider is required here. --format json needs provider flags or environment variables.',
18813
18947
  options: {
18814
18948
  force: { type: "boolean", short: "f", default: false },
18949
+ ref: stringOption(),
18815
18950
  "gemini-key": stringOption(),
18816
18951
  "openai-key": stringOption(),
18817
18952
  "claude-key": stringOption(),
@@ -18830,6 +18965,7 @@ var SYSTEM_CLI_COMMANDS = [
18830
18965
  run: async (input) => {
18831
18966
  await initCommand({
18832
18967
  force: getBoolean(input.values, "force"),
18968
+ ref: getString(input.values, "ref"),
18833
18969
  geminiKey: getString(input.values, "gemini-key"),
18834
18970
  openaiKey: getString(input.values, "openai-key"),
18835
18971
  claudeKey: getString(input.values, "claude-key"),
@@ -18849,17 +18985,19 @@ var SYSTEM_CLI_COMMANDS = [
18849
18985
  },
18850
18986
  {
18851
18987
  path: ["bootstrap"],
18852
- usage: "canonry bootstrap [--format json]",
18988
+ usage: "canonry bootstrap [--ref <tag>] [--format json]",
18853
18989
  help: "Create or reconcile the local config, SQLite database, and default API key from supported environment variables. Provider credentials are optional. Safe to rerun: existing settings are preserved and the result reports created, updated, or unchanged. The legacy --force/-f flag is accepted but has no effect.",
18854
18990
  options: {
18855
18991
  // Compatibility for scripts written before bootstrap became idempotent.
18856
18992
  // Keep this out of usage: the flag is unnecessary and has no behavior.
18857
- force: { type: "boolean", short: "f", default: false }
18993
+ force: { type: "boolean", short: "f", default: false },
18994
+ ref: stringOption()
18858
18995
  },
18859
18996
  allowPositionals: false,
18860
18997
  run: async (input) => {
18861
18998
  await bootstrapCommand({
18862
- format: input.format
18999
+ format: input.format,
19000
+ ref: getString(input.values, "ref")
18863
19001
  });
18864
19002
  }
18865
19003
  },
@@ -18915,6 +19053,15 @@ var SYSTEM_CLI_COMMANDS = [
18915
19053
  stopDaemon(input.format);
18916
19054
  }
18917
19055
  },
19056
+ {
19057
+ path: ["support"],
19058
+ usage: "canonry support [--format json]",
19059
+ help: "Where to get help: the Canonry Discord, the issue tracker, and canonry feedback.",
19060
+ allowPositionals: false,
19061
+ run: async (input) => {
19062
+ supportCommand(input.format);
19063
+ }
19064
+ },
18918
19065
  {
18919
19066
  path: ["feedback"],
18920
19067
  usage: FEEDBACK_USAGE,
@@ -19927,14 +20074,14 @@ Usage: ${usage}`, {
19927
20074
  }
19928
20075
  }
19929
20076
  ];
19930
- function siteHealthAlias(sourcePath, path12, usage) {
20077
+ function siteHealthAlias(sourcePath, path13, usage) {
19931
20078
  const source = TECHNICAL_AEO_CLI_COMMANDS_BASE.find(
19932
20079
  (candidate) => candidate.path.join(" ") === sourcePath.join(" ")
19933
20080
  );
19934
20081
  if (!source) throw new Error(`Missing Site Health compatibility source: ${sourcePath.join(" ")}`);
19935
20082
  return {
19936
20083
  ...source,
19937
- path: path12,
20084
+ path: path13,
19938
20085
  usage,
19939
20086
  run: async (input) => {
19940
20087
  try {
@@ -19945,7 +20092,7 @@ function siteHealthAlias(sourcePath, path12, usage) {
19945
20092
  throw usageError(`${firstLine}
19946
20093
  Usage: ${usage}`, {
19947
20094
  message: error.message,
19948
- details: { ...error.details, command: path12.join("."), usage }
20095
+ details: { ...error.details, command: path13.join("."), usage }
19949
20096
  });
19950
20097
  }
19951
20098
  }
@@ -20318,7 +20465,7 @@ var VISIBILITY_STATS_CLI_COMMANDS = [
20318
20465
  ];
20319
20466
 
20320
20467
  // src/cli-commands/wordpress.ts
20321
- import fs19 from "fs";
20468
+ import fs20 from "fs";
20322
20469
 
20323
20470
  // src/commands/wordpress.ts
20324
20471
  function getClient28() {
@@ -20557,12 +20704,12 @@ async function wordpressSetMeta(project2, body) {
20557
20704
  printPageDetail(result);
20558
20705
  }
20559
20706
  async function wordpressBulkSetMeta(project2, opts) {
20560
- const fs20 = await import("fs/promises");
20561
- const path12 = await import("path");
20562
- const filePath = path12.resolve(opts.from);
20707
+ const fs21 = await import("fs/promises");
20708
+ const path13 = await import("path");
20709
+ const filePath = path13.resolve(opts.from);
20563
20710
  let raw;
20564
20711
  try {
20565
- raw = await fs20.readFile(filePath, "utf8");
20712
+ raw = await fs21.readFile(filePath, "utf8");
20566
20713
  } catch {
20567
20714
  throw new CliError({
20568
20715
  code: "FILE_READ_ERROR",
@@ -20659,13 +20806,13 @@ async function wordpressSetSchema(project2, body) {
20659
20806
  printManualAssist(`Schema update for "${body.slug}"`, result);
20660
20807
  }
20661
20808
  async function wordpressSchemaDeploy(project2, opts) {
20662
- const fs20 = await import("fs/promises");
20663
- const path12 = await import("path");
20809
+ const fs21 = await import("fs/promises");
20810
+ const path13 = await import("path");
20664
20811
  const yaml = await loadYamlModule();
20665
- const filePath = path12.resolve(opts.profile);
20812
+ const filePath = path13.resolve(opts.profile);
20666
20813
  let raw;
20667
20814
  try {
20668
- raw = await fs20.readFile(filePath, "utf8");
20815
+ raw = await fs21.readFile(filePath, "utf8");
20669
20816
  } catch {
20670
20817
  throw new CliError({
20671
20818
  code: "FILE_READ_ERROR",
@@ -20770,13 +20917,13 @@ async function wordpressOnboard(project2, opts) {
20770
20917
  }
20771
20918
  let profileData;
20772
20919
  if (opts.profile) {
20773
- const fs20 = await import("fs/promises");
20774
- const path12 = await import("path");
20920
+ const fs21 = await import("fs/promises");
20921
+ const path13 = await import("path");
20775
20922
  const yaml = await loadYamlModule();
20776
- const filePath = path12.resolve(opts.profile);
20923
+ const filePath = path13.resolve(opts.profile);
20777
20924
  let raw;
20778
20925
  try {
20779
- raw = await fs20.readFile(filePath, "utf8");
20926
+ raw = await fs21.readFile(filePath, "utf8");
20780
20927
  } catch {
20781
20928
  throw new CliError({
20782
20929
  code: "FILE_READ_ERROR",
@@ -20925,7 +21072,7 @@ function resolveContent(input, command, usage, options) {
20925
21072
  }
20926
21073
  if (contentFile) {
20927
21074
  try {
20928
- return fs19.readFileSync(contentFile, "utf-8");
21075
+ return fs20.readFileSync(contentFile, "utf-8");
20929
21076
  } catch (error) {
20930
21077
  const message = describeError(error);
20931
21078
  throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
@@ -21403,6 +21550,7 @@ async function agentAttach(opts) {
21403
21550
  const existing = await client.listNotifications(opts.project);
21404
21551
  const hasAgent = existing.some((n) => n.source === "agent");
21405
21552
  if (hasAgent) {
21553
+ trackCliConnection({ integration: "agent_webhook", action: "connect", status: "cancelled", reasonCode: "ALREADY_CONNECTED" });
21406
21554
  if (isMachineFormat(opts.format)) {
21407
21555
  console.log(JSON.stringify({ status: "already-attached", project: opts.project }));
21408
21556
  } else {
@@ -21427,6 +21575,7 @@ async function agentDetach(opts) {
21427
21575
  const existing = await client.listNotifications(opts.project);
21428
21576
  const agentNotif = existing.find((n) => n.source === "agent");
21429
21577
  if (!agentNotif) {
21578
+ trackCliConnection({ integration: "agent_webhook", action: "disconnect", status: "cancelled", reasonCode: "NOT_CONNECTED" });
21430
21579
  if (isMachineFormat(opts.format)) {
21431
21580
  console.log(JSON.stringify({ status: "not-attached", project: opts.project }));
21432
21581
  } else {
@@ -22385,6 +22534,7 @@ Admin:
22385
22534
  backfill Backfill answer visibility or insights
22386
22535
  telemetry Manage anonymous telemetry
22387
22536
  history <project> Show audit trail
22537
+ support Get help: Canonry Discord and issue tracker
22388
22538
 
22389
22539
  Global options:
22390
22540
  --format json Machine-readable output: one JSON document (all commands)
@@ -5,11 +5,11 @@ import {
5
5
  computeFactorAverages,
6
6
  layoutSiteCrawlGraphInput,
7
7
  persistSiteCrawlGraphLayout
8
- } from "./chunk-222NAFTY.js";
8
+ } from "./chunk-7Q7QCBJ3.js";
9
9
  import {
10
10
  PACKAGE_VERSION,
11
11
  canonryMcpTools
12
- } from "./chunk-5IXQAOVK.js";
12
+ } from "./chunk-DS2ZKB3T.js";
13
13
  import {
14
14
  HISTORY_WINDOW_RUNS,
15
15
  IntelligenceService,
@@ -85,7 +85,7 @@ import {
85
85
  siteCrawlPages,
86
86
  siteCrawlSnapshots,
87
87
  trafficSources
88
- } from "./chunk-46JZR2IV.js";
88
+ } from "./chunk-O5UMRQVN.js";
89
89
  import {
90
90
  RunKinds,
91
91
  SiteCrawlFetchStates,
@@ -115,7 +115,7 @@ import {
115
115
  roundPreservingTotal,
116
116
  siteAuditPageFactorSchema,
117
117
  wilsonInterval
118
- } from "./chunk-MRKV43TV.js";
118
+ } from "./chunk-IM2BFAAT.js";
119
119
 
120
120
  // src/demo-server.ts
121
121
  import { dirname, join as join2 } from "path";
package/dist/index.d.ts CHANGED
@@ -423,6 +423,8 @@ interface CanonryConfig {
423
423
  research?: ResearchConfigEntry;
424
424
  telemetry?: boolean;
425
425
  anonymousId?: string;
426
+ /** First-touch campaign tag (`--ref` / `CANONRY_REF`), kept once and sent with install telemetry. */
427
+ installRef?: string;
426
428
  lastSeenVersion?: string;
427
429
  /**
428
430
  * The engine version the installed skill trees were last synced against.
package/dist/index.js CHANGED
@@ -3,14 +3,14 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-3OPV5XBY.js";
6
+ } from "./chunk-SG3MPCDL.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-UF7MH6TU.js";
10
- import "./chunk-222NAFTY.js";
11
- import "./chunk-5IXQAOVK.js";
12
- import "./chunk-46JZR2IV.js";
13
- import "./chunk-MRKV43TV.js";
9
+ } from "./chunk-JJBVYL45.js";
10
+ import "./chunk-7Q7QCBJ3.js";
11
+ import "./chunk-DS2ZKB3T.js";
12
+ import "./chunk-O5UMRQVN.js";
13
+ import "./chunk-IM2BFAAT.js";
14
14
  export {
15
15
  GoogleMarketingRuntimeError,
16
16
  createGoogleMarketingCredentialStore,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  HISTORY_WINDOW_RUNS,
3
3
  IntelligenceService
4
- } from "./chunk-46JZR2IV.js";
5
- import "./chunk-MRKV43TV.js";
4
+ } from "./chunk-O5UMRQVN.js";
5
+ import "./chunk-IM2BFAAT.js";
6
6
  export {
7
7
  HISTORY_WINDOW_RUNS,
8
8
  IntelligenceService