@canonry/canonry 4.104.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 (52) hide show
  1. package/LICENSE +105 -0
  2. package/README.md +117 -0
  3. package/assets/agent-workspace/AGENTS.md +88 -0
  4. package/assets/agent-workspace/USER.md +23 -0
  5. package/assets/agent-workspace/skills/aero/SKILL.md +72 -0
  6. package/assets/agent-workspace/skills/aero/references/aeo-discovery.md +161 -0
  7. package/assets/agent-workspace/skills/aero/references/memory-patterns.md +66 -0
  8. package/assets/agent-workspace/skills/aero/references/orchestration.md +67 -0
  9. package/assets/agent-workspace/skills/aero/references/regression-playbook.md +58 -0
  10. package/assets/agent-workspace/skills/aero/references/reporting.md +98 -0
  11. package/assets/agent-workspace/skills/aero/references/wordpress-elementor-mcp.md +223 -0
  12. package/assets/agent-workspace/skills/aero/soul.md +30 -0
  13. package/assets/agent-workspace/skills/canonry/SKILL.md +152 -0
  14. package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +200 -0
  15. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +714 -0
  16. package/assets/agent-workspace/skills/canonry/references/google-business-profile.md +300 -0
  17. package/assets/agent-workspace/skills/canonry/references/indexing.md +163 -0
  18. package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +372 -0
  19. package/assets/agent-workspace/skills/canonry/references/wordpress-integration.md +61 -0
  20. package/assets/apple-touch-icon.png +0 -0
  21. package/assets/assets/BacklinksPage-CixBvw--.js +1 -0
  22. package/assets/assets/ChartPrimitives-CjX4mQCy.js +1 -0
  23. package/assets/assets/ProjectPage-C09hcd6v.js +7 -0
  24. package/assets/assets/RunRow-Bel7mWgu.js +1 -0
  25. package/assets/assets/RunsPage-wqKWA2Ei.js +1 -0
  26. package/assets/assets/SettingsPage-esOqUkW5.js +1 -0
  27. package/assets/assets/TrafficPage-V0iCV3l4.js +1 -0
  28. package/assets/assets/TrafficSourceDetailPage-DUeuOh2v.js +1 -0
  29. package/assets/assets/arrow-left-CVZPxpW6.js +1 -0
  30. package/assets/assets/extract-error-message-BWAlA7ea.js +1 -0
  31. package/assets/assets/index-LsyRFLMG.css +1 -0
  32. package/assets/assets/index-avJiga1P.js +210 -0
  33. package/assets/assets/trash-2-CA_L6uGC.js +1 -0
  34. package/assets/assets/vendor-markdown-6dwjPOKK.js +14 -0
  35. package/assets/assets/vendor-radix-CQ77EO2y.js +45 -0
  36. package/assets/assets/vendor-recharts-C9EZkgbP.js +36 -0
  37. package/assets/assets/vendor-tanstack-Be8JdV5M.js +1 -0
  38. package/assets/favicon-32.png +0 -0
  39. package/assets/favicon.svg +24 -0
  40. package/assets/index.html +25 -0
  41. package/bin/canonry-mcp.mjs +2 -0
  42. package/bin/canonry.mjs +2 -0
  43. package/dist/chunk-23DCY7DL.js +7030 -0
  44. package/dist/chunk-GIKNEXFE.js +12414 -0
  45. package/dist/chunk-GNMZTR6Y.js +35673 -0
  46. package/dist/chunk-Q7FFDQU4.js +5743 -0
  47. package/dist/cli.js +13344 -0
  48. package/dist/index.d.ts +260 -0
  49. package/dist/index.js +12 -0
  50. package/dist/intelligence-service-GM3RXE2F.js +7 -0
  51. package/dist/mcp.js +442 -0
  52. package/package.json +97 -0
package/dist/mcp.js ADDED
@@ -0,0 +1,442 @@
1
+ import {
2
+ CliError,
3
+ PACKAGE_VERSION,
4
+ canonryMcpTools,
5
+ createApiClient
6
+ } from "./chunk-23DCY7DL.js";
7
+ import {
8
+ isReadOnlyKey
9
+ } from "./chunk-Q7FFDQU4.js";
10
+
11
+ // src/mcp/cli.ts
12
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
+
14
+ // src/mcp/server.ts
15
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
+ import { z } from "zod";
17
+
18
+ // src/mcp/results.ts
19
+ import { ZodError } from "zod";
20
+ function jsonToolResult(value) {
21
+ const result = value === void 0 ? { ok: true } : value;
22
+ return {
23
+ content: [
24
+ {
25
+ type: "text",
26
+ text: JSON.stringify(result, null, 2)
27
+ }
28
+ ]
29
+ };
30
+ }
31
+ function errorToolResult(error) {
32
+ return {
33
+ isError: true,
34
+ content: [
35
+ {
36
+ type: "text",
37
+ text: JSON.stringify(toCanonryErrorEnvelope(error), null, 2)
38
+ }
39
+ ]
40
+ };
41
+ }
42
+ async function withToolErrors(handler) {
43
+ try {
44
+ return jsonToolResult(await handler());
45
+ } catch (error) {
46
+ return errorToolResult(error);
47
+ }
48
+ }
49
+ function toCanonryErrorEnvelope(error) {
50
+ if (error instanceof ZodError) {
51
+ return {
52
+ error: {
53
+ code: "VALIDATION_ERROR",
54
+ message: zodErrorMessage(error),
55
+ details: { issues: error.issues.map(formatZodIssue) }
56
+ }
57
+ };
58
+ }
59
+ if (error instanceof CliError) {
60
+ return {
61
+ error: {
62
+ code: error.code,
63
+ message: error.message,
64
+ ...error.details ? { details: error.details } : {}
65
+ }
66
+ };
67
+ }
68
+ if (hasErrorEnvelope(error)) {
69
+ const { code, message, details } = error.error;
70
+ return {
71
+ error: {
72
+ code: typeof code === "string" ? code : "API_ERROR",
73
+ message: typeof message === "string" ? message : "Canonry API error",
74
+ ...details !== void 0 ? { details } : {}
75
+ }
76
+ };
77
+ }
78
+ if (error instanceof Error) {
79
+ return {
80
+ error: {
81
+ code: "MCP_TOOL_ERROR",
82
+ message: error.message
83
+ }
84
+ };
85
+ }
86
+ return {
87
+ error: {
88
+ code: "MCP_TOOL_ERROR",
89
+ message: "Unknown MCP tool error"
90
+ }
91
+ };
92
+ }
93
+ function hasErrorEnvelope(value) {
94
+ if (!value || typeof value !== "object" || !("error" in value)) return false;
95
+ const error = value.error;
96
+ return Boolean(error && typeof error === "object");
97
+ }
98
+ function formatZodIssue(issue) {
99
+ return { path: issue.path.map(String).join("."), message: issue.message };
100
+ }
101
+ function zodErrorMessage(error) {
102
+ const first = error.issues[0];
103
+ if (!first) return "Input validation failed";
104
+ const path = first.path.map(String).join(".");
105
+ return path ? `${path}: ${first.message}` : first.message;
106
+ }
107
+
108
+ // src/mcp/toolkits.ts
109
+ var CANONRY_MCP_TOOLKIT_NAMES = ["monitoring", "setup", "gsc", "ga", "gbp", "ads", "traffic", "agent", "discovery"];
110
+ var CANONRY_MCP_TOOLKITS = [
111
+ {
112
+ name: "monitoring",
113
+ title: "Runs, snapshots, insights, health",
114
+ description: "Inspect run history, query snapshots, intelligence insights, and health timelines.",
115
+ whenToLoad: "Load when investigating regressions, comparing runs, or reviewing insights and health history."
116
+ },
117
+ {
118
+ name: "setup",
119
+ title: "Project configuration",
120
+ description: "Manage queries, competitors, schedules, project upsert, and config-as-code roundtrips.",
121
+ whenToLoad: "Load when onboarding a new project or editing tracked queries, competitors, or schedules."
122
+ },
123
+ {
124
+ name: "gsc",
125
+ title: "Google Search Console",
126
+ description: "Read GSC performance, inspections, coverage, sitemaps, and deindexed URLs.",
127
+ whenToLoad: "Load when you need indexing, coverage, or sitemap data from Google Search Console."
128
+ },
129
+ {
130
+ name: "ga",
131
+ title: "Google Analytics 4",
132
+ description: "Read GA traffic, AI/social referral history, attribution trend, and session history.",
133
+ whenToLoad: "Load when you need traffic, referral, or attribution data from Google Analytics 4."
134
+ },
135
+ {
136
+ name: "gbp",
137
+ title: "Google Business Profile",
138
+ description: "Local AEO signals: discover GBP locations under a connected account and toggle which ones sync. Future phases will add reviews, keyword impressions, daily performance metrics, and hotel attributes.",
139
+ whenToLoad: "Load when the project tracks local search visibility or has connected Google Business Profile."
140
+ },
141
+ {
142
+ name: "ads",
143
+ title: "OpenAI ads (ChatGPT ads)",
144
+ description: "Paid-surface data for the connected OpenAI ad account: connection status, campaign/ad-group snapshots (context hints), daily paid-performance rollups (spend in integer micros), and the composite summary. Trigger ads-sync runs.",
145
+ whenToLoad: "Load when the project runs ChatGPT ads and you need paid performance, campaign structure, or to trigger an ads sync."
146
+ },
147
+ {
148
+ name: "traffic",
149
+ title: "Server-side traffic ingestion",
150
+ description: "Connect Cloud Run traffic sources, trigger syncs, and read crawler / AI-referral hourly rollups straight from server logs (no GA dependency).",
151
+ whenToLoad: "Load when you need server-log evidence of crawler hits or AI-referral sessions (e.g. confirming GPTBot or ChatGPT-User on a page), or when wiring up / syncing a Cloud Run traffic source."
152
+ },
153
+ {
154
+ name: "agent",
155
+ title: "Aero agent lifecycle and memory",
156
+ description: "Manage the built-in Aero agent: durable project-scoped memory (list/set/forget), clear the rolling transcript, and detach the external-agent webhook.",
157
+ whenToLoad: "Load when reading or writing project-scoped Aero notes, clearing a stuck conversation, or removing an external agent webhook."
158
+ },
159
+ {
160
+ name: "discovery",
161
+ title: "Tracked-basket discovery (ICP \u2192 buckets)",
162
+ description: "Start and inspect discovery sessions. Each session expands an ICP description into a deduped set of representative queries, probes them against Gemini grounding, classifies each probe into cited / aspirational / wasted-surface, and aggregates a competitor map for the project.",
163
+ whenToLoad: "Load when the operator wants to expand or audit a project's tracked-query basket, audit competitive surface, or preview a promotion plan from a discovery session."
164
+ }
165
+ ];
166
+ function isCanonryMcpToolkitName(value) {
167
+ return CANONRY_MCP_TOOLKIT_NAMES.includes(value);
168
+ }
169
+
170
+ // src/mcp/dynamic-catalog.ts
171
+ var DynamicToolCatalog = class {
172
+ entries;
173
+ loaded = /* @__PURE__ */ new Set();
174
+ eager;
175
+ scope;
176
+ server;
177
+ constructor(server, entries, scope, options = {}) {
178
+ this.server = server;
179
+ this.entries = entries;
180
+ this.scope = scope;
181
+ this.eager = Boolean(options.eager);
182
+ if (this.eager) {
183
+ for (const toolkit of CANONRY_MCP_TOOLKITS) {
184
+ if (this.toolsForToolkit(toolkit.name).length > 0) {
185
+ this.loaded.add(toolkit.name);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ applyInitialEnablement() {
191
+ if (this.eager) return;
192
+ this.batchListChanged(() => {
193
+ for (const entry of this.entries) {
194
+ if (entry.tool.tier !== "core") entry.registered.disable();
195
+ }
196
+ });
197
+ }
198
+ loadToolkit(rawName) {
199
+ if (!isCanonryMcpToolkitName(rawName)) {
200
+ const valid = CANONRY_MCP_TOOLKITS.map((t) => t.name).join(", ");
201
+ throw new Error(`Unknown toolkit "${rawName}". Available: ${valid}.`);
202
+ }
203
+ const name = rawName;
204
+ const matches = this.entries.filter((entry) => entry.tool.tier === name);
205
+ if (matches.length === 0) {
206
+ return { status: "empty", name, tools: [] };
207
+ }
208
+ if (this.loaded.has(name)) {
209
+ return { status: "already-loaded", name, tools: matches.map((entry) => entry.tool.name) };
210
+ }
211
+ this.batchListChanged(() => {
212
+ for (const entry of matches) {
213
+ entry.registered.enable();
214
+ }
215
+ });
216
+ this.loaded.add(name);
217
+ return { status: "loaded", name, tools: matches.map((entry) => entry.tool.name) };
218
+ }
219
+ helpResult() {
220
+ return {
221
+ scope: this.scope,
222
+ eager: this.eager,
223
+ loadedToolkits: [...this.loaded].sort(),
224
+ coreTools: this.entries.filter((entry) => entry.tool.tier === "core").map((entry) => entry.tool.name),
225
+ toolkits: CANONRY_MCP_TOOLKITS.map((toolkit) => this.toolkitEntry(toolkit)).filter((entry) => entry.toolCount > 0),
226
+ usage: "Call canonry_load_toolkit with one of the toolkit names listed in `toolkits[].name` to register its tools for the rest of this session. Wait for its response before calling any newly enabled tool."
227
+ };
228
+ }
229
+ toolkitEntry(toolkit) {
230
+ const tools = this.toolsForToolkit(toolkit.name);
231
+ return {
232
+ name: toolkit.name,
233
+ title: toolkit.title,
234
+ description: toolkit.description,
235
+ whenToLoad: toolkit.whenToLoad,
236
+ toolCount: tools.length,
237
+ tools,
238
+ loaded: this.loaded.has(toolkit.name)
239
+ };
240
+ }
241
+ toolsForToolkit(name) {
242
+ return this.entries.filter((entry) => entry.tool.tier === name).map((entry) => entry.tool.name);
243
+ }
244
+ // RegisteredTool.enable/disable each call sendToolListChanged on the McpServer
245
+ // we registered with. Loading an 11-tool toolkit emits 11 notifications under
246
+ // that contract, which a spec-compliant client will treat as 11 catalog
247
+ // refetches. Coalesce them into one notification per batch by intercepting
248
+ // the SDK's sender for the duration of the batch.
249
+ batchListChanged(fn) {
250
+ const host = this.server;
251
+ const original = host.sendToolListChanged.bind(host);
252
+ let suppressed = false;
253
+ host.sendToolListChanged = () => {
254
+ suppressed = true;
255
+ };
256
+ try {
257
+ fn();
258
+ } finally {
259
+ host.sendToolListChanged = original;
260
+ }
261
+ if (suppressed) original();
262
+ }
263
+ };
264
+
265
+ // src/mcp/server.ts
266
+ function createCanonryMcpServer(options = {}) {
267
+ return createCanonryMcpServerWithCatalog(options).server;
268
+ }
269
+ function createCanonryMcpServerWithCatalog(options = {}) {
270
+ const clientFactory = options.clientFactory ?? createApiClient;
271
+ const client = clientFactory();
272
+ const scope = options.scope ?? "all";
273
+ const server = new McpServer({
274
+ name: "canonry",
275
+ version: PACKAGE_VERSION
276
+ });
277
+ server.validateToolInput = async (_tool, args) => args;
278
+ const entries = [];
279
+ for (const registryTool of getCanonryMcpTools(scope)) {
280
+ const tool = registryTool;
281
+ const handler = tool.handler;
282
+ const registered = server.registerTool(
283
+ tool.name,
284
+ {
285
+ title: tool.title,
286
+ description: tool.description,
287
+ inputSchema: tool.inputSchema,
288
+ annotations: tool.annotations
289
+ },
290
+ async (input) => withToolErrors(async () => {
291
+ const parsed = tool.inputSchema.parse(input ?? {});
292
+ return handler(client, parsed);
293
+ })
294
+ );
295
+ entries.push({ tool, registered });
296
+ }
297
+ const catalog = new DynamicToolCatalog(server, entries, scope, { eager: options.eager });
298
+ catalog.applyInitialEnablement();
299
+ registerMetaTools(server, catalog);
300
+ return { server, catalog };
301
+ }
302
+ var loadToolkitInputSchema = z.object({
303
+ name: z.enum(CANONRY_MCP_TOOLKIT_NAMES).describe("Toolkit name. List options with canonry_help.")
304
+ });
305
+ function registerMetaTools(server, catalog) {
306
+ server.registerTool(
307
+ "canonry_help",
308
+ {
309
+ title: "List Canonry MCP toolkits",
310
+ description: "List available toolkits and which are loaded. Call before canonry_load_toolkit if unsure which to load.",
311
+ inputSchema: {},
312
+ annotations: { readOnlyHint: true }
313
+ },
314
+ async () => withToolErrors(async () => catalog.helpResult())
315
+ );
316
+ server.registerTool(
317
+ "canonry_load_toolkit",
318
+ {
319
+ title: "Load a Canonry MCP toolkit",
320
+ description: `Register a toolkit's tools for this session and emit one notifications/tools/list_changed. Idempotent. Loaded toolkits remain loaded for the rest of the session. Wait for this call to return before calling any newly enabled tool \u2014 pipelining the call with a tools/call on the same connection can race the registration and fail with "MCP error -32602: Tool ... disabled".`,
321
+ inputSchema: loadToolkitInputSchema.shape,
322
+ annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: false }
323
+ },
324
+ async (input) => withToolErrors(async () => {
325
+ const parsed = loadToolkitInputSchema.parse(input ?? {});
326
+ return catalog.loadToolkit(parsed.name);
327
+ })
328
+ );
329
+ }
330
+ function getCanonryMcpTools(scope = "all") {
331
+ return scope === "read-only" ? canonryMcpTools.filter((tool) => tool.access === "read") : [...canonryMcpTools];
332
+ }
333
+
334
+ // src/mcp/cli.ts
335
+ var HELP_TEXT = `Usage: canonry-mcp [--read-only | --scope=<all|read-only>] [--eager]
336
+
337
+ Stdio MCP adapter over the Canonry public API. Inherits config from
338
+ ~/.canonry/config.yaml (or $CANONRY_CONFIG_DIR/config.yaml).
339
+
340
+ Flags:
341
+ --read-only Expose read tools only
342
+ --scope=<all|read-only>
343
+ Same as --read-only when "read-only"
344
+ --eager Load all toolkits at start (skip progressive discovery)
345
+ --help, -h Show this message
346
+
347
+ Environment variables:
348
+ CANONRY_MCP_SCOPE "all" (default) or "read-only"
349
+ CANONRY_MCP_EAGER "1" / "true" / "yes" to enable eager mode
350
+ `;
351
+ var HelpRequested = class extends Error {
352
+ constructor() {
353
+ super("canonry-mcp --help requested");
354
+ this.name = "HelpRequested";
355
+ }
356
+ };
357
+ async function main(argv = process.argv.slice(2)) {
358
+ let options;
359
+ try {
360
+ options = parseCliOptions(argv);
361
+ } catch (error) {
362
+ if (error instanceof HelpRequested) {
363
+ process.stderr.write(HELP_TEXT);
364
+ return;
365
+ }
366
+ throw error;
367
+ }
368
+ const client = createApiClient();
369
+ const scope = await resolveEffectiveScope(client, options.scope);
370
+ const server = createCanonryMcpServer({ scope, eager: options.eager, clientFactory: () => client });
371
+ await server.connect(new StdioServerTransport());
372
+ }
373
+ async function resolveEffectiveScope(client, flagScope) {
374
+ if (flagScope === "read-only") return "read-only";
375
+ try {
376
+ const self = await client.getApiKeySelf();
377
+ if (isReadOnlyKey(self.scopes)) {
378
+ process.stderr.write(
379
+ "canonry-mcp: configured API key is read-only \u2014 restricting to read tools.\n"
380
+ );
381
+ return "read-only";
382
+ }
383
+ } catch {
384
+ }
385
+ return flagScope;
386
+ }
387
+ function parseCliOptions(argv, env = process.env) {
388
+ if (argv.includes("--help") || argv.includes("-h")) {
389
+ throw new HelpRequested();
390
+ }
391
+ let scope = normalizeScope(env.CANONRY_MCP_SCOPE);
392
+ let eager = parseEagerEnv(env.CANONRY_MCP_EAGER);
393
+ for (let i = 0; i < argv.length; i += 1) {
394
+ const arg = argv[i];
395
+ if (arg === "--read-only") {
396
+ scope = "read-only";
397
+ continue;
398
+ }
399
+ if (arg === "--eager") {
400
+ eager = true;
401
+ continue;
402
+ }
403
+ if (arg === "--scope") {
404
+ const next = argv[i + 1];
405
+ if (!next) throw new Error("Missing value for --scope");
406
+ scope = normalizeScope(next);
407
+ i += 1;
408
+ continue;
409
+ }
410
+ if (arg?.startsWith("--scope=")) {
411
+ scope = normalizeScope(arg.slice("--scope=".length));
412
+ continue;
413
+ }
414
+ throw new Error(`Unknown canonry-mcp argument: ${arg}`);
415
+ }
416
+ return { scope, eager };
417
+ }
418
+ function normalizeScope(value) {
419
+ if (!value || value === "all") return "all";
420
+ if (value === "read-only") return "read-only";
421
+ throw new Error(`Invalid MCP scope "${value}". Expected "all" or "read-only".`);
422
+ }
423
+ function parseEagerEnv(value) {
424
+ if (!value) return false;
425
+ const normalized = value.trim().toLowerCase();
426
+ return normalized === "1" || normalized === "true" || normalized === "yes";
427
+ }
428
+ if (import.meta.url === `file://${process.argv[1]}`) {
429
+ main().catch((error) => {
430
+ const message = error instanceof Error ? error.message : "canonry-mcp failed";
431
+ process.stderr.write(`${message}
432
+ `);
433
+ process.exitCode = 1;
434
+ });
435
+ }
436
+ export {
437
+ HELP_TEXT,
438
+ HelpRequested,
439
+ main,
440
+ parseCliOptions,
441
+ resolveEffectiveScope
442
+ };
package/package.json ADDED
@@ -0,0 +1,97 @@
1
+ {
2
+ "name": "@canonry/canonry",
3
+ "version": "4.104.0",
4
+ "type": "module",
5
+ "description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
6
+ "license": "FSL-1.1-ALv2",
7
+ "homepage": "https://canonry.ai",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/Canonry/canonry.git",
11
+ "directory": "packages/canonry"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/Canonry/canonry/issues"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "bin": {
20
+ "canonry": "./bin/canonry.mjs",
21
+ "cnry": "./bin/canonry.mjs",
22
+ "canonry-mcp": "./bin/canonry-mcp.mjs"
23
+ },
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
28
+ }
29
+ },
30
+ "types": "./dist/index.d.ts",
31
+ "files": [
32
+ "bin/",
33
+ "dist/",
34
+ "assets/",
35
+ "package.json",
36
+ "README.md"
37
+ ],
38
+ "engines": {
39
+ "node": ">=22.14.0"
40
+ },
41
+ "dependencies": {
42
+ "@ainyc/aeo-audit": "4.0.1",
43
+ "@anthropic-ai/sdk": "^0.91.1",
44
+ "@fastify/static": "^9.1.1",
45
+ "@google/genai": "^1.46.0",
46
+ "@mariozechner/pi-agent-core": "0.67.6",
47
+ "@mariozechner/pi-ai": "0.67.6",
48
+ "@modelcontextprotocol/sdk": "^1.29.0",
49
+ "@sinclair/typebox": "^0.34.41",
50
+ "better-sqlite3": "^12.6.2",
51
+ "chrome-remote-interface": "^0.33.2",
52
+ "cron-parser": "^5.5.0",
53
+ "drizzle-orm": "^0.45.2",
54
+ "fastify": "^5.8.5",
55
+ "node-cron": "^4.2.1",
56
+ "openai": "^6.0.0",
57
+ "pdf-lib": "^1.17.1",
58
+ "pino-pretty": "^13.1.3",
59
+ "undici": "^7.24.2",
60
+ "yaml": "^2.7.1",
61
+ "zod": "^4.1.12"
62
+ },
63
+ "devDependencies": {
64
+ "@types/better-sqlite3": "^7.6.13",
65
+ "@types/node-cron": "^3.0.11",
66
+ "tsup": "^8.5.1",
67
+ "tsx": "^4.19.0",
68
+ "@ainyc/canonry-api-client": "0.0.0",
69
+ "@ainyc/canonry-contracts": "0.0.0",
70
+ "@ainyc/canonry-api-routes": "0.0.0",
71
+ "@ainyc/canonry-config": "0.0.0",
72
+ "@ainyc/canonry-db": "0.0.0",
73
+ "@ainyc/canonry-integration-bing": "0.0.0",
74
+ "@ainyc/canonry-integration-openai-ads": "0.0.0",
75
+ "@ainyc/canonry-integration-cloud-run": "0.0.0",
76
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
77
+ "@ainyc/canonry-integration-google": "0.0.0",
78
+ "@ainyc/canonry-integration-google-business-profile": "0.0.0",
79
+ "@ainyc/canonry-integration-traffic": "0.0.0",
80
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
81
+ "@ainyc/canonry-intelligence": "0.0.0",
82
+ "@ainyc/canonry-integration-google-places": "0.0.0",
83
+ "@ainyc/canonry-provider-cdp": "0.0.0",
84
+ "@ainyc/canonry-provider-claude": "0.0.0",
85
+ "@ainyc/canonry-provider-gemini": "0.0.0",
86
+ "@ainyc/canonry-provider-openai": "0.0.0",
87
+ "@ainyc/canonry-provider-local": "0.0.0",
88
+ "@ainyc/canonry-provider-perplexity": "0.0.0"
89
+ },
90
+ "scripts": {
91
+ "build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",
92
+ "build:web": "tsx build-web.ts",
93
+ "typecheck": "tsc --noEmit -p tsconfig.json",
94
+ "test": "vitest run --config ../../vitest.package.config.ts",
95
+ "lint": "eslint src/ test/"
96
+ }
97
+ }