@canonry/canonry 6.2.0 → 6.3.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 (29) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +12 -0
  2. package/assets/assets/{AuditHistoryPanel-CuEMfkaA.js → AuditHistoryPanel-BUYWIjlG.js} +1 -1
  3. package/assets/assets/{BacklinksPage-CftadZ1-.js → BacklinksPage-aBcxpN_9.js} +1 -1
  4. package/assets/assets/{HistoryPage-DouRgshN.js → HistoryPage-PoBuxiY_.js} +1 -1
  5. package/assets/assets/{MeasurementPropertyPage-qfqv2qs9.js → MeasurementPropertyPage-DyjyqHac.js} +1 -1
  6. package/assets/assets/{ProjectPage-DqEVwe4t.js → ProjectPage-Mmv5YW-K.js} +1 -1
  7. package/assets/assets/{RunsPage-CUkMs0gB.js → RunsPage-CHHxOpzo.js} +1 -1
  8. package/assets/assets/{SettingsPage-LLQ40tSh.js → SettingsPage-CNKnrLXC.js} +1 -1
  9. package/assets/assets/{SiteHealthSection-Ba7foDiU.js → SiteHealthSection-C9RivT7G.js} +3 -3
  10. package/assets/assets/{TrafficPage-Cu_ZR3--.js → TrafficPage-CY-ibXnb.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-NKZq7jf9.js → TrafficSourceDetailPage-BgiPXubs.js} +1 -1
  12. package/assets/assets/{extract-error-message-BdQYrz9N.js → extract-error-message-H1dzzfBP.js} +1 -1
  13. package/assets/assets/{index-HOeLz1ZL.js → index-DSCun3I9.js} +21 -21
  14. package/assets/assets/{react-sigma_core.esm.min-CZaoE5cU.js → react-sigma_core.esm.min-uiYtR_DU.js} +1 -1
  15. package/assets/assets/{v2-overview-adapter-BVx4nI49.js → v2-overview-adapter-BQokN2eb.js} +1 -1
  16. package/assets/index.html +1 -1
  17. package/dist/{chunk-Y2B7JRSB.js → chunk-5GY5WBAF.js} +3 -3
  18. package/dist/{chunk-W3GDZZNF.js → chunk-DERWXLHX.js} +15 -1
  19. package/dist/{chunk-6EPN4XY5.js → chunk-EV5G5QRR.js} +324 -280
  20. package/dist/{chunk-4EKOKWQT.js → chunk-FSRVG2H7.js} +2 -2
  21. package/dist/{chunk-2RFHQ6XT.js → chunk-OTROA7CN.js} +199 -106
  22. package/dist/{chunk-ENI3GOWW.js → chunk-QVGG76UW.js} +78 -24
  23. package/dist/{chunk-V6JUBTNL.js → chunk-ZZEPF3A5.js} +23 -3
  24. package/dist/cli.js +53 -8
  25. package/dist/{demo-server-CH7MOBXW.js → demo-server-5MNLI6GC.js} +4 -4
  26. package/dist/index.js +6 -6
  27. package/dist/{intelligence-service-4IONMOFB.js → intelligence-service-5J36RBEM.js} +2 -2
  28. package/dist/mcp.js +4 -4
  29. package/package.json +11 -11
@@ -354,6 +354,8 @@ import {
354
354
  extractAnswerMentions,
355
355
  extractDomainsFromText,
356
356
  factorStatusFromScore,
357
+ feedbackAcceptedDtoSchema,
358
+ feedbackSubmissionSchema,
357
359
  filterBrandedSeedCandidates,
358
360
  filterCapturedCitedUrls,
359
361
  findDuplicateLocationLabels,
@@ -869,7 +871,7 @@ import {
869
871
  wordpressSchemaDeployResultDtoSchema,
870
872
  wordpressSchemaStatusResultDtoSchema,
871
873
  wordpressStatusDtoSchema
872
- } from "./chunk-6EPN4XY5.js";
874
+ } from "./chunk-EV5G5QRR.js";
873
875
 
874
876
  // src/intelligence-service.ts
875
877
  import { eq as eq82, desc as desc36, asc as asc15, and as and69, ne as ne14, or as or20, inArray as inArray32, gte as gte22, lte as lte20, isNull as isNull16, sql as sql31, exists } from "drizzle-orm";
@@ -12466,7 +12468,7 @@ function resolveTrustProxy(raw) {
12466
12468
  }
12467
12469
  var FORWARDED_HEADERS = ["x-forwarded-for", "forwarded", "x-real-ip"];
12468
12470
  function hasForwardedHeaders(request) {
12469
- return FORWARDED_HEADERS.some((header) => request.headers[header] !== void 0);
12471
+ return FORWARDED_HEADERS.some((header2) => request.headers[header2] !== void 0);
12470
12472
  }
12471
12473
  function resolveCallerKey(request, trustProxyConfigured) {
12472
12474
  const socketAddress = request.socket.remoteAddress ?? request.ip;
@@ -12496,9 +12498,9 @@ function cookieIsSecure(request, configured) {
12496
12498
  function anyUsersExist(db) {
12497
12499
  return db.select({ id: users.id }).from(users).limit(1).get() !== void 0;
12498
12500
  }
12499
- function parseCookieHeader(header) {
12500
- if (!header) return {};
12501
- return header.split(";").map((part) => part.trim()).filter(Boolean).reduce((cookies, part) => {
12501
+ function parseCookieHeader(header2) {
12502
+ if (!header2) return {};
12503
+ return header2.split(";").map((part) => part.trim()).filter(Boolean).reduce((cookies, part) => {
12502
12504
  const eqIdx = part.indexOf("=");
12503
12505
  if (eqIdx <= 0) return cookies;
12504
12506
  const name = part.slice(0, eqIdx).trim();
@@ -13018,11 +13020,11 @@ async function authPlugin(app, opts = {}) {
13018
13020
  app.addHook("onRequest", async (request, reply) => {
13019
13021
  const url = request.url.split("?")[0];
13020
13022
  if (shouldSkipAuth(url)) return;
13021
- const header = request.headers.authorization;
13023
+ const header2 = request.headers.authorization;
13022
13024
  let key;
13023
13025
  let keyArrivedInCookie = false;
13024
- if (header) {
13025
- const parts = header.split(" ");
13026
+ if (header2) {
13027
+ const parts = header2.split(" ");
13026
13028
  if (parts.length !== 2 || parts[0] !== "Bearer") {
13027
13029
  throw authRequired();
13028
13030
  }
@@ -22569,11 +22571,11 @@ function parseCsvRecords(value) {
22569
22571
  return records.filter((record) => !record.physicalBlank);
22570
22572
  }
22571
22573
  function parseHeader(records) {
22572
- const header = records.at(0);
22573
- if (!header) csvError("csv-header-missing", "CSV must include a property and group header row.");
22574
+ const header2 = records.at(0);
22575
+ if (!header2) csvError("csv-header-missing", "CSV must include a property and group header row.");
22574
22576
  const allowed = /* @__PURE__ */ new Set(["property", "group", "url"]);
22575
22577
  const indices = /* @__PURE__ */ new Map();
22576
- for (const [index2, raw] of header.fields.entries()) {
22578
+ for (const [index2, raw] of header2.fields.entries()) {
22577
22579
  const name = normalizeGroupMembershipLabel(raw);
22578
22580
  if (!allowed.has(name)) {
22579
22581
  csvError("csv-header-invalid", `CSV header "${raw}" is not supported. Use property, group, and optional url.`, 400, { header: raw });
@@ -22588,7 +22590,7 @@ function parseHeader(records) {
22588
22590
  csvError("csv-header-missing", `CSV is missing the required "${required2}" header.`, 400, { header: required2 });
22589
22591
  }
22590
22592
  }
22591
- return { indices, length: header.fields.length };
22593
+ return { indices, length: header2.fields.length };
22592
22594
  }
22593
22595
  function parseGroupMembershipCsv(csv) {
22594
22596
  const byteLength = Buffer.byteLength(csv, "utf8");
@@ -22601,7 +22603,7 @@ function parseGroupMembershipCsv(csv) {
22601
22603
  );
22602
22604
  }
22603
22605
  const records = parseCsvRecords(csv);
22604
- const header = parseHeader(records);
22606
+ const header2 = parseHeader(records);
22605
22607
  const dataRecords = records.slice(1);
22606
22608
  if (dataRecords.length > MEASUREMENT_GROUP_MEMBERSHIP_CSV_MAX_DATA_ROWS) {
22607
22609
  csvError(
@@ -22611,9 +22613,9 @@ function parseGroupMembershipCsv(csv) {
22611
22613
  { rowCount: dataRecords.length, maxRows: MEASUREMENT_GROUP_MEMBERSHIP_CSV_MAX_DATA_ROWS }
22612
22614
  );
22613
22615
  }
22614
- const propertyIndex = header.indices.get("property");
22615
- const groupIndex = header.indices.get("group");
22616
- const urlIndex = header.indices.get("url");
22616
+ const propertyIndex = header2.indices.get("property");
22617
+ const groupIndex = header2.indices.get("group");
22618
+ const urlIndex = header2.indices.get("url");
22617
22619
  const rows = dataRecords.map((record, index2) => {
22618
22620
  const property = record.fields[propertyIndex] ?? "";
22619
22621
  const group = record.fields[groupIndex] ?? "";
@@ -22626,7 +22628,7 @@ function parseGroupMembershipCsv(csv) {
22626
22628
  url,
22627
22629
  normalizedProperty: normalizeGroupMembershipLabel(property),
22628
22630
  normalizedGroupLabel: normalizeGroupMembershipLabel(group),
22629
- columnCountValid: record.fields.length === header.length
22631
+ columnCountValid: record.fields.length === header2.length
22630
22632
  };
22631
22633
  });
22632
22634
  return { sourceChecksum: sourceChecksum(csv), rows };
@@ -33766,7 +33768,7 @@ function renderWinsLosses(insights2, heading, emptyMessage, audience) {
33766
33768
  </tr>`;
33767
33769
  }).join("");
33768
33770
  const headerCells = (isClient ? REPORT_SECTION_COPY["whats-changed"].client : REPORT_SECTION_COPY["whats-changed"].agency).insightHeaders;
33769
- const headers = `<tr>${headerCells.map((header) => `<th>${header}</th>`).join("")}</tr>`;
33771
+ const headers = `<tr>${headerCells.map((header2) => `<th>${header2}</th>`).join("")}</tr>`;
33770
33772
  return `<div class="chart-card"><h3>${escapeHtml2(heading)}</h3>
33771
33773
  <div class="table-scroll"><table class="report-table">
33772
33774
  <thead>${headers}</thead>
@@ -39924,6 +39926,8 @@ var SCHEMA_TABLE = {
39924
39926
  SnapshotRequest: z.object(snapshotRequestInputSchema.shape).omit({ phrases: true }),
39925
39927
  SourceBreakdownDto: sourceBreakdownDtoSchema,
39926
39928
  TelemetryEventAcceptedDto: telemetryEventAcceptedDtoSchema,
39929
+ FeedbackSubmission: feedbackSubmissionSchema,
39930
+ FeedbackAcceptedDto: feedbackAcceptedDtoSchema,
39927
39931
  TelemetryStatusDto: telemetryStatusDtoSchema,
39928
39932
  OperationalLogListDto: operationalLogListDtoSchema,
39929
39933
  CloudflareWorkerIngestRequest: cloudflareWorkerIngestRequestSchema,
@@ -43053,6 +43057,28 @@ var routeCatalog = [
43053
43057
  400: errorResponse("Invalid onboarding telemetry event.")
43054
43058
  }
43055
43059
  },
43060
+ {
43061
+ method: "post",
43062
+ path: "/api/v1/feedback",
43063
+ summary: "Send product feedback about Canonry",
43064
+ description: "Forwards a struggle, bug, improvement, or other note about Canonry itself to the Canonry team (canonry.ai). Free text is redacted for credentials before it leaves the machine; never include customer data. The server adds version, OS, and the calling surface and agent; the anonymous install id is attached only while telemetry is enabled.",
43065
+ tags: ["feedback"],
43066
+ requestBody: {
43067
+ required: true,
43068
+ content: {
43069
+ "application/json": {
43070
+ schema: { $ref: "#/components/schemas/FeedbackSubmission" }
43071
+ }
43072
+ }
43073
+ },
43074
+ responses: {
43075
+ 202: jsonResponse("Feedback accepted by the collector.", "FeedbackAcceptedDto"),
43076
+ 400: errorResponse("Invalid feedback."),
43077
+ 429: errorResponse("Feedback rate limit reached; try again later."),
43078
+ 501: errorResponse("Feedback is not available in this deployment."),
43079
+ 502: errorResponse("The feedback collector could not be reached or rejected the submission.")
43080
+ }
43081
+ },
43056
43082
  {
43057
43083
  method: "get",
43058
43084
  path: "/api/v1/screenshots/{snapshotId}",
@@ -47791,6 +47817,31 @@ async function telemetryRoutes(app, opts) {
47791
47817
  });
47792
47818
  }
47793
47819
 
47820
+ // ../api-routes/src/feedback.ts
47821
+ function header(value) {
47822
+ return Array.isArray(value) ? value[0] : value;
47823
+ }
47824
+ async function feedbackRoutes(app, opts) {
47825
+ app.post("/feedback", async (request, reply) => {
47826
+ const parsed = feedbackSubmissionSchema.safeParse(request.body);
47827
+ if (!parsed.success) {
47828
+ throw validationError("Invalid feedback", {
47829
+ issues: parsed.error.issues.map((issue) => ({ code: issue.code, path: issue.path.join(".") }))
47830
+ });
47831
+ }
47832
+ if (!opts.submitFeedback) {
47833
+ throw notImplemented("Feedback is not available in this deployment");
47834
+ }
47835
+ const accepted = await opts.submitFeedback(parsed.data, {
47836
+ userAgent: header(request.headers["user-agent"]),
47837
+ surface: header(request.headers[USAGE_TELEMETRY_HEADERS.surface]),
47838
+ agent: header(request.headers[USAGE_TELEMETRY_HEADERS.agent]),
47839
+ mcpClient: header(request.headers[USAGE_TELEMETRY_HEADERS.mcpClient])
47840
+ });
47841
+ return reply.status(202).send(accepted);
47842
+ });
47843
+ }
47844
+
47794
47845
  // ../api-routes/src/operational-logs.ts
47795
47846
  async function operationalLogsRoutes(app, opts = {}) {
47796
47847
  app.get("/operations/logs", async (request, reply) => {
@@ -47811,8 +47862,8 @@ async function operationalLogsRoutes(app, opts = {}) {
47811
47862
  return withoutUnrequestedFields(page3, requestedFields(request.headers[OPERATIONAL_LOG_FIELDS_HEADER]));
47812
47863
  });
47813
47864
  }
47814
- function requestedFields(header) {
47815
- const names = (Array.isArray(header) ? header : [header ?? ""]).flatMap((value) => value.split(",")).map((name) => name.trim());
47865
+ function requestedFields(header2) {
47866
+ const names = (Array.isArray(header2) ? header2 : [header2 ?? ""]).flatMap((value) => value.split(",")).map((name) => name.trim());
47816
47867
  return new Set(OPERATIONAL_LOG_OPT_IN_CONTEXT_FIELDS.filter((field) => names.includes(field)));
47817
47868
  }
47818
47869
  function withoutUnrequestedFields(page3, requested) {
@@ -49250,7 +49301,7 @@ function createServiceAccountJwt(clientEmail, privateKey, scope) {
49250
49301
  validatePrivateKey(privateKey);
49251
49302
  validateScope(scope);
49252
49303
  const now = Math.floor(Date.now() / 1e3);
49253
- const header = { alg: "RS256", typ: "JWT" };
49304
+ const header2 = { alg: "RS256", typ: "JWT" };
49254
49305
  const payload = {
49255
49306
  iss: clientEmail,
49256
49307
  scope,
@@ -49260,7 +49311,7 @@ function createServiceAccountJwt(clientEmail, privateKey, scope) {
49260
49311
  // 1 hour
49261
49312
  };
49262
49313
  const encode = (obj) => Buffer.from(JSON.stringify(obj)).toString("base64url");
49263
- const headerB64 = encode(header);
49314
+ const headerB64 = encode(header2);
49264
49315
  const payloadB64 = encode(payload);
49265
49316
  const signingInput = `${headerB64}.${payloadB64}`;
49266
49317
  const sign = crypto30.createSign("RSA-SHA256");
@@ -63466,7 +63517,7 @@ function createServiceAccountJwt2(clientEmail, privateKey, scope) {
63466
63517
  if (!privateKey) throw new CloudRunAuthError("privateKey is required");
63467
63518
  if (!scope) throw new CloudRunAuthError("scope is required");
63468
63519
  const now = Math.floor(Date.now() / 1e3);
63469
- const header = { alg: "RS256", typ: "JWT" };
63520
+ const header2 = { alg: "RS256", typ: "JWT" };
63470
63521
  const payload = {
63471
63522
  iss: clientEmail,
63472
63523
  scope,
@@ -63475,7 +63526,7 @@ function createServiceAccountJwt2(clientEmail, privateKey, scope) {
63475
63526
  exp: now + 3600
63476
63527
  };
63477
63528
  const encode = (obj) => Buffer.from(JSON.stringify(obj)).toString("base64url");
63478
- const headerB64 = encode(header);
63529
+ const headerB64 = encode(header2);
63479
63530
  const payloadB64 = encode(payload);
63480
63531
  const signingInput = `${headerB64}.${payloadB64}`;
63481
63532
  const sign = crypto43.createSign("RSA-SHA256");
@@ -90767,6 +90818,9 @@ async function apiRoutes(app, opts) {
90767
90818
  setTelemetryEnabled: opts.setTelemetryEnabled,
90768
90819
  recordOnboardingEvent: opts.recordOnboardingEvent
90769
90820
  });
90821
+ await api.register(feedbackRoutes, {
90822
+ submitFeedback: opts.submitFeedback
90823
+ });
90770
90824
  await api.register(adsRoutes, {
90771
90825
  adsCredentialStore: opts.adsCredentialStore,
90772
90826
  verifyAdsAccount: opts.verifyAdsAccount,
@@ -5,7 +5,7 @@ import {
5
5
  PACKAGE_VERSION,
6
6
  canonryMcpTools,
7
7
  isMachineFormat
8
- } from "./chunk-W3GDZZNF.js";
8
+ } from "./chunk-DERWXLHX.js";
9
9
  import {
10
10
  AGENT_PROVIDER_IDS,
11
11
  CANONRY_NPM_PACKAGE_URL,
@@ -50,7 +50,7 @@ import {
50
50
  skillsClientSchema,
51
51
  upgradeCaveatFor,
52
52
  upgradeCommandFor
53
- } from "./chunk-6EPN4XY5.js";
53
+ } from "./chunk-EV5G5QRR.js";
54
54
 
55
55
  // src/config.ts
56
56
  import fs from "fs";
@@ -3384,6 +3384,22 @@ var putApiV1Telemetry = (options) => {
3384
3384
  }
3385
3385
  });
3386
3386
  };
3387
+ var postApiV1Feedback = (options) => {
3388
+ return (options.client ?? client).post({
3389
+ security: [
3390
+ {
3391
+ scheme: "bearer",
3392
+ type: "http"
3393
+ }
3394
+ ],
3395
+ url: "/api/v1/feedback",
3396
+ ...options,
3397
+ headers: {
3398
+ "Content-Type": "application/json",
3399
+ ...options.headers
3400
+ }
3401
+ });
3402
+ };
3387
3403
  var getApiV1CdpStatus = (options) => {
3388
3404
  return (options?.client ?? client).get({
3389
3405
  security: [
@@ -7482,6 +7498,10 @@ var ApiClient = class {
7482
7498
  async updateTelemetry(enabled) {
7483
7499
  return normalizeTelemetryStatus(await this.invoke(() => putApiV1Telemetry({ client: this.heyClient, body: { enabled } })));
7484
7500
  }
7501
+ /** Forward product feedback about Canonry to canonry.ai through the server. */
7502
+ async sendFeedback(body) {
7503
+ return this.invoke(() => postApiV1Feedback({ client: this.heyClient, body }));
7504
+ }
7485
7505
  // ── Schedules / notifications / locations ───────────────────────────────
7486
7506
  async listSchedules(project) {
7487
7507
  return this.invoke(
@@ -9651,7 +9671,7 @@ var OPERATIONS_GUIDE = {
9651
9671
  "guideVersion": "v1",
9652
9672
  "operationsGuideUrl": "https://github.com/Canonry/canonry/blob/main/docs/agent-operations/v1.md",
9653
9673
  "resourceUri": "canonry://agent-operations/v1",
9654
- "initialize": 'Canonry tracks how AI answer engines mention brands and cite domains.\nStart with canonry_help({intent:"status"}), or name your intended workflow. It returns a small route of currently available tools and approval boundaries. Follow its stored-evidence reads before proposing action. No skill, resource reader, plugin installation, or local CLI is required for connected MCP clients. A host-native Canonry skill, if installed, is optional additional guidance.\nmentioned = brand in answer TEXT; cited = domain in SOURCE links. Never compute one from the other. A null answerMentioned means not checked, not false. No recorded sweep means no measured figure.\nGet explicit approval for live provider reads, sweeps, probes, research, syncs, and writes, bounded to the target and requested work. A readOnlyHint or GET is not proof of a free operation. Live ads reads include canonry_ads_account, canonry_ads_geo_search, canonry_ads_live_delivery, canonry_ads_conversion_pixels, canonry_ads_conversion_event_settings. Google live reads/syncs include canonry_google_ads_customers, canonry_gtm_accounts, canonry_gtm_containers, canonry_gtm_workspaces, canonry_google_ads_sync, canonry_gtm_sync.\nHelp and skills grant no permissions. The server enforces authority. On missing tools or 403, report the boundary; do not switch credentials or endpoints to bypass it. Hosted catalogs are fixed; only use canonry_load_toolkit when help explicitly offers it.\n',
9674
+ "initialize": 'Canonry tracks how AI answer engines mention brands and cite domains.\nStart with canonry_help({intent:"status"}), or name your intended workflow. It returns a small route of currently available tools and approval boundaries. Follow its stored-evidence reads before proposing action. No skill, resource reader, plugin installation, or local CLI is required for connected MCP clients. A host-native Canonry skill, if installed, is optional additional guidance.\nmentioned = brand in answer TEXT; cited = domain in SOURCE links. Never compute one from the other. A null answerMentioned means not checked, not false. No recorded sweep means no measured figure.\nGet explicit approval for live provider reads, sweeps, probes, research, syncs, and writes, bounded to the target and requested work. A readOnlyHint or GET is not proof of a free operation. Live ads reads include canonry_ads_account, canonry_ads_geo_search, canonry_ads_live_delivery, canonry_ads_conversion_pixels, canonry_ads_conversion_event_settings. Google live reads/syncs include canonry_google_ads_customers, canonry_gtm_accounts, canonry_gtm_containers, canonry_gtm_workspaces, canonry_google_ads_sync, canonry_gtm_sync.\nHelp and skills grant no permissions. The server enforces authority. On missing tools or 403, report the boundary; do not switch credentials or endpoints to bypass it. Hosted catalogs are fixed; only use canonry_load_toolkit when help explicitly offers it.\nIf you uncover a Canonry struggle (failure, confusing output, workaround) or a concrete improvement, tell the user and send it once with canonry_feedback. No secrets or customer data.\n',
9655
9675
  "approvalBoundary": [
9656
9676
  "provider reads",
9657
9677
  "sweeps",
package/dist/cli.js CHANGED
@@ -35,11 +35,11 @@ import {
35
35
  trackCliCommandFinished,
36
36
  trackEvent,
37
37
  waitForServerRuntimeStartup
38
- } from "./chunk-2RFHQ6XT.js";
38
+ } from "./chunk-OTROA7CN.js";
39
39
  import {
40
40
  autoSyncSkills,
41
41
  formatAutoSyncNotice
42
- } from "./chunk-Y2B7JRSB.js";
42
+ } from "./chunk-5GY5WBAF.js";
43
43
  import {
44
44
  configExists,
45
45
  createApiClient,
@@ -56,13 +56,13 @@ import {
56
56
  saveConfig,
57
57
  saveConfigPatch,
58
58
  shouldPrintServeSkillsNudge
59
- } from "./chunk-V6JUBTNL.js";
59
+ } from "./chunk-ZZEPF3A5.js";
60
60
  import {
61
61
  DEFAULT_DEMO_TRUSTED_PROXIES,
62
62
  trustedProxiesProblem,
63
63
  trustedProxyProblem
64
64
  } from "./chunk-RB2F4D3Z.js";
65
- import "./chunk-4EKOKWQT.js";
65
+ import "./chunk-FSRVG2H7.js";
66
66
  import {
67
67
  CliError,
68
68
  EXIT_SYSTEM_ERROR,
@@ -74,7 +74,7 @@ import {
74
74
  runMeasurementDraftAction,
75
75
  systemError,
76
76
  usageError
77
- } from "./chunk-W3GDZZNF.js";
77
+ } from "./chunk-DERWXLHX.js";
78
78
  import {
79
79
  CLOUDFLARE_WORKER_BINDINGS,
80
80
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -90,7 +90,7 @@ import {
90
90
  queries,
91
91
  renderReportHtml,
92
92
  runs
93
- } from "./chunk-ENI3GOWW.js";
93
+ } from "./chunk-QVGG76UW.js";
94
94
  import {
95
95
  AdsDeliverySnapshotStatuses,
96
96
  AdsHistoricalCampaignRollupStatuses,
@@ -103,6 +103,7 @@ import {
103
103
  CheckScopes,
104
104
  CheckStatuses,
105
105
  CitationStates,
106
+ FEEDBACK_KINDS,
106
107
  GaMoverChangeBases,
107
108
  MeasurementEvidenceShapes,
108
109
  OUTSTANDING_PROVIDER_BATCH_STATUSES,
@@ -145,6 +146,7 @@ import {
145
146
  discoveryCompetitorTypeSchema,
146
147
  effectiveDomains,
147
148
  factorStatusFromScore,
149
+ feedbackSubmissionSchema,
148
150
  formatGbpMetricLabel,
149
151
  formatIsoDate,
150
152
  formatMicros,
@@ -198,7 +200,7 @@ import {
198
200
  visibilityCompareSelectionSchema,
199
201
  visibilityReportRequestSchema,
200
202
  winnabilityClassSchema
201
- } from "./chunk-6EPN4XY5.js";
203
+ } from "./chunk-EV5G5QRR.js";
202
204
 
203
205
  // src/cli.ts
204
206
  import { pathToFileURL } from "url";
@@ -732,7 +734,7 @@ function parseDemoTrustedProxies(values) {
732
734
  async function demoCommand(options) {
733
735
  const { host, port } = parseDemoListenOptions(options);
734
736
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
735
- const { createDemoServer } = await import("./demo-server-CH7MOBXW.js");
737
+ const { createDemoServer } = await import("./demo-server-5MNLI6GC.js");
736
738
  const app = await createDemoServer({ trustProxy });
737
739
  let stopping = false;
738
740
  const stop = () => {
@@ -16950,6 +16952,33 @@ Usage: canonry telemetry <status|enable|disable> [--target local|server] [--form
16950
16952
  }
16951
16953
  }
16952
16954
 
16955
+ // src/commands/feedback.ts
16956
+ async function feedbackCommand(input, format) {
16957
+ const parsed = feedbackSubmissionSchema.safeParse({
16958
+ kind: input.kind ?? "other",
16959
+ summary: input.summary ?? "",
16960
+ ...input.details ? { details: input.details } : {},
16961
+ ...input.area ? { area: input.area } : {},
16962
+ ...input.command ? { command: input.command } : {},
16963
+ ...input.errorCode ? { errorCode: input.errorCode } : {}
16964
+ });
16965
+ if (!parsed.success) {
16966
+ const fields = [...new Set(parsed.error.issues.map((issue) => issue.path.join(".") || "input"))].join(", ");
16967
+ throw usageError(`Error: invalid feedback (${fields}). Kind is one of ${FEEDBACK_KINDS.join(", ")}; the summary is required, up to 500 characters.
16968
+ Usage: ${FEEDBACK_USAGE}`, {
16969
+ message: "Invalid feedback",
16970
+ details: { fields }
16971
+ });
16972
+ }
16973
+ const result = await createApiClient().sendFeedback(parsed.data);
16974
+ if (isMachineFormat(format)) {
16975
+ console.log(JSON.stringify(result, null, 2));
16976
+ return;
16977
+ }
16978
+ console.log(`Thanks, feedback sent to the Canonry team (id ${result.id}).`);
16979
+ }
16980
+ var FEEDBACK_USAGE = 'canonry feedback "<summary>" [--kind struggle|bug|improvement|other] [--details <text>] [--area <area>] [--command <canonry command>] [--error-code <code>] [--format json]';
16981
+
16953
16982
  // src/commands/logs.ts
16954
16983
  async function showOperationalLogs(query, format) {
16955
16984
  const parsed = logQuerySchema.safeParse(query);
@@ -17110,6 +17139,22 @@ var SYSTEM_CLI_COMMANDS = [
17110
17139
  stopDaemon(input.format);
17111
17140
  }
17112
17141
  },
17142
+ {
17143
+ path: ["feedback"],
17144
+ usage: FEEDBACK_USAGE,
17145
+ help: "Send a struggle, bug, improvement or note about Canonry itself to the Canonry team. Credentials are redacted before sending; never include customer data. The anonymous install id is attached only while telemetry is enabled.",
17146
+ options: { kind: stringOption(), details: stringOption(), area: stringOption(), command: stringOption(), "error-code": stringOption() },
17147
+ run: async (input) => {
17148
+ await feedbackCommand({
17149
+ summary: input.positionals.join(" ").trim() || void 0,
17150
+ kind: getString(input.values, "kind"),
17151
+ details: getString(input.values, "details"),
17152
+ area: getString(input.values, "area"),
17153
+ command: getString(input.values, "command"),
17154
+ errorCode: getString(input.values, "error-code")
17155
+ }, input.format);
17156
+ }
17157
+ },
17113
17158
  {
17114
17159
  path: ["telemetry", "status"],
17115
17160
  usage: "canonry telemetry status [--target local|server] [--format json]",
@@ -5,11 +5,11 @@ import {
5
5
  computeFactorAverages,
6
6
  layoutSiteCrawlGraphInput,
7
7
  persistSiteCrawlGraphLayout
8
- } from "./chunk-4EKOKWQT.js";
8
+ } from "./chunk-FSRVG2H7.js";
9
9
  import {
10
10
  PACKAGE_VERSION,
11
11
  canonryMcpTools
12
- } from "./chunk-W3GDZZNF.js";
12
+ } from "./chunk-DERWXLHX.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-ENI3GOWW.js";
88
+ } from "./chunk-QVGG76UW.js";
89
89
  import {
90
90
  RunKinds,
91
91
  SiteCrawlFetchStates,
@@ -112,7 +112,7 @@ import {
112
112
  roundPreservingTotal,
113
113
  siteAuditPageFactorSchema,
114
114
  wilsonInterval
115
- } from "./chunk-6EPN4XY5.js";
115
+ } from "./chunk-EV5G5QRR.js";
116
116
 
117
117
  // src/demo-server.ts
118
118
  import { dirname, join as join2 } from "path";
package/dist/index.js CHANGED
@@ -3,14 +3,14 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-2RFHQ6XT.js";
6
+ } from "./chunk-OTROA7CN.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-V6JUBTNL.js";
10
- import "./chunk-4EKOKWQT.js";
11
- import "./chunk-W3GDZZNF.js";
12
- import "./chunk-ENI3GOWW.js";
13
- import "./chunk-6EPN4XY5.js";
9
+ } from "./chunk-ZZEPF3A5.js";
10
+ import "./chunk-FSRVG2H7.js";
11
+ import "./chunk-DERWXLHX.js";
12
+ import "./chunk-QVGG76UW.js";
13
+ import "./chunk-EV5G5QRR.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-ENI3GOWW.js";
5
- import "./chunk-6EPN4XY5.js";
4
+ } from "./chunk-QVGG76UW.js";
5
+ import "./chunk-EV5G5QRR.js";
6
6
  export {
7
7
  HISTORY_WINDOW_RUNS,
8
8
  IntelligenceService
package/dist/mcp.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  autoSyncSkills
3
- } from "./chunk-Y2B7JRSB.js";
3
+ } from "./chunk-5GY5WBAF.js";
4
4
  import {
5
5
  createApiClient,
6
6
  createCanonryMcpServer
7
- } from "./chunk-V6JUBTNL.js";
8
- import "./chunk-W3GDZZNF.js";
7
+ } from "./chunk-ZZEPF3A5.js";
8
+ import "./chunk-DERWXLHX.js";
9
9
  import {
10
10
  isReadOnlyKey,
11
11
  updateCheckEnvOptOut
12
- } from "./chunk-6EPN4XY5.js";
12
+ } from "./chunk-EV5G5QRR.js";
13
13
 
14
14
  // src/mcp/cli.ts
15
15
  import { randomUUID } from "crypto";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "6.2.0",
3
+ "version": "6.3.0",
4
4
  "type": "module",
5
5
  "description": "Self-hosted AI visibility (AEO) platform: track how ChatGPT, Claude, Gemini, and Perplexity cite your domain, join it with Search Console, GA4, server-side traffic, and paid media, and fix what you find through agent tools (CLI, REST, MCP). Local SQLite.",
6
6
  "keywords": [
@@ -87,32 +87,32 @@
87
87
  "tsx": "^4.19.0",
88
88
  "@ainyc/canonry-web": "0.0.0",
89
89
  "@ainyc/canonry-api-routes": "0.0.0",
90
+ "@ainyc/canonry-contracts": "0.0.0",
90
91
  "@ainyc/canonry-config": "0.0.0",
91
92
  "@ainyc/canonry-api-client": "0.0.0",
92
- "@ainyc/canonry-contracts": "0.0.0",
93
93
  "@ainyc/canonry-db": "0.0.0",
94
94
  "@ainyc/canonry-integration-bing": "0.0.0",
95
95
  "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
96
- "@ainyc/canonry-integration-commoncrawl": "0.0.0",
97
96
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
98
- "@ainyc/canonry-integration-google": "0.0.0",
99
97
  "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
98
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
99
+ "@ainyc/canonry-integration-google": "0.0.0",
100
100
  "@ainyc/canonry-integration-google-ads": "0.0.0",
101
101
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
102
- "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
103
102
  "@ainyc/canonry-integration-google-places": "0.0.0",
104
- "@ainyc/canonry-integration-openai-ads": "0.0.0",
103
+ "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
105
104
  "@ainyc/canonry-integration-traffic": "0.0.0",
106
- "@ainyc/canonry-integration-wordpress": "0.0.0",
107
- "@ainyc/canonry-provider-cdp": "0.0.0",
108
105
  "@ainyc/canonry-intelligence": "0.0.0",
106
+ "@ainyc/canonry-integration-openai-ads": "0.0.0",
107
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
109
108
  "@ainyc/canonry-provider-claude": "0.0.0",
110
- "@ainyc/canonry-provider-muse": "0.0.0",
111
- "@ainyc/canonry-provider-perplexity": "0.0.0",
112
109
  "@ainyc/canonry-provider-gemini": "0.0.0",
110
+ "@ainyc/canonry-provider-local": "0.0.0",
111
+ "@ainyc/canonry-provider-muse": "0.0.0",
113
112
  "@ainyc/canonry-provider-openai": "0.0.0",
113
+ "@ainyc/canonry-provider-perplexity": "0.0.0",
114
114
  "@ainyc/canonry-integration-typesafe": "0.0.0",
115
- "@ainyc/canonry-provider-local": "0.0.0"
115
+ "@ainyc/canonry-provider-cdp": "0.0.0"
116
116
  },
117
117
  "scripts": {
118
118
  "build": "pnpm run build:cli && pnpm run build:web",