@ainyc/canonry 4.14.0 → 4.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  coerceAgentProvider,
4
4
  computeCompetitorOverlap,
5
5
  createServer,
6
+ detectAndTrackUpgrade,
6
7
  determineCitationState,
7
8
  extractRecommendedCompetitors,
8
9
  formatAuditFactorScore,
@@ -16,9 +17,10 @@ import {
16
17
  reparseStoredResult3,
17
18
  reparseStoredResult4,
18
19
  setGoogleAuthConfig,
20
+ setTelemetrySource,
19
21
  showFirstRunNotice,
20
22
  trackEvent
21
- } from "./chunk-UQHWSCTE.js";
23
+ } from "./chunk-IVNWS2YU.js";
22
24
  import {
23
25
  CliError,
24
26
  EXIT_SYSTEM_ERROR,
@@ -29,11 +31,12 @@ import {
29
31
  getConfigPath,
30
32
  isEndpointMissing,
31
33
  loadConfig,
34
+ loadConfigRaw,
32
35
  printCliError,
33
36
  saveConfig,
34
37
  saveConfigPatch,
35
38
  usageError
36
- } from "./chunk-5NYG5EC7.js";
39
+ } from "./chunk-7SRKUAZO.js";
37
40
  import {
38
41
  apiKeys,
39
42
  competitors,
@@ -43,9 +46,10 @@ import {
43
46
  migrate,
44
47
  parseJsonColumn,
45
48
  projects,
49
+ queries,
46
50
  querySnapshots,
47
51
  runs
48
- } from "./chunk-7HBZCGRL.js";
52
+ } from "./chunk-MI33SQL6.js";
49
53
  import {
50
54
  CcReleaseSyncStatuses,
51
55
  CheckScopes,
@@ -56,6 +60,7 @@ import {
56
60
  RunKinds,
57
61
  RunStatuses,
58
62
  SkillsClients,
63
+ TrafficEventKinds,
59
64
  determineAnswerMentioned,
60
65
  effectiveDomains,
61
66
  formatRunErrorOneLine,
@@ -64,11 +69,47 @@ import {
64
69
  providerQuotaPolicySchema,
65
70
  resolveProviderInput,
66
71
  skillsClientSchema
67
- } from "./chunk-6QTH5NS5.js";
72
+ } from "./chunk-ONI3TX2A.js";
68
73
 
69
74
  // src/cli.ts
70
75
  import { pathToFileURL } from "url";
71
76
 
77
+ // src/setup-state.ts
78
+ import fs from "fs";
79
+ function buildSetupState() {
80
+ if (!configExists()) return void 0;
81
+ let provider_count = 0;
82
+ let has_keywords = false;
83
+ let project_count = 0;
84
+ let is_first_run = true;
85
+ let dbPath;
86
+ try {
87
+ const raw = loadConfigRaw();
88
+ if (raw) {
89
+ is_first_run = !raw.anonymousId;
90
+ if (raw.providers) {
91
+ provider_count = Object.values(raw.providers).filter(
92
+ (p) => Boolean(p?.apiKey) || Boolean(p?.baseUrl)
93
+ ).length;
94
+ }
95
+ if (typeof raw.database === "string" && raw.database.length > 0) {
96
+ dbPath = raw.database;
97
+ }
98
+ }
99
+ } catch {
100
+ }
101
+ if (dbPath && fs.existsSync(dbPath)) {
102
+ try {
103
+ const db = createClient(dbPath);
104
+ project_count = db.select({ id: projects.id }).from(projects).all().length;
105
+ const firstQuery = db.select({ id: queries.id }).from(queries).limit(1).all();
106
+ has_keywords = firstQuery.length > 0;
107
+ } catch {
108
+ }
109
+ }
110
+ return { provider_count, has_keywords, project_count, is_first_run };
111
+ }
112
+
72
113
  // src/cli-dispatch.ts
73
114
  import { parseArgs } from "util";
74
115
  function commandId(spec) {
@@ -580,7 +621,7 @@ function readStoredGroundingSources(rawResponse) {
580
621
  return result;
581
622
  }
582
623
  async function backfillInsightsCommand(project, opts) {
583
- const { IntelligenceService } = await import("./intelligence-service-BCKXIKIL.js");
624
+ const { IntelligenceService } = await import("./intelligence-service-JYV3CO4H.js");
584
625
  const config = loadConfig();
585
626
  const db = createClient(config.database);
586
627
  migrate(db);
@@ -2018,9 +2059,9 @@ async function gaConnect(project, opts) {
2018
2059
  propertyId: opts.propertyId
2019
2060
  };
2020
2061
  if (opts.keyFile) {
2021
- const fs12 = await import("fs");
2062
+ const fs13 = await import("fs");
2022
2063
  try {
2023
- const content = fs12.readFileSync(opts.keyFile, "utf-8");
2064
+ const content = fs13.readFileSync(opts.keyFile, "utf-8");
2024
2065
  JSON.parse(content);
2025
2066
  body.keyJson = content;
2026
2067
  } catch (e) {
@@ -2698,10 +2739,10 @@ async function trafficConnectCloudRun(project, opts) {
2698
2739
  details: { project }
2699
2740
  });
2700
2741
  }
2701
- const fs12 = await import("fs");
2742
+ const fs13 = await import("fs");
2702
2743
  let keyJson;
2703
2744
  try {
2704
- keyJson = fs12.readFileSync(opts.serviceAccountKey, "utf-8");
2745
+ keyJson = fs13.readFileSync(opts.serviceAccountKey, "utf-8");
2705
2746
  JSON.parse(keyJson);
2706
2747
  } catch (e) {
2707
2748
  const msg = e instanceof Error ? e.message : String(e);
@@ -2761,6 +2802,128 @@ async function trafficSync(project, opts) {
2761
2802
  console.log(` Sample rows: ${result.sampleRows}`);
2762
2803
  console.log(` Synced at: ${result.syncedAt}`);
2763
2804
  }
2805
+ function formatSourceLine(source) {
2806
+ const parts = [
2807
+ source.id,
2808
+ source.sourceType,
2809
+ source.status,
2810
+ source.lastSyncedAt ?? "never synced",
2811
+ source.displayName
2812
+ ];
2813
+ return parts.join(" ");
2814
+ }
2815
+ async function trafficSources(project, opts) {
2816
+ const client = createApiClient();
2817
+ const result = await client.trafficListSources(project);
2818
+ if (opts.format === "json") {
2819
+ console.log(JSON.stringify(result, null, 2));
2820
+ return;
2821
+ }
2822
+ if (result.sources.length === 0) {
2823
+ console.log(`No traffic sources connected for project "${project}".`);
2824
+ console.log("Run: canonry traffic connect cloud-run <project> --gcp-project <id> --service-account-key <path>");
2825
+ return;
2826
+ }
2827
+ console.log(`Traffic sources for "${project}":`);
2828
+ console.log(" ID TYPE STATUS LAST_SYNCED DISPLAY_NAME");
2829
+ for (const source of result.sources) {
2830
+ console.log(` ${formatSourceLine(source)}`);
2831
+ }
2832
+ }
2833
+ async function trafficStatus(project, opts) {
2834
+ const client = createApiClient();
2835
+ const result = await client.trafficStatus(project);
2836
+ const details = result.sources;
2837
+ if (opts.format === "json") {
2838
+ console.log(JSON.stringify(result, null, 2));
2839
+ return;
2840
+ }
2841
+ if (details.length === 0) {
2842
+ console.log(`No traffic sources connected for project "${project}".`);
2843
+ console.log("Run: canonry traffic connect cloud-run <project> --gcp-project <id> --service-account-key <path>");
2844
+ return;
2845
+ }
2846
+ for (const d of details) {
2847
+ console.log(`Source ${d.id} (${d.sourceType})`);
2848
+ console.log(` Display name: ${d.displayName}`);
2849
+ console.log(` Status: ${d.status}`);
2850
+ console.log(` Last synced: ${d.lastSyncedAt ?? "never"}`);
2851
+ if (d.lastError) console.log(` Last error: ${d.lastError}`);
2852
+ console.log(` 24h crawler: ${d.totals24h.crawlerHits} hits`);
2853
+ console.log(` 24h AI referral: ${d.totals24h.aiReferralHits} hits`);
2854
+ console.log(` 24h samples: ${d.totals24h.sampleCount}`);
2855
+ if (d.latestRun) {
2856
+ console.log(` Latest run: ${d.latestRun.runId} (${d.latestRun.status})`);
2857
+ console.log(` Started: ${d.latestRun.startedAt}`);
2858
+ if (d.latestRun.finishedAt) console.log(` Finished: ${d.latestRun.finishedAt}`);
2859
+ if (d.latestRun.error) console.log(` Error: ${d.latestRun.error}`);
2860
+ } else {
2861
+ console.log(` Latest run: (none)`);
2862
+ }
2863
+ console.log("");
2864
+ }
2865
+ }
2866
+ function formatEventLine(event) {
2867
+ if (event.kind === TrafficEventKinds.crawler) {
2868
+ return [
2869
+ event.tsHour,
2870
+ "crawler",
2871
+ event.botId,
2872
+ event.verificationStatus,
2873
+ String(event.status),
2874
+ event.pathNormalized,
2875
+ `${event.hits} hits`
2876
+ ].join(" ");
2877
+ }
2878
+ return [
2879
+ event.tsHour,
2880
+ "ai-referral",
2881
+ event.product,
2882
+ event.evidenceType,
2883
+ event.sourceDomain,
2884
+ event.landingPathNormalized,
2885
+ `${event.hits} hits`
2886
+ ].join(" ");
2887
+ }
2888
+ async function trafficEvents(project, opts) {
2889
+ if (opts.kind && opts.kind !== "all" && opts.kind !== TrafficEventKinds.crawler && opts.kind !== TrafficEventKinds["ai-referral"]) {
2890
+ throw new CliError({
2891
+ code: "TRAFFIC_INVALID_KIND",
2892
+ message: `--kind must be one of: all, ${TrafficEventKinds.crawler}, ${TrafficEventKinds["ai-referral"]}`,
2893
+ displayMessage: `Error: --kind must be "all", "${TrafficEventKinds.crawler}", or "${TrafficEventKinds["ai-referral"]}"`,
2894
+ details: { project, kind: opts.kind }
2895
+ });
2896
+ }
2897
+ const params = {};
2898
+ if (opts.kind && opts.kind !== "all") params.kind = opts.kind;
2899
+ if (opts.source) params.sourceId = opts.source;
2900
+ if (opts.limit !== void 0) params.limit = opts.limit;
2901
+ if (opts.sinceMinutes !== void 0) {
2902
+ const since = new Date(Date.now() - opts.sinceMinutes * 6e4).toISOString();
2903
+ params.since = since;
2904
+ } else if (opts.since) {
2905
+ params.since = opts.since;
2906
+ }
2907
+ if (opts.until) params.until = opts.until;
2908
+ const client = createApiClient();
2909
+ const result = await client.trafficListEvents(project, params);
2910
+ if (opts.format === "json") {
2911
+ console.log(JSON.stringify(result, null, 2));
2912
+ return;
2913
+ }
2914
+ console.log(`Traffic events for "${project}" ${result.windowStart} \u2192 ${result.windowEnd}`);
2915
+ console.log(` Crawler hits (window): ${result.totals.crawlerHits}`);
2916
+ console.log(` AI referral hits (window): ${result.totals.aiReferralHits}`);
2917
+ console.log("");
2918
+ if (result.events.length === 0) {
2919
+ console.log("No events in this window.");
2920
+ return;
2921
+ }
2922
+ console.log(" TS_HOUR KIND IDENTITY EVIDENCE/STATUS PATH HITS");
2923
+ for (const event of result.events) {
2924
+ console.log(` ${formatEventLine(event)}`);
2925
+ }
2926
+ }
2764
2927
 
2765
2928
  // src/cli-commands/traffic.ts
2766
2929
  var TRAFFIC_CLI_COMMANDS = [
@@ -2807,7 +2970,7 @@ var TRAFFIC_CLI_COMMANDS = [
2807
2970
  },
2808
2971
  {
2809
2972
  path: ["traffic", "sync"],
2810
- usage: "canonry traffic sync <project> --source <id> [--since-minutes 60] [--format json]",
2973
+ usage: "canonry traffic sync <project> --source <id> [--since-minutes 43200] [--format json]",
2811
2974
  options: {
2812
2975
  source: stringOption(),
2813
2976
  "since-minutes": stringOption()
@@ -2816,12 +2979,15 @@ var TRAFFIC_CLI_COMMANDS = [
2816
2979
  const project = requireProject(
2817
2980
  input,
2818
2981
  "traffic.sync",
2819
- "canonry traffic sync <project> --source <id> [--since-minutes 60]"
2982
+ "canonry traffic sync <project> --source <id> [--since-minutes 43200]"
2820
2983
  );
2821
2984
  const source = getString(input.values, "source");
2822
2985
  if (!source) throw new Error("--source <id> is required");
2823
- const sinceStr = getString(input.values, "since-minutes");
2824
- const sinceMinutes = sinceStr ? parseInt(sinceStr, 10) : void 0;
2986
+ const sinceMinutes = parseIntegerOption(input, "since-minutes", {
2987
+ command: "traffic.sync",
2988
+ usage: "canonry traffic sync <project> --source <id> [--since-minutes 43200]",
2989
+ message: "--since-minutes must be an integer"
2990
+ });
2825
2991
  await trafficSync(project, {
2826
2992
  source,
2827
2993
  sinceMinutes,
@@ -2829,6 +2995,68 @@ var TRAFFIC_CLI_COMMANDS = [
2829
2995
  });
2830
2996
  }
2831
2997
  },
2998
+ {
2999
+ path: ["traffic", "sources"],
3000
+ usage: "canonry traffic sources <project> [--format json]",
3001
+ run: async (input) => {
3002
+ const project = requireProject(
3003
+ input,
3004
+ "traffic.sources",
3005
+ "canonry traffic sources <project>"
3006
+ );
3007
+ await trafficSources(project, { format: input.format });
3008
+ }
3009
+ },
3010
+ {
3011
+ path: ["traffic", "status"],
3012
+ usage: "canonry traffic status <project> [--format json]",
3013
+ run: async (input) => {
3014
+ const project = requireProject(
3015
+ input,
3016
+ "traffic.status",
3017
+ "canonry traffic status <project>"
3018
+ );
3019
+ await trafficStatus(project, { format: input.format });
3020
+ }
3021
+ },
3022
+ {
3023
+ path: ["traffic", "events"],
3024
+ usage: "canonry traffic events <project> [--kind crawler|ai-referral|all] [--source <id>] [--since-minutes 1440] [--since <iso>] [--until <iso>] [--limit 500] [--format json]",
3025
+ options: {
3026
+ kind: stringOption(),
3027
+ source: stringOption(),
3028
+ "since-minutes": stringOption(),
3029
+ since: stringOption(),
3030
+ until: stringOption(),
3031
+ limit: stringOption()
3032
+ },
3033
+ run: async (input) => {
3034
+ const project = requireProject(
3035
+ input,
3036
+ "traffic.events",
3037
+ "canonry traffic events <project>"
3038
+ );
3039
+ const sinceMinutes = parseIntegerOption(input, "since-minutes", {
3040
+ command: "traffic.events",
3041
+ usage: "canonry traffic events <project> [--since-minutes 1440]",
3042
+ message: "--since-minutes must be an integer"
3043
+ });
3044
+ const limit = parseIntegerOption(input, "limit", {
3045
+ command: "traffic.events",
3046
+ usage: "canonry traffic events <project> [--limit 500]",
3047
+ message: "--limit must be an integer"
3048
+ });
3049
+ await trafficEvents(project, {
3050
+ kind: getString(input.values, "kind"),
3051
+ source: getString(input.values, "source"),
3052
+ sinceMinutes,
3053
+ since: getString(input.values, "since"),
3054
+ until: getString(input.values, "until"),
3055
+ limit,
3056
+ format: input.format
3057
+ });
3058
+ }
3059
+ },
2832
3060
  {
2833
3061
  path: ["traffic"],
2834
3062
  usage: "canonry traffic <subcommand> <project> [args]",
@@ -2836,7 +3064,7 @@ var TRAFFIC_CLI_COMMANDS = [
2836
3064
  unknownSubcommand(input.positionals[0], {
2837
3065
  command: "traffic",
2838
3066
  usage: "canonry traffic <subcommand> <project> [args]",
2839
- available: ["connect", "sync"]
3067
+ available: ["connect", "sync", "status", "sources", "events"]
2840
3068
  });
2841
3069
  }
2842
3070
  }
@@ -3837,7 +4065,7 @@ var GOOGLE_CLI_COMMANDS = [
3837
4065
  ];
3838
4066
 
3839
4067
  // src/commands/keyword.ts
3840
- import fs from "fs";
4068
+ import fs2 from "fs";
3841
4069
  function getClient8() {
3842
4070
  return createApiClient();
3843
4071
  }
@@ -3902,7 +4130,7 @@ async function listKeywords(project, format) {
3902
4130
  }
3903
4131
  }
3904
4132
  async function importKeywords(project, filePath, format) {
3905
- if (!fs.existsSync(filePath)) {
4133
+ if (!fs2.existsSync(filePath)) {
3906
4134
  throw new CliError({
3907
4135
  code: "KEYWORD_IMPORT_FILE_NOT_FOUND",
3908
4136
  message: `File not found: ${filePath}`,
@@ -3913,7 +4141,7 @@ async function importKeywords(project, filePath, format) {
3913
4141
  }
3914
4142
  });
3915
4143
  }
3916
- const content = fs.readFileSync(filePath, "utf-8");
4144
+ const content = fs2.readFileSync(filePath, "utf-8");
3917
4145
  const keywords = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
3918
4146
  if (keywords.length === 0) {
3919
4147
  if (format === "json") {
@@ -4114,46 +4342,46 @@ var KEYWORD_CLI_COMMANDS = [
4114
4342
  ];
4115
4343
 
4116
4344
  // src/commands/query.ts
4117
- import fs2 from "fs";
4345
+ import fs3 from "fs";
4118
4346
  function getClient9() {
4119
4347
  return createApiClient();
4120
4348
  }
4121
- async function addQueries(project, queries, format) {
4349
+ async function addQueries(project, queries2, format) {
4122
4350
  const client = getClient9();
4123
- await client.appendQueries(project, queries);
4351
+ await client.appendQueries(project, queries2);
4124
4352
  if (format === "json") {
4125
4353
  console.log(JSON.stringify({
4126
4354
  project,
4127
- queries,
4128
- addedCount: queries.length
4355
+ queries: queries2,
4356
+ addedCount: queries2.length
4129
4357
  }, null, 2));
4130
4358
  return;
4131
4359
  }
4132
- console.log(`Added ${queries.length} ${queries.length === 1 ? "query" : "queries"} to "${project}".`);
4360
+ console.log(`Added ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} to "${project}".`);
4133
4361
  }
4134
- async function replaceQueries(project, queries, format) {
4362
+ async function replaceQueries(project, queries2, format) {
4135
4363
  const client = getClient9();
4136
- await client.putQueries(project, queries);
4364
+ await client.putQueries(project, queries2);
4137
4365
  if (format === "json") {
4138
4366
  console.log(JSON.stringify({
4139
4367
  project,
4140
- queries,
4141
- replacedCount: queries.length
4368
+ queries: queries2,
4369
+ replacedCount: queries2.length
4142
4370
  }, null, 2));
4143
4371
  return;
4144
4372
  }
4145
- console.log(`Set ${queries.length} ${queries.length === 1 ? "query" : "queries"} for "${project}".`);
4373
+ console.log(`Set ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} for "${project}".`);
4146
4374
  }
4147
- async function removeQueries(project, queries, format) {
4375
+ async function removeQueries(project, queries2, format) {
4148
4376
  const client = getClient9();
4149
4377
  const existing = await client.listQueries(project);
4150
4378
  const existingSet = new Set(existing.map((q) => q.query));
4151
- const removedQueries = queries.filter((q) => existingSet.has(q));
4152
- await client.deleteQueries(project, queries);
4379
+ const removedQueries = queries2.filter((q) => existingSet.has(q));
4380
+ await client.deleteQueries(project, queries2);
4153
4381
  if (format === "json") {
4154
4382
  console.log(JSON.stringify({
4155
4383
  project,
4156
- queries,
4384
+ queries: queries2,
4157
4385
  removedQueries,
4158
4386
  removedCount: removedQueries.length
4159
4387
  }, null, 2));
@@ -4179,7 +4407,7 @@ async function listQueries(project, format) {
4179
4407
  }
4180
4408
  }
4181
4409
  async function importQueries(project, filePath, format) {
4182
- if (!fs2.existsSync(filePath)) {
4410
+ if (!fs3.existsSync(filePath)) {
4183
4411
  throw new CliError({
4184
4412
  code: "QUERY_IMPORT_FILE_NOT_FOUND",
4185
4413
  message: `File not found: ${filePath}`,
@@ -4190,9 +4418,9 @@ async function importQueries(project, filePath, format) {
4190
4418
  }
4191
4419
  });
4192
4420
  }
4193
- const content = fs2.readFileSync(filePath, "utf-8");
4194
- const queries = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
4195
- if (queries.length === 0) {
4421
+ const content = fs3.readFileSync(filePath, "utf-8");
4422
+ const queries2 = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
4423
+ if (queries2.length === 0) {
4196
4424
  if (format === "json") {
4197
4425
  console.log(JSON.stringify({
4198
4426
  project,
@@ -4206,17 +4434,17 @@ async function importQueries(project, filePath, format) {
4206
4434
  return;
4207
4435
  }
4208
4436
  const client = getClient9();
4209
- await client.appendQueries(project, queries);
4437
+ await client.appendQueries(project, queries2);
4210
4438
  if (format === "json") {
4211
4439
  console.log(JSON.stringify({
4212
4440
  project,
4213
4441
  filePath,
4214
- queries,
4215
- importedCount: queries.length
4442
+ queries: queries2,
4443
+ importedCount: queries2.length
4216
4444
  }, null, 2));
4217
4445
  return;
4218
4446
  }
4219
- console.log(`Imported ${queries.length} ${queries.length === 1 ? "query" : "queries"} to "${project}".`);
4447
+ console.log(`Imported ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} to "${project}".`);
4220
4448
  }
4221
4449
  async function generateQueries(project, provider, opts) {
4222
4450
  const client = getClient9();
@@ -4259,8 +4487,8 @@ var QUERY_CLI_COMMANDS = [
4259
4487
  usage: "canonry query add <project> <query...> [--format json]",
4260
4488
  run: async (input) => {
4261
4489
  const project = requireProject(input, "query.add", "canonry query add <project> <query...> [--format json]");
4262
- const queries = input.positionals.slice(1);
4263
- if (queries.length === 0) {
4490
+ const queries2 = input.positionals.slice(1);
4491
+ if (queries2.length === 0) {
4264
4492
  throw usageError("Error: project name and at least one query required\nUsage: canonry query add <project> <query...> [--format json]", {
4265
4493
  message: "project name and at least one query required",
4266
4494
  details: {
@@ -4269,7 +4497,7 @@ var QUERY_CLI_COMMANDS = [
4269
4497
  }
4270
4498
  });
4271
4499
  }
4272
- await addQueries(project, queries, input.format);
4500
+ await addQueries(project, queries2, input.format);
4273
4501
  }
4274
4502
  },
4275
4503
  {
@@ -4277,8 +4505,8 @@ var QUERY_CLI_COMMANDS = [
4277
4505
  usage: "canonry query replace <project> <query...> [--format json]",
4278
4506
  run: async (input) => {
4279
4507
  const project = requireProject(input, "query.replace", "canonry query replace <project> <query...> [--format json]");
4280
- const queries = input.positionals.slice(1);
4281
- if (queries.length === 0) {
4508
+ const queries2 = input.positionals.slice(1);
4509
+ if (queries2.length === 0) {
4282
4510
  throw usageError("Error: project name and at least one query required\nUsage: canonry query replace <project> <query...> [--format json]", {
4283
4511
  message: "project name and at least one query required",
4284
4512
  details: {
@@ -4287,7 +4515,7 @@ var QUERY_CLI_COMMANDS = [
4287
4515
  }
4288
4516
  });
4289
4517
  }
4290
- await replaceQueries(project, queries, input.format);
4518
+ await replaceQueries(project, queries2, input.format);
4291
4519
  }
4292
4520
  },
4293
4521
  {
@@ -4295,8 +4523,8 @@ var QUERY_CLI_COMMANDS = [
4295
4523
  usage: "canonry query remove <project> <query...> [--format json]",
4296
4524
  run: async (input) => {
4297
4525
  const project = requireProject(input, "query.remove", "canonry query remove <project> <query...> [--format json]");
4298
- const queries = input.positionals.slice(1);
4299
- if (queries.length === 0) {
4526
+ const queries2 = input.positionals.slice(1);
4527
+ if (queries2.length === 0) {
4300
4528
  throw usageError("Error: project name and at least one query required\nUsage: canonry query remove <project> <query...> [--format json]", {
4301
4529
  message: "project name and at least one query required",
4302
4530
  details: {
@@ -4305,7 +4533,7 @@ var QUERY_CLI_COMMANDS = [
4305
4533
  }
4306
4534
  });
4307
4535
  }
4308
- await removeQueries(project, queries, input.format);
4536
+ await removeQueries(project, queries2, input.format);
4309
4537
  }
4310
4538
  },
4311
4539
  {
@@ -4313,8 +4541,8 @@ var QUERY_CLI_COMMANDS = [
4313
4541
  usage: "canonry query delete <project> <query...> [--format json]",
4314
4542
  run: async (input) => {
4315
4543
  const project = requireProject(input, "query.delete", "canonry query delete <project> <query...> [--format json]");
4316
- const queries = input.positionals.slice(1);
4317
- if (queries.length === 0) {
4544
+ const queries2 = input.positionals.slice(1);
4545
+ if (queries2.length === 0) {
4318
4546
  throw usageError("Error: project name and at least one query required\nUsage: canonry query delete <project> <query...> [--format json]", {
4319
4547
  message: "project name and at least one query required",
4320
4548
  details: {
@@ -4323,7 +4551,7 @@ var QUERY_CLI_COMMANDS = [
4323
4551
  }
4324
4552
  });
4325
4553
  }
4326
- await removeQueries(project, queries, input.format);
4554
+ await removeQueries(project, queries2, input.format);
4327
4555
  }
4328
4556
  },
4329
4557
  {
@@ -4391,7 +4619,7 @@ var QUERY_CLI_COMMANDS = [
4391
4619
  ];
4392
4620
 
4393
4621
  // src/commands/mcp.ts
4394
- import fs3 from "fs";
4622
+ import fs4 from "fs";
4395
4623
  import path2 from "path";
4396
4624
  import { createRequire } from "module";
4397
4625
 
@@ -4497,8 +4725,8 @@ function renderClientSnippet(client, serverName, entry) {
4497
4725
  return renderJsonSnippet(serverName, entry, client.format);
4498
4726
  }
4499
4727
  function readJsonConfig(configPath) {
4500
- if (!fs3.existsSync(configPath)) return {};
4501
- const raw = fs3.readFileSync(configPath, "utf-8").trim();
4728
+ if (!fs4.existsSync(configPath)) return {};
4729
+ const raw = fs4.readFileSync(configPath, "utf-8").trim();
4502
4730
  if (!raw) return {};
4503
4731
  try {
4504
4732
  const parsed = JSON.parse(raw);
@@ -4516,14 +4744,14 @@ function readJsonConfig(configPath) {
4516
4744
  }
4517
4745
  }
4518
4746
  function writeJsonConfig(configPath, value) {
4519
- fs3.mkdirSync(path2.dirname(configPath), { recursive: true });
4520
- fs3.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
4747
+ fs4.mkdirSync(path2.dirname(configPath), { recursive: true });
4748
+ fs4.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
4521
4749
  `, "utf-8");
4522
4750
  }
4523
4751
  function backupConfigIfPresent(configPath) {
4524
- if (!fs3.existsSync(configPath)) return void 0;
4752
+ if (!fs4.existsSync(configPath)) return void 0;
4525
4753
  const backupPath = `${configPath}.canonry.bak`;
4526
- fs3.copyFileSync(configPath, backupPath);
4754
+ fs4.copyFileSync(configPath, backupPath);
4527
4755
  return backupPath;
4528
4756
  }
4529
4757
  function findClientOrThrow(id) {
@@ -4870,13 +5098,13 @@ var NOTIFY_CLI_COMMANDS = [
4870
5098
  ];
4871
5099
 
4872
5100
  // src/commands/apply.ts
4873
- import fs4 from "fs";
5101
+ import fs5 from "fs";
4874
5102
  import { parseAllDocuments } from "yaml";
4875
5103
  async function applyConfigFile(filePath) {
4876
- if (!fs4.existsSync(filePath)) {
5104
+ if (!fs5.existsSync(filePath)) {
4877
5105
  throw new Error(`File not found: ${filePath}`);
4878
5106
  }
4879
- const content = fs4.readFileSync(filePath, "utf-8");
5107
+ const content = fs5.readFileSync(filePath, "utf-8");
4880
5108
  const docs = parseAllDocuments(content);
4881
5109
  const client = createApiClient();
4882
5110
  const errors = [];
@@ -5620,7 +5848,7 @@ var PROJECT_CLI_COMMANDS = [
5620
5848
  ];
5621
5849
 
5622
5850
  // src/commands/report.ts
5623
- import fs5 from "fs";
5851
+ import fs6 from "fs";
5624
5852
  import path3 from "path";
5625
5853
  function defaultOutputPath(project, audience) {
5626
5854
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -5637,10 +5865,10 @@ async function runReportCommand(project, opts = {}) {
5637
5865
  const html = renderReportHtml(report, { audience });
5638
5866
  const targetPath = opts.output ? path3.resolve(opts.output) : defaultOutputPath(project, audience);
5639
5867
  const dir = path3.dirname(targetPath);
5640
- if (!fs5.existsSync(dir)) {
5641
- fs5.mkdirSync(dir, { recursive: true });
5868
+ if (!fs6.existsSync(dir)) {
5869
+ fs6.mkdirSync(dir, { recursive: true });
5642
5870
  }
5643
- fs5.writeFileSync(targetPath, html, "utf-8");
5871
+ fs6.writeFileSync(targetPath, html, "utf-8");
5644
5872
  console.log(`Report written to ${targetPath}`);
5645
5873
  }
5646
5874
 
@@ -6381,7 +6609,7 @@ Usage: canonry settings provider ${name} --api-key <key> [--model <model>] [--ma
6381
6609
  ];
6382
6610
 
6383
6611
  // src/commands/skills.ts
6384
- import fs6 from "fs";
6612
+ import fs7 from "fs";
6385
6613
  import path4 from "path";
6386
6614
  import { fileURLToPath } from "url";
6387
6615
  var BUNDLED_SKILL_NAMES = ["canonry-setup", "aero"];
@@ -6393,7 +6621,7 @@ function resolveBundledSkillsRoot(pkgDir) {
6393
6621
  path4.join(here, "../../../../skills")
6394
6622
  ];
6395
6623
  for (const candidate of candidates) {
6396
- if (BUNDLED_SKILL_NAMES.every((name) => fs6.existsSync(path4.join(candidate, name, "SKILL.md")))) {
6624
+ if (BUNDLED_SKILL_NAMES.every((name) => fs7.existsSync(path4.join(candidate, name, "SKILL.md")))) {
6397
6625
  return candidate;
6398
6626
  }
6399
6627
  }
@@ -6416,13 +6644,13 @@ function getBundledSkills(pkgDir) {
6416
6644
  return BUNDLED_SKILL_NAMES.map((name) => {
6417
6645
  const skillDir = path4.join(root, name);
6418
6646
  const skillFile = path4.join(skillDir, "SKILL.md");
6419
- const content = fs6.readFileSync(skillFile, "utf-8");
6647
+ const content = fs7.readFileSync(skillFile, "utf-8");
6420
6648
  return { name, description: parseDescription(content), bundledPath: skillDir };
6421
6649
  });
6422
6650
  }
6423
6651
  function walkRelative(dir, prefix = "") {
6424
6652
  const out = [];
6425
- for (const entry of fs6.readdirSync(dir, { withFileTypes: true })) {
6653
+ for (const entry of fs7.readdirSync(dir, { withFileTypes: true })) {
6426
6654
  const rel = prefix ? path4.join(prefix, entry.name) : entry.name;
6427
6655
  const full = path4.join(dir, entry.name);
6428
6656
  if (entry.isDirectory()) {
@@ -6434,28 +6662,28 @@ function walkRelative(dir, prefix = "") {
6434
6662
  return out.sort();
6435
6663
  }
6436
6664
  function compareDirContent(srcDir, destDir) {
6437
- if (!fs6.existsSync(destDir)) return "missing";
6438
- if (!fs6.statSync(destDir).isDirectory()) return "different";
6665
+ if (!fs7.existsSync(destDir)) return "missing";
6666
+ if (!fs7.statSync(destDir).isDirectory()) return "different";
6439
6667
  const srcFiles = walkRelative(srcDir);
6440
6668
  const destFiles = walkRelative(destDir);
6441
6669
  if (srcFiles.length !== destFiles.length) return "different";
6442
6670
  for (let i = 0; i < srcFiles.length; i++) {
6443
6671
  if (srcFiles[i] !== destFiles[i]) return "different";
6444
- const srcBytes = fs6.readFileSync(path4.join(srcDir, srcFiles[i]));
6445
- const destBytes = fs6.readFileSync(path4.join(destDir, destFiles[i]));
6672
+ const srcBytes = fs7.readFileSync(path4.join(srcDir, srcFiles[i]));
6673
+ const destBytes = fs7.readFileSync(path4.join(destDir, destFiles[i]));
6446
6674
  if (!srcBytes.equals(destBytes)) return "different";
6447
6675
  }
6448
6676
  return "match";
6449
6677
  }
6450
6678
  function copyDirRecursive(src, dest) {
6451
- fs6.mkdirSync(dest, { recursive: true });
6452
- for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
6679
+ fs7.mkdirSync(dest, { recursive: true });
6680
+ for (const entry of fs7.readdirSync(src, { withFileTypes: true })) {
6453
6681
  const srcPath = path4.join(src, entry.name);
6454
6682
  const destPath = path4.join(dest, entry.name);
6455
6683
  if (entry.isDirectory()) {
6456
6684
  copyDirRecursive(srcPath, destPath);
6457
6685
  } else if (entry.isFile()) {
6458
- fs6.copyFileSync(srcPath, destPath);
6686
+ fs7.copyFileSync(srcPath, destPath);
6459
6687
  }
6460
6688
  }
6461
6689
  }
@@ -6480,7 +6708,7 @@ function installClaudeSkill(skill, targetDir, force) {
6480
6708
  });
6481
6709
  }
6482
6710
  if (compare === "different") {
6483
- fs6.rmSync(targetPath, { recursive: true, force: true });
6711
+ fs7.rmSync(targetPath, { recursive: true, force: true });
6484
6712
  }
6485
6713
  copyDirRecursive(skill.bundledPath, targetPath);
6486
6714
  return {
@@ -6495,15 +6723,15 @@ function installCodexSymlink(skill, targetDir, force) {
6495
6723
  const codexPath = path4.join(targetDir, ".codex", "skills", skill.name);
6496
6724
  const claudePath = path4.join(targetDir, ".claude", "skills", skill.name);
6497
6725
  const linkTarget = path4.relative(path4.dirname(codexPath), claudePath);
6498
- fs6.mkdirSync(path4.dirname(codexPath), { recursive: true });
6726
+ fs7.mkdirSync(path4.dirname(codexPath), { recursive: true });
6499
6727
  let stat;
6500
6728
  try {
6501
- stat = fs6.lstatSync(codexPath);
6729
+ stat = fs7.lstatSync(codexPath);
6502
6730
  } catch {
6503
6731
  stat = void 0;
6504
6732
  }
6505
6733
  if (stat?.isSymbolicLink()) {
6506
- const existing = fs6.readlinkSync(codexPath);
6734
+ const existing = fs7.readlinkSync(codexPath);
6507
6735
  if (existing === linkTarget) {
6508
6736
  return {
6509
6737
  skill: skill.name,
@@ -6521,8 +6749,8 @@ function installCodexSymlink(skill, targetDir, force) {
6521
6749
  exitCode: 1
6522
6750
  });
6523
6751
  }
6524
- fs6.unlinkSync(codexPath);
6525
- fs6.symlinkSync(linkTarget, codexPath);
6752
+ fs7.unlinkSync(codexPath);
6753
+ fs7.symlinkSync(linkTarget, codexPath);
6526
6754
  return {
6527
6755
  skill: skill.name,
6528
6756
  client: CodingAgents.codex,
@@ -6540,9 +6768,9 @@ function installCodexSymlink(skill, targetDir, force) {
6540
6768
  exitCode: 1
6541
6769
  });
6542
6770
  }
6543
- fs6.rmSync(codexPath, { recursive: true, force: true });
6771
+ fs7.rmSync(codexPath, { recursive: true, force: true });
6544
6772
  }
6545
- fs6.symlinkSync(linkTarget, codexPath);
6773
+ fs7.symlinkSync(linkTarget, codexPath);
6546
6774
  return {
6547
6775
  skill: skill.name,
6548
6776
  client: CodingAgents.codex,
@@ -6574,7 +6802,7 @@ async function installSkills(opts = {}) {
6574
6802
  });
6575
6803
  }
6576
6804
  const skillsToInstall = allSkills.filter((s) => requestedNames.includes(s.name));
6577
- fs6.mkdirSync(targetDir, { recursive: true });
6805
+ fs7.mkdirSync(targetDir, { recursive: true });
6578
6806
  const results = [];
6579
6807
  for (const skill of skillsToInstall) {
6580
6808
  results.push(installClaudeSkill(skill, targetDir, force));
@@ -6675,11 +6903,11 @@ var SKILLS_CLI_COMMANDS = [
6675
6903
  ];
6676
6904
 
6677
6905
  // src/commands/snapshot.ts
6678
- import fs8 from "fs";
6906
+ import fs9 from "fs";
6679
6907
  import path6 from "path";
6680
6908
 
6681
6909
  // src/snapshot-pdf.ts
6682
- import fs7 from "fs";
6910
+ import fs8 from "fs";
6683
6911
  import path5 from "path";
6684
6912
  import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
6685
6913
  var PAGE_WIDTH = 612;
@@ -6890,8 +7118,8 @@ async function writeSnapshotPdf(report, outputPath) {
6890
7118
  renderQueries(pdf, report);
6891
7119
  const bytes = await doc.save();
6892
7120
  const resolvedPath = path5.resolve(outputPath);
6893
- fs7.mkdirSync(path5.dirname(resolvedPath), { recursive: true });
6894
- fs7.writeFileSync(resolvedPath, bytes);
7121
+ fs8.mkdirSync(path5.dirname(resolvedPath), { recursive: true });
7122
+ fs8.writeFileSync(resolvedPath, bytes);
6895
7123
  return resolvedPath;
6896
7124
  }
6897
7125
  function renderCover(pdf, report) {
@@ -7050,8 +7278,8 @@ PDF saved: ${savedPdfPath}`);
7050
7278
  }
7051
7279
  function writeSnapshotMarkdown(report, outputPath) {
7052
7280
  const resolvedPath = path6.resolve(outputPath);
7053
- fs8.mkdirSync(path6.dirname(resolvedPath), { recursive: true });
7054
- fs8.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
7281
+ fs9.mkdirSync(path6.dirname(resolvedPath), { recursive: true });
7282
+ fs9.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
7055
7283
  return resolvedPath;
7056
7284
  }
7057
7285
  function formatSnapshotMarkdown(report) {
@@ -7993,7 +8221,7 @@ async function bootstrapCommand(_opts) {
7993
8221
 
7994
8222
  // src/commands/daemon.ts
7995
8223
  import { spawn } from "child_process";
7996
- import fs9 from "fs";
8224
+ import fs10 from "fs";
7997
8225
  import path8 from "path";
7998
8226
  function getPidPath() {
7999
8227
  return path8.join(getConfigDir(), "canonry.pid");
@@ -8023,8 +8251,8 @@ async function waitForReady(host, port, maxMs = 1e4) {
8023
8251
  async function startDaemon(opts) {
8024
8252
  const pidPath = getPidPath();
8025
8253
  const format = opts.format ?? "text";
8026
- if (fs9.existsSync(pidPath)) {
8027
- const existingPid = parseInt(fs9.readFileSync(pidPath, "utf-8").trim(), 10);
8254
+ if (fs10.existsSync(pidPath)) {
8255
+ const existingPid = parseInt(fs10.readFileSync(pidPath, "utf-8").trim(), 10);
8028
8256
  if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
8029
8257
  throw new CliError({
8030
8258
  code: "DAEMON_ALREADY_RUNNING",
@@ -8035,7 +8263,7 @@ async function startDaemon(opts) {
8035
8263
  }
8036
8264
  });
8037
8265
  }
8038
- fs9.unlinkSync(pidPath);
8266
+ fs10.unlinkSync(pidPath);
8039
8267
  }
8040
8268
  const cliPath = path8.resolve(new URL(import.meta.url).pathname);
8041
8269
  const inSourceMode = new URL(import.meta.url).pathname.endsWith(".ts");
@@ -8056,10 +8284,10 @@ async function startDaemon(opts) {
8056
8284
  });
8057
8285
  }
8058
8286
  const configDir = getConfigDir();
8059
- if (!fs9.existsSync(configDir)) {
8060
- fs9.mkdirSync(configDir, { recursive: true });
8287
+ if (!fs10.existsSync(configDir)) {
8288
+ fs10.mkdirSync(configDir, { recursive: true });
8061
8289
  }
8062
- fs9.writeFileSync(pidPath, String(child.pid), "utf-8");
8290
+ fs10.writeFileSync(pidPath, String(child.pid), "utf-8");
8063
8291
  const port = opts.port ?? "4100";
8064
8292
  const host = opts.host ?? "127.0.0.1";
8065
8293
  if (format !== "json") {
@@ -8068,7 +8296,7 @@ async function startDaemon(opts) {
8068
8296
  const ready = await waitForReady(host, port);
8069
8297
  if (!ready) {
8070
8298
  try {
8071
- fs9.unlinkSync(pidPath);
8299
+ fs10.unlinkSync(pidPath);
8072
8300
  } catch {
8073
8301
  }
8074
8302
  throw new CliError({
@@ -8100,7 +8328,7 @@ async function startDaemon(opts) {
8100
8328
  }
8101
8329
  function stopDaemon(format = "text") {
8102
8330
  const pidPath = getPidPath();
8103
- if (!fs9.existsSync(pidPath)) {
8331
+ if (!fs10.existsSync(pidPath)) {
8104
8332
  if (format === "json") {
8105
8333
  console.log(JSON.stringify({
8106
8334
  stopped: false,
@@ -8111,7 +8339,7 @@ function stopDaemon(format = "text") {
8111
8339
  console.log("Canonry is not running (no PID file found)");
8112
8340
  return;
8113
8341
  }
8114
- const pid = parseInt(fs9.readFileSync(pidPath, "utf-8").trim(), 10);
8342
+ const pid = parseInt(fs10.readFileSync(pidPath, "utf-8").trim(), 10);
8115
8343
  if (isNaN(pid)) {
8116
8344
  if (format === "json") {
8117
8345
  console.log(JSON.stringify({
@@ -8122,7 +8350,7 @@ function stopDaemon(format = "text") {
8122
8350
  } else {
8123
8351
  console.error("Invalid PID file. Removing it.");
8124
8352
  }
8125
- fs9.unlinkSync(pidPath);
8353
+ fs10.unlinkSync(pidPath);
8126
8354
  return;
8127
8355
  }
8128
8356
  if (!isProcessAlive(pid)) {
@@ -8136,12 +8364,12 @@ function stopDaemon(format = "text") {
8136
8364
  } else {
8137
8365
  console.log(`Canonry is not running (stale PID: ${pid}). Cleaning up.`);
8138
8366
  }
8139
- fs9.unlinkSync(pidPath);
8367
+ fs10.unlinkSync(pidPath);
8140
8368
  return;
8141
8369
  }
8142
8370
  try {
8143
8371
  process.kill(pid, "SIGTERM");
8144
- fs9.unlinkSync(pidPath);
8372
+ fs10.unlinkSync(pidPath);
8145
8373
  if (format === "json") {
8146
8374
  console.log(JSON.stringify({
8147
8375
  stopped: true,
@@ -8165,7 +8393,7 @@ function stopDaemon(format = "text") {
8165
8393
 
8166
8394
  // src/commands/init.ts
8167
8395
  import crypto2 from "crypto";
8168
- import fs10 from "fs";
8396
+ import fs11 from "fs";
8169
8397
  import readline from "readline";
8170
8398
  import path9 from "path";
8171
8399
  function prompt(question) {
@@ -8189,7 +8417,7 @@ var PROJECT_MARKERS = [".git", "canonry.yaml", "canonry.yml", "package.json"];
8189
8417
  function cwdLooksLikeProject(dir) {
8190
8418
  const home = process.env.HOME ?? "";
8191
8419
  if (home && path9.resolve(dir) === path9.resolve(home)) return false;
8192
- return PROJECT_MARKERS.some((marker) => fs10.existsSync(path9.join(dir, marker)));
8420
+ return PROJECT_MARKERS.some((marker) => fs11.existsSync(path9.join(dir, marker)));
8193
8421
  }
8194
8422
  var DEFAULT_AGENT_MODELS = {
8195
8423
  anthropic: "anthropic/claude-sonnet-4-6",
@@ -8219,8 +8447,8 @@ async function initCommand(opts) {
8219
8447
  return void 0;
8220
8448
  }
8221
8449
  const configDir = getConfigDir();
8222
- if (!fs10.existsSync(configDir)) {
8223
- fs10.mkdirSync(configDir, { recursive: true });
8450
+ if (!fs11.existsSync(configDir)) {
8451
+ fs11.mkdirSync(configDir, { recursive: true });
8224
8452
  }
8225
8453
  const bootstrapEnv = getBootstrapEnv(process.env, {
8226
8454
  GEMINI_API_KEY: opts?.geminiKey,
@@ -8530,6 +8758,7 @@ Received ${signal}, stopping server...`);
8530
8758
  Canonry server running at ${url}`);
8531
8759
  console.log("Press Ctrl+C to stop.\n");
8532
8760
  }
8761
+ setTelemetrySource("cli-server");
8533
8762
  const providerNames = Object.keys(config.providers ?? {}).filter(
8534
8763
  (k) => config.providers?.[k]?.apiKey || config.providers?.[k]?.baseUrl
8535
8764
  );
@@ -8815,7 +9044,7 @@ var SYSTEM_CLI_COMMANDS = [
8815
9044
  ];
8816
9045
 
8817
9046
  // src/cli-commands/wordpress.ts
8818
- import fs11 from "fs";
9047
+ import fs12 from "fs";
8819
9048
 
8820
9049
  // src/commands/wordpress.ts
8821
9050
  function getClient20() {
@@ -9051,12 +9280,12 @@ async function wordpressSetMeta(project, body) {
9051
9280
  printPageDetail(result);
9052
9281
  }
9053
9282
  async function wordpressBulkSetMeta(project, opts) {
9054
- const fs12 = await import("fs/promises");
9283
+ const fs13 = await import("fs/promises");
9055
9284
  const path10 = await import("path");
9056
9285
  const filePath = path10.resolve(opts.from);
9057
9286
  let raw;
9058
9287
  try {
9059
- raw = await fs12.readFile(filePath, "utf8");
9288
+ raw = await fs13.readFile(filePath, "utf8");
9060
9289
  } catch {
9061
9290
  throw new CliError({
9062
9291
  code: "FILE_READ_ERROR",
@@ -9153,13 +9382,13 @@ async function wordpressSetSchema(project, body) {
9153
9382
  printManualAssist(`Schema update for "${body.slug}"`, result);
9154
9383
  }
9155
9384
  async function wordpressSchemaDeploy(project, opts) {
9156
- const fs12 = await import("fs/promises");
9385
+ const fs13 = await import("fs/promises");
9157
9386
  const path10 = await import("path");
9158
9387
  const yaml = await import("yaml").catch(() => null);
9159
9388
  const filePath = path10.resolve(opts.profile);
9160
9389
  let raw;
9161
9390
  try {
9162
- raw = await fs12.readFile(filePath, "utf8");
9391
+ raw = await fs13.readFile(filePath, "utf8");
9163
9392
  } catch {
9164
9393
  throw new CliError({
9165
9394
  code: "FILE_READ_ERROR",
@@ -9264,13 +9493,13 @@ async function wordpressOnboard(project, opts) {
9264
9493
  }
9265
9494
  let profileData;
9266
9495
  if (opts.profile) {
9267
- const fs12 = await import("fs/promises");
9496
+ const fs13 = await import("fs/promises");
9268
9497
  const path10 = await import("path");
9269
9498
  const yaml = await import("yaml").catch(() => null);
9270
9499
  const filePath = path10.resolve(opts.profile);
9271
9500
  let raw;
9272
9501
  try {
9273
- raw = await fs12.readFile(filePath, "utf8");
9502
+ raw = await fs13.readFile(filePath, "utf8");
9274
9503
  } catch {
9275
9504
  throw new CliError({
9276
9505
  code: "FILE_READ_ERROR",
@@ -9419,7 +9648,7 @@ function resolveContent(input, command, usage, options) {
9419
9648
  }
9420
9649
  if (contentFile) {
9421
9650
  try {
9422
- return fs11.readFileSync(contentFile, "utf-8");
9651
+ return fs12.readFileSync(contentFile, "utf-8");
9423
9652
  } catch (error) {
9424
9653
  const message = error instanceof Error ? error.message : String(error);
9425
9654
  throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
@@ -10410,6 +10639,7 @@ Integrations:
10410
10639
  google Google Search Console / Analytics
10411
10640
  bing Bing Webmaster Tools
10412
10641
  wordpress WordPress REST API
10642
+ traffic Server-side traffic ingestion (Cloud Run)
10413
10643
 
10414
10644
  Automation:
10415
10645
  schedule Manage scheduled runs
@@ -10451,7 +10681,7 @@ async function runCli(args = process.argv.slice(2)) {
10451
10681
  showFirstRunNotice();
10452
10682
  getOrCreateAnonymousId();
10453
10683
  }
10454
- const SUBCOMMAND_COMMANDS = /* @__PURE__ */ new Set(["backfill", "project", "query", "keyword", "competitor", "schedule", "notify", "settings", "telemetry", "google", "bing", "wordpress", "cdp"]);
10684
+ const SUBCOMMAND_COMMANDS = /* @__PURE__ */ new Set(["backfill", "project", "query", "keyword", "competitor", "schedule", "notify", "settings", "telemetry", "google", "bing", "wordpress", "cdp", "traffic"]);
10455
10685
  const MIXED_SUBCOMMANDS = {
10456
10686
  insights: /* @__PURE__ */ new Set(["dismiss"]),
10457
10687
  run: /* @__PURE__ */ new Set(["show", "cancel"])
@@ -10465,7 +10695,12 @@ async function runCli(args = process.argv.slice(2)) {
10465
10695
  resolvedCommand = command;
10466
10696
  }
10467
10697
  if (!isHelpRequest && command !== "telemetry") {
10468
- trackEvent("cli.command", { command: resolvedCommand });
10698
+ detectAndTrackUpgrade();
10699
+ const setupState = buildSetupState();
10700
+ trackEvent("cli.command", {
10701
+ command: resolvedCommand,
10702
+ ...setupState ? { setup_state: setupState } : {}
10703
+ });
10469
10704
  }
10470
10705
  try {
10471
10706
  if (await dispatchRegisteredCommand(args, format, REGISTERED_CLI_COMMANDS)) {