@ainyc/canonry 5.0.0 → 5.1.1

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 (28) hide show
  1. package/README.md +1 -1
  2. package/assets/agent-workspace/skills/canonry/SKILL.md +24 -5
  3. package/assets/assets/{AuditHistoryPanel-vWUtVXOI.js → AuditHistoryPanel-9F_74jKS.js} +1 -1
  4. package/assets/assets/{BacklinksPage-CSzuMEuv.js → BacklinksPage-DmF2rbwy.js} +1 -1
  5. package/assets/assets/{HistoryPage-DtCTQ7CQ.js → HistoryPage-DKopwAjo.js} +1 -1
  6. package/assets/assets/{MeasurementPropertyPage-B7gTMWNs.js → MeasurementPropertyPage-Dpd_DYQv.js} +1 -1
  7. package/assets/assets/{ProjectPage-Bsm0_jUY.js → ProjectPage-b1-57SzP.js} +1 -1
  8. package/assets/assets/{RunRow-D5k4Rdo4.js → RunRow-BjrSRp6X.js} +1 -1
  9. package/assets/assets/{RunsPage-DIXBB2Om.js → RunsPage-BFrymZ7D.js} +1 -1
  10. package/assets/assets/{SettingsPage-wYhBm6Az.js → SettingsPage-DdVa6pLK.js} +1 -1
  11. package/assets/assets/{SiteHealthSection-YebzsgDh.js → SiteHealthSection-CizmiBjA.js} +3 -3
  12. package/assets/assets/{TrafficPage-DtnSRFO8.js → TrafficPage-N7sZgb9x.js} +1 -1
  13. package/assets/assets/{TrafficSourceDetailPage-DYQ6mpYq.js → TrafficSourceDetailPage-BNQmMJ5D.js} +1 -1
  14. package/assets/assets/{extract-error-message-BdChrkmo.js → extract-error-message-Bh9mRIz9.js} +1 -1
  15. package/assets/assets/{index-C_E2NRYY.js → index-BnR1Tu9j.js} +3 -3
  16. package/assets/assets/{react-sigma_core.esm.min-sKWUk_tG.js → react-sigma_core.esm.min-DGaYwjLZ.js} +1 -1
  17. package/assets/assets/{v2-overview-adapter-CkPt-sRK.js → v2-overview-adapter-eqN-8OK5.js} +1 -1
  18. package/assets/index.html +1 -1
  19. package/dist/{chunk-3WODA4JF.js → chunk-3DL3KV6C.js} +2 -0
  20. package/dist/{chunk-LJKAB4CX.js → chunk-66KGNC4U.js} +2 -2
  21. package/dist/{chunk-UMU3IBXM.js → chunk-LHHJVGH7.js} +39 -26
  22. package/dist/{chunk-B2XM6AQ6.js → chunk-MEQGGODM.js} +21 -9
  23. package/dist/{chunk-67NX4KVE.js → chunk-ZWVXUPLP.js} +128 -8
  24. package/dist/cli.js +6 -5
  25. package/dist/index.js +4 -4
  26. package/dist/{intelligence-service-MGQRJW4V.js → intelligence-service-7XX5ZRYE.js} +2 -2
  27. package/dist/mcp.js +6 -7
  28. package/package.json +26 -11
@@ -14,8 +14,9 @@ import {
14
14
  isMachineFormat,
15
15
  loadConfig,
16
16
  loadConfigRaw,
17
+ resolveOperatorApiKeyIds,
17
18
  saveConfigPatch
18
- } from "./chunk-B2XM6AQ6.js";
19
+ } from "./chunk-MEQGGODM.js";
19
20
  import {
20
21
  CC_CACHE_DIR,
21
22
  DUCKDB_SPEC,
@@ -166,7 +167,7 @@ import {
166
167
  siteCrawlSnapshots,
167
168
  toAlertView,
168
169
  usageCounters
169
- } from "./chunk-UMU3IBXM.js";
170
+ } from "./chunk-LHHJVGH7.js";
170
171
  import {
171
172
  AGENT_MEMORY_VALUE_MAX_BYTES,
172
173
  AGENT_PROVIDER_IDS,
@@ -311,7 +312,7 @@ import {
311
312
  validationError,
312
313
  winnabilityClassLabel,
313
314
  withRetry
314
- } from "./chunk-3WODA4JF.js";
315
+ } from "./chunk-3DL3KV6C.js";
315
316
 
316
317
  // src/telemetry.ts
317
318
  import crypto from "crypto";
@@ -6285,7 +6286,7 @@ function nonBlank(value) {
6285
6286
  return trimmed ? trimmed : void 0;
6286
6287
  }
6287
6288
  function resolveBuildCommit(env = process.env) {
6288
- const embedded = true ? "b510dfd8c0f64721829f5edb0df5afd951d7b960" : void 0;
6289
+ const embedded = true ? "2c66fea459e20ff285096090dbc6a6ee788c4c00" : void 0;
6289
6290
  return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
6290
6291
  }
6291
6292
  function resolveInstanceIdentity(env = process.env) {
@@ -6341,6 +6342,30 @@ function buildRunCompletedProps(input) {
6341
6342
  if (input.location) props.location = input.location;
6342
6343
  return props;
6343
6344
  }
6345
+ function buildSiteAuditCompletedProps(input) {
6346
+ const props = {
6347
+ status: input.status,
6348
+ durationMs: Date.now() - input.startTime
6349
+ };
6350
+ if (input.trigger) props.trigger = input.trigger;
6351
+ const domainHash = hashDomain(input.canonicalDomain ?? null);
6352
+ if (domainHash) props.domainHash = domainHash;
6353
+ const crawl = input.crawl;
6354
+ if (!crawl) return props;
6355
+ props.complete = crawl.complete;
6356
+ if (crawl.termination) props.termination = crawl.termination;
6357
+ props.pagesDiscovered = crawl.pagesDiscovered;
6358
+ props.pagesFetched = crawl.pagesFetched;
6359
+ props.pagesAudited = crawl.pagesAudited;
6360
+ props.pagesErrored = crawl.pagesErrored;
6361
+ if (crawl.pagesAudited > 0 && typeof crawl.aggregateScore === "number" && Number.isFinite(crawl.aggregateScore)) {
6362
+ props.aggregateScore = crawl.aggregateScore;
6363
+ }
6364
+ props.pageBudget = crawl.pageBudget;
6365
+ props.checkDeadLinks = crawl.checkDeadLinks;
6366
+ if (crawl.checkDeadLinks) props.deadLinksFound = crawl.deadLinksFound;
6367
+ return props;
6368
+ }
6344
6369
 
6345
6370
  // src/provider-execution-gate.ts
6346
6371
  var ProviderExecutionGate = class {
@@ -6609,7 +6634,14 @@ var JobRunner = class {
6609
6634
  this.registry = registry;
6610
6635
  }
6611
6636
  recoverStaleRuns() {
6612
- const stale = this.db.select({ id: runs.id, projectId: runs.projectId, kind: runs.kind, status: runs.status }).from(runs).where(inArray(runs.status, ["running", "queued"])).all();
6637
+ const stale = this.db.select({
6638
+ id: runs.id,
6639
+ projectId: runs.projectId,
6640
+ kind: runs.kind,
6641
+ status: runs.status,
6642
+ trigger: runs.trigger,
6643
+ startedAt: runs.startedAt
6644
+ }).from(runs).where(inArray(runs.status, ["running", "queued"])).all();
6613
6645
  if (stale.length === 0) return;
6614
6646
  const now = (/* @__PURE__ */ new Date()).toISOString();
6615
6647
  for (const run of stale) {
@@ -6627,6 +6659,35 @@ var JobRunner = class {
6627
6659
  });
6628
6660
  if (!recovered) continue;
6629
6661
  log2.warn("run.recovered-stale", { runId: run.id, previousStatus: run.status });
6662
+ if (run.kind === RunKinds["site-audit"]) this.trackRecoveredSiteAudit(run);
6663
+ }
6664
+ }
6665
+ /**
6666
+ * A crawl the process died under never reaches `executeSiteAudit`'s terminal
6667
+ * telemetry, so without this every interrupted audit vanishes from the data.
6668
+ * Emitted after the recovery transaction commits. `durationMs` runs from the
6669
+ * crawl's start to this recovery, so it includes the downtime and is an upper
6670
+ * bound; a run that was still queued has no start and reports 0.
6671
+ *
6672
+ * The source is set explicitly: recovery runs during server construction,
6673
+ * before `canonry serve` switches the process source to `cli-server`.
6674
+ */
6675
+ trackRecoveredSiteAudit(run) {
6676
+ try {
6677
+ const startedAt = run.startedAt ? Date.parse(run.startedAt) : Number.NaN;
6678
+ const project = this.db.select({ canonicalDomain: projects.canonicalDomain }).from(projects).where(eq2(projects.id, run.projectId)).get();
6679
+ trackEvent(
6680
+ "site_audit.completed",
6681
+ buildSiteAuditCompletedProps({
6682
+ status: "failed",
6683
+ startTime: Number.isFinite(startedAt) ? startedAt : Date.now(),
6684
+ trigger: run.trigger,
6685
+ canonicalDomain: project?.canonicalDomain ?? null
6686
+ }),
6687
+ { errorCode: "SERVER_RESTARTED", source: "cli-server" }
6688
+ );
6689
+ } catch (err) {
6690
+ log2.warn("telemetry.recovered-site-audit-failed", { runId: run.id, error: describeError(err) });
6630
6691
  }
6631
6692
  }
6632
6693
  async executeRun(runId, projectId, providerOverride, locationOverride) {
@@ -7148,6 +7209,7 @@ var JobRunner = class {
7148
7209
  if (existingRun.kind === "answer-visibility" && runTrigger !== "probe" && totalSnapshotsInserted > 0 && !this.hasPriorActivation(projectId, runId)) {
7149
7210
  trackEvent("activation.completed", {
7150
7211
  flowVersion: ONBOARDING_FLOW_VERSION,
7212
+ kind: "answer_visibility",
7151
7213
  status: finalStatus,
7152
7214
  providerCountBucket: bucketOnboardingCount(executionContext.providerCount),
7153
7215
  queryCountBucket: bucketOnboardingCount(executionContext.queryCount),
@@ -10030,7 +10092,7 @@ function buildDiscoveryInsightTitle(input) {
10030
10092
 
10031
10093
  // src/execute-site-audit.ts
10032
10094
  import crypto19 from "crypto";
10033
- import { and as and13, eq as eq15, sql as sql6 } from "drizzle-orm";
10095
+ import { and as and13, eq as eq15, ne as ne3, sql as sql6 } from "drizzle-orm";
10034
10096
  import { runSiteCrawl } from "@canonry/aeo-audit";
10035
10097
 
10036
10098
  // src/site-audit-root.ts
@@ -11107,10 +11169,35 @@ function deadLinkCheckedCount(edges, pages) {
11107
11169
  }
11108
11170
  return new Set([...edges].filter((edge) => edge.type === "anchor" && edge.classification === "internal" && attemptedUrls.has(edge.to)).map((edge) => edge.to)).size;
11109
11171
  }
11172
+ function trackSiteAuditOutcome(db, input) {
11173
+ try {
11174
+ const errorCode = input.status === "cancelled" ? "RUN_CANCELLED" : input.status === "failed" ? "UNKNOWN" : void 0;
11175
+ trackEvent(
11176
+ "site_audit.completed",
11177
+ buildSiteAuditCompletedProps(input),
11178
+ errorCode ? { errorCode } : void 0
11179
+ );
11180
+ const pagesAudited = input.crawl?.pagesAudited ?? 0;
11181
+ if (pagesAudited === 0 || input.trigger === RunTriggers.probe) return;
11182
+ const prior = db.select({ id: siteAuditSnapshots.id }).from(siteAuditSnapshots).where(and13(eq15(siteAuditSnapshots.projectId, input.projectId), ne3(siteAuditSnapshots.runId, input.runId))).limit(1).get();
11183
+ if (prior) return;
11184
+ trackEvent("activation.completed", {
11185
+ flowVersion: ONBOARDING_FLOW_VERSION,
11186
+ kind: "site_health",
11187
+ status: input.status,
11188
+ pagesAuditedBucket: bucketOnboardingCount(pagesAudited)
11189
+ });
11190
+ } catch (error) {
11191
+ log13.warn("telemetry.failed", { runId: input.runId, projectId: input.projectId, error: describeError(error) });
11192
+ }
11193
+ }
11110
11194
  async function executeSiteAudit(db, runId, projectId, opts = {}) {
11111
11195
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
11112
11196
  const claim = db.update(runs).set({ status: "running", startedAt }).where(and13(eq15(runs.id, runId), eq15(runs.projectId, projectId), eq15(runs.status, "queued"))).run();
11113
11197
  if (claim.changes === 0) return;
11198
+ const startTime = Date.now();
11199
+ const trigger = db.select({ trigger: runs.trigger }).from(runs).where(eq15(runs.id, runId)).get()?.trigger ?? null;
11200
+ let canonicalDomain = null;
11114
11201
  const attemptNumber = (db.select({ value: sql6`coalesce(max(${siteCrawlAttempts.attemptNumber}), 0)` }).from(siteCrawlAttempts).where(eq15(siteCrawlAttempts.runId, runId)).get()?.value ?? 0) + 1;
11115
11202
  const attemptId = crypto19.randomUUID();
11116
11203
  db.insert(siteCrawlAttempts).values({
@@ -11390,6 +11477,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
11390
11477
  try {
11391
11478
  const project = db.select().from(projects).where(eq15(projects.id, projectId)).get();
11392
11479
  if (!project) throw new Error(`Project not found: ${projectId}`);
11480
+ canonicalDomain = project.canonicalDomain;
11393
11481
  const homepageUrl = toHomepageUrl(project.canonicalDomain);
11394
11482
  const maxPages = clampSiteAuditLimit(opts.maxPages ?? opts.limit);
11395
11483
  const maxEdges = clampSiteAuditEdgeLimit(opts.maxEdges);
@@ -11609,6 +11697,26 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
11609
11697
  throw new Error(`Site audit run was no longer running before publication: ${runId}`);
11610
11698
  }
11611
11699
  log13.info("completed", { runId, projectId, status: terminalStatus, complete: crawlSummary.complete, pages: crawlSummary.pagesObserved });
11700
+ trackSiteAuditOutcome(db, {
11701
+ runId,
11702
+ projectId,
11703
+ status: crawlSummary.complete ? "completed" : "partial",
11704
+ startTime,
11705
+ trigger,
11706
+ canonicalDomain,
11707
+ crawl: {
11708
+ complete: crawlSummary.complete,
11709
+ termination: crawlSummary.terminationReason,
11710
+ pagesDiscovered: crawlSummary.pagesDiscovered,
11711
+ pagesFetched: crawlSummary.pagesFetched,
11712
+ pagesAudited: crawlSummary.auditRollup.auditedPages,
11713
+ pagesErrored: errorCount,
11714
+ aggregateScore: crawlSummary.auditRollup.aggregateScore,
11715
+ pageBudget: maxPages,
11716
+ checkDeadLinks: opts.checkDeadLinks ?? false,
11717
+ deadLinksFound
11718
+ }
11719
+ });
11612
11720
  } catch (error) {
11613
11721
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
11614
11722
  const cancelled = isCancelled(error, opts.signal);
@@ -11627,6 +11735,14 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
11627
11735
  }).where(and13(eq15(runs.id, runId), eq15(runs.status, "running"))).run();
11628
11736
  });
11629
11737
  log13.error("failed", { runId, projectId, cancelled, error: message });
11738
+ trackSiteAuditOutcome(db, {
11739
+ runId,
11740
+ projectId,
11741
+ status: cancelled ? "cancelled" : "failed",
11742
+ startTime,
11743
+ trigger,
11744
+ canonicalDomain
11745
+ });
11630
11746
  throw error;
11631
11747
  }
11632
11748
  }
@@ -12096,7 +12212,7 @@ function readStoredGroundingSources(rawResponse) {
12096
12212
  return result;
12097
12213
  }
12098
12214
  async function backfillInsightsCommand(project, opts) {
12099
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-MGQRJW4V.js");
12215
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-7XX5ZRYE.js");
12100
12216
  const config = loadConfig();
12101
12217
  const db = createClient(config.database);
12102
12218
  migrate(db);
@@ -15861,6 +15977,7 @@ function registerMcpHttpRoutes(scope, opts) {
15861
15977
  server = createCanonryMcpServer({
15862
15978
  scope: segment.readOnly || isReadOnlyKey(scopes) ? "read-only" : "all",
15863
15979
  credentialScopes: scopes,
15980
+ operator: request.operatorAccess === true,
15864
15981
  tiers: segment.tiers,
15865
15982
  clientFactory: () => client
15866
15983
  });
@@ -15908,7 +16025,8 @@ function registerMcpHttpRoutes(scope, opts) {
15908
16025
  kind: request.principal?.kind ?? "api-key",
15909
16026
  id: principal.id,
15910
16027
  scopes: [...new Set(principal.scopes)].sort(),
15911
- projectId: principal.projectId ?? null
16028
+ projectId: principal.projectId ?? null,
16029
+ operator: request.operatorAccess === true
15912
16030
  }));
15913
16031
  const sessionId = request.headers["mcp-session-id"];
15914
16032
  const existing = typeof sessionId === "string" ? sessions.get(sessionId) : void 0;
@@ -17699,6 +17817,7 @@ function createGoogleMarketingConfigCredentialStore(input) {
17699
17817
  };
17700
17818
  }
17701
17819
  async function createServer(opts) {
17820
+ const operatorApiKeyIds = resolveOperatorApiKeyIds(process.env);
17702
17821
  const dashboardManagedRunKinds = resolveDashboardManagedRunKinds(process.env, opts.config);
17703
17822
  const operationalLogs = new OperationalLogStore(opts.db, {
17704
17823
  retention: opts.config.database === ":memory:" ? "process" : "durable"
@@ -19305,6 +19424,7 @@ async function createServer(opts) {
19305
19424
  }
19306
19425
  });
19307
19426
  },
19427
+ operatorApiKeyIds,
19308
19428
  listOperationalLogs: (query) => operationalLogs.list(query),
19309
19429
  getTelemetryStatus,
19310
19430
  setTelemetryEnabled: (enabled) => {
package/dist/cli.js CHANGED
@@ -29,11 +29,11 @@ import {
29
29
  trackCliCommandFinished,
30
30
  trackEvent,
31
31
  waitForServerRuntimeStartup
32
- } from "./chunk-67NX4KVE.js";
32
+ } from "./chunk-ZWVXUPLP.js";
33
33
  import {
34
34
  autoSyncSkills,
35
35
  formatAutoSyncNotice
36
- } from "./chunk-LJKAB4CX.js";
36
+ } from "./chunk-66KGNC4U.js";
37
37
  import {
38
38
  CliError,
39
39
  EXIT_SYSTEM_ERROR,
@@ -59,7 +59,7 @@ import {
59
59
  saveConfigPatch,
60
60
  systemError,
61
61
  usageError
62
- } from "./chunk-B2XM6AQ6.js";
62
+ } from "./chunk-MEQGGODM.js";
63
63
  import {
64
64
  CLOUDFLARE_WORKER_BINDINGS,
65
65
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -73,7 +73,7 @@ import {
73
73
  projects,
74
74
  queries,
75
75
  renderReportHtml
76
- } from "./chunk-UMU3IBXM.js";
76
+ } from "./chunk-LHHJVGH7.js";
77
77
  import {
78
78
  AdsDeliverySnapshotStatuses,
79
79
  AdsHistoricalCampaignRollupStatuses,
@@ -152,7 +152,7 @@ import {
152
152
  snapshotProviderModeSchema,
153
153
  visibilityReportRequestSchema,
154
154
  winnabilityClassSchema
155
- } from "./chunk-3WODA4JF.js";
155
+ } from "./chunk-3DL3KV6C.js";
156
156
 
157
157
  // src/cli.ts
158
158
  import { pathToFileURL } from "url";
@@ -9235,6 +9235,7 @@ async function showApiKeySelf(format) {
9235
9235
  console.log(`API key "${key.name}" (${key.keyPrefix})`);
9236
9236
  console.log(` Scopes: ${key.scopes.join(", ")}`);
9237
9237
  console.log(` Read-only: ${key.readOnly ? "yes" : "no"}`);
9238
+ console.log(` Operator: ${key.operator === true ? "yes" : "no"}`);
9238
9239
  console.log(` Reach: ${keyReach(key)}`);
9239
9240
  console.log(` Status: ${keyStatus(key)}`);
9240
9241
  }
package/dist/index.js CHANGED
@@ -3,12 +3,12 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-67NX4KVE.js";
6
+ } from "./chunk-ZWVXUPLP.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-B2XM6AQ6.js";
10
- import "./chunk-UMU3IBXM.js";
11
- import "./chunk-3WODA4JF.js";
9
+ } from "./chunk-MEQGGODM.js";
10
+ import "./chunk-LHHJVGH7.js";
11
+ import "./chunk-3DL3KV6C.js";
12
12
  export {
13
13
  GoogleMarketingRuntimeError,
14
14
  createGoogleMarketingCredentialStore,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-UMU3IBXM.js";
4
- import "./chunk-3WODA4JF.js";
3
+ } from "./chunk-LHHJVGH7.js";
4
+ import "./chunk-3DL3KV6C.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  autoSyncSkills
3
- } from "./chunk-LJKAB4CX.js";
3
+ } from "./chunk-66KGNC4U.js";
4
4
  import {
5
5
  createApiClient,
6
6
  createCanonryMcpServer
7
- } from "./chunk-B2XM6AQ6.js";
7
+ } from "./chunk-MEQGGODM.js";
8
8
  import {
9
9
  isReadOnlyKey
10
- } from "./chunk-3WODA4JF.js";
10
+ } from "./chunk-3DL3KV6C.js";
11
11
 
12
12
  // src/mcp/cli.ts
13
13
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -54,19 +54,18 @@ async function resolveEffectiveScope(client, flagScope) {
54
54
  return (await resolveEffectiveAuthorization(client, flagScope)).scope;
55
55
  }
56
56
  async function resolveEffectiveAuthorization(client, flagScope) {
57
- if (flagScope === "read-only") return { scope: "read-only" };
58
57
  try {
59
58
  const self = await client.getApiKeySelf();
60
59
  if (isReadOnlyKey(self.scopes)) {
61
60
  process.stderr.write(
62
61
  "canonry-mcp: configured API key is read-only \u2014 restricting to read tools.\n"
63
62
  );
64
- return { scope: "read-only", credentialScopes: self.scopes };
63
+ return { scope: "read-only", credentialScopes: self.scopes, operator: self.operator === true };
65
64
  }
66
- return { scope: flagScope, credentialScopes: self.scopes };
65
+ return { scope: flagScope, credentialScopes: self.scopes, operator: self.operator === true };
67
66
  } catch {
68
67
  }
69
- return { scope: flagScope };
68
+ return { scope: flagScope, operator: false };
70
69
  }
71
70
  function parseCliOptions(argv, env = process.env) {
72
71
  if (argv.includes("--help") || argv.includes("-h")) {
package/package.json CHANGED
@@ -1,8 +1,23 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "5.0.0",
3
+ "version": "5.1.1",
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
+ "keywords": [
7
+ "aeo",
8
+ "answer-engine-optimization",
9
+ "geo",
10
+ "generative-engine-optimization",
11
+ "ai-visibility",
12
+ "citation-tracking",
13
+ "seo",
14
+ "mcp",
15
+ "chatgpt",
16
+ "claude",
17
+ "gemini",
18
+ "perplexity",
19
+ "self-hosted"
20
+ ],
6
21
  "license": "MIT",
7
22
  "homepage": "https://canonry.ai",
8
23
  "repository": {
@@ -73,30 +88,30 @@
73
88
  "tsx": "^4.19.0",
74
89
  "@ainyc/canonry-web": "0.0.0",
75
90
  "@ainyc/canonry-api-client": "0.0.0",
76
- "@ainyc/canonry-api-routes": "0.0.0",
77
- "@ainyc/canonry-contracts": "0.0.0",
78
91
  "@ainyc/canonry-config": "0.0.0",
79
- "@ainyc/canonry-integration-bing": "0.0.0",
92
+ "@ainyc/canonry-contracts": "0.0.0",
93
+ "@ainyc/canonry-api-routes": "0.0.0",
80
94
  "@ainyc/canonry-db": "0.0.0",
81
- "@ainyc/canonry-integration-cloud-run": "0.0.0",
95
+ "@ainyc/canonry-integration-bing": "0.0.0",
82
96
  "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
83
97
  "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
84
- "@ainyc/canonry-integration-google": "0.0.0",
85
- "@ainyc/canonry-integration-google-business-profile": "0.0.0",
86
98
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
99
+ "@ainyc/canonry-integration-cloud-run": "0.0.0",
100
+ "@ainyc/canonry-integration-google": "0.0.0",
87
101
  "@ainyc/canonry-integration-google-ads": "0.0.0",
102
+ "@ainyc/canonry-integration-google-business-profile": "0.0.0",
88
103
  "@ainyc/canonry-integration-google-places": "0.0.0",
89
104
  "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
90
- "@ainyc/canonry-integration-openai-ads": "0.0.0",
91
105
  "@ainyc/canonry-integration-traffic": "0.0.0",
106
+ "@ainyc/canonry-integration-openai-ads": "0.0.0",
92
107
  "@ainyc/canonry-integration-wordpress": "0.0.0",
93
- "@ainyc/canonry-intelligence": "0.0.0",
94
108
  "@ainyc/canonry-provider-cdp": "0.0.0",
109
+ "@ainyc/canonry-intelligence": "0.0.0",
95
110
  "@ainyc/canonry-provider-claude": "0.0.0",
111
+ "@ainyc/canonry-provider-local": "0.0.0",
96
112
  "@ainyc/canonry-provider-gemini": "0.0.0",
97
113
  "@ainyc/canonry-provider-openai": "0.0.0",
98
- "@ainyc/canonry-provider-perplexity": "0.0.0",
99
- "@ainyc/canonry-provider-local": "0.0.0"
114
+ "@ainyc/canonry-provider-perplexity": "0.0.0"
100
115
  },
101
116
  "scripts": {
102
117
  "build": "pnpm run build:cli && pnpm run build:web",