@ainyc/canonry 2.3.1 → 2.4.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/assets/index.html CHANGED
@@ -12,8 +12,8 @@
12
12
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
13
13
  <link rel="apple-touch-icon" href="./apple-touch-icon.png" />
14
14
  <title>Canonry</title>
15
- <script type="module" crossorigin src="./assets/index-C_pxQt0X.js"></script>
16
- <link rel="stylesheet" crossorigin href="./assets/index-yF1fs-OW.css">
15
+ <script type="module" crossorigin src="./assets/index-Nrl3ecFY.js"></script>
16
+ <link rel="stylesheet" crossorigin href="./assets/index-CAewPdsZ.css">
17
17
  </head>
18
18
  <body>
19
19
  <div id="root"></div>
@@ -61,6 +61,7 @@ var projects = sqliteTable("projects", {
61
61
  providers: text("providers").notNull().default("[]"),
62
62
  locations: text("locations").notNull().default("[]"),
63
63
  defaultLocation: text("default_location"),
64
+ autoExtractBacklinks: integer("auto_extract_backlinks").notNull().default(0),
64
65
  configSource: text("config_source").notNull().default("cli"),
65
66
  configRevision: integer("config_revision").notNull().default(1),
66
67
  createdAt: text("created_at").notNull(),
@@ -1031,7 +1032,11 @@ var MIGRATIONS = [
1031
1032
  created_at TEXT NOT NULL
1032
1033
  )`,
1033
1034
  `CREATE UNIQUE INDEX IF NOT EXISTS idx_backlink_summaries_project_release ON backlink_summaries(project_id, release)`,
1034
- `CREATE INDEX IF NOT EXISTS idx_backlink_summaries_project ON backlink_summaries(project_id)`
1035
+ `CREATE INDEX IF NOT EXISTS idx_backlink_summaries_project ON backlink_summaries(project_id)`,
1036
+ // v42: Per-project auto-extract toggle — when a release sync transitions
1037
+ // to ready, projects with this flag get a backlink-extract run enqueued.
1038
+ // Stored as INTEGER (0/1) to match SQLite boolean convention.
1039
+ `ALTER TABLE projects ADD COLUMN auto_extract_backlinks INTEGER NOT NULL DEFAULT 0`
1035
1040
  ];
1036
1041
  function isDuplicateColumnError(err) {
1037
1042
  if (!(err instanceof Error)) return false;
@@ -30,7 +30,7 @@ import {
30
30
  runs,
31
31
  schedules,
32
32
  usageCounters
33
- } from "./chunk-CW6CAPBQ.js";
33
+ } from "./chunk-GZF3YIHY.js";
34
34
 
35
35
  // src/config.ts
36
36
  import fs from "fs";
@@ -347,7 +347,7 @@ function printCliError(err, format) {
347
347
  // src/server.ts
348
348
  import { createRequire as createRequire3 } from "module";
349
349
  import crypto27 from "crypto";
350
- import fs12 from "fs";
350
+ import fs13 from "fs";
351
351
  import path15 from "path";
352
352
  import { fileURLToPath as fileURLToPath2 } from "url";
353
353
  import { eq as eq29 } from "drizzle-orm";
@@ -448,6 +448,7 @@ var projectUpsertRequestSchema = z3.object({
448
448
  providers: z3.array(providerNameSchema).optional(),
449
449
  locations: z3.array(locationContextSchema).optional(),
450
450
  defaultLocation: z3.string().nullable().optional(),
451
+ autoExtractBacklinks: z3.boolean().optional(),
451
452
  configSource: configSourceSchema.optional()
452
453
  });
453
454
  var projectDtoSchema = z3.object({
@@ -462,6 +463,7 @@ var projectDtoSchema = z3.object({
462
463
  labels: z3.record(z3.string(), z3.string()).default({}),
463
464
  locations: z3.array(locationContextSchema).default([]),
464
465
  defaultLocation: z3.string().nullable().optional(),
466
+ autoExtractBacklinks: z3.boolean().default(false),
465
467
  configSource: configSourceSchema.default("cli"),
466
468
  configRevision: z3.number().int().positive().default(1),
467
469
  createdAt: z3.string().optional(),
@@ -535,7 +537,8 @@ var configSpecSchema = z4.object({
535
537
  defaultLocation: z4.string().optional(),
536
538
  schedule: configScheduleSchema,
537
539
  notifications: z4.array(configNotificationSchema).optional().default([]),
538
- google: configGoogleSchema
540
+ google: configGoogleSchema,
541
+ autoExtractBacklinks: z4.boolean().optional().default(false)
539
542
  }).superRefine((spec, ctx) => {
540
543
  const duplicateLabels = findDuplicateLocationLabels(spec.locations);
541
544
  if (duplicateLabels.length > 0) {
@@ -1679,6 +1682,7 @@ async function projectRoutes(app, opts) {
1679
1682
  defaultLocation: nextDefaultLocation
1680
1683
  });
1681
1684
  }
1685
+ const nextAutoExtractBacklinks = body.autoExtractBacklinks !== void 0 ? body.autoExtractBacklinks ? 1 : 0 : existing?.autoExtractBacklinks ?? 0;
1682
1686
  if (existing) {
1683
1687
  app.db.transaction((tx) => {
1684
1688
  tx.update(projects).set({
@@ -1692,6 +1696,7 @@ async function projectRoutes(app, opts) {
1692
1696
  providers: JSON.stringify(body.providers ?? []),
1693
1697
  locations: JSON.stringify(nextLocations),
1694
1698
  defaultLocation: nextDefaultLocation,
1699
+ autoExtractBacklinks: nextAutoExtractBacklinks,
1695
1700
  configSource: body.configSource ?? "api",
1696
1701
  configRevision: existing.configRevision + 1,
1697
1702
  updatedAt: now
@@ -1723,6 +1728,7 @@ async function projectRoutes(app, opts) {
1723
1728
  providers: JSON.stringify(body.providers ?? []),
1724
1729
  locations: JSON.stringify(nextLocations),
1725
1730
  defaultLocation: nextDefaultLocation,
1731
+ autoExtractBacklinks: nextAutoExtractBacklinks,
1726
1732
  configSource: body.configSource ?? "api",
1727
1733
  configRevision: 1,
1728
1734
  createdAt: now,
@@ -1869,6 +1875,7 @@ async function projectRoutes(app, opts) {
1869
1875
  providers: parseJsonColumn(project.providers, []),
1870
1876
  locations: parseJsonColumn(project.locations, []),
1871
1877
  ...project.defaultLocation ? { defaultLocation: project.defaultLocation } : {},
1878
+ ...project.autoExtractBacklinks === 1 ? { autoExtractBacklinks: true } : {},
1872
1879
  notifications: notificationRows.map((row) => {
1873
1880
  const cfg = parseJsonColumn(row.config, { url: "", events: [] });
1874
1881
  return {
@@ -1903,6 +1910,7 @@ function formatProject(row) {
1903
1910
  providers: parseJsonColumn(row.providers, []),
1904
1911
  locations: parseJsonColumn(row.locations, []),
1905
1912
  defaultLocation: row.defaultLocation,
1913
+ autoExtractBacklinks: row.autoExtractBacklinks === 1,
1906
1914
  configSource: row.configSource,
1907
1915
  configRevision: row.configRevision,
1908
1916
  createdAt: row.createdAt,
@@ -2742,6 +2750,7 @@ async function applyRoutes(app, opts) {
2742
2750
  providers: JSON.stringify(config.spec.providers ?? []),
2743
2751
  locations: JSON.stringify(config.spec.locations ?? []),
2744
2752
  defaultLocation: config.spec.defaultLocation ?? null,
2753
+ autoExtractBacklinks: config.spec.autoExtractBacklinks ? 1 : 0,
2745
2754
  configSource: "config-file",
2746
2755
  configRevision: existing.configRevision + 1,
2747
2756
  updatedAt: now
@@ -2768,6 +2777,7 @@ async function applyRoutes(app, opts) {
2768
2777
  providers: JSON.stringify(config.spec.providers ?? []),
2769
2778
  locations: JSON.stringify(config.spec.locations ?? []),
2770
2779
  defaultLocation: config.spec.defaultLocation ?? null,
2780
+ autoExtractBacklinks: config.spec.autoExtractBacklinks ? 1 : 0,
2771
2781
  configSource: "config-file",
2772
2782
  configRevision: 1,
2773
2783
  createdAt: now,
@@ -2891,6 +2901,7 @@ async function applyRoutes(app, opts) {
2891
2901
  providers: parseJsonColumn(project.providers, []),
2892
2902
  locations: parseJsonColumn(project.locations, []),
2893
2903
  defaultLocation: project.defaultLocation,
2904
+ autoExtractBacklinks: project.autoExtractBacklinks === 1,
2894
2905
  configSource: project.configSource,
2895
2906
  configRevision: project.configRevision,
2896
2907
  createdAt: project.createdAt,
@@ -15361,6 +15372,21 @@ async function executeReleaseSync(db, syncId, opts) {
15361
15372
  projectsProcessed: allProjects.length,
15362
15373
  domainsDiscovered: rows.length
15363
15374
  });
15375
+ if (deps.enqueueAutoExtract) {
15376
+ const autoExtractProjects = allProjects.filter((p) => p.autoExtractBacklinks === 1);
15377
+ for (const p of autoExtractProjects) {
15378
+ try {
15379
+ deps.enqueueAutoExtract({ projectId: p.id, release });
15380
+ } catch (err) {
15381
+ log4.error("auto-extract.enqueue-failed", {
15382
+ syncId,
15383
+ release,
15384
+ projectId: p.id,
15385
+ error: err instanceof Error ? err.message : String(err)
15386
+ });
15387
+ }
15388
+ }
15389
+ }
15364
15390
  } catch (err) {
15365
15391
  const errorMsg = err instanceof Error ? err.message : String(err);
15366
15392
  const finishedAt = deps.now().toISOString();
@@ -15404,6 +15430,7 @@ function computeSummary(rows) {
15404
15430
 
15405
15431
  // src/backlink-extract.ts
15406
15432
  import crypto23 from "crypto";
15433
+ import fs9 from "fs";
15407
15434
  import { and as and12, desc as desc9, eq as eq23 } from "drizzle-orm";
15408
15435
  var log5 = createLogger("BacklinkExtract");
15409
15436
  function defaultDeps2() {
@@ -15432,6 +15459,11 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
15432
15459
  if (!sync.vertexPath || !sync.edgesPath) {
15433
15460
  throw new Error(`Release ${sync.release} is missing cached file paths`);
15434
15461
  }
15462
+ if (!fs9.existsSync(sync.vertexPath) || !fs9.existsSync(sync.edgesPath)) {
15463
+ throw new Error(
15464
+ `Cache for release ${sync.release} is missing from disk (expected at ${sync.vertexPath}). The sync record exists in the database, but the ~16 GB dump was deleted or never present on this machine. Re-sync this release from the Backlinks admin page to restore the cache.`
15465
+ );
15466
+ }
15435
15467
  const duckdb = deps.loadDuckdb();
15436
15468
  const rows = await deps.queryBacklinks({
15437
15469
  vertexPath: sync.vertexPath,
@@ -15946,7 +15978,7 @@ import crypto26 from "crypto";
15946
15978
  import { eq as eq27 } from "drizzle-orm";
15947
15979
 
15948
15980
  // src/agent/session.ts
15949
- import fs11 from "fs";
15981
+ import fs12 from "fs";
15950
15982
  import path14 from "path";
15951
15983
  import { Agent } from "@mariozechner/pi-agent-core";
15952
15984
  import { registerBuiltInApiProviders } from "@mariozechner/pi-ai";
@@ -16048,7 +16080,7 @@ function buildAgentProvidersResponse(config) {
16048
16080
  }
16049
16081
 
16050
16082
  // src/agent/skill-paths.ts
16051
- import fs9 from "fs";
16083
+ import fs10 from "fs";
16052
16084
  import path12 from "path";
16053
16085
  import { fileURLToPath } from "url";
16054
16086
  function resolveAeroSkillDir(pkgDir) {
@@ -16059,14 +16091,14 @@ function resolveAeroSkillDir(pkgDir) {
16059
16091
  path12.join(here, "../../../../skills/aero")
16060
16092
  ];
16061
16093
  for (const candidate of candidates) {
16062
- if (fs9.existsSync(path12.join(candidate, "SKILL.md"))) return candidate;
16094
+ if (fs10.existsSync(path12.join(candidate, "SKILL.md"))) return candidate;
16063
16095
  }
16064
16096
  throw new Error(`Aero skill not found. Searched:
16065
16097
  ${candidates.join("\n ")}`);
16066
16098
  }
16067
16099
 
16068
16100
  // src/agent/skill-tools.ts
16069
- import fs10 from "fs";
16101
+ import fs11 from "fs";
16070
16102
  import path13 from "path";
16071
16103
  import { Type } from "@sinclair/typebox";
16072
16104
  var MAX_DOC_CHARS = 2e4;
@@ -16089,12 +16121,12 @@ function parseDescription(body) {
16089
16121
  }
16090
16122
  function scanSkillDocs(skillDir) {
16091
16123
  const refsDir = path13.join(skillDir ?? resolveAeroSkillDir(), "references");
16092
- if (!fs10.existsSync(refsDir)) return [];
16124
+ if (!fs11.existsSync(refsDir)) return [];
16093
16125
  const entries = [];
16094
- for (const file of fs10.readdirSync(refsDir)) {
16126
+ for (const file of fs11.readdirSync(refsDir)) {
16095
16127
  if (!file.endsWith(".md")) continue;
16096
16128
  const filePath = path13.join(refsDir, file);
16097
- const body = fs10.readFileSync(filePath, "utf-8");
16129
+ const body = fs11.readFileSync(filePath, "utf-8");
16098
16130
  entries.push({
16099
16131
  slug: file.replace(/\.md$/, ""),
16100
16132
  description: parseDescription(body),
@@ -16138,7 +16170,7 @@ function buildReadSkillDocTool() {
16138
16170
  });
16139
16171
  }
16140
16172
  const filePath = path13.join(skillDir, "references", `${match.slug}.md`);
16141
- const content = fs10.readFileSync(filePath, "utf-8");
16173
+ const content = fs11.readFileSync(filePath, "utf-8");
16142
16174
  if (content.length > MAX_DOC_CHARS) {
16143
16175
  return textResult({
16144
16176
  slug: match.slug,
@@ -16692,10 +16724,10 @@ function ensureBuiltinsRegistered() {
16692
16724
  }
16693
16725
  function loadAeroSystemPrompt(pkgDir) {
16694
16726
  const skillDir = resolveAeroSkillDir(pkgDir);
16695
- const skillBody = fs11.readFileSync(path14.join(skillDir, "SKILL.md"), "utf-8");
16727
+ const skillBody = fs12.readFileSync(path14.join(skillDir, "SKILL.md"), "utf-8");
16696
16728
  const soulPath = path14.join(skillDir, "soul.md");
16697
- if (!fs11.existsSync(soulPath)) return skillBody;
16698
- const soulBody = fs11.readFileSync(soulPath, "utf-8");
16729
+ if (!fs12.existsSync(soulPath)) return skillBody;
16730
+ const soulBody = fs12.readFileSync(soulPath, "utf-8");
16699
16731
  return `${soulBody.trimEnd()}
16700
16732
 
16701
16733
  ---
@@ -18990,7 +19022,7 @@ async function createServer(opts) {
18990
19022
  jobRunner.onRunCompleted = (runId, projectId) => runCoordinator.onRunCompleted(runId, projectId);
18991
19023
  const snapshotService = new SnapshotService(registry);
18992
19024
  const orphanedOpenClawDir = path15.join(os6.homedir(), ".openclaw-aero");
18993
- if (fs12.existsSync(orphanedOpenClawDir)) {
19025
+ if (fs13.existsSync(orphanedOpenClawDir)) {
18994
19026
  app.log.warn(
18995
19027
  { path: orphanedOpenClawDir },
18996
19028
  "OpenClaw gateway is no longer used. Remove ~/.openclaw-aero/ manually to reclaim the directory."
@@ -19314,7 +19346,26 @@ async function createServer(opts) {
19314
19346
  };
19315
19347
  },
19316
19348
  onReleaseSyncRequested: (syncId, release) => {
19317
- executeReleaseSync(opts.db, syncId, { release }).catch((err) => {
19349
+ executeReleaseSync(opts.db, syncId, {
19350
+ release,
19351
+ deps: {
19352
+ enqueueAutoExtract: ({ projectId, release: r }) => {
19353
+ const now = (/* @__PURE__ */ new Date()).toISOString();
19354
+ const runId = crypto27.randomUUID();
19355
+ opts.db.insert(runs).values({
19356
+ id: runId,
19357
+ projectId,
19358
+ kind: RunKinds["backlink-extract"],
19359
+ status: RunStatuses.queued,
19360
+ trigger: RunTriggers.scheduled,
19361
+ createdAt: now
19362
+ }).run();
19363
+ executeBacklinkExtract(opts.db, runId, projectId, { release: r }).catch((err) => {
19364
+ app.log.error({ runId, projectId, err }, "Auto backlink extract failed");
19365
+ });
19366
+ }
19367
+ }
19368
+ }).catch((err) => {
19318
19369
  app.log.error({ syncId, err }, "Common Crawl release sync failed");
19319
19370
  });
19320
19371
  },
@@ -19559,7 +19610,7 @@ async function createServer(opts) {
19559
19610
  });
19560
19611
  const dirname = path15.dirname(fileURLToPath2(import.meta.url));
19561
19612
  const assetsDir = path15.join(dirname, "..", "assets");
19562
- if (fs12.existsSync(assetsDir)) {
19613
+ if (fs13.existsSync(assetsDir)) {
19563
19614
  const indexPath = path15.join(assetsDir, "index.html");
19564
19615
  const injectConfig = (html) => {
19565
19616
  const clientConfig = {};
@@ -19578,8 +19629,8 @@ async function createServer(opts) {
19578
19629
  index: false
19579
19630
  });
19580
19631
  const serveIndex = (_request, reply) => {
19581
- if (fs12.existsSync(indexPath)) {
19582
- const html = fs12.readFileSync(indexPath, "utf-8");
19632
+ if (fs13.existsSync(indexPath)) {
19633
+ const html = fs13.readFileSync(indexPath, "utf-8");
19583
19634
  return reply.type("text/html").send(injectConfig(html));
19584
19635
  }
19585
19636
  return reply.status(404).send({ error: "Dashboard not built" });
@@ -19599,8 +19650,8 @@ async function createServer(opts) {
19599
19650
  if (basePath && !url.startsWith(basePath)) {
19600
19651
  return reply.status(404).send({ error: "Not found", path: request.url });
19601
19652
  }
19602
- if (fs12.existsSync(indexPath)) {
19603
- const html = fs12.readFileSync(indexPath, "utf-8");
19653
+ if (fs13.existsSync(indexPath)) {
19654
+ const html = fs13.readFileSync(indexPath, "utf-8");
19604
19655
  return reply.type("text/html").send(injectConfig(html));
19605
19656
  }
19606
19657
  return reply.status(404).send({ error: "Not found" });
@@ -19690,8 +19741,10 @@ export {
19690
19741
  resolveProviderInput,
19691
19742
  notificationEventSchema,
19692
19743
  effectiveDomains,
19744
+ RunStatuses,
19693
19745
  RunKinds,
19694
19746
  determineAnswerMentioned,
19747
+ CcReleaseSyncStatuses,
19695
19748
  reparseStoredResult2 as reparseStoredResult,
19696
19749
  reparseStoredResult3 as reparseStoredResult2,
19697
19750
  reparseStoredResult as reparseStoredResult3,
package/dist/cli.js CHANGED
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node --import tsx
2
2
  import {
3
+ CcReleaseSyncStatuses,
3
4
  CliError,
4
5
  EXIT_SYSTEM_ERROR,
5
6
  ProviderNames,
6
7
  RunKinds,
8
+ RunStatuses,
7
9
  coerceAgentProvider,
8
10
  computeCompetitorOverlap,
9
11
  configExists,
@@ -36,7 +38,7 @@ import {
36
38
  showFirstRunNotice,
37
39
  trackEvent,
38
40
  usageError
39
- } from "./chunk-JXOUZ6JH.js";
41
+ } from "./chunk-Y7GCG4GD.js";
40
42
  import {
41
43
  apiKeys,
42
44
  competitors,
@@ -46,7 +48,7 @@ import {
46
48
  projects,
47
49
  querySnapshots,
48
50
  runs
49
- } from "./chunk-CW6CAPBQ.js";
51
+ } from "./chunk-GZF3YIHY.js";
50
52
 
51
53
  // src/cli.ts
52
54
  import { pathToFileURL } from "url";
@@ -293,7 +295,7 @@ async function backfillAnswerVisibilityCommand(opts) {
293
295
  console.log(` Errors: ${providerErrors}`);
294
296
  }
295
297
  async function backfillInsightsCommand(project, opts) {
296
- const { IntelligenceService } = await import("./intelligence-service-232P7625.js");
298
+ const { IntelligenceService } = await import("./intelligence-service-KM64AW7J.js");
297
299
  const config = loadConfig();
298
300
  const db = createClient(config.database);
299
301
  migrate(db);
@@ -573,7 +575,10 @@ function formatInstallResult(result) {
573
575
  }
574
576
  async function pollSync(id, format) {
575
577
  const client = getClient();
576
- const terminal = /* @__PURE__ */ new Set(["ready", "failed"]);
578
+ const terminal = /* @__PURE__ */ new Set([
579
+ CcReleaseSyncStatuses.ready,
580
+ CcReleaseSyncStatuses.failed
581
+ ]);
577
582
  while (true) {
578
583
  const syncs = await client.backlinksListSyncs();
579
584
  const row = syncs.find((s) => s.id === id);
@@ -587,7 +592,11 @@ async function pollSync(id, format) {
587
592
  }
588
593
  async function pollRun(runId, format) {
589
594
  const client = getClient();
590
- const terminal = /* @__PURE__ */ new Set(["completed", "failed", "partial"]);
595
+ const terminal = /* @__PURE__ */ new Set([
596
+ RunStatuses.completed,
597
+ RunStatuses.failed,
598
+ RunStatuses.partial
599
+ ]);
591
600
  while (true) {
592
601
  const run = await client.getRun(runId);
593
602
  if (terminal.has(run.status)) return run;
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createServer,
3
3
  loadConfig
4
- } from "./chunk-JXOUZ6JH.js";
5
- import "./chunk-CW6CAPBQ.js";
4
+ } from "./chunk-Y7GCG4GD.js";
5
+ import "./chunk-GZF3YIHY.js";
6
6
  export {
7
7
  createServer,
8
8
  loadConfig
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-CW6CAPBQ.js";
3
+ } from "./chunk-GZF3YIHY.js";
4
4
  export {
5
5
  IntelligenceService
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "2.3.1",
3
+ "version": "2.4.2",
4
4
  "type": "module",
5
5
  "description": "The ultimate open-source AEO monitoring tool - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -57,19 +57,19 @@
57
57
  "@types/node-cron": "^3.0.11",
58
58
  "tsup": "^8.5.1",
59
59
  "tsx": "^4.19.0",
60
+ "@ainyc/canonry-contracts": "0.0.0",
60
61
  "@ainyc/canonry-api-routes": "0.0.0",
61
- "@ainyc/canonry-config": "0.0.0",
62
62
  "@ainyc/canonry-intelligence": "0.0.0",
63
- "@ainyc/canonry-contracts": "0.0.0",
64
63
  "@ainyc/canonry-db": "0.0.0",
64
+ "@ainyc/canonry-config": "0.0.0",
65
65
  "@ainyc/canonry-integration-bing": "0.0.0",
66
66
  "@ainyc/canonry-integration-google": "0.0.0",
67
67
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
68
68
  "@ainyc/canonry-integration-wordpress": "0.0.0",
69
- "@ainyc/canonry-provider-cdp": "0.0.0",
70
- "@ainyc/canonry-provider-gemini": "0.0.0",
71
69
  "@ainyc/canonry-provider-claude": "0.0.0",
72
70
  "@ainyc/canonry-provider-local": "0.0.0",
71
+ "@ainyc/canonry-provider-gemini": "0.0.0",
72
+ "@ainyc/canonry-provider-cdp": "0.0.0",
73
73
  "@ainyc/canonry-provider-openai": "0.0.0",
74
74
  "@ainyc/canonry-provider-perplexity": "0.0.0"
75
75
  },