@ainyc/canonry 4.194.0 → 5.0.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/LICENSE +21 -105
  2. package/README.md +4 -1
  3. package/assets/agent-workspace/skills/canonry/SKILL.md +71 -1
  4. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +30 -0
  5. package/assets/assets/{AuditHistoryPanel-C8hUWnaD.js → AuditHistoryPanel-vWUtVXOI.js} +1 -1
  6. package/assets/assets/{BacklinksPage-BcIdghvA.js → BacklinksPage-CSzuMEuv.js} +1 -1
  7. package/assets/assets/{HistoryPage-DF7PLFi1.js → HistoryPage-DtCTQ7CQ.js} +1 -1
  8. package/assets/assets/{MeasurementPropertyPage-BTlXQbYt.js → MeasurementPropertyPage-B7gTMWNs.js} +1 -1
  9. package/assets/assets/{ProjectPage-YiZ_i5eX.js → ProjectPage-Bsm0_jUY.js} +1 -1
  10. package/assets/assets/{RunRow-CfzuyTSM.js → RunRow-D5k4Rdo4.js} +1 -1
  11. package/assets/assets/{RunsPage-Diu_-YWl.js → RunsPage-DIXBB2Om.js} +1 -1
  12. package/assets/assets/{SettingsPage-g93aqkg2.js → SettingsPage-wYhBm6Az.js} +1 -1
  13. package/assets/assets/{SiteHealthSection-DroMQJVF.js → SiteHealthSection-YebzsgDh.js} +3 -3
  14. package/assets/assets/{TrafficPage-BhHjy5Ee.js → TrafficPage-DtnSRFO8.js} +1 -1
  15. package/assets/assets/{TrafficSourceDetailPage-DI3aZIHo.js → TrafficSourceDetailPage-DYQ6mpYq.js} +1 -1
  16. package/assets/assets/{extract-error-message-DjXzzSl3.js → extract-error-message-BdChrkmo.js} +1 -1
  17. package/assets/assets/{index-33ZEL6A5.js → index-C_E2NRYY.js} +26 -26
  18. package/assets/assets/{react-sigma_core.esm.min-BlfsZb5G.js → react-sigma_core.esm.min-sKWUk_tG.js} +1 -1
  19. package/assets/assets/{v2-overview-adapter-C6cROMod.js → v2-overview-adapter-CkPt-sRK.js} +1 -1
  20. package/assets/index.html +1 -1
  21. package/dist/{chunk-X5AE3GNC.js → chunk-3WODA4JF.js} +276 -5
  22. package/dist/{chunk-ZA2QVBQO.js → chunk-67NX4KVE.js} +73 -36
  23. package/dist/{chunk-7XXXNNE2.js → chunk-B2XM6AQ6.js} +280 -26
  24. package/dist/{chunk-AO5QJJE6.js → chunk-LJKAB4CX.js} +2 -2
  25. package/dist/{chunk-QVYGI6CL.js → chunk-UMU3IBXM.js} +1225 -474
  26. package/dist/cli.js +233 -154
  27. package/dist/index.js +4 -4
  28. package/dist/{intelligence-service-M4V3YPFZ.js → intelligence-service-MGQRJW4V.js} +2 -2
  29. package/dist/mcp.js +4 -4
  30. package/package.json +12 -11
package/dist/cli.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  formatAuditFactorScore,
18
18
  getCloudflareTrafficConnectionBySourceId,
19
19
  getOrCreateAnonymousId,
20
+ getTelemetryStatus,
20
21
  isAeroToolProfile,
21
22
  isFirstRun,
22
23
  isLoopbackBindHost,
@@ -28,11 +29,11 @@ import {
28
29
  trackCliCommandFinished,
29
30
  trackEvent,
30
31
  waitForServerRuntimeStartup
31
- } from "./chunk-ZA2QVBQO.js";
32
+ } from "./chunk-67NX4KVE.js";
32
33
  import {
33
34
  autoSyncSkills,
34
35
  formatAutoSyncNotice
35
- } from "./chunk-AO5QJJE6.js";
36
+ } from "./chunk-LJKAB4CX.js";
36
37
  import {
37
38
  CliError,
38
39
  EXIT_SYSTEM_ERROR,
@@ -58,7 +59,7 @@ import {
58
59
  saveConfigPatch,
59
60
  systemError,
60
61
  usageError
61
- } from "./chunk-7XXXNNE2.js";
62
+ } from "./chunk-B2XM6AQ6.js";
62
63
  import {
63
64
  CLOUDFLARE_WORKER_BINDINGS,
64
65
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -72,7 +73,7 @@ import {
72
73
  projects,
73
74
  queries,
74
75
  renderReportHtml
75
- } from "./chunk-QVYGI6CL.js";
76
+ } from "./chunk-UMU3IBXM.js";
76
77
  import {
77
78
  AdsDeliverySnapshotStatuses,
78
79
  AdsHistoricalCampaignRollupStatuses,
@@ -122,6 +123,7 @@ import {
122
123
  formatRatio,
123
124
  formatRunErrorOneLine,
124
125
  googleAdsMetricsWindowSchema,
126
+ logQuerySchema,
125
127
  measurementChangesQuerySchema,
126
128
  measurementDataQualityQuerySchema,
127
129
  measurementDiscoveryRequestSchema,
@@ -138,6 +140,7 @@ import {
138
140
  measurementQueryTemplateUpsertRequestSchema,
139
141
  measurementQuestionResultQuerySchema,
140
142
  normalizeProjectAliases,
143
+ normalizeTelemetryStatus,
141
144
  notificationEventSchema,
142
145
  queryTrackingCommitRequestSchema,
143
146
  queryTrackingPreviewRequestSchema,
@@ -149,7 +152,7 @@ import {
149
152
  snapshotProviderModeSchema,
150
153
  visibilityReportRequestSchema,
151
154
  winnabilityClassSchema
152
- } from "./chunk-X5AE3GNC.js";
155
+ } from "./chunk-3WODA4JF.js";
153
156
 
154
157
  // src/cli.ts
155
158
  import { pathToFileURL } from "url";
@@ -10932,8 +10935,12 @@ var EVENT_DESCRIPTIONS = {
10932
10935
  "health.degraded": "A scheduled health check found the measurement itself degraded (sent on change, not every pass)",
10933
10936
  "health.recovered": "A previously degraded health check is passing again"
10934
10937
  };
10935
- function listEvents(format) {
10936
- const events = notificationEventSchema.options;
10938
+ function listEvents(format, target = "local") {
10939
+ if (target === "server") return getClient16().listNotificationEvents().then((events) => printEvents(events, format));
10940
+ if (target !== "local") throw usageError("--target must be local or server");
10941
+ printEvents(notificationEventSchema.options, format);
10942
+ }
10943
+ function printEvents(events, format) {
10937
10944
  const catalog = events.map((e) => ({ event: e, description: EVENT_DESCRIPTIONS[e] ?? "" }));
10938
10945
  if (format === "json") {
10939
10946
  console.log(JSON.stringify(catalog, null, 2));
@@ -10959,9 +10966,10 @@ function printNotification(n) {
10959
10966
  var NOTIFY_CLI_COMMANDS = [
10960
10967
  {
10961
10968
  path: ["notify", "events"],
10962
- usage: "canonry notify events [--format json]",
10963
- run: (input) => {
10964
- listEvents(input.format);
10969
+ usage: "canonry notify events [--target local|server] [--format json|jsonl]",
10970
+ options: { target: stringOption() },
10971
+ run: async (input) => {
10972
+ await listEvents(input.format, getString(input.values, "target"));
10965
10973
  }
10966
10974
  },
10967
10975
  {
@@ -11501,6 +11509,7 @@ async function showHistory(project, format, opts = {}) {
11501
11509
  if (entry.diff != null) console.log(` diff: ${JSON.stringify(entry.diff)}`);
11502
11510
  }
11503
11511
  } catch (err) {
11512
+ if (err instanceof CliError) throw err;
11504
11513
  const message = describeError(err);
11505
11514
  throw new CliError({
11506
11515
  code: "HISTORY_FETCH_FAILED",
@@ -11597,6 +11606,21 @@ function parseResultsExportFormat(value) {
11597
11606
  Usage: ${RESULTS_EXPORT_USAGE}`);
11598
11607
  }
11599
11608
  var RESULTS_CLEAR_USAGE = "canonry results clear <project> [--run <id> ...] [--research-run <id> ...] [--confirm] [--format json|jsonl]";
11609
+ function parseHistoryPageOption(input, key, usage) {
11610
+ const value = getString(input.values, key);
11611
+ if (value === void 0) return void 0;
11612
+ if (!/^\d+$/.test(value)) {
11613
+ throw usageError(`Error: --${key} must be a ${key === "limit" ? "whole number from 1 to 500" : "nonnegative integer"}
11614
+ Usage: ${usage}`);
11615
+ }
11616
+ const parsed = Number(value);
11617
+ const valid = Number.isSafeInteger(parsed) && (key === "limit" ? parsed >= 1 && parsed <= 500 : parsed >= 0);
11618
+ if (!valid) {
11619
+ throw usageError(`Error: --${key} must be a ${key === "limit" ? "whole number from 1 to 500" : "nonnegative integer"}
11620
+ Usage: ${usage}`);
11621
+ }
11622
+ return parsed;
11623
+ }
11600
11624
  var OPERATOR_CLI_COMMANDS = [
11601
11625
  {
11602
11626
  path: ["results", "clear"],
@@ -11649,20 +11673,22 @@ var OPERATOR_CLI_COMMANDS = [
11649
11673
  },
11650
11674
  {
11651
11675
  path: ["history"],
11652
- usage: "canonry history <project> [--limit <n>] [--since <ISO>] [--action <action>] [--actor <actor>] [--entity-type <type>] [--format json|jsonl]\n canonry history --all [same filters]",
11676
+ usage: "canonry history <project> [--limit <1..500>] [--offset <n>] [--since <ISO>] [--action <action>] [--actor <actor>] [--entity-type <type>] [--format json|jsonl]\n canonry history --all [same filters]",
11653
11677
  options: {
11654
11678
  all: { type: "boolean", default: false },
11655
11679
  limit: stringOption(),
11680
+ offset: stringOption(),
11656
11681
  since: stringOption(),
11657
11682
  action: stringOption(),
11658
11683
  actor: stringOption(),
11659
11684
  "entity-type": stringOption()
11660
11685
  },
11661
11686
  run: async (input) => {
11662
- const usage = "canonry history <project> [--limit <n>] [--since <ISO>] [--action <action>] [--actor <actor>] [--entity-type <type>] [--format json|jsonl]\n canonry history --all [same filters]";
11687
+ const usage = "canonry history <project> [--limit <1..500>] [--offset <n>] [--since <ISO>] [--action <action>] [--actor <actor>] [--entity-type <type>] [--format json|jsonl]\n canonry history --all [same filters]";
11663
11688
  const project = getBoolean(input.values, "all") ? void 0 : requireProject(input, "history", usage);
11664
11689
  await showHistory(project, input.format, {
11665
- limit: parseIntegerOption(input, "limit", { command: "history", usage, message: "--limit must be an integer" }),
11690
+ limit: parseHistoryPageOption(input, "limit", usage),
11691
+ offset: parseHistoryPageOption(input, "offset", usage),
11666
11692
  since: getString(input.values, "since"),
11667
11693
  action: getString(input.values, "action"),
11668
11694
  actor: getString(input.values, "actor"),
@@ -12800,6 +12826,19 @@ var RUN_CLI_COMMANDS = [
12800
12826
  ];
12801
12827
 
12802
12828
  // src/commands/schedule.ts
12829
+ async function listSchedules(project, format) {
12830
+ const schedules = await getClient24().listSchedules(project);
12831
+ if (format === "jsonl") {
12832
+ emitJsonl(schedules);
12833
+ return;
12834
+ }
12835
+ if (format === "json") {
12836
+ console.log(JSON.stringify(schedules, null, 2));
12837
+ return;
12838
+ }
12839
+ if (!schedules.length) console.log(`No schedules configured for "${project}"`);
12840
+ for (const schedule of schedules) printSchedule(schedule);
12841
+ }
12803
12842
  function getClient24() {
12804
12843
  return createApiClient();
12805
12844
  }
@@ -12904,6 +12943,13 @@ function printSchedule(s) {
12904
12943
 
12905
12944
  // src/cli-commands/schedule.ts
12906
12945
  var SCHEDULE_CLI_COMMANDS = [
12946
+ {
12947
+ path: ["schedule", "list"],
12948
+ usage: "canonry schedule list <project> [--format json|jsonl]",
12949
+ run: async (input) => {
12950
+ await listSchedules(requireProject(input, "schedule.list", "canonry schedule list <project>"), input.format);
12951
+ }
12952
+ },
12907
12953
  {
12908
12954
  path: ["schedule", "set"],
12909
12955
  usage: "canonry schedule set <project> (--preset <preset> | --cron <expr> | --every-days <n> --start-date <YYYY-MM-DD> --at <HH:mm>) [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh|backlinks-sync] [--source <id>] [--timezone <tz>] [--provider <name>...] [--format json]",
@@ -12992,12 +13038,12 @@ var SCHEDULE_CLI_COMMANDS = [
12992
13038
  },
12993
13039
  {
12994
13040
  path: ["schedule"],
12995
- usage: "canonry schedule <set|show|enable|disable|remove> <project>",
13041
+ usage: "canonry schedule <list|set|show|enable|disable|remove> <project>",
12996
13042
  run: async (input) => {
12997
13043
  unknownSubcommand(input.positionals[0], {
12998
13044
  command: "schedule",
12999
- usage: "canonry schedule <set|show|enable|disable|remove> <project>",
13000
- available: ["set", "show", "enable", "disable", "remove"]
13045
+ usage: "canonry schedule <list|set|show|enable|disable|remove> <project>",
13046
+ available: ["list", "set", "show", "enable", "disable", "remove"]
13001
13047
  });
13002
13048
  }
13003
13049
  }
@@ -13025,15 +13071,9 @@ async function setProvider(name, opts) {
13025
13071
  }
13026
13072
  async function showSettings(format) {
13027
13073
  const client = getClient25();
13028
- const config = loadConfig();
13029
13074
  const settings = await client.getSettings();
13030
13075
  if (isMachineFormat(format)) {
13031
- console.log(JSON.stringify({
13032
- ...settings,
13033
- google: {
13034
- configured: Boolean(config.google?.clientId && config.google?.clientSecret)
13035
- }
13036
- }, null, 2));
13076
+ console.log(JSON.stringify(settings, null, 2));
13037
13077
  return;
13038
13078
  }
13039
13079
  console.log("Provider settings:\n");
@@ -13049,9 +13089,21 @@ async function showSettings(format) {
13049
13089
  }
13050
13090
  }
13051
13091
  console.log("\nGoogle OAuth:\n");
13052
- console.log(` ${config.google?.clientId && config.google?.clientSecret ? "configured" : "not configured"}`);
13092
+ console.log(` ${settings.google.configured ? "configured" : "not configured"}`);
13053
13093
  }
13054
- function setGoogleAuth(opts) {
13094
+ async function setGoogleAuth(opts) {
13095
+ if (opts.target === "server") {
13096
+ const result = await getClient25().updateGoogleSettings({
13097
+ clientId: opts.clientId,
13098
+ clientSecret: opts.clientSecret
13099
+ });
13100
+ if (isMachineFormat(opts.format)) {
13101
+ console.log(JSON.stringify(result, null, 2));
13102
+ return;
13103
+ }
13104
+ console.log("Google OAuth credentials updated on the server.");
13105
+ return;
13106
+ }
13055
13107
  const config = loadConfig();
13056
13108
  setGoogleAuthConfig(config, {
13057
13109
  clientId: opts.clientId,
@@ -13091,8 +13143,25 @@ var SETTINGS_CLI_COMMANDS = [
13091
13143
  });
13092
13144
  const apiKey = getString(input.values, "api-key");
13093
13145
  const baseUrl = getString(input.values, "base-url");
13146
+ const model = getString(input.values, "model");
13147
+ const maxConcurrency = parseIntegerOption(input, "max-concurrent", {
13148
+ command: "settings.provider",
13149
+ usage: "canonry settings provider <name> [--api-key <key>] [--base-url <url>] [--model <model>] [--max-concurrent <n>] [--max-per-minute <n>] [--max-per-day <n>] [--format json]",
13150
+ message: "--max-concurrent must be an integer"
13151
+ });
13152
+ const maxRequestsPerMinute = parseIntegerOption(input, "max-per-minute", {
13153
+ command: "settings.provider",
13154
+ usage: "canonry settings provider <name> [--api-key <key>] [--base-url <url>] [--model <model>] [--max-concurrent <n>] [--max-per-minute <n>] [--max-per-day <n>] [--format json]",
13155
+ message: "--max-per-minute must be an integer"
13156
+ });
13157
+ const maxRequestsPerDay = parseIntegerOption(input, "max-per-day", {
13158
+ command: "settings.provider",
13159
+ usage: "canonry settings provider <name> [--api-key <key>] [--base-url <url>] [--model <model>] [--max-concurrent <n>] [--max-per-minute <n>] [--max-per-day <n>] [--format json]",
13160
+ message: "--max-per-day must be an integer"
13161
+ });
13162
+ const hasNonCredentialEdit = Boolean(model) || maxConcurrency != null || maxRequestsPerMinute != null || maxRequestsPerDay != null;
13094
13163
  if (name === "local") {
13095
- if (!baseUrl) {
13164
+ if (!baseUrl && !hasNonCredentialEdit) {
13096
13165
  throw usageError(
13097
13166
  "Error: --base-url is required for the local provider\nUsage: canonry settings provider local --base-url <url> [--api-key <key>] [--model <model>] [--max-concurrent <n>] [--max-per-minute <n>] [--max-per-day <n>] [--format json]",
13098
13167
  {
@@ -13105,7 +13174,7 @@ var SETTINGS_CLI_COMMANDS = [
13105
13174
  }
13106
13175
  );
13107
13176
  }
13108
- } else if (!apiKey) {
13177
+ } else if (!apiKey && !baseUrl && !hasNonCredentialEdit) {
13109
13178
  throw usageError(
13110
13179
  `Error: --api-key is required
13111
13180
  Usage: canonry settings provider ${name} --api-key <key> [--model <model>] [--max-concurrent <n>] [--max-per-minute <n>] [--max-per-day <n>] [--format json]`,
@@ -13119,21 +13188,6 @@ Usage: canonry settings provider ${name} --api-key <key> [--model <model>] [--ma
13119
13188
  }
13120
13189
  );
13121
13190
  }
13122
- const maxConcurrency = parseIntegerOption(input, "max-concurrent", {
13123
- command: "settings.provider",
13124
- usage: "canonry settings provider <name> [--api-key <key>] [--base-url <url>] [--model <model>] [--max-concurrent <n>] [--max-per-minute <n>] [--max-per-day <n>] [--format json]",
13125
- message: "--max-concurrent must be an integer"
13126
- });
13127
- const maxRequestsPerMinute = parseIntegerOption(input, "max-per-minute", {
13128
- command: "settings.provider",
13129
- usage: "canonry settings provider <name> [--api-key <key>] [--base-url <url>] [--model <model>] [--max-concurrent <n>] [--max-per-minute <n>] [--max-per-day <n>] [--format json]",
13130
- message: "--max-per-minute must be an integer"
13131
- });
13132
- const maxRequestsPerDay = parseIntegerOption(input, "max-per-day", {
13133
- command: "settings.provider",
13134
- usage: "canonry settings provider <name> [--api-key <key>] [--base-url <url>] [--model <model>] [--max-concurrent <n>] [--max-per-minute <n>] [--max-per-day <n>] [--format json]",
13135
- message: "--max-per-day must be an integer"
13136
- });
13137
13191
  const quota = maxConcurrency != null || maxRequestsPerMinute != null || maxRequestsPerDay != null ? {
13138
13192
  ...maxConcurrency != null ? { maxConcurrency } : {},
13139
13193
  ...maxRequestsPerMinute != null ? { maxRequestsPerMinute } : {},
@@ -13142,7 +13196,7 @@ Usage: canonry settings provider ${name} --api-key <key> [--model <model>] [--ma
13142
13196
  await setProvider(name, {
13143
13197
  apiKey,
13144
13198
  baseUrl,
13145
- model: getString(input.values, "model"),
13199
+ model,
13146
13200
  quota,
13147
13201
  format: input.format
13148
13202
  });
@@ -13150,10 +13204,11 @@ Usage: canonry settings provider ${name} --api-key <key> [--model <model>] [--ma
13150
13204
  },
13151
13205
  {
13152
13206
  path: ["settings", "google"],
13153
- usage: "canonry settings google --client-id <id> --client-secret <secret> [--format json]",
13207
+ usage: "canonry settings google --client-id <id> --client-secret <secret> [--target local|server] [--format json]",
13154
13208
  options: {
13155
13209
  "client-id": stringOption(),
13156
- "client-secret": stringOption()
13210
+ "client-secret": stringOption(),
13211
+ target: stringOption()
13157
13212
  },
13158
13213
  run: async (input) => {
13159
13214
  const clientId = getString(input.values, "client-id");
@@ -13171,9 +13226,25 @@ Usage: canonry settings provider ${name} --api-key <key> [--model <model>] [--ma
13171
13226
  }
13172
13227
  );
13173
13228
  }
13174
- setGoogleAuth({
13229
+ const target = getString(input.values, "target") ?? "local";
13230
+ if (target !== "local" && target !== "server") {
13231
+ throw usageError(
13232
+ "Error: invalid --target; must be local or server\nUsage: canonry settings google --client-id <id> --client-secret <secret> [--target local|server] [--format json]",
13233
+ {
13234
+ message: "invalid --target; must be local or server",
13235
+ details: {
13236
+ command: "settings.google",
13237
+ usage: "canonry settings google --client-id <id> --client-secret <secret> [--target local|server] [--format json]",
13238
+ option: "target",
13239
+ value: target
13240
+ }
13241
+ }
13242
+ );
13243
+ }
13244
+ await setGoogleAuth({
13175
13245
  clientId,
13176
13246
  clientSecret,
13247
+ target,
13177
13248
  format: input.format
13178
13249
  });
13179
13250
  }
@@ -15662,131 +15733,117 @@ Canonry server running at ${url}`);
15662
15733
  }
15663
15734
 
15664
15735
  // src/commands/telemetry.ts
15665
- function telemetryCommand(subcommand, format = "text") {
15736
+ function telemetryTarget(value) {
15737
+ if (value === void 0 || value === "local") return "local";
15738
+ if (value === "server") return "server";
15739
+ throw usageError('Error: --target must be "local" or "server"', {
15740
+ message: '--target must be "local" or "server"',
15741
+ details: { command: "telemetry", target: value, allowed: ["local", "server"] }
15742
+ });
15743
+ }
15744
+ function emitStatus(payload, format, mutation = false) {
15745
+ if (isMachineFormat(format)) {
15746
+ console.log(JSON.stringify(payload, null, 2));
15747
+ return;
15748
+ }
15749
+ const targetSuffix = payload.target === "server" ? " (server)" : "";
15750
+ if (mutation && payload.enabled === payload.configuredEnabled) {
15751
+ console.log(`Telemetry${targetSuffix} ${payload.enabled ? "enabled." : "disabled. No data will be sent."}`);
15752
+ } else if (payload.reason === "CANONRY_TELEMETRY_DISABLED") {
15753
+ console.log(`Telemetry${targetSuffix}: disabled (CANONRY_TELEMETRY_DISABLED=1)`);
15754
+ } else if (payload.reason === "DO_NOT_TRACK") {
15755
+ console.log(`Telemetry${targetSuffix}: disabled (DO_NOT_TRACK=1)`);
15756
+ } else if (payload.reason === "CI") {
15757
+ console.log(`Telemetry${targetSuffix}: disabled (CI environment detected)`);
15758
+ } else if (payload.reason === "NO_CONFIG") {
15759
+ console.log(`Telemetry${targetSuffix}: enabled (no config yet \u2014 run "canonry bootstrap" first)`);
15760
+ } else {
15761
+ console.log(`Telemetry${targetSuffix}: ${payload.enabled ? "enabled" : "disabled"}`);
15762
+ }
15763
+ if (payload.enabled !== payload.configuredEnabled) {
15764
+ console.log(`Configured telemetry: ${payload.configuredEnabled ? "enabled" : "disabled"}; effective state overridden by ${payload.reason}.`);
15765
+ }
15766
+ if (payload.anonymousId) console.log(`Anonymous ID: ${payload.anonymousId}`);
15767
+ }
15768
+ function localStatus() {
15769
+ const status = getTelemetryStatus();
15770
+ return {
15771
+ ...status,
15772
+ ...status.anonymousId ? { anonymousIdMasked: status.anonymousId } : {},
15773
+ ...configExists() ? { configPath: getConfigPath() } : {}
15774
+ };
15775
+ }
15776
+ function requireLocalConfig(command) {
15777
+ if (!configExists()) {
15778
+ throw new CliError({
15779
+ code: "CONFIG_REQUIRED",
15780
+ message: 'No config found. Run "canonry bootstrap" first.',
15781
+ displayMessage: 'No config found. Run "canonry bootstrap" first.',
15782
+ details: { command }
15783
+ });
15784
+ }
15785
+ }
15786
+ async function serverTelemetryCommand(subcommand, format) {
15787
+ const client = createApiClient();
15788
+ const response = subcommand === "status" ? await client.getTelemetry() : await client.updateTelemetry(subcommand === "enable");
15789
+ emitStatus(normalizeTelemetryStatus(response), format, subcommand !== "status");
15790
+ }
15791
+ function telemetryCommand(subcommand, format = "text", targetValue) {
15666
15792
  const available = ["status", "enable", "disable"];
15793
+ const target = telemetryTarget(targetValue);
15794
+ if (target === "server") {
15795
+ if (subcommand === "status" || subcommand === "enable" || subcommand === "disable") {
15796
+ return serverTelemetryCommand(subcommand, format);
15797
+ }
15798
+ }
15667
15799
  switch (subcommand) {
15668
15800
  case "status": {
15669
- const payload = {
15670
- enabled: true
15671
- };
15672
- if (process.env.CANONRY_TELEMETRY_DISABLED === "1") {
15673
- payload.enabled = false;
15674
- payload.reason = "CANONRY_TELEMETRY_DISABLED";
15675
- if (isMachineFormat(format)) {
15676
- console.log(JSON.stringify(payload, null, 2));
15677
- return;
15678
- }
15679
- console.log("Telemetry: disabled (CANONRY_TELEMETRY_DISABLED=1)");
15680
- return;
15681
- }
15682
- if (process.env.DO_NOT_TRACK === "1") {
15683
- payload.enabled = false;
15684
- payload.reason = "DO_NOT_TRACK";
15685
- if (isMachineFormat(format)) {
15686
- console.log(JSON.stringify(payload, null, 2));
15687
- return;
15688
- }
15689
- console.log("Telemetry: disabled (DO_NOT_TRACK=1)");
15690
- return;
15691
- }
15692
- if (process.env.CI) {
15693
- payload.enabled = false;
15694
- payload.reason = "CI";
15695
- if (isMachineFormat(format)) {
15696
- console.log(JSON.stringify(payload, null, 2));
15697
- return;
15698
- }
15699
- console.log("Telemetry: disabled (CI environment detected)");
15700
- return;
15701
- }
15702
- if (!configExists()) {
15703
- payload.reason = "NO_CONFIG";
15704
- if (isMachineFormat(format)) {
15705
- console.log(JSON.stringify(payload, null, 2));
15706
- return;
15707
- }
15708
- console.log('Telemetry: enabled (no config yet \u2014 run "canonry bootstrap" first)');
15709
- return;
15710
- }
15711
- const config = loadConfig();
15712
- payload.enabled = config.telemetry !== false;
15713
- payload.configPath = getConfigPath();
15714
- if (config.anonymousId) {
15715
- payload.anonymousIdMasked = config.anonymousId.slice(0, 8) + "...";
15716
- }
15717
- if (isMachineFormat(format)) {
15718
- console.log(JSON.stringify(payload, null, 2));
15719
- return;
15720
- }
15721
- const enabled = payload.enabled;
15722
- console.log(`Telemetry: ${enabled ? "enabled" : "disabled"}`);
15723
- if (config.anonymousId) {
15724
- const masked = config.anonymousId.slice(0, 8) + "...";
15725
- console.log(`Anonymous ID: ${masked}`);
15726
- }
15801
+ emitStatus(localStatus(), format);
15727
15802
  break;
15728
15803
  }
15729
15804
  case "enable": {
15730
- if (!configExists()) {
15731
- throw new CliError({
15732
- code: "CONFIG_REQUIRED",
15733
- message: 'No config found. Run "canonry bootstrap" first.',
15734
- displayMessage: 'No config found. Run "canonry bootstrap" first.',
15735
- details: {
15736
- command: "telemetry.enable"
15737
- }
15738
- });
15739
- }
15805
+ requireLocalConfig("telemetry.enable");
15740
15806
  const config = loadConfig();
15741
15807
  config.telemetry = true;
15742
15808
  saveConfigPatch(config);
15743
- if (isMachineFormat(format)) {
15744
- console.log(JSON.stringify({
15745
- enabled: true,
15746
- configPath: getConfigPath()
15747
- }, null, 2));
15748
- return;
15749
- }
15750
- console.log("Telemetry enabled.");
15809
+ emitStatus(localStatus(), format, true);
15751
15810
  break;
15752
15811
  }
15753
15812
  case "disable": {
15754
- if (!configExists()) {
15755
- throw new CliError({
15756
- code: "CONFIG_REQUIRED",
15757
- message: 'No config found. Run "canonry bootstrap" first.',
15758
- displayMessage: 'No config found. Run "canonry bootstrap" first.',
15759
- details: {
15760
- command: "telemetry.disable"
15761
- }
15762
- });
15763
- }
15813
+ requireLocalConfig("telemetry.disable");
15764
15814
  const config = loadConfig();
15765
15815
  config.telemetry = false;
15766
15816
  saveConfigPatch(config);
15767
- if (isMachineFormat(format)) {
15768
- console.log(JSON.stringify({
15769
- enabled: false,
15770
- configPath: getConfigPath()
15771
- }, null, 2));
15772
- return;
15773
- }
15774
- console.log("Telemetry disabled. No data will be sent.");
15817
+ emitStatus(localStatus(), format, true);
15775
15818
  break;
15776
15819
  }
15777
15820
  default:
15778
15821
  throw usageError(`Error: unknown telemetry subcommand: ${subcommand ?? "(none)"}
15779
- Usage: canonry telemetry <status|enable|disable> [--format json]`, {
15822
+ Usage: canonry telemetry <status|enable|disable> [--target local|server] [--format json]`, {
15780
15823
  message: `unknown telemetry subcommand: ${subcommand ?? "(none)"}`,
15781
15824
  details: {
15782
15825
  command: "telemetry",
15783
- usage: "canonry telemetry <status|enable|disable> [--format json]",
15826
+ usage: "canonry telemetry <status|enable|disable> [--target local|server] [--format json]",
15784
15827
  available
15785
15828
  }
15786
15829
  });
15787
15830
  }
15788
15831
  }
15789
15832
 
15833
+ // src/commands/logs.ts
15834
+ async function showOperationalLogs(query, format) {
15835
+ const parsed = logQuerySchema.safeParse(query);
15836
+ if (!parsed.success) throw usageError("Invalid logs filters", { details: { issues: parsed.error.issues } });
15837
+ const result = await createApiClient().listOperationalLogs(parsed.data);
15838
+ if (isMachineFormat(format)) {
15839
+ console.log(JSON.stringify(result, null, 2));
15840
+ return;
15841
+ }
15842
+ console.log(`Runtime logs (${result.retention}; ${result.entries.length} entries; ${result.dropped} evicted; ${result.captureErrors ?? 0} capture errors). Separate from audit history.`);
15843
+ for (const entry of result.entries) console.log(`${entry.ts} ${entry.level} [${entry.module}] ${entry.action}${entry.message ? ` ${entry.message}` : ""} ${JSON.stringify(entry.context)}`);
15844
+ if (result.nextCursor) console.log(`Next cursor: ${result.nextCursor}`);
15845
+ }
15846
+
15790
15847
  // src/cli-commands/system.ts
15791
15848
  function applyServerEnv(values) {
15792
15849
  const port = typeof values.port === "string" ? values.port : void 0;
@@ -15805,6 +15862,25 @@ function applyServerEnv(values) {
15805
15862
  process.env.CANONRY_EMBED_PROJECT_TABS = embedProjectTabs.join(",");
15806
15863
  }
15807
15864
  var SYSTEM_CLI_COMMANDS = [
15865
+ {
15866
+ path: ["logs"],
15867
+ usage: "canonry logs [--level trace|debug|info|warn|error|fatal] [--module <name>] [--run-id <id>] [--project-id <id>] [--actor <user:id|api-key:id>] [--request-id <id>] [--since <ISO>] [--until <ISO>] [--limit <1..200>] [--cursor <cursor>] [--format json|jsonl]",
15868
+ help: "Read bounded, redacted runtime logs. Inspect retention and captureErrors for coverage. Requires an instance-wide logs.read grant; project filters do not grant access. Use history for audit events.",
15869
+ options: { level: stringOption(), module: stringOption(), "run-id": stringOption(), "project-id": stringOption(), actor: stringOption(), "request-id": stringOption(), since: stringOption(), until: stringOption(), limit: stringOption(), cursor: stringOption() },
15870
+ allowPositionals: false,
15871
+ run: async (input) => showOperationalLogs({
15872
+ level: getString(input.values, "level"),
15873
+ module: getString(input.values, "module"),
15874
+ runId: getString(input.values, "run-id"),
15875
+ projectId: getString(input.values, "project-id"),
15876
+ actor: getString(input.values, "actor"),
15877
+ requestId: getString(input.values, "request-id"),
15878
+ since: getString(input.values, "since"),
15879
+ until: getString(input.values, "until"),
15880
+ limit: getString(input.values, "limit"),
15881
+ cursor: getString(input.values, "cursor")
15882
+ }, input.format)
15883
+ },
15808
15884
  {
15809
15885
  path: ["init"],
15810
15886
  usage: "canonry init [--force] [--gemini-key <key>] [--openai-key <key>] [--claude-key <key>] [--perplexity-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]",
@@ -15913,35 +15989,38 @@ var SYSTEM_CLI_COMMANDS = [
15913
15989
  },
15914
15990
  {
15915
15991
  path: ["telemetry", "status"],
15916
- usage: "canonry telemetry status [--format json]",
15992
+ usage: "canonry telemetry status [--target local|server] [--format json]",
15993
+ options: { target: stringOption() },
15917
15994
  allowPositionals: false,
15918
- run: (input) => {
15919
- telemetryCommand("status", input.format);
15995
+ run: async (input) => {
15996
+ await telemetryCommand("status", input.format, getString(input.values, "target"));
15920
15997
  }
15921
15998
  },
15922
15999
  {
15923
16000
  path: ["telemetry", "enable"],
15924
- usage: "canonry telemetry enable [--format json]",
16001
+ usage: "canonry telemetry enable [--target local|server] [--format json]",
16002
+ options: { target: stringOption() },
15925
16003
  allowPositionals: false,
15926
- run: (input) => {
15927
- telemetryCommand("enable", input.format);
16004
+ run: async (input) => {
16005
+ await telemetryCommand("enable", input.format, getString(input.values, "target"));
15928
16006
  }
15929
16007
  },
15930
16008
  {
15931
16009
  path: ["telemetry", "disable"],
15932
- usage: "canonry telemetry disable [--format json]",
16010
+ usage: "canonry telemetry disable [--target local|server] [--format json]",
16011
+ options: { target: stringOption() },
15933
16012
  allowPositionals: false,
15934
- run: (input) => {
15935
- telemetryCommand("disable", input.format);
16013
+ run: async (input) => {
16014
+ await telemetryCommand("disable", input.format, getString(input.values, "target"));
15936
16015
  }
15937
16016
  },
15938
16017
  {
15939
16018
  path: ["telemetry"],
15940
- usage: "canonry telemetry <status|enable|disable> [--format json]",
16019
+ usage: "canonry telemetry <status|enable|disable> [--target local|server] [--format json]",
15941
16020
  run: (input) => {
15942
16021
  unknownSubcommand(input.positionals[0], {
15943
16022
  command: "telemetry",
15944
- usage: "canonry telemetry <status|enable|disable> [--format json]",
16023
+ usage: "canonry telemetry <status|enable|disable> [--target local|server] [--format json]",
15945
16024
  available: ["status", "enable", "disable"]
15946
16025
  });
15947
16026
  }
package/dist/index.js CHANGED
@@ -3,12 +3,12 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-ZA2QVBQO.js";
6
+ } from "./chunk-67NX4KVE.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-7XXXNNE2.js";
10
- import "./chunk-QVYGI6CL.js";
11
- import "./chunk-X5AE3GNC.js";
9
+ } from "./chunk-B2XM6AQ6.js";
10
+ import "./chunk-UMU3IBXM.js";
11
+ import "./chunk-3WODA4JF.js";
12
12
  export {
13
13
  GoogleMarketingRuntimeError,
14
14
  createGoogleMarketingCredentialStore,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-QVYGI6CL.js";
4
- import "./chunk-X5AE3GNC.js";
3
+ } from "./chunk-UMU3IBXM.js";
4
+ import "./chunk-3WODA4JF.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };