@ainyc/canonry 2.5.0 → 2.6.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.
@@ -1,3 +1,55 @@
1
+ import {
2
+ AGENT_MEMORY_KEY_MAX_LENGTH,
3
+ AGENT_MEMORY_VALUE_MAX_BYTES,
4
+ AGENT_PROVIDER_IDS,
5
+ AgentProviderIds,
6
+ ApiClient,
7
+ AppError,
8
+ CcReleaseSyncStatuses,
9
+ MemorySources,
10
+ RunKinds,
11
+ RunStatuses,
12
+ RunTriggers,
13
+ agentBusy,
14
+ agentMemoryDeleteRequestSchema,
15
+ agentMemoryUpsertRequestSchema,
16
+ authInvalid,
17
+ authRequired,
18
+ brandKeyFromText,
19
+ categorizeSource,
20
+ categoryLabel,
21
+ configExists,
22
+ deliveryFailed,
23
+ determineAnswerMentioned,
24
+ effectiveDomains,
25
+ extractAnswerMentions,
26
+ findDuplicateLocationLabels,
27
+ hasLocationLabel,
28
+ internalError,
29
+ isAgentProviderId,
30
+ isBrowserProvider,
31
+ loadConfig,
32
+ locationContextSchema,
33
+ missingDependency,
34
+ normalizeProjectDomain,
35
+ notFound,
36
+ notImplemented,
37
+ parseWindow,
38
+ projectConfigSchema,
39
+ projectUpsertRequestSchema,
40
+ providerError,
41
+ runInProgress,
42
+ runNotCancellable,
43
+ runTriggerRequestSchema,
44
+ saveConfigPatch,
45
+ scheduleUpsertRequestSchema,
46
+ snapshotRequestSchema,
47
+ unsupportedKind,
48
+ validationError,
49
+ visibilityStateFromAnswerMentioned,
50
+ windowCutoff,
51
+ wordpressEnvSchema
52
+ } from "./chunk-3DUTT6H2.js";
1
53
  import {
2
54
  IntelligenceService,
3
55
  agentMemory,
@@ -30,172 +82,7 @@ import {
30
82
  runs,
31
83
  schedules,
32
84
  usageCounters
33
- } from "./chunk-32YTAZBL.js";
34
-
35
- // src/config.ts
36
- import fs from "fs";
37
- import path from "path";
38
- import os from "os";
39
- import { parse, stringify } from "yaml";
40
- function normalizeGoogleConfig(config) {
41
- if (!config.google) return;
42
- config.google.connections = (config.google.connections ?? []).map((connection) => ({
43
- ...connection,
44
- propertyId: connection.propertyId ?? null,
45
- refreshToken: connection.refreshToken ?? null,
46
- tokenExpiresAt: connection.tokenExpiresAt ?? null,
47
- scopes: connection.scopes ?? []
48
- }));
49
- }
50
- function normalizeWordpressConfig(config) {
51
- if (!config.wordpress) return;
52
- config.wordpress.connections = (config.wordpress.connections ?? []).map((connection) => ({
53
- ...connection,
54
- url: connection.url.replace(/\/$/, ""),
55
- stagingUrl: connection.stagingUrl?.replace(/\/$/, ""),
56
- defaultEnv: connection.defaultEnv ?? "live"
57
- }));
58
- }
59
- function getConfigDir() {
60
- const override = process.env.CANONRY_CONFIG_DIR?.trim();
61
- if (override) {
62
- return override;
63
- }
64
- return path.join(os.homedir(), ".canonry");
65
- }
66
- function getConfigPath() {
67
- return path.join(getConfigDir(), "config.yaml");
68
- }
69
- function loadConfig() {
70
- const configPath = getConfigPath();
71
- if (!fs.existsSync(configPath)) {
72
- throw new Error(
73
- `Config not found at ${configPath}.
74
- Run "canonry init" to set up interactively, or "canonry init --gemini-key <key>" for non-interactive setup.
75
- For CI/Docker, use "canonry bootstrap" with env vars (GEMINI_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY, PERPLEXITY_API_KEY, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET).`
76
- );
77
- }
78
- const raw = fs.readFileSync(configPath, "utf-8");
79
- const parsed = parse(raw);
80
- if (!parsed.apiUrl || !parsed.database || !parsed.apiKey) {
81
- const missing = [
82
- !parsed.apiUrl && "apiUrl",
83
- !parsed.database && "database",
84
- !parsed.apiKey && "apiKey"
85
- ].filter(Boolean).join(", ");
86
- throw new Error(
87
- `Invalid config at ${configPath} \u2014 missing: ${missing}.
88
- These fields are auto-generated. Run "canonry init" (or "canonry init --gemini-key <key>" for non-interactive setup) to create a valid config.
89
- Do not write config.yaml by hand; use "canonry init", "canonry settings", or "canonry bootstrap" instead.`
90
- );
91
- }
92
- if (parsed.geminiApiKey && !parsed.providers?.gemini) {
93
- parsed.providers = {
94
- ...parsed.providers,
95
- gemini: {
96
- apiKey: parsed.geminiApiKey,
97
- model: parsed.geminiModel,
98
- quota: parsed.geminiQuota
99
- }
100
- };
101
- }
102
- normalizeGoogleConfig(parsed);
103
- normalizeWordpressConfig(parsed);
104
- const portOverride = process.env.CANONRY_PORT?.trim();
105
- if (portOverride) {
106
- try {
107
- const url = new URL(parsed.apiUrl);
108
- url.port = portOverride;
109
- parsed.apiUrl = url.origin;
110
- } catch {
111
- }
112
- }
113
- if ("CANONRY_BASE_PATH" in process.env) {
114
- const val = process.env.CANONRY_BASE_PATH.trim();
115
- parsed.basePath = val || void 0;
116
- }
117
- if (parsed.basePath) {
118
- const normalizedBase = "/" + parsed.basePath.replace(/^\/|\/$/g, "");
119
- try {
120
- const url = new URL(parsed.apiUrl);
121
- if (normalizedBase !== "/" && !url.pathname.startsWith(normalizedBase)) {
122
- parsed.apiUrl = url.origin + normalizedBase;
123
- }
124
- } catch {
125
- }
126
- }
127
- return parsed;
128
- }
129
- function loadConfigRaw() {
130
- const configPath = getConfigPath();
131
- if (!fs.existsSync(configPath)) return null;
132
- try {
133
- return parse(fs.readFileSync(configPath, "utf-8")) ?? null;
134
- } catch {
135
- return null;
136
- }
137
- }
138
- function saveConfig(config) {
139
- const configDir = getConfigDir();
140
- if (!fs.existsSync(configDir)) {
141
- fs.mkdirSync(configDir, { recursive: true });
142
- }
143
- const configPath = getConfigPath();
144
- const onDisk = loadConfigRaw();
145
- const merged = onDisk ? { ...onDisk } : {};
146
- for (const [key, value] of Object.entries(config)) {
147
- if (value !== void 0) {
148
- merged[key] = value;
149
- }
150
- }
151
- if (onDisk) {
152
- if (process.env.CANONRY_PORT?.trim() || onDisk.basePath) {
153
- merged.apiUrl = onDisk.apiUrl;
154
- }
155
- if ("CANONRY_BASE_PATH" in process.env) {
156
- if (onDisk.basePath !== void 0) {
157
- merged.basePath = onDisk.basePath;
158
- } else {
159
- delete merged.basePath;
160
- }
161
- }
162
- }
163
- const yaml = stringify(merged);
164
- fs.writeFileSync(configPath, yaml, { encoding: "utf-8", mode: 384 });
165
- }
166
- function saveConfigPatch(patch) {
167
- const configDir = getConfigDir();
168
- if (!fs.existsSync(configDir)) {
169
- fs.mkdirSync(configDir, { recursive: true });
170
- }
171
- const configPath = getConfigPath();
172
- let base = {};
173
- if (fs.existsSync(configPath)) {
174
- try {
175
- const raw = fs.readFileSync(configPath, "utf-8");
176
- base = parse(raw) ?? {};
177
- } catch {
178
- base = {};
179
- }
180
- }
181
- const merged = { ...base, ...patch };
182
- if (base.database) merged.database = base.database;
183
- if (base.apiKey) merged.apiKey = base.apiKey;
184
- if (base.anonymousId) merged.anonymousId = base.anonymousId;
185
- if (base.dashboardPasswordHash) merged.dashboardPasswordHash = base.dashboardPasswordHash;
186
- if (base.providers && patch.providers) {
187
- merged.providers = { ...base.providers };
188
- for (const [key, patchEntry] of Object.entries(patch.providers)) {
189
- const baseEntry = base.providers[key] ?? {};
190
- merged.providers[key] = { ...baseEntry, ...patchEntry };
191
- }
192
- }
193
- const yaml = stringify(merged);
194
- fs.writeFileSync(configPath, yaml, { encoding: "utf-8", mode: 384 });
195
- }
196
- function configExists() {
197
- return fs.existsSync(getConfigPath());
198
- }
85
+ } from "./chunk-PYHANJ3B.js";
199
86
 
200
87
  // src/telemetry.ts
201
88
  import crypto from "crypto";
@@ -269,1259 +156,15 @@ function trackEvent(event, properties) {
269
156
  }).finally(() => clearTimeout(timeout));
270
157
  }
271
158
 
272
- // src/cli-error.ts
273
- var EXIT_USER_ERROR = 1;
274
- var EXIT_SYSTEM_ERROR = 2;
275
- var CliError = class extends Error {
276
- code;
277
- displayMessage;
278
- details;
279
- exitCode;
280
- constructor(options) {
281
- super(options.message);
282
- this.name = "CliError";
283
- this.code = options.code;
284
- this.displayMessage = options.displayMessage;
285
- this.details = options.details;
286
- this.exitCode = options.exitCode ?? EXIT_USER_ERROR;
287
- }
288
- };
289
- function usageError(displayMessage, options) {
290
- const firstLine = displayMessage.split("\n", 1)[0] ?? "Error: invalid command usage";
291
- return new CliError({
292
- code: "CLI_USAGE_ERROR",
293
- message: options?.message ?? firstLine.replace(/^Error:\s*/, ""),
294
- displayMessage,
295
- details: options?.details
296
- });
297
- }
298
- function isEndpointMissing(err) {
299
- if (!(err instanceof CliError)) return false;
300
- const status = err.details?.httpStatus;
301
- return status === 404 || status === 405;
302
- }
303
- function printCliError(err, format) {
304
- if (format === "json") {
305
- if (err instanceof CliError) {
306
- console.error(
307
- JSON.stringify(
308
- {
309
- error: {
310
- code: err.code,
311
- message: err.message,
312
- ...err.details ? { details: err.details } : {}
313
- }
314
- },
315
- null,
316
- 2
317
- )
318
- );
319
- return;
320
- }
321
- const message = err instanceof Error ? err.message : "An unexpected error occurred";
322
- console.error(
323
- JSON.stringify(
324
- {
325
- error: {
326
- code: "CLI_ERROR",
327
- message
328
- }
329
- },
330
- null,
331
- 2
332
- )
333
- );
334
- return;
335
- }
336
- if (err instanceof CliError && err.displayMessage) {
337
- console.error(err.displayMessage);
338
- return;
339
- }
340
- if (err instanceof Error) {
341
- console.error(`Error: ${err.message}`);
342
- return;
343
- }
344
- console.error("An unexpected error occurred");
345
- }
346
-
347
159
  // src/server.ts
348
160
  import { createRequire as createRequire3 } from "module";
349
161
  import crypto28 from "crypto";
350
- import fs13 from "fs";
351
- import path15 from "path";
162
+ import fs12 from "fs";
163
+ import path14 from "path";
352
164
  import { fileURLToPath as fileURLToPath2 } from "url";
353
165
  import { eq as eq30 } from "drizzle-orm";
354
166
  import Fastify from "fastify";
355
167
 
356
- // ../contracts/src/config-schema.ts
357
- import { z as z4 } from "zod";
358
-
359
- // ../contracts/src/provider.ts
360
- import { z } from "zod";
361
- var providerQuotaPolicySchema = z.object({
362
- maxConcurrency: z.number().int().positive(),
363
- maxRequestsPerMinute: z.number().int().positive(),
364
- maxRequestsPerDay: z.number().int().positive()
365
- });
366
- var ProviderNames = {
367
- gemini: "gemini",
368
- openai: "openai",
369
- claude: "claude",
370
- perplexity: "perplexity",
371
- local: "local",
372
- cdpChatgpt: "cdp:chatgpt"
373
- };
374
- var providerNameSchema = z.string().min(1);
375
- var apiProviderNameSchema = z.string().min(1);
376
- function isBrowserProvider(name) {
377
- return name.startsWith("cdp:");
378
- }
379
- var CDP_TARGETS = ["cdp:chatgpt"];
380
- function resolveProviderInput(input) {
381
- const lower = input.trim().toLowerCase();
382
- if (lower === "cdp") {
383
- return [...CDP_TARGETS];
384
- }
385
- return lower ? [lower] : [];
386
- }
387
- var locationContextSchema = z.object({
388
- label: z.string().min(1),
389
- city: z.string().min(1),
390
- region: z.string().min(1),
391
- country: z.string().length(2),
392
- timezone: z.string().optional()
393
- });
394
-
395
- // ../contracts/src/notification.ts
396
- import { z as z2 } from "zod";
397
- var notificationEventSchema = z2.enum([
398
- "citation.lost",
399
- "citation.gained",
400
- "run.completed",
401
- "run.failed",
402
- "insight.critical",
403
- "insight.high"
404
- ]);
405
- var notificationDtoSchema = z2.object({
406
- id: z2.string(),
407
- projectId: z2.string(),
408
- channel: z2.literal("webhook"),
409
- url: z2.string().url(),
410
- urlDisplay: z2.string(),
411
- urlHost: z2.string(),
412
- events: z2.array(notificationEventSchema),
413
- enabled: z2.boolean().default(true),
414
- /** Opaque tag identifying the creator (e.g. `"agent"` for Aero webhooks). */
415
- source: z2.string().optional(),
416
- webhookSecret: z2.string().optional(),
417
- createdAt: z2.string(),
418
- updatedAt: z2.string()
419
- });
420
-
421
- // ../contracts/src/project.ts
422
- import { z as z3 } from "zod";
423
- var configSourceSchema = z3.enum(["cli", "api", "config-file"]);
424
- function findDuplicateLocationLabels(locations) {
425
- const seen = /* @__PURE__ */ new Set();
426
- const duplicates = /* @__PURE__ */ new Set();
427
- for (const location of locations) {
428
- if (seen.has(location.label)) {
429
- duplicates.add(location.label);
430
- continue;
431
- }
432
- seen.add(location.label);
433
- }
434
- return [...duplicates];
435
- }
436
- function hasLocationLabel(locations, label) {
437
- if (!label) return true;
438
- return locations.some((location) => location.label === label);
439
- }
440
- var projectUpsertRequestSchema = z3.object({
441
- displayName: z3.string().min(1),
442
- canonicalDomain: z3.string().min(1),
443
- ownedDomains: z3.array(z3.string().min(1)).optional(),
444
- country: z3.string().length(2),
445
- language: z3.string().min(2),
446
- tags: z3.array(z3.string()).optional(),
447
- labels: z3.record(z3.string(), z3.string()).optional(),
448
- providers: z3.array(providerNameSchema).optional(),
449
- locations: z3.array(locationContextSchema).optional(),
450
- defaultLocation: z3.string().nullable().optional(),
451
- autoExtractBacklinks: z3.boolean().optional(),
452
- configSource: configSourceSchema.optional()
453
- });
454
- var projectDtoSchema = z3.object({
455
- id: z3.string(),
456
- name: z3.string(),
457
- displayName: z3.string().optional(),
458
- canonicalDomain: z3.string(),
459
- ownedDomains: z3.array(z3.string()).default([]),
460
- country: z3.string().length(2),
461
- language: z3.string().min(2),
462
- tags: z3.array(z3.string()).default([]),
463
- labels: z3.record(z3.string(), z3.string()).default({}),
464
- locations: z3.array(locationContextSchema).default([]),
465
- defaultLocation: z3.string().nullable().optional(),
466
- autoExtractBacklinks: z3.boolean().default(false),
467
- configSource: configSourceSchema.default("cli"),
468
- configRevision: z3.number().int().positive().default(1),
469
- createdAt: z3.string().optional(),
470
- updatedAt: z3.string().optional()
471
- });
472
- function normalizeProjectDomain(input) {
473
- let domain = input.trim().toLowerCase();
474
- try {
475
- if (domain.includes("://")) {
476
- domain = new URL(domain).hostname.toLowerCase();
477
- }
478
- } catch {
479
- }
480
- return domain.replace(/^www\./, "");
481
- }
482
- function effectiveDomains(project) {
483
- const all = [project.canonicalDomain, ...project.ownedDomains ?? []];
484
- const seen = /* @__PURE__ */ new Set();
485
- const result = [];
486
- for (const d of all) {
487
- const trimmed = d.trim();
488
- if (!trimmed) continue;
489
- const norm = normalizeProjectDomain(trimmed);
490
- if (seen.has(norm)) continue;
491
- seen.add(norm);
492
- result.push(trimmed);
493
- }
494
- return result;
495
- }
496
-
497
- // ../contracts/src/config-schema.ts
498
- var configMetadataSchema = z4.object({
499
- name: z4.string().min(1).max(63).regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, {
500
- message: "Name must be a lowercase slug (letters, numbers, hyphens)"
501
- }),
502
- labels: z4.record(z4.string(), z4.string()).optional().default({})
503
- });
504
- var configScheduleSchema = z4.object({
505
- preset: z4.string().optional(),
506
- cron: z4.string().optional(),
507
- timezone: z4.string().optional().default("UTC"),
508
- providers: z4.array(providerNameSchema).optional().default([])
509
- }).refine(
510
- (data) => data.preset && !data.cron || !data.preset && data.cron,
511
- { message: 'Exactly one of "preset" or "cron" must be provided' }
512
- ).optional();
513
- var configNotificationSchema = z4.object({
514
- channel: z4.literal("webhook"),
515
- url: z4.string().url(),
516
- events: z4.array(notificationEventSchema).min(1)
517
- });
518
- var configGoogleSchema = z4.object({
519
- gsc: z4.object({
520
- propertyUrl: z4.string()
521
- }).optional(),
522
- syncSchedule: z4.object({
523
- preset: z4.string().optional(),
524
- cron: z4.string().optional()
525
- }).optional()
526
- }).optional();
527
- var configSpecSchema = z4.object({
528
- displayName: z4.string().min(1),
529
- canonicalDomain: z4.string().min(1),
530
- ownedDomains: z4.array(z4.string().min(1)).optional().default([]),
531
- country: z4.string().length(2),
532
- language: z4.string().min(2),
533
- keywords: z4.array(z4.string().min(1)).optional().default([]),
534
- competitors: z4.array(z4.string().min(1)).optional().default([]),
535
- providers: z4.array(providerNameSchema).optional().default([]),
536
- locations: z4.array(locationContextSchema).optional().default([]),
537
- defaultLocation: z4.string().optional(),
538
- schedule: configScheduleSchema,
539
- notifications: z4.array(configNotificationSchema).optional().default([]),
540
- google: configGoogleSchema,
541
- autoExtractBacklinks: z4.boolean().optional().default(false)
542
- }).superRefine((spec, ctx) => {
543
- const duplicateLabels = findDuplicateLocationLabels(spec.locations);
544
- if (duplicateLabels.length > 0) {
545
- ctx.addIssue({
546
- code: "custom",
547
- message: `Duplicate location labels are not allowed: ${duplicateLabels.join(", ")}`,
548
- path: ["locations"]
549
- });
550
- }
551
- if (!hasLocationLabel(spec.locations, spec.defaultLocation)) {
552
- ctx.addIssue({
553
- code: "custom",
554
- message: `defaultLocation "${spec.defaultLocation}" must match a configured location label`,
555
- path: ["defaultLocation"]
556
- });
557
- }
558
- });
559
- var projectConfigSchema = z4.object({
560
- apiVersion: z4.literal("canonry/v1"),
561
- kind: z4.literal("Project"),
562
- metadata: configMetadataSchema,
563
- spec: configSpecSchema
564
- });
565
-
566
- // ../contracts/src/errors.ts
567
- var AppError = class extends Error {
568
- code;
569
- statusCode;
570
- details;
571
- constructor(code, message, statusCode, details) {
572
- super(message);
573
- this.name = "AppError";
574
- this.code = code;
575
- this.statusCode = statusCode;
576
- this.details = details;
577
- }
578
- toJSON() {
579
- return {
580
- error: {
581
- code: this.code,
582
- message: this.message,
583
- ...this.details ? { details: this.details } : {}
584
- }
585
- };
586
- }
587
- };
588
- function notFound(entity, id) {
589
- return new AppError("NOT_FOUND", `${entity} '${id}' not found`, 404);
590
- }
591
- function validationError(message, details) {
592
- return new AppError("VALIDATION_ERROR", message, 400, details);
593
- }
594
- function authRequired() {
595
- return new AppError("AUTH_REQUIRED", "Authentication required", 401);
596
- }
597
- function authInvalid() {
598
- return new AppError("AUTH_INVALID", "Invalid API key", 401);
599
- }
600
- function providerError(message, details) {
601
- return new AppError("PROVIDER_ERROR", message, 502, details);
602
- }
603
- function runInProgress(projectName) {
604
- return new AppError("RUN_IN_PROGRESS", `A run is already in progress for '${projectName}'`, 409);
605
- }
606
- function runNotCancellable(runId, status) {
607
- return new AppError("RUN_NOT_CANCELLABLE", `Run '${runId}' is already in terminal state '${status}' and cannot be cancelled`, 409);
608
- }
609
- function unsupportedKind(kind) {
610
- return new AppError("UNSUPPORTED_KIND", `Kind '${kind}' is not supported in this version`, 400);
611
- }
612
- function notImplemented(message) {
613
- return new AppError("NOT_IMPLEMENTED", message, 501);
614
- }
615
- function deliveryFailed(message) {
616
- return new AppError("DELIVERY_FAILED", message, 502);
617
- }
618
- function agentBusy(projectName) {
619
- return new AppError(
620
- "AGENT_BUSY",
621
- `Aero is already running a turn for '${projectName}'. Retry after the current turn settles.`,
622
- 409
623
- );
624
- }
625
- function missingDependency(message, details) {
626
- return new AppError("MISSING_DEPENDENCY", message, 422, details);
627
- }
628
- function internalError(message, details) {
629
- return new AppError("INTERNAL_ERROR", message, 500, details);
630
- }
631
-
632
- // ../contracts/src/google.ts
633
- import { z as z5 } from "zod";
634
- var googleConnectionTypeSchema = z5.enum(["gsc", "ga4"]);
635
- var googleConnectionDtoSchema = z5.object({
636
- id: z5.string(),
637
- domain: z5.string(),
638
- connectionType: googleConnectionTypeSchema,
639
- propertyId: z5.string().nullable().optional(),
640
- sitemapUrl: z5.string().nullable().optional(),
641
- scopes: z5.array(z5.string()).default([]),
642
- createdAt: z5.string(),
643
- updatedAt: z5.string()
644
- });
645
- var gscSearchDataDtoSchema = z5.object({
646
- date: z5.string(),
647
- query: z5.string(),
648
- page: z5.string(),
649
- country: z5.string().nullable().optional(),
650
- device: z5.string().nullable().optional(),
651
- clicks: z5.number(),
652
- impressions: z5.number(),
653
- ctr: z5.number(),
654
- position: z5.number()
655
- });
656
- var gscUrlInspectionDtoSchema = z5.object({
657
- id: z5.string(),
658
- url: z5.string(),
659
- indexingState: z5.string().nullable().optional(),
660
- verdict: z5.string().nullable().optional(),
661
- coverageState: z5.string().nullable().optional(),
662
- pageFetchState: z5.string().nullable().optional(),
663
- robotsTxtState: z5.string().nullable().optional(),
664
- crawlTime: z5.string().nullable().optional(),
665
- lastCrawlResult: z5.string().nullable().optional(),
666
- isMobileFriendly: z5.boolean().nullable().optional(),
667
- richResults: z5.array(z5.string()).default([]),
668
- inspectedAt: z5.string()
669
- });
670
- var indexTransitionSchema = z5.enum(["stable", "reindexed", "deindexed", "still-missing", "new"]);
671
- var gscDeindexedRowSchema = z5.object({
672
- url: z5.string(),
673
- previousState: z5.string().nullable(),
674
- currentState: z5.string().nullable(),
675
- transitionDate: z5.string()
676
- });
677
- var gscReasonGroupSchema = z5.object({
678
- reason: z5.string(),
679
- count: z5.number(),
680
- urls: z5.array(gscUrlInspectionDtoSchema).default([])
681
- });
682
- var gscCoverageSummaryDtoSchema = z5.object({
683
- summary: z5.object({
684
- total: z5.number(),
685
- indexed: z5.number(),
686
- notIndexed: z5.number(),
687
- deindexed: z5.number(),
688
- percentage: z5.number()
689
- }),
690
- lastInspectedAt: z5.string().nullable(),
691
- indexed: z5.array(gscUrlInspectionDtoSchema).default([]),
692
- notIndexed: z5.array(gscUrlInspectionDtoSchema).default([]),
693
- deindexed: z5.array(gscDeindexedRowSchema).default([]),
694
- reasonGroups: z5.array(gscReasonGroupSchema).default([])
695
- });
696
- var indexingNotificationDtoSchema = z5.object({
697
- url: z5.string(),
698
- type: z5.enum(["URL_UPDATED", "URL_DELETED"]),
699
- notifiedAt: z5.string()
700
- });
701
- var indexingRequestResultDtoSchema = z5.object({
702
- url: z5.string(),
703
- type: z5.enum(["URL_UPDATED", "URL_DELETED"]),
704
- notifiedAt: z5.string(),
705
- status: z5.enum(["success", "error"]),
706
- error: z5.string().optional()
707
- });
708
- var gscCoverageSnapshotDtoSchema = z5.object({
709
- date: z5.string(),
710
- indexed: z5.number(),
711
- notIndexed: z5.number(),
712
- reasonBreakdown: z5.record(z5.string(), z5.number()).default({})
713
- });
714
-
715
- // ../contracts/src/bing.ts
716
- import { z as z6 } from "zod";
717
- var bingConnectionDtoSchema = z6.object({
718
- id: z6.string(),
719
- domain: z6.string(),
720
- siteUrl: z6.string().nullable().optional(),
721
- createdAt: z6.string(),
722
- updatedAt: z6.string()
723
- });
724
- var bingUrlInspectionDtoSchema = z6.object({
725
- id: z6.string(),
726
- url: z6.string(),
727
- httpCode: z6.number().nullable().optional(),
728
- inIndex: z6.boolean().nullable().optional(),
729
- lastCrawledDate: z6.string().nullable().optional(),
730
- inIndexDate: z6.string().nullable().optional(),
731
- inspectedAt: z6.string(),
732
- // Fields derived from GetUrlInfo response (more reliable than InIndex)
733
- documentSize: z6.number().nullable().optional(),
734
- anchorCount: z6.number().nullable().optional(),
735
- discoveryDate: z6.string().nullable().optional()
736
- });
737
- var bingCoverageSummaryDtoSchema = z6.object({
738
- summary: z6.object({
739
- total: z6.number(),
740
- indexed: z6.number(),
741
- notIndexed: z6.number(),
742
- unknown: z6.number().optional(),
743
- percentage: z6.number()
744
- }),
745
- lastInspectedAt: z6.string().nullable(),
746
- indexed: z6.array(bingUrlInspectionDtoSchema).default([]),
747
- notIndexed: z6.array(bingUrlInspectionDtoSchema).default([]),
748
- unknown: z6.array(bingUrlInspectionDtoSchema).default([]).optional()
749
- });
750
- var bingKeywordStatsDtoSchema = z6.object({
751
- query: z6.string(),
752
- impressions: z6.number(),
753
- clicks: z6.number(),
754
- ctr: z6.number(),
755
- averagePosition: z6.number()
756
- });
757
- var bingCoverageSnapshotDtoSchema = z6.object({
758
- date: z6.string(),
759
- indexed: z6.number(),
760
- notIndexed: z6.number(),
761
- unknown: z6.number()
762
- });
763
- var bingSubmitResultDtoSchema = z6.object({
764
- url: z6.string(),
765
- status: z6.enum(["success", "error"]),
766
- submittedAt: z6.string(),
767
- error: z6.string().optional()
768
- });
769
-
770
- // ../contracts/src/wordpress.ts
771
- import { z as z7 } from "zod";
772
- var wordpressEnvSchema = z7.enum(["live", "staging"]);
773
- var wordpressConnectionDtoSchema = z7.object({
774
- projectName: z7.string(),
775
- url: z7.string(),
776
- stagingUrl: z7.string().optional(),
777
- username: z7.string(),
778
- defaultEnv: wordpressEnvSchema,
779
- createdAt: z7.string(),
780
- updatedAt: z7.string()
781
- });
782
- var wordpressSiteStatusDtoSchema = z7.object({
783
- url: z7.string(),
784
- reachable: z7.boolean(),
785
- pageCount: z7.number().nullable().optional(),
786
- version: z7.string().nullable().optional(),
787
- error: z7.string().nullable().optional(),
788
- plugins: z7.array(z7.string()).optional(),
789
- authenticatedUser: z7.object({
790
- id: z7.number(),
791
- slug: z7.string()
792
- }).nullable().optional()
793
- });
794
- var wordpressStatusDtoSchema = z7.object({
795
- connected: z7.boolean(),
796
- projectName: z7.string(),
797
- defaultEnv: wordpressEnvSchema,
798
- live: wordpressSiteStatusDtoSchema.nullable(),
799
- staging: wordpressSiteStatusDtoSchema.nullable(),
800
- adminUrl: z7.string().nullable().optional()
801
- });
802
- var wordpressPageSummaryDtoSchema = z7.object({
803
- id: z7.number(),
804
- slug: z7.string(),
805
- title: z7.string(),
806
- status: z7.string(),
807
- modifiedAt: z7.string().nullable().optional(),
808
- link: z7.string().nullable().optional()
809
- });
810
- var wordpressSeoStateDtoSchema = z7.object({
811
- title: z7.string().nullable(),
812
- description: z7.string().nullable(),
813
- noindex: z7.boolean().nullable(),
814
- writable: z7.boolean().default(false),
815
- writeTargets: z7.array(z7.string()).default([])
816
- });
817
- var wordpressSchemaBlockDtoSchema = z7.object({
818
- type: z7.string(),
819
- json: z7.record(z7.string(), z7.unknown())
820
- });
821
- var wordpressPageDetailDtoSchema = wordpressPageSummaryDtoSchema.extend({
822
- env: wordpressEnvSchema,
823
- content: z7.string(),
824
- seo: wordpressSeoStateDtoSchema,
825
- schemaBlocks: z7.array(wordpressSchemaBlockDtoSchema).default([])
826
- });
827
- var wordpressDiffPageDtoSchema = wordpressPageDetailDtoSchema.extend({
828
- contentHash: z7.string(),
829
- contentSnippet: z7.string()
830
- });
831
- var wordpressManualAssistDtoSchema = z7.object({
832
- manualRequired: z7.literal(true),
833
- targetUrl: z7.string(),
834
- adminUrl: z7.string().nullable().optional(),
835
- content: z7.string(),
836
- nextSteps: z7.array(z7.string()).default([])
837
- });
838
- var wordpressAuditIssueDtoSchema = z7.object({
839
- slug: z7.string(),
840
- severity: z7.enum(["high", "medium", "low"]),
841
- code: z7.enum([
842
- "noindex",
843
- "missing-seo-title",
844
- "missing-meta-description",
845
- "missing-schema",
846
- "thin-content"
847
- ]),
848
- message: z7.string()
849
- });
850
- var wordpressAuditPageDtoSchema = z7.object({
851
- slug: z7.string(),
852
- title: z7.string(),
853
- status: z7.string(),
854
- wordCount: z7.number(),
855
- seo: wordpressSeoStateDtoSchema,
856
- schemaPresent: z7.boolean(),
857
- issues: z7.array(wordpressAuditIssueDtoSchema).default([])
858
- });
859
- var wordpressBulkMetaEntryResultDtoSchema = z7.object({
860
- slug: z7.string(),
861
- status: z7.enum(["applied", "skipped", "manual"]),
862
- error: z7.string().optional(),
863
- manualAssist: wordpressManualAssistDtoSchema.optional()
864
- });
865
- var wordpressBulkMetaResultDtoSchema = z7.object({
866
- env: wordpressEnvSchema,
867
- strategy: z7.enum(["plugin", "manual"]),
868
- results: z7.array(wordpressBulkMetaEntryResultDtoSchema)
869
- });
870
- var wordpressSchemaDeployEntryResultDtoSchema = z7.object({
871
- slug: z7.string(),
872
- status: z7.enum(["deployed", "stripped", "skipped", "failed"]),
873
- schemasInjected: z7.array(z7.string()).optional(),
874
- manualAssist: wordpressManualAssistDtoSchema.optional(),
875
- error: z7.string().optional()
876
- });
877
- var wordpressSchemaDeployResultDtoSchema = z7.object({
878
- env: wordpressEnvSchema,
879
- results: z7.array(wordpressSchemaDeployEntryResultDtoSchema)
880
- });
881
- var wordpressSchemaStatusPageDtoSchema = z7.object({
882
- slug: z7.string(),
883
- title: z7.string(),
884
- canonrySchemas: z7.array(z7.string()),
885
- thirdPartySchemas: z7.array(z7.string()),
886
- hasCanonrySchema: z7.boolean()
887
- });
888
- var wordpressSchemaStatusResultDtoSchema = z7.object({
889
- env: wordpressEnvSchema,
890
- pages: z7.array(wordpressSchemaStatusPageDtoSchema)
891
- });
892
- var wordpressOnboardStepDtoSchema = z7.object({
893
- name: z7.string(),
894
- status: z7.enum(["completed", "skipped", "failed"]),
895
- summary: z7.string().optional(),
896
- error: z7.string().optional()
897
- });
898
- var wordpressOnboardResultDtoSchema = z7.object({
899
- projectName: z7.string(),
900
- steps: z7.array(wordpressOnboardStepDtoSchema)
901
- });
902
- var wordpressDiffDtoSchema = z7.object({
903
- slug: z7.string(),
904
- live: wordpressDiffPageDtoSchema,
905
- staging: wordpressDiffPageDtoSchema,
906
- hasDifferences: z7.boolean(),
907
- differences: z7.object({
908
- title: z7.boolean(),
909
- slug: z7.boolean(),
910
- content: z7.boolean(),
911
- seoTitle: z7.boolean(),
912
- seoDescription: z7.boolean(),
913
- noindex: z7.boolean(),
914
- schema: z7.boolean()
915
- })
916
- });
917
-
918
- // ../contracts/src/providers.ts
919
- var ProviderIds = {
920
- claude: "claude",
921
- openai: "openai",
922
- gemini: "gemini",
923
- perplexity: "perplexity",
924
- local: "local",
925
- cdpChatgpt: "cdp:chatgpt",
926
- zai: "zai"
927
- };
928
- var PROVIDER_IDS = Object.values(ProviderIds);
929
- var SweepProviderIds = {
930
- claude: ProviderIds.claude,
931
- openai: ProviderIds.openai,
932
- gemini: ProviderIds.gemini,
933
- perplexity: ProviderIds.perplexity,
934
- local: ProviderIds.local,
935
- cdpChatgpt: ProviderIds.cdpChatgpt
936
- };
937
- var SWEEP_PROVIDER_IDS = Object.values(SweepProviderIds);
938
- var AgentProviderIds = {
939
- claude: ProviderIds.claude,
940
- openai: ProviderIds.openai,
941
- gemini: ProviderIds.gemini,
942
- zai: ProviderIds.zai
943
- };
944
- var AGENT_PROVIDER_IDS = Object.values(AgentProviderIds);
945
- function isAgentProviderId(value) {
946
- return AGENT_PROVIDER_IDS.includes(value);
947
- }
948
-
949
- // ../contracts/src/run.ts
950
- import { z as z8 } from "zod";
951
- var runStatusSchema = z8.enum(["queued", "running", "completed", "partial", "failed", "cancelled"]);
952
- var RunStatuses = runStatusSchema.enum;
953
- var runKindSchema = z8.enum([
954
- "answer-visibility",
955
- "site-audit",
956
- "gsc-sync",
957
- "inspect-sitemap",
958
- "ga-sync",
959
- "bing-inspect",
960
- "bing-inspect-sitemap",
961
- "backlink-extract"
962
- ]);
963
- var RunKinds = runKindSchema.enum;
964
- var runTriggerSchema = z8.enum(["manual", "scheduled", "config-apply"]);
965
- var RunTriggers = runTriggerSchema.enum;
966
- var citationStateSchema = z8.enum(["cited", "not-cited"]);
967
- var CitationStates = citationStateSchema.enum;
968
- var visibilityStateSchema = z8.enum(["visible", "not-visible"]);
969
- var VisibilityStates = visibilityStateSchema.enum;
970
- var computedTransitionSchema = z8.enum(["new", "cited", "lost", "emerging", "not-cited"]);
971
- var ComputedTransitions = computedTransitionSchema.enum;
972
- var runDtoSchema = z8.object({
973
- id: z8.string(),
974
- projectId: z8.string(),
975
- kind: runKindSchema,
976
- status: runStatusSchema,
977
- trigger: runTriggerSchema.default("manual"),
978
- location: z8.string().nullable().optional(),
979
- startedAt: z8.string().nullable().optional(),
980
- finishedAt: z8.string().nullable().optional(),
981
- error: z8.string().nullable().optional(),
982
- createdAt: z8.string()
983
- });
984
- var groundingSourceSchema = z8.object({
985
- uri: z8.string(),
986
- title: z8.string()
987
- });
988
- var querySnapshotDtoSchema = z8.object({
989
- id: z8.string(),
990
- runId: z8.string(),
991
- keywordId: z8.string(),
992
- keyword: z8.string().optional(),
993
- provider: providerNameSchema,
994
- citationState: citationStateSchema,
995
- answerMentioned: z8.boolean().optional(),
996
- visibilityState: visibilityStateSchema.optional(),
997
- transition: computedTransitionSchema.optional(),
998
- answerText: z8.string().nullable().optional(),
999
- citedDomains: z8.array(z8.string()).default([]),
1000
- competitorOverlap: z8.array(z8.string()).default([]),
1001
- recommendedCompetitors: z8.array(z8.string()).default([]),
1002
- matchedTerms: z8.array(z8.string()).default([]),
1003
- groundingSources: z8.array(groundingSourceSchema).default([]),
1004
- searchQueries: z8.array(z8.string()).default([]),
1005
- model: z8.string().nullable().optional(),
1006
- location: z8.string().nullable().optional(),
1007
- createdAt: z8.string()
1008
- });
1009
- var runDetailDtoSchema = runDtoSchema.extend({
1010
- snapshots: z8.array(querySnapshotDtoSchema).optional()
1011
- });
1012
- var latestProjectRunDtoSchema = z8.object({
1013
- totalRuns: z8.number().int().nonnegative(),
1014
- run: runDetailDtoSchema.nullable()
1015
- });
1016
- var auditLogEntrySchema = z8.object({
1017
- id: z8.string(),
1018
- projectId: z8.string().nullable().optional(),
1019
- actor: z8.string(),
1020
- action: z8.string(),
1021
- entityType: z8.string(),
1022
- entityId: z8.string().nullable().optional(),
1023
- diff: z8.unknown().optional(),
1024
- createdAt: z8.string()
1025
- });
1026
-
1027
- // ../contracts/src/snapshot.ts
1028
- import { z as z9 } from "zod";
1029
- var snapshotAccuracySchema = z9.enum(["yes", "no", "unknown", "not-mentioned"]);
1030
- var snapshotRequestSchema = z9.object({
1031
- companyName: z9.string().min(1),
1032
- domain: z9.string().min(1),
1033
- phrases: z9.array(z9.string().min(1)).optional().default([]),
1034
- competitors: z9.array(z9.string().min(1)).optional().default([])
1035
- });
1036
- var snapshotCompetitorEntrySchema = z9.object({
1037
- name: z9.string(),
1038
- count: z9.number().int().nonnegative()
1039
- });
1040
- var snapshotAuditFactorSchema = z9.object({
1041
- id: z9.string(),
1042
- name: z9.string(),
1043
- weight: z9.number(),
1044
- score: z9.number(),
1045
- grade: z9.string(),
1046
- status: z9.enum(["pass", "partial", "fail"]),
1047
- findings: z9.array(z9.object({
1048
- type: z9.string(),
1049
- message: z9.string()
1050
- })).default([]),
1051
- recommendations: z9.array(z9.string()).default([])
1052
- });
1053
- var snapshotAuditSchema = z9.object({
1054
- url: z9.string(),
1055
- finalUrl: z9.string(),
1056
- auditedAt: z9.string(),
1057
- overallScore: z9.number(),
1058
- overallGrade: z9.string(),
1059
- summary: z9.string(),
1060
- factors: z9.array(snapshotAuditFactorSchema).default([])
1061
- });
1062
- var snapshotProfileSchema = z9.object({
1063
- industry: z9.string(),
1064
- summary: z9.string(),
1065
- services: z9.array(z9.string()).default([]),
1066
- categoryTerms: z9.array(z9.string()).default([])
1067
- });
1068
- var snapshotProviderResultSchema = z9.object({
1069
- provider: z9.string(),
1070
- displayName: z9.string(),
1071
- model: z9.string().nullable().optional(),
1072
- mentioned: z9.boolean(),
1073
- cited: z9.boolean(),
1074
- describedAccurately: snapshotAccuracySchema,
1075
- accuracyNotes: z9.string().nullable().optional(),
1076
- incorrectClaims: z9.array(z9.string()).default([]),
1077
- recommendedCompetitors: z9.array(z9.string()).default([]),
1078
- citedDomains: z9.array(z9.string()).default([]),
1079
- groundingSources: z9.array(groundingSourceSchema).default([]),
1080
- searchQueries: z9.array(z9.string()).default([]),
1081
- answerText: z9.string(),
1082
- error: z9.string().nullable().optional()
1083
- });
1084
- var snapshotQueryResultSchema = z9.object({
1085
- phrase: z9.string(),
1086
- providerResults: z9.array(snapshotProviderResultSchema).default([])
1087
- });
1088
- var snapshotSummarySchema = z9.object({
1089
- totalQueries: z9.number().int().nonnegative(),
1090
- totalProviders: z9.number().int().nonnegative(),
1091
- totalComparisons: z9.number().int().nonnegative(),
1092
- mentionCount: z9.number().int().nonnegative(),
1093
- citationCount: z9.number().int().nonnegative(),
1094
- topCompetitors: z9.array(snapshotCompetitorEntrySchema).default([]),
1095
- visibilityGap: z9.string(),
1096
- whatThisMeans: z9.array(z9.string()).default([]),
1097
- recommendedActions: z9.array(z9.string()).default([])
1098
- });
1099
- var snapshotReportSchema = z9.object({
1100
- companyName: z9.string(),
1101
- domain: z9.string(),
1102
- homepageUrl: z9.string(),
1103
- generatedAt: z9.string(),
1104
- phrases: z9.array(z9.string()).default([]),
1105
- competitors: z9.array(z9.string()).default([]),
1106
- profile: snapshotProfileSchema,
1107
- audit: snapshotAuditSchema,
1108
- queryResults: z9.array(snapshotQueryResultSchema).default([]),
1109
- summary: snapshotSummarySchema
1110
- });
1111
-
1112
- // ../contracts/src/schedule.ts
1113
- import { z as z10 } from "zod";
1114
- var scheduleDtoSchema = z10.object({
1115
- id: z10.string(),
1116
- projectId: z10.string(),
1117
- cronExpr: z10.string(),
1118
- preset: z10.string().nullable().optional(),
1119
- timezone: z10.string().default("UTC"),
1120
- enabled: z10.boolean().default(true),
1121
- providers: z10.array(providerNameSchema).default([]),
1122
- lastRunAt: z10.string().nullable().optional(),
1123
- nextRunAt: z10.string().nullable().optional(),
1124
- createdAt: z10.string(),
1125
- updatedAt: z10.string()
1126
- });
1127
- var scheduleUpsertRequestSchema = z10.object({
1128
- preset: z10.string().optional(),
1129
- cron: z10.string().optional(),
1130
- timezone: z10.string().optional().default("UTC"),
1131
- enabled: z10.boolean().optional().default(true),
1132
- providers: z10.array(providerNameSchema).optional().default([])
1133
- }).refine(
1134
- (data) => data.preset && !data.cron || !data.preset && data.cron,
1135
- { message: 'Exactly one of "preset" or "cron" must be provided' }
1136
- );
1137
-
1138
- // ../contracts/src/analytics.ts
1139
- import { z as z11 } from "zod";
1140
- var visibilityMetricModeSchema = z11.enum(["answer", "citation"]);
1141
- var VisibilityMetricModes = visibilityMetricModeSchema.enum;
1142
- function parseWindow(value) {
1143
- if (value === "7d" || value === "30d" || value === "90d" || value === "all") return value;
1144
- return "all";
1145
- }
1146
- function windowCutoff(window) {
1147
- if (window === "all") return null;
1148
- const days = window === "7d" ? 7 : window === "30d" ? 30 : 90;
1149
- const d = /* @__PURE__ */ new Date();
1150
- d.setDate(d.getDate() - days);
1151
- return d.toISOString();
1152
- }
1153
-
1154
- // ../contracts/src/source-categories.ts
1155
- var SOURCE_CATEGORY_RULES = [
1156
- // Forums
1157
- { pattern: "reddit.com", category: "forum", label: "Reddit" },
1158
- { pattern: "quora.com", category: "forum", label: "Quora" },
1159
- { pattern: "stackexchange.com", category: "forum", label: "Stack Exchange" },
1160
- { pattern: "stackoverflow.com", category: "forum", label: "Stack Overflow" },
1161
- { pattern: "discourse.org", category: "forum", label: "Discourse" },
1162
- // Social
1163
- { pattern: "linkedin.com", category: "social", label: "LinkedIn" },
1164
- { pattern: "twitter.com", category: "social", label: "X (Twitter)" },
1165
- { pattern: "x.com", category: "social", label: "X (Twitter)" },
1166
- { pattern: "facebook.com", category: "social", label: "Facebook" },
1167
- { pattern: "instagram.com", category: "social", label: "Instagram" },
1168
- { pattern: "threads.net", category: "social", label: "Threads" },
1169
- { pattern: "pinterest.com", category: "social", label: "Pinterest" },
1170
- { pattern: "tiktok.com", category: "social", label: "TikTok" },
1171
- // Video
1172
- { pattern: "youtube.com", category: "video", label: "YouTube" },
1173
- { pattern: "youtu.be", category: "video", label: "YouTube" },
1174
- { pattern: "vimeo.com", category: "video", label: "Vimeo" },
1175
- // News
1176
- { pattern: "nytimes.com", category: "news", label: "NY Times" },
1177
- { pattern: "bbc.com", category: "news", label: "BBC" },
1178
- { pattern: "bbc.co.uk", category: "news", label: "BBC" },
1179
- { pattern: "cnn.com", category: "news", label: "CNN" },
1180
- { pattern: "reuters.com", category: "news", label: "Reuters" },
1181
- { pattern: "apnews.com", category: "news", label: "AP News" },
1182
- { pattern: "theguardian.com", category: "news", label: "The Guardian" },
1183
- { pattern: "washingtonpost.com", category: "news", label: "Washington Post" },
1184
- { pattern: "wsj.com", category: "news", label: "WSJ" },
1185
- { pattern: "forbes.com", category: "news", label: "Forbes" },
1186
- { pattern: "techcrunch.com", category: "news", label: "TechCrunch" },
1187
- { pattern: "theverge.com", category: "news", label: "The Verge" },
1188
- { pattern: "wired.com", category: "news", label: "Wired" },
1189
- { pattern: "arstechnica.com", category: "news", label: "Ars Technica" },
1190
- // Reference
1191
- { pattern: "wikipedia.org", category: "reference", label: "Wikipedia" },
1192
- { pattern: "wikimedia.org", category: "reference", label: "Wikimedia" },
1193
- { pattern: "britannica.com", category: "reference", label: "Britannica" },
1194
- { pattern: "merriam-webster.com", category: "reference", label: "Merriam-Webster" },
1195
- // Blog / Content platforms
1196
- { pattern: "medium.com", category: "blog", label: "Medium" },
1197
- { pattern: "substack.com", category: "blog", label: "Substack" },
1198
- { pattern: "dev.to", category: "blog", label: "DEV Community" },
1199
- { pattern: "hashnode.dev", category: "blog", label: "Hashnode" },
1200
- { pattern: "wordpress.com", category: "blog", label: "WordPress" },
1201
- { pattern: "blogger.com", category: "blog", label: "Blogger" },
1202
- { pattern: "hubspot.com", category: "blog", label: "HubSpot" },
1203
- // E-commerce
1204
- { pattern: "amazon.com", category: "ecommerce", label: "Amazon" },
1205
- { pattern: "amazon.co.uk", category: "ecommerce", label: "Amazon UK" },
1206
- { pattern: "shopify.com", category: "ecommerce", label: "Shopify" },
1207
- { pattern: "ebay.com", category: "ecommerce", label: "eBay" },
1208
- // Academic
1209
- { pattern: "scholar.google.com", category: "academic", label: "Google Scholar" },
1210
- { pattern: "arxiv.org", category: "academic", label: "arXiv" },
1211
- { pattern: "pubmed.ncbi.nlm.nih.gov", category: "academic", label: "PubMed" },
1212
- { pattern: "researchgate.net", category: "academic", label: "ResearchGate" },
1213
- { pattern: ".edu", category: "academic", label: "Academic (.edu)" }
1214
- ];
1215
- var CATEGORY_LABELS = {
1216
- social: "Social Media",
1217
- forum: "Forums & Q&A",
1218
- news: "News & Media",
1219
- reference: "Reference",
1220
- blog: "Blogs & Content",
1221
- ecommerce: "E-commerce",
1222
- video: "Video",
1223
- academic: "Academic",
1224
- other: "Other"
1225
- };
1226
- function categorizeSource(uri) {
1227
- let domain;
1228
- try {
1229
- const url = new URL(uri.startsWith("http") ? uri : `https://${uri}`);
1230
- domain = url.hostname.replace(/^www\./, "");
1231
- } catch {
1232
- domain = uri.replace(/^https?:\/\//, "").replace(/^www\./, "").split("/")[0] ?? uri;
1233
- }
1234
- const domainLower = domain.toLowerCase();
1235
- for (const rule of SOURCE_CATEGORY_RULES) {
1236
- if (domainLower === rule.pattern || domainLower.endsWith(`.${rule.pattern}`) || rule.pattern.startsWith(".") && domainLower.endsWith(rule.pattern)) {
1237
- return { category: rule.category, label: rule.label, domain };
1238
- }
1239
- }
1240
- return { category: "other", label: CATEGORY_LABELS.other, domain };
1241
- }
1242
- function categoryLabel(category) {
1243
- return CATEGORY_LABELS[category];
1244
- }
1245
-
1246
- // ../contracts/src/ga.ts
1247
- import { z as z12 } from "zod";
1248
- var ga4ConnectionDtoSchema = z12.object({
1249
- id: z12.string(),
1250
- projectId: z12.string(),
1251
- propertyId: z12.string(),
1252
- clientEmail: z12.string(),
1253
- connected: z12.boolean(),
1254
- createdAt: z12.string(),
1255
- updatedAt: z12.string()
1256
- });
1257
- var ga4TrafficSnapshotDtoSchema = z12.object({
1258
- date: z12.string(),
1259
- landingPage: z12.string(),
1260
- sessions: z12.number(),
1261
- organicSessions: z12.number(),
1262
- users: z12.number()
1263
- });
1264
- var ga4SourceDimensionSchema = z12.enum(["session", "first_user", "manual_utm"]);
1265
- var ga4AiReferralDtoSchema = z12.object({
1266
- source: z12.string(),
1267
- medium: z12.string(),
1268
- sessions: z12.number(),
1269
- users: z12.number(),
1270
- sourceDimension: ga4SourceDimensionSchema
1271
- });
1272
- var ga4SocialReferralDtoSchema = z12.object({
1273
- source: z12.string(),
1274
- medium: z12.string(),
1275
- sessions: z12.number(),
1276
- users: z12.number(),
1277
- /** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
1278
- channelGroup: z12.string()
1279
- });
1280
- var ga4TrafficSummaryDtoSchema = z12.object({
1281
- totalSessions: z12.number(),
1282
- totalOrganicSessions: z12.number(),
1283
- totalUsers: z12.number(),
1284
- topPages: z12.array(z12.object({
1285
- landingPage: z12.string(),
1286
- sessions: z12.number(),
1287
- organicSessions: z12.number(),
1288
- users: z12.number()
1289
- })),
1290
- aiReferrals: z12.array(ga4AiReferralDtoSchema),
1291
- /** Deduped AI session total: MAX(sessions) per date+source+medium across attribution dimensions, then summed. */
1292
- aiSessionsDeduped: z12.number(),
1293
- /** Deduped AI user total: MAX(users) per date+source+medium across attribution dimensions, then summed. */
1294
- aiUsersDeduped: z12.number(),
1295
- socialReferrals: z12.array(ga4SocialReferralDtoSchema),
1296
- /** Total social sessions (session-scoped, no cross-dimension dedup needed). */
1297
- socialSessions: z12.number(),
1298
- /** Total social users (session-scoped, no cross-dimension dedup needed). */
1299
- socialUsers: z12.number(),
1300
- /** Organic sessions as a percentage of total sessions (0–100, rounded). */
1301
- organicSharePct: z12.number(),
1302
- /** Deduped AI sessions as a percentage of total sessions (0–100, rounded). */
1303
- aiSharePct: z12.number(),
1304
- /** Social sessions as a percentage of total sessions (0–100, rounded). */
1305
- socialSharePct: z12.number(),
1306
- lastSyncedAt: z12.string().nullable()
1307
- });
1308
- var ga4AiReferralHistoryEntrySchema = z12.object({
1309
- date: z12.string(),
1310
- source: z12.string(),
1311
- medium: z12.string(),
1312
- sessions: z12.number(),
1313
- users: z12.number(),
1314
- /** Which GA4 dimension this row came from: session (sessionSource), first_user (firstUserSource), or manual_utm (utm_source parameter) */
1315
- sourceDimension: ga4SourceDimensionSchema
1316
- });
1317
- var ga4SocialReferralHistoryEntrySchema = z12.object({
1318
- date: z12.string(),
1319
- source: z12.string(),
1320
- medium: z12.string(),
1321
- sessions: z12.number(),
1322
- users: z12.number(),
1323
- /** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
1324
- channelGroup: z12.string()
1325
- });
1326
- var ga4SessionHistoryEntrySchema = z12.object({
1327
- date: z12.string(),
1328
- sessions: z12.number(),
1329
- organicSessions: z12.number(),
1330
- users: z12.number()
1331
- });
1332
-
1333
- // ../contracts/src/answer-visibility.ts
1334
- var GENERIC_TOKENS = /* @__PURE__ */ new Set([
1335
- "agency",
1336
- "app",
1337
- "company",
1338
- "corp",
1339
- "group",
1340
- "health",
1341
- "inc",
1342
- "llc",
1343
- "online",
1344
- "platform",
1345
- "services",
1346
- "site",
1347
- "solutions",
1348
- "software",
1349
- "systems",
1350
- "tech"
1351
- ]);
1352
- function extractAnswerMentions(answerText, displayName, domains) {
1353
- if (!answerText) return { mentioned: false, matchedTerms: [] };
1354
- const matchedTerms = [];
1355
- const lowerAnswer = answerText.toLowerCase();
1356
- for (const domain of domains) {
1357
- const normalizedDomain = normalizeProjectDomain(domain);
1358
- if (!normalizedDomain || !normalizedDomain.includes(".")) continue;
1359
- if (domainMentioned(lowerAnswer, normalizedDomain)) {
1360
- matchedTerms.push(normalizedDomain);
1361
- }
1362
- }
1363
- const normalizedDisplayName = normalizeText(displayName);
1364
- if (normalizedDisplayName && normalizeText(answerText).includes(normalizedDisplayName)) {
1365
- matchedTerms.push(displayName);
1366
- }
1367
- const tokens = collectDistinctiveTokens(displayName, domains);
1368
- let tokenMatches = 0;
1369
- const matchedTokens = [];
1370
- for (const token of tokens) {
1371
- if (new RegExp(`\\b${escapeRegExp(token)}\\b`).test(lowerAnswer)) {
1372
- tokenMatches++;
1373
- matchedTokens.push(token);
1374
- }
1375
- }
1376
- const tokenThresholdMet = tokens.length > 0 && (tokens.length === 1 && tokenMatches >= 1 || tokenMatches >= Math.min(2, tokens.length));
1377
- if (tokenThresholdMet) {
1378
- matchedTerms.push(...matchedTokens);
1379
- }
1380
- const unique = [...new Set(matchedTerms)];
1381
- const domainMatches3 = unique.filter((t) => t.includes("."));
1382
- const dedupedFinal = unique.filter((term) => {
1383
- if (term.includes(".")) return true;
1384
- return !domainMatches3.some((d) => d.toLowerCase().startsWith(term.toLowerCase() + "."));
1385
- });
1386
- return { mentioned: dedupedFinal.length > 0, matchedTerms: dedupedFinal };
1387
- }
1388
- function determineAnswerMentioned(answerText, displayName, domains) {
1389
- return extractAnswerMentions(answerText, displayName, domains).mentioned;
1390
- }
1391
- function visibilityStateFromAnswerMentioned(answerMentioned) {
1392
- return answerMentioned ? "visible" : "not-visible";
1393
- }
1394
- function brandKeyFromText(value) {
1395
- return value.toLowerCase().replace(/[^a-z0-9]/g, "");
1396
- }
1397
- function domainMentioned(lowerAnswer, normalizedDomain) {
1398
- const escapedDomain = escapeRegExp(normalizedDomain.toLowerCase());
1399
- const patterns = [
1400
- new RegExp(`(^|[^a-z0-9-])${escapedDomain}($|[^a-z0-9-])`),
1401
- new RegExp(`https?://(?:www\\.)?${escapedDomain}(?:[/:?#]|$)`),
1402
- new RegExp(`www\\.${escapedDomain}(?:[/:?#]|$)`)
1403
- ];
1404
- return patterns.some((pattern) => pattern.test(lowerAnswer));
1405
- }
1406
- function collectDistinctiveTokens(displayName, domains) {
1407
- const tokens = /* @__PURE__ */ new Set();
1408
- for (const token of extractDistinctiveTokens(displayName)) {
1409
- tokens.add(token);
1410
- }
1411
- for (const domain of domains) {
1412
- const hostname = normalizeProjectDomain(domain).split("/")[0] ?? "";
1413
- for (const label of hostname.split(".").filter(Boolean)) {
1414
- const token = label.replace(/[^a-z0-9]/gi, "").toLowerCase();
1415
- if (isDistinctiveToken(token)) tokens.add(token);
1416
- }
1417
- }
1418
- return [...tokens];
1419
- }
1420
- function extractDistinctiveTokens(value) {
1421
- return normalizeText(value).split(" ").filter(isDistinctiveToken);
1422
- }
1423
- function isDistinctiveToken(token) {
1424
- if (token.length < 4) return false;
1425
- return !GENERIC_TOKENS.has(token);
1426
- }
1427
- function normalizeText(value) {
1428
- return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
1429
- }
1430
- function escapeRegExp(value) {
1431
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1432
- }
1433
-
1434
- // ../contracts/src/agent.ts
1435
- import { z as z13 } from "zod";
1436
- var memorySourceSchema = z13.enum(["aero", "user", "compaction"]);
1437
- var MemorySources = memorySourceSchema.enum;
1438
- var AGENT_MEMORY_VALUE_MAX_BYTES = 2 * 1024;
1439
- var AGENT_MEMORY_KEY_MAX_LENGTH = 128;
1440
- var agentMemoryUpsertRequestSchema = z13.object({
1441
- key: z13.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH),
1442
- value: z13.string().min(1)
1443
- });
1444
- var agentMemoryDeleteRequestSchema = z13.object({
1445
- key: z13.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH)
1446
- });
1447
-
1448
- // ../contracts/src/backlinks.ts
1449
- import { z as z14 } from "zod";
1450
- var ccReleaseSyncStatusSchema = z14.enum(["queued", "downloading", "querying", "ready", "failed"]);
1451
- var CcReleaseSyncStatuses = ccReleaseSyncStatusSchema.enum;
1452
- var ccReleaseSyncDtoSchema = z14.object({
1453
- id: z14.string(),
1454
- release: z14.string(),
1455
- status: ccReleaseSyncStatusSchema,
1456
- phaseDetail: z14.string().nullable().optional(),
1457
- vertexPath: z14.string().nullable().optional(),
1458
- edgesPath: z14.string().nullable().optional(),
1459
- vertexSha256: z14.string().nullable().optional(),
1460
- edgesSha256: z14.string().nullable().optional(),
1461
- vertexBytes: z14.number().int().nullable().optional(),
1462
- edgesBytes: z14.number().int().nullable().optional(),
1463
- projectsProcessed: z14.number().int().nullable().optional(),
1464
- domainsDiscovered: z14.number().int().nullable().optional(),
1465
- downloadStartedAt: z14.string().nullable().optional(),
1466
- downloadFinishedAt: z14.string().nullable().optional(),
1467
- queryStartedAt: z14.string().nullable().optional(),
1468
- queryFinishedAt: z14.string().nullable().optional(),
1469
- error: z14.string().nullable().optional(),
1470
- createdAt: z14.string(),
1471
- updatedAt: z14.string()
1472
- });
1473
- var backlinkDomainDtoSchema = z14.object({
1474
- linkingDomain: z14.string(),
1475
- numHosts: z14.number().int()
1476
- });
1477
- var backlinkSummaryDtoSchema = z14.object({
1478
- projectId: z14.string(),
1479
- release: z14.string(),
1480
- targetDomain: z14.string(),
1481
- totalLinkingDomains: z14.number().int(),
1482
- totalHosts: z14.number().int(),
1483
- top10HostsShare: z14.string(),
1484
- queriedAt: z14.string()
1485
- });
1486
- var backlinkListResponseSchema = z14.object({
1487
- summary: backlinkSummaryDtoSchema.nullable(),
1488
- total: z14.number().int(),
1489
- rows: z14.array(backlinkDomainDtoSchema)
1490
- });
1491
- var backlinkHistoryEntrySchema = z14.object({
1492
- release: z14.string(),
1493
- totalLinkingDomains: z14.number().int(),
1494
- totalHosts: z14.number().int(),
1495
- top10HostsShare: z14.string(),
1496
- queriedAt: z14.string()
1497
- });
1498
- var backlinksInstallStatusDtoSchema = z14.object({
1499
- duckdbInstalled: z14.boolean(),
1500
- duckdbVersion: z14.string().nullable().optional(),
1501
- duckdbSpec: z14.string(),
1502
- pluginDir: z14.string()
1503
- });
1504
- var backlinksInstallResultDtoSchema = z14.object({
1505
- installed: z14.boolean(),
1506
- version: z14.string(),
1507
- path: z14.string(),
1508
- alreadyPresent: z14.boolean()
1509
- });
1510
- var ccAvailableReleaseSchema = z14.object({
1511
- release: z14.string(),
1512
- vertexUrl: z14.string(),
1513
- edgesUrl: z14.string(),
1514
- vertexBytes: z14.number().int().nullable(),
1515
- edgesBytes: z14.number().int().nullable(),
1516
- lastModified: z14.string().nullable()
1517
- });
1518
- var ccCachedReleaseSchema = z14.object({
1519
- release: z14.string(),
1520
- syncStatus: ccReleaseSyncStatusSchema.nullable(),
1521
- bytes: z14.number().int(),
1522
- lastUsedAt: z14.string().nullable()
1523
- });
1524
-
1525
168
  // ../api-routes/src/auth.ts
1526
169
  import crypto2 from "crypto";
1527
170
  import { eq } from "drizzle-orm";
@@ -2132,11 +775,11 @@ function queueRunIfProjectIdle(db, params) {
2132
775
  async function runRoutes(app, opts) {
2133
776
  app.post("/projects/:name/runs", async (request, reply) => {
2134
777
  const project = resolveProject(app.db, request.params.name);
2135
- const kind = request.body?.kind ?? "answer-visibility";
2136
- if (kind !== "answer-visibility") throw unsupportedKind(kind);
778
+ const body = parseRunTriggerRequest(request.body ?? {});
2137
779
  const now = (/* @__PURE__ */ new Date()).toISOString();
2138
- const trigger = request.body?.trigger ?? "manual";
2139
- const rawProviders = request.body?.providers;
780
+ const kind = body.kind ?? RunKinds["answer-visibility"];
781
+ const trigger = body.trigger ?? RunTriggers.manual;
782
+ const rawProviders = body.providers;
2140
783
  if (rawProviders?.length) {
2141
784
  const normalized = rawProviders.map((p) => p.trim().toLowerCase()).filter(Boolean);
2142
785
  const validNames = opts.validProviderNames ?? [];
@@ -2154,13 +797,13 @@ async function runRoutes(app, opts) {
2154
797
  const providers = rawProviders?.length ? rawProviders : void 0;
2155
798
  let resolvedLocation;
2156
799
  const projectLocations = parseJsonColumn(project.locations, []);
2157
- if (request.body?.noLocation) {
800
+ if (body.noLocation) {
2158
801
  resolvedLocation = null;
2159
- } else if (request.body?.allLocations) {
2160
- } else if (request.body?.location) {
2161
- const loc = projectLocations.find((l) => l.label === request.body.location);
802
+ } else if (body.allLocations) {
803
+ } else if (body.location) {
804
+ const loc = projectLocations.find((l) => l.label === body.location);
2162
805
  if (!loc) {
2163
- throw validationError(`Location "${request.body.location}" not found. Configure it first.`);
806
+ throw validationError(`Location "${body.location}" not found. Configure it first.`);
2164
807
  }
2165
808
  resolvedLocation = loc;
2166
809
  } else if (project.defaultLocation) {
@@ -2170,7 +813,7 @@ async function runRoutes(app, opts) {
2170
813
  }
2171
814
  resolvedLocation = loc;
2172
815
  }
2173
- if (request.body?.allLocations) {
816
+ if (body.allLocations) {
2174
817
  if (projectLocations.length === 0) {
2175
818
  throw validationError("No locations configured for this project");
2176
819
  }
@@ -2339,6 +982,11 @@ async function runRoutes(app, opts) {
2339
982
  return reply.send(loadRunDetail(app, run));
2340
983
  });
2341
984
  }
985
+ function parseRunTriggerRequest(value) {
986
+ const result = runTriggerRequestSchema.safeParse(value);
987
+ if (result.success) return result.data;
988
+ throw validationError("Invalid run trigger request", { issues: result.error.issues });
989
+ }
2342
990
  function formatRun(row) {
2343
991
  return {
2344
992
  id: row.id,
@@ -2550,7 +1198,7 @@ async function deliverWebhook(target, payload, webhookSecret) {
2550
1198
  const body = JSON.stringify(payload);
2551
1199
  const isHttps = target.url.protocol === "https:";
2552
1200
  const port = target.url.port ? Number(target.url.port) : isHttps ? 443 : 80;
2553
- const path16 = `${target.url.pathname}${target.url.search}`;
1201
+ const path15 = `${target.url.pathname}${target.url.search}`;
2554
1202
  const headers = {
2555
1203
  "Content-Length": String(Buffer.byteLength(body)),
2556
1204
  "Content-Type": "application/json",
@@ -2566,7 +1214,7 @@ async function deliverWebhook(target, payload, webhookSecret) {
2566
1214
  headers,
2567
1215
  hostname: target.address,
2568
1216
  method: "POST",
2569
- path: path16,
1217
+ path: path15,
2570
1218
  port,
2571
1219
  timeout: REQUEST_TIMEOUT_MS
2572
1220
  };
@@ -3604,7 +2252,7 @@ var booleanSchema = { type: "boolean" };
3604
2252
  var integerSchema = { type: "integer" };
3605
2253
  var objectSchema = { type: "object", additionalProperties: true };
3606
2254
  var stringArraySchema = { type: "array", items: stringSchema };
3607
- var googleConnectionTypeSchema2 = { type: "string", enum: ["gsc", "ga4"] };
2255
+ var googleConnectionTypeSchema = { type: "string", enum: ["gsc", "ga4"] };
3608
2256
  var locationSchema = {
3609
2257
  type: "object",
3610
2258
  required: ["label", "city", "region", "country"],
@@ -3656,7 +2304,7 @@ var googleTypeParameter = {
3656
2304
  in: "path",
3657
2305
  required: true,
3658
2306
  description: "Google connection type.",
3659
- schema: googleConnectionTypeSchema2
2307
+ schema: googleConnectionTypeSchema
3660
2308
  };
3661
2309
  var projectRunIdParameter = {
3662
2310
  name: "runId",
@@ -4649,7 +3297,7 @@ var routeCatalog = [
4649
3297
  type: "object",
4650
3298
  required: ["type"],
4651
3299
  properties: {
4652
- type: googleConnectionTypeSchema2,
3300
+ type: googleConnectionTypeSchema,
4653
3301
  propertyId: stringSchema,
4654
3302
  publicUrl: stringSchema
4655
3303
  }
@@ -6071,8 +4719,8 @@ async function openApiRoutes(app, opts = {}) {
6071
4719
  return reply.type("application/json").send(buildOpenApiDocument(opts));
6072
4720
  });
6073
4721
  }
6074
- function buildOperationId(method, path16) {
6075
- const parts = path16.split("/").filter(Boolean).map((part) => {
4722
+ function buildOperationId(method, path15) {
4723
+ const parts = path15.split("/").filter(Boolean).map((part) => {
6076
4724
  if (part.startsWith("{") && part.endsWith("}")) {
6077
4725
  return `by-${part.slice(1, -1)}`;
6078
4726
  }
@@ -6594,7 +5242,7 @@ async function exchangeCode(clientId, clientSecret, code, redirectUri) {
6594
5242
  const parsed = JSON.parse(body);
6595
5243
  if (parsed.error) detail = parsed.error;
6596
5244
  if (parsed.error_description) {
6597
- const sanitized = parsed.error_description.replace(new RegExp(escapeRegExp2(clientId), "g"), "***").replace(new RegExp(escapeRegExp2(clientSecret), "g"), "***").replace(new RegExp(escapeRegExp2(code), "g"), "***");
5245
+ const sanitized = parsed.error_description.replace(new RegExp(escapeRegExp(clientId), "g"), "***").replace(new RegExp(escapeRegExp(clientSecret), "g"), "***").replace(new RegExp(escapeRegExp(code), "g"), "***");
6598
5246
  detail += detail ? `: ${sanitized}` : sanitized;
6599
5247
  }
6600
5248
  } catch {
@@ -6604,7 +5252,7 @@ async function exchangeCode(clientId, clientSecret, code, redirectUri) {
6604
5252
  }
6605
5253
  return await res.json();
6606
5254
  }
6607
- function escapeRegExp2(str) {
5255
+ function escapeRegExp(str) {
6608
5256
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6609
5257
  }
6610
5258
  async function refreshAccessToken(clientId, clientSecret, currentRefreshToken) {
@@ -6632,7 +5280,7 @@ async function refreshAccessToken(clientId, clientSecret, currentRefreshToken) {
6632
5280
  const parsed = JSON.parse(body);
6633
5281
  if (parsed.error) detail = parsed.error;
6634
5282
  if (parsed.error_description) {
6635
- const sanitized = parsed.error_description.replace(new RegExp(escapeRegExp2(clientId), "g"), "***").replace(new RegExp(escapeRegExp2(clientSecret), "g"), "***").replace(new RegExp(escapeRegExp2(currentRefreshToken), "g"), "***");
5283
+ const sanitized = parsed.error_description.replace(new RegExp(escapeRegExp(clientId), "g"), "***").replace(new RegExp(escapeRegExp(clientSecret), "g"), "***").replace(new RegExp(escapeRegExp(currentRefreshToken), "g"), "***");
6636
5284
  detail += detail ? `: ${sanitized}` : sanitized;
6637
5285
  }
6638
5286
  } catch {
@@ -6946,13 +5594,13 @@ async function getAccessToken(clientEmail, privateKey) {
6946
5594
  const body = await res.text().catch(() => "");
6947
5595
  ga4Log("error", "token.failed", { httpStatus: res.status });
6948
5596
  const detail = body.length <= 200 ? body : `${body.slice(0, 200)}... [truncated]`;
6949
- const sanitizedDetail = detail.replace(new RegExp(escapeRegExp3(clientEmail), "g"), "***").replace(new RegExp(escapeRegExp3(privateKey.slice(0, 32)), "g"), "***");
5597
+ const sanitizedDetail = detail.replace(new RegExp(escapeRegExp2(clientEmail), "g"), "***").replace(new RegExp(escapeRegExp2(privateKey.slice(0, 32)), "g"), "***");
6950
5598
  throw new GA4ApiError(`Failed to get access token: ${sanitizedDetail}`, res.status);
6951
5599
  }
6952
5600
  const data = await res.json();
6953
5601
  return data.access_token;
6954
5602
  }
6955
- function escapeRegExp3(str) {
5603
+ function escapeRegExp2(str) {
6956
5604
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6957
5605
  }
6958
5606
  async function runReport(accessToken, propertyId, request) {
@@ -8082,7 +6730,7 @@ function bingClientLog(level, action, ctx) {
8082
6730
  const stream = level === "error" ? process.stderr : process.stdout;
8083
6731
  stream.write(JSON.stringify(entry) + "\n");
8084
6732
  }
8085
- function escapeRegExp4(str) {
6733
+ function escapeRegExp3(str) {
8086
6734
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8087
6735
  }
8088
6736
  async function bingFetch(apiKey, endpoint, opts) {
@@ -8110,7 +6758,7 @@ async function bingFetch(apiKey, endpoint, opts) {
8110
6758
  const body = await res.text();
8111
6759
  bingClientLog("error", "http.error", { endpoint, method, httpStatus: res.status });
8112
6760
  let detail = body.length <= 500 ? body : `${body.slice(0, 500)}... [truncated]`;
8113
- detail = detail.replace(new RegExp(escapeRegExp4(apiKey), "g"), "***");
6761
+ detail = detail.replace(new RegExp(escapeRegExp3(apiKey), "g"), "***");
8114
6762
  throw new BingApiError(`Bing API error (${res.status}): ${detail}`, res.status);
8115
6763
  }
8116
6764
  const text = await res.text();
@@ -8654,12 +7302,12 @@ async function bingRoutes(app, opts) {
8654
7302
  }
8655
7303
 
8656
7304
  // ../api-routes/src/cdp.ts
8657
- import fs2 from "fs";
8658
- import path2 from "path";
8659
- import os2 from "os";
7305
+ import fs from "fs";
7306
+ import path from "path";
7307
+ import os from "os";
8660
7308
  import { eq as eq16, and as and5 } from "drizzle-orm";
8661
7309
  function getScreenshotDir() {
8662
- return path2.join(os2.homedir(), ".canonry", "screenshots");
7310
+ return path.join(os.homedir(), ".canonry", "screenshots");
8663
7311
  }
8664
7312
  async function cdpRoutes(app, opts) {
8665
7313
  app.get("/screenshots/:snapshotId", async (request, reply) => {
@@ -8669,17 +7317,17 @@ async function cdpRoutes(app, opts) {
8669
7317
  const err = notFound("Screenshot", snapshotId);
8670
7318
  return reply.code(err.statusCode).send(err.toJSON());
8671
7319
  }
8672
- const base = path2.resolve(getScreenshotDir());
8673
- const fullPath = path2.resolve(path2.join(base, snapshot.screenshotPath));
8674
- if (!fullPath.startsWith(base + path2.sep) && fullPath !== base) {
7320
+ const base = path.resolve(getScreenshotDir());
7321
+ const fullPath = path.resolve(path.join(base, snapshot.screenshotPath));
7322
+ if (!fullPath.startsWith(base + path.sep) && fullPath !== base) {
8675
7323
  const err = notFound("Screenshot", snapshotId);
8676
7324
  return reply.code(err.statusCode).send(err.toJSON());
8677
7325
  }
8678
- if (!fs2.existsSync(fullPath)) {
7326
+ if (!fs.existsSync(fullPath)) {
8679
7327
  const err = notFound("Screenshot file", snapshotId);
8680
7328
  return reply.code(err.statusCode).send(err.toJSON());
8681
7329
  }
8682
- const stream = fs2.createReadStream(fullPath);
7330
+ const stream = fs.createReadStream(fullPath);
8683
7331
  return reply.type("image/png").send(stream);
8684
7332
  });
8685
7333
  app.put("/settings/cdp", async (request, reply) => {
@@ -9684,10 +8332,10 @@ function buildAuthErrorMessage(res, responseText) {
9684
8332
  }
9685
8333
  return "WordPress credentials are invalid or lack permission for this action";
9686
8334
  }
9687
- async function fetchJson(connection, siteUrl, path16, init) {
8335
+ async function fetchJson(connection, siteUrl, path15, init) {
9688
8336
  if (siteUrl.startsWith("http:")) {
9689
8337
  }
9690
- const res = await fetch(`${normalizeSiteUrl(siteUrl)}${path16}`, {
8338
+ const res = await fetch(`${normalizeSiteUrl(siteUrl)}${path15}`, {
9691
8339
  ...init,
9692
8340
  headers: {
9693
8341
  "Authorization": `Basic ${encodeBasicAuth(connection.username, connection.appPassword)}`,
@@ -10202,12 +8850,12 @@ var CANONRY_SCHEMA_START = "<!-- canonry:schema:start -->";
10202
8850
  var CANONRY_SCHEMA_END = "<!-- canonry:schema:end -->";
10203
8851
  function stripCanonrySchema(content) {
10204
8852
  const regex = new RegExp(
10205
- `${escapeRegExp5(CANONRY_SCHEMA_START)}[\\s\\S]*?${escapeRegExp5(CANONRY_SCHEMA_END)}`,
8853
+ `${escapeRegExp4(CANONRY_SCHEMA_START)}[\\s\\S]*?${escapeRegExp4(CANONRY_SCHEMA_END)}`,
10206
8854
  "g"
10207
8855
  );
10208
8856
  return content.replace(regex, "").replace(/\n{3,}/g, "\n\n").trim();
10209
8857
  }
10210
- function escapeRegExp5(str) {
8858
+ function escapeRegExp4(str) {
10211
8859
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10212
8860
  }
10213
8861
  function injectCanonrySchema(content, schemas) {
@@ -10314,7 +8962,7 @@ async function getSchemaStatus(connection, env) {
10314
8962
  const thirdPartySchemas = [];
10315
8963
  if (hasCanonryMarker) {
10316
8964
  const markerRegex = new RegExp(
10317
- `${escapeRegExp5(CANONRY_SCHEMA_START)}([\\s\\S]*?)${escapeRegExp5(CANONRY_SCHEMA_END)}`
8965
+ `${escapeRegExp4(CANONRY_SCHEMA_START)}([\\s\\S]*?)${escapeRegExp4(CANONRY_SCHEMA_END)}`
10318
8966
  );
10319
8967
  const match = markerRegex.exec(rawContent);
10320
8968
  if (match?.[1]) {
@@ -11108,13 +9756,13 @@ import crypto18 from "crypto";
11108
9756
  import { and as and7, asc as asc2, desc as desc8, eq as eq18, sql as sql5 } from "drizzle-orm";
11109
9757
 
11110
9758
  // ../integration-commoncrawl/src/constants.ts
11111
- import os3 from "os";
11112
- import path3 from "path";
9759
+ import os2 from "os";
9760
+ import path2 from "path";
11113
9761
  var CC_BASE_URL = "https://data.commoncrawl.org/projects/hyperlinkgraph";
11114
- var PLUGIN_DIR = path3.join(os3.homedir(), ".canonry", "plugins");
11115
- var PLUGIN_PKG_JSON = path3.join(PLUGIN_DIR, "package.json");
9762
+ var PLUGIN_DIR = path2.join(os2.homedir(), ".canonry", "plugins");
9763
+ var PLUGIN_PKG_JSON = path2.join(PLUGIN_DIR, "package.json");
11116
9764
  var DUCKDB_SPEC = process.env.CANONRY_DUCKDB_SPEC ?? "@duckdb/node-api@1.4.4-r.3";
11117
- var CC_CACHE_DIR = process.env.CANONRY_CC_CACHE_DIR ?? path3.join(os3.homedir(), ".canonry", "cache", "commoncrawl");
9765
+ var CC_CACHE_DIR = process.env.CANONRY_CC_CACHE_DIR ?? path2.join(os2.homedir(), ".canonry", "cache", "commoncrawl");
11118
9766
  var RELEASE_ID_REGEX = /^cc-main-(\d{4})-(jan-feb-mar|apr-may-jun|jul-aug-sep|oct-nov-dec)$/;
11119
9767
  function ccReleasePaths(release) {
11120
9768
  const base = `${CC_BASE_URL}/${release}/domain`;
@@ -11144,8 +9792,8 @@ function isValidReleaseId(id) {
11144
9792
  // ../integration-commoncrawl/src/downloader.ts
11145
9793
  import { createHash } from "crypto";
11146
9794
  import { createWriteStream } from "fs";
11147
- import fs3 from "fs/promises";
11148
- import path4 from "path";
9795
+ import fs2 from "fs/promises";
9796
+ import path3 from "path";
11149
9797
  import { pipeline } from "stream/promises";
11150
9798
  import { Readable, Transform } from "stream";
11151
9799
  async function downloadFile(opts) {
@@ -11153,7 +9801,7 @@ async function downloadFile(opts) {
11153
9801
  const fetchImpl = opts.fetchImpl ?? fetch;
11154
9802
  const sidecarPath = `${opts.destPath}.sha256`;
11155
9803
  try {
11156
- const stat = await fs3.stat(opts.destPath);
9804
+ const stat = await fs2.stat(opts.destPath);
11157
9805
  const sidecar = await readSidecar(sidecarPath);
11158
9806
  const sha2562 = sidecar ?? await hashFile(opts.destPath);
11159
9807
  if (!sidecar) await writeSidecar(sidecarPath, sha2562);
@@ -11161,7 +9809,7 @@ async function downloadFile(opts) {
11161
9809
  } catch {
11162
9810
  }
11163
9811
  const partialPath = `${opts.destPath}.partial`;
11164
- await fs3.mkdir(path4.dirname(opts.destPath), { recursive: true });
9812
+ await fs2.mkdir(path3.dirname(opts.destPath), { recursive: true });
11165
9813
  await unlinkIfExists(partialPath);
11166
9814
  const res = await fetchImpl(opts.url);
11167
9815
  if (!res.ok || !res.body) {
@@ -11184,13 +9832,13 @@ async function downloadFile(opts) {
11184
9832
  createWriteStream(partialPath)
11185
9833
  );
11186
9834
  const sha256 = hasher.digest("hex");
11187
- await fs3.rename(partialPath, opts.destPath);
9835
+ await fs2.rename(partialPath, opts.destPath);
11188
9836
  await writeSidecar(sidecarPath, sha256);
11189
9837
  return { bytes, sha256, cached: false, elapsedMs: Date.now() - start };
11190
9838
  }
11191
9839
  async function hashFile(filePath) {
11192
9840
  const hasher = createHash("sha256");
11193
- const handle = await fs3.open(filePath, "r");
9841
+ const handle = await fs2.open(filePath, "r");
11194
9842
  try {
11195
9843
  const stream = handle.createReadStream();
11196
9844
  for await (const chunk of stream) hasher.update(chunk);
@@ -11201,7 +9849,7 @@ async function hashFile(filePath) {
11201
9849
  }
11202
9850
  async function readSidecar(sidecarPath) {
11203
9851
  try {
11204
- const raw = await fs3.readFile(sidecarPath, "utf8");
9852
+ const raw = await fs2.readFile(sidecarPath, "utf8");
11205
9853
  const trimmed = raw.trim();
11206
9854
  return /^[0-9a-f]{64}$/i.test(trimmed) ? trimmed.toLowerCase() : null;
11207
9855
  } catch {
@@ -11209,12 +9857,12 @@ async function readSidecar(sidecarPath) {
11209
9857
  }
11210
9858
  }
11211
9859
  async function writeSidecar(sidecarPath, sha256) {
11212
- await fs3.writeFile(sidecarPath, `${sha256}
9860
+ await fs2.writeFile(sidecarPath, `${sha256}
11213
9861
  `);
11214
9862
  }
11215
9863
  async function unlinkIfExists(p) {
11216
9864
  try {
11217
- await fs3.unlink(p);
9865
+ await fs2.unlink(p);
11218
9866
  } catch {
11219
9867
  }
11220
9868
  }
@@ -11225,20 +9873,20 @@ function parseContentLength(value) {
11225
9873
  }
11226
9874
 
11227
9875
  // ../integration-commoncrawl/src/plugin-resolver.ts
11228
- import fs4 from "fs";
9876
+ import fs3 from "fs";
11229
9877
  import { createRequire as createRequire2 } from "module";
11230
- import path5 from "path";
9878
+ import path4 from "path";
11231
9879
  function pluginDirFor(pkgJson) {
11232
- return path5.dirname(pkgJson);
9880
+ return path4.dirname(pkgJson);
11233
9881
  }
11234
9882
  function duckdbPkgJsonFor(pluginDir) {
11235
- return path5.join(pluginDir, "node_modules", "@duckdb", "node-api", "package.json");
9883
+ return path4.join(pluginDir, "node_modules", "@duckdb", "node-api", "package.json");
11236
9884
  }
11237
9885
  function loadDuckdb(opts = {}) {
11238
9886
  const pkgJson = opts.pluginPkgJson ?? PLUGIN_PKG_JSON;
11239
9887
  const pluginDir = pluginDirFor(pkgJson);
11240
9888
  const duckdbPkg = duckdbPkgJsonFor(pluginDir);
11241
- if (!fs4.existsSync(duckdbPkg)) {
9889
+ if (!fs3.existsSync(duckdbPkg)) {
11242
9890
  throw missingDependency(
11243
9891
  "@duckdb/node-api is not installed. Run `canonry backlinks install` to enable the backlinks feature.",
11244
9892
  { pluginDir }
@@ -11256,12 +9904,12 @@ function loadDuckdb(opts = {}) {
11256
9904
  }
11257
9905
  function isDuckdbInstalled(opts = {}) {
11258
9906
  const pkgJson = opts.pluginPkgJson ?? PLUGIN_PKG_JSON;
11259
- return fs4.existsSync(duckdbPkgJsonFor(pluginDirFor(pkgJson)));
9907
+ return fs3.existsSync(duckdbPkgJsonFor(pluginDirFor(pkgJson)));
11260
9908
  }
11261
9909
  function readInstalledVersion(opts = {}) {
11262
9910
  const pluginDir = opts.pluginPkgJson ? pluginDirFor(opts.pluginPkgJson) : PLUGIN_DIR;
11263
9911
  try {
11264
- const raw = fs4.readFileSync(duckdbPkgJsonFor(pluginDir), "utf8");
9912
+ const raw = fs3.readFileSync(duckdbPkgJsonFor(pluginDir), "utf8");
11265
9913
  const pkg = JSON.parse(raw);
11266
9914
  return pkg.version ?? null;
11267
9915
  } catch {
@@ -11271,11 +9919,11 @@ function readInstalledVersion(opts = {}) {
11271
9919
 
11272
9920
  // ../integration-commoncrawl/src/plugin-installer.ts
11273
9921
  import { spawn } from "child_process";
11274
- import fs5 from "fs/promises";
11275
- import path6 from "path";
9922
+ import fs4 from "fs/promises";
9923
+ import path5 from "path";
11276
9924
  async function installDuckdb(opts = {}) {
11277
9925
  const pluginDir = opts.pluginDir ?? PLUGIN_DIR;
11278
- const pluginPkgJson = path6.join(pluginDir, "package.json");
9926
+ const pluginPkgJson = path5.join(pluginDir, "package.json");
11279
9927
  const spec = opts.spec ?? DUCKDB_SPEC;
11280
9928
  const pkgManager = opts.packageManager ?? "npm";
11281
9929
  await ensurePluginDir(pluginDir, pluginPkgJson);
@@ -11291,12 +9939,12 @@ async function installDuckdb(opts = {}) {
11291
9939
  return { alreadyPresent: false, version, path: pluginDir };
11292
9940
  }
11293
9941
  async function ensurePluginDir(pluginDir = PLUGIN_DIR, pluginPkgJson = PLUGIN_PKG_JSON) {
11294
- await fs5.mkdir(pluginDir, { recursive: true });
9942
+ await fs4.mkdir(pluginDir, { recursive: true });
11295
9943
  try {
11296
- await fs5.access(pluginPkgJson);
9944
+ await fs4.access(pluginPkgJson);
11297
9945
  } catch {
11298
9946
  const contents = JSON.stringify({ name: "canonry-plugins", private: true, dependencies: {} }, null, 2);
11299
- await fs5.writeFile(pluginPkgJson, `${contents}
9947
+ await fs4.writeFile(pluginPkgJson, `${contents}
11300
9948
  `);
11301
9949
  }
11302
9950
  }
@@ -11389,8 +10037,8 @@ function quote(s) {
11389
10037
  }
11390
10038
 
11391
10039
  // ../integration-commoncrawl/src/cache.ts
11392
- import fs6 from "fs";
11393
- import path7 from "path";
10040
+ import fs5 from "fs";
10041
+ import path6 from "path";
11394
10042
  function cacheRoot(opts = {}) {
11395
10043
  return opts.cacheDir ?? CC_CACHE_DIR;
11396
10044
  }
@@ -11400,18 +10048,18 @@ function directoryBytesAndLastUsed(dir) {
11400
10048
  const walk = (p) => {
11401
10049
  let stat;
11402
10050
  try {
11403
- stat = fs6.statSync(p);
10051
+ stat = fs5.statSync(p);
11404
10052
  } catch {
11405
10053
  return;
11406
10054
  }
11407
10055
  if (stat.isDirectory()) {
11408
10056
  let entries;
11409
10057
  try {
11410
- entries = fs6.readdirSync(p);
10058
+ entries = fs5.readdirSync(p);
11411
10059
  } catch {
11412
10060
  return;
11413
10061
  }
11414
- for (const e of entries) walk(path7.join(p, e));
10062
+ for (const e of entries) walk(path6.join(p, e));
11415
10063
  } else if (stat.isFile()) {
11416
10064
  bytes += stat.size;
11417
10065
  const mtime = Math.max(stat.mtimeMs, stat.atimeMs);
@@ -11426,13 +10074,13 @@ function directoryBytesAndLastUsed(dir) {
11426
10074
  }
11427
10075
  function listCachedReleases(opts = {}) {
11428
10076
  const root = cacheRoot(opts);
11429
- if (!fs6.existsSync(root)) return [];
11430
- const entries = fs6.readdirSync(root, { withFileTypes: true });
10077
+ if (!fs5.existsSync(root)) return [];
10078
+ const entries = fs5.readdirSync(root, { withFileTypes: true });
11431
10079
  const result = [];
11432
10080
  for (const entry of entries) {
11433
10081
  if (!entry.isDirectory()) continue;
11434
10082
  if (!RELEASE_ID_REGEX.test(entry.name)) continue;
11435
- const dir = path7.join(root, entry.name);
10083
+ const dir = path6.join(root, entry.name);
11436
10084
  const stats = directoryBytesAndLastUsed(dir);
11437
10085
  result.push({ release: entry.name, bytes: stats.bytes, lastUsedAt: stats.lastUsedAt });
11438
10086
  }
@@ -11443,8 +10091,8 @@ function pruneCachedRelease(release, opts = {}) {
11443
10091
  if (!RELEASE_ID_REGEX.test(release)) {
11444
10092
  throw new Error(`Invalid release id: ${release}`);
11445
10093
  }
11446
- const dir = path7.join(cacheRoot(opts), release);
11447
- fs6.rmSync(dir, { recursive: true, force: true });
10094
+ const dir = path6.join(cacheRoot(opts), release);
10095
+ fs5.rmSync(dir, { recursive: true, force: true });
11448
10096
  }
11449
10097
 
11450
10098
  // ../api-routes/src/backlinks.ts
@@ -11787,7 +10435,7 @@ async function apiRoutes(app, opts) {
11787
10435
  }
11788
10436
 
11789
10437
  // src/server.ts
11790
- import os6 from "os";
10438
+ import os5 from "os";
11791
10439
 
11792
10440
  // ../provider-gemini/src/normalize.ts
11793
10441
  import { GoogleGenAI } from "@google/genai";
@@ -13175,8 +11823,8 @@ var localAdapter = {
13175
11823
  };
13176
11824
 
13177
11825
  // ../provider-cdp/src/adapter.ts
13178
- import path9 from "path";
13179
- import os4 from "os";
11826
+ import path8 from "path";
11827
+ import os3 from "os";
13180
11828
 
13181
11829
  // ../provider-cdp/src/connection.ts
13182
11830
  import CDP from "chrome-remote-interface";
@@ -13540,12 +12188,12 @@ function sleep2(ms) {
13540
12188
  }
13541
12189
 
13542
12190
  // ../provider-cdp/src/screenshot.ts
13543
- import fs7 from "fs";
13544
- import path8 from "path";
12191
+ import fs6 from "fs";
12192
+ import path7 from "path";
13545
12193
  async function captureElementScreenshot(client, selector, outputPath) {
13546
- const dir = path8.dirname(outputPath);
13547
- if (!fs7.existsSync(dir)) {
13548
- fs7.mkdirSync(dir, { recursive: true });
12194
+ const dir = path7.dirname(outputPath);
12195
+ if (!fs6.existsSync(dir)) {
12196
+ fs6.mkdirSync(dir, { recursive: true });
13549
12197
  }
13550
12198
  let clip;
13551
12199
  try {
@@ -13579,7 +12227,7 @@ async function captureElementScreenshot(client, selector, outputPath) {
13579
12227
  }
13580
12228
  const { data } = await client.Page.captureScreenshot(screenshotParams);
13581
12229
  const buffer = Buffer.from(data, "base64");
13582
- fs7.writeFileSync(outputPath, buffer);
12230
+ fs6.writeFileSync(outputPath, buffer);
13583
12231
  return outputPath;
13584
12232
  }
13585
12233
 
@@ -13640,7 +12288,7 @@ function getConnection(config) {
13640
12288
  return conn;
13641
12289
  }
13642
12290
  function getScreenshotDir2() {
13643
- return path9.join(os4.homedir(), ".canonry", "screenshots");
12291
+ return path8.join(os3.homedir(), ".canonry", "screenshots");
13644
12292
  }
13645
12293
  var cdpChatgptAdapter = {
13646
12294
  name: "cdp:chatgpt",
@@ -13704,7 +12352,7 @@ var cdpChatgptAdapter = {
13704
12352
  const answerText = await target.extractAnswer(client);
13705
12353
  const groundingSources = await target.extractCitations(client);
13706
12354
  const screenshotId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
13707
- const screenshotPath = path9.join(getScreenshotDir2(), `${screenshotId}.png`);
12355
+ const screenshotPath = path8.join(getScreenshotDir2(), `${screenshotId}.png`);
13708
12356
  let capturedScreenshotPath;
13709
12357
  try {
13710
12358
  capturedScreenshotPath = await captureElementScreenshot(
@@ -14241,9 +12889,9 @@ function removeWordpressConnection(config, projectName) {
14241
12889
 
14242
12890
  // src/job-runner.ts
14243
12891
  import crypto19 from "crypto";
14244
- import fs8 from "fs";
14245
- import path10 from "path";
14246
- import os5 from "os";
12892
+ import fs7 from "fs";
12893
+ import path9 from "path";
12894
+ import os4 from "os";
14247
12895
  import { and as and8, eq as eq19, inArray as inArray3, sql as sql6 } from "drizzle-orm";
14248
12896
 
14249
12897
  // src/citation-utils.ts
@@ -14627,12 +13275,12 @@ var JobRunner = class {
14627
13275
  competitorDomains
14628
13276
  );
14629
13277
  let screenshotRelPath = null;
14630
- if (raw.screenshotPath && fs8.existsSync(raw.screenshotPath)) {
13278
+ if (raw.screenshotPath && fs7.existsSync(raw.screenshotPath)) {
14631
13279
  const snapshotId = crypto19.randomUUID();
14632
- const screenshotDir = path10.join(os5.homedir(), ".canonry", "screenshots", runId);
14633
- if (!fs8.existsSync(screenshotDir)) fs8.mkdirSync(screenshotDir, { recursive: true });
14634
- const destPath = path10.join(screenshotDir, `${snapshotId}.png`);
14635
- fs8.renameSync(raw.screenshotPath, destPath);
13280
+ const screenshotDir = path9.join(os4.homedir(), ".canonry", "screenshots", runId);
13281
+ if (!fs7.existsSync(screenshotDir)) fs7.mkdirSync(screenshotDir, { recursive: true });
13282
+ const destPath = path9.join(screenshotDir, `${snapshotId}.png`);
13283
+ fs7.renameSync(raw.screenshotPath, destPath);
14636
13284
  screenshotRelPath = `${runId}/${snapshotId}.png`;
14637
13285
  this.db.insert(querySnapshots).values({
14638
13286
  id: snapshotId,
@@ -14998,6 +13646,7 @@ import crypto21 from "crypto";
14998
13646
  import { eq as eq21, and as and10 } from "drizzle-orm";
14999
13647
 
15000
13648
  // src/sitemap-parser.ts
13649
+ var log3 = createLogger("SitemapParser");
15001
13650
  var LOC_REGEX = /<loc>\s*([^<]+?)\s*<\/loc>/gi;
15002
13651
  var SITEMAP_TAG_REGEX = /<sitemap>[\s\S]*?<\/sitemap>/gi;
15003
13652
  var PRIVATE_IP_PATTERNS = [
@@ -15027,26 +13676,77 @@ function validateSitemapUrl(url) {
15027
13676
  }
15028
13677
  }
15029
13678
  }
13679
+ async function readSitemapBody(res) {
13680
+ const buf = await res.arrayBuffer();
13681
+ const bytes = new Uint8Array(buf);
13682
+ const isGzipped = bytes.length >= 2 && bytes[0] === 31 && bytes[1] === 139;
13683
+ if (!isGzipped) {
13684
+ return new TextDecoder().decode(bytes);
13685
+ }
13686
+ const decompressed = new Blob([buf]).stream().pipeThrough(new DecompressionStream("gzip"));
13687
+ return new Response(decompressed).text();
13688
+ }
15030
13689
  async function fetchAndParseSitemap(sitemapUrl) {
15031
13690
  const urls = /* @__PURE__ */ new Set();
15032
- await parseSitemapRecursive(sitemapUrl, urls, 0);
13691
+ const visited = /* @__PURE__ */ new Set();
13692
+ await parseSitemapRecursive(
13693
+ sitemapUrl,
13694
+ urls,
13695
+ visited,
13696
+ 0,
13697
+ /* isChild */
13698
+ false
13699
+ );
15033
13700
  return [...urls];
15034
13701
  }
15035
- async function parseSitemapRecursive(url, urls, depth) {
13702
+ async function parseSitemapRecursive(url, urls, visited, depth, isChild) {
15036
13703
  if (depth > 3) return;
13704
+ if (visited.has(url)) return;
13705
+ visited.add(url);
15037
13706
  validateSitemapUrl(url);
15038
- const res = await fetch(url);
13707
+ let res;
13708
+ try {
13709
+ res = await fetch(url);
13710
+ } catch (err) {
13711
+ if (!isChild) throw err;
13712
+ log3.warn("child-sitemap.fetch-failed", {
13713
+ url,
13714
+ error: err instanceof Error ? err.message : String(err)
13715
+ });
13716
+ return;
13717
+ }
15039
13718
  if (!res.ok) {
15040
- throw new Error(`Failed to fetch sitemap at ${url}: ${res.status} ${res.statusText}`);
13719
+ if (!isChild) {
13720
+ throw new Error(`Failed to fetch sitemap at ${url}: ${res.status} ${res.statusText}`);
13721
+ }
13722
+ log3.warn("child-sitemap.http-error", { url, status: res.status, statusText: res.statusText });
13723
+ return;
13724
+ }
13725
+ let xml;
13726
+ try {
13727
+ xml = await readSitemapBody(res);
13728
+ } catch (err) {
13729
+ if (!isChild) throw err;
13730
+ log3.warn("child-sitemap.parse-failed", {
13731
+ url,
13732
+ error: err instanceof Error ? err.message : String(err)
13733
+ });
13734
+ return;
15041
13735
  }
15042
- const xml = await res.text();
15043
13736
  const sitemapEntries = xml.match(SITEMAP_TAG_REGEX);
15044
13737
  if (sitemapEntries) {
15045
13738
  for (const entry of sitemapEntries) {
15046
13739
  const locMatch = LOC_REGEX.exec(entry);
15047
13740
  LOC_REGEX.lastIndex = 0;
15048
13741
  if (locMatch?.[1]) {
15049
- await parseSitemapRecursive(locMatch[1], urls, depth + 1);
13742
+ await parseSitemapRecursive(
13743
+ locMatch[1],
13744
+ urls,
13745
+ visited,
13746
+ depth + 1,
13747
+ /* isChild */
13748
+ true
13749
+ );
15050
13750
  }
15051
13751
  }
15052
13752
  return;
@@ -15061,7 +13761,7 @@ async function parseSitemapRecursive(url, urls, depth) {
15061
13761
  }
15062
13762
 
15063
13763
  // src/gsc-inspect-sitemap.ts
15064
- var log3 = createLogger("InspectSitemap");
13764
+ var log4 = createLogger("InspectSitemap");
15065
13765
  async function executeInspectSitemap(db, runId, projectId, opts) {
15066
13766
  const now = (/* @__PURE__ */ new Date()).toISOString();
15067
13767
  db.update(runs).set({ status: "running", startedAt: now }).where(eq21(runs.id, runId)).run();
@@ -15094,9 +13794,9 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
15094
13794
  saveConfigPatch(opts.config);
15095
13795
  }
15096
13796
  const sitemapUrl = opts.sitemapUrl || conn.sitemapUrl || `https://${project.canonicalDomain}/sitemap.xml`;
15097
- log3.info("sitemap.fetch", { runId, projectId, sitemapUrl });
13797
+ log4.info("sitemap.fetch", { runId, projectId, sitemapUrl });
15098
13798
  const urls = await fetchAndParseSitemap(sitemapUrl);
15099
- log3.info("sitemap.parsed", { runId, projectId, urlCount: urls.length, sitemapUrl });
13799
+ log4.info("sitemap.parsed", { runId, projectId, urlCount: urls.length, sitemapUrl });
15100
13800
  if (urls.length === 0) {
15101
13801
  throw new Error("No URLs found in sitemap");
15102
13802
  }
@@ -15129,10 +13829,10 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
15129
13829
  createdAt: inspectedAt
15130
13830
  }).run();
15131
13831
  inspected++;
15132
- log3.info("inspect.url-done", { runId, projectId, url: pageUrl, progress: `${inspected}/${urls.length}` });
13832
+ log4.info("inspect.url-done", { runId, projectId, url: pageUrl, progress: `${inspected}/${urls.length}` });
15133
13833
  } catch (err) {
15134
13834
  errors++;
15135
- log3.error("inspect.url-failed", { runId, projectId, url: pageUrl, error: err instanceof Error ? err.message : String(err) });
13835
+ log4.error("inspect.url-failed", { runId, projectId, url: pageUrl, error: err instanceof Error ? err.message : String(err) });
15136
13836
  }
15137
13837
  if (inspected + errors < urls.length) {
15138
13838
  await new Promise((r) => setTimeout(r, 1e3));
@@ -15172,11 +13872,11 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
15172
13872
  }).run();
15173
13873
  const status = errors > 0 && inspected > 0 ? "partial" : errors === urls.length ? "failed" : "completed";
15174
13874
  db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq21(runs.id, runId)).run();
15175
- log3.info("inspect.completed", { runId, projectId, inspected, errors, total: urls.length, indexed: snapIndexed, notIndexed: snapNotIndexed });
13875
+ log4.info("inspect.completed", { runId, projectId, inspected, errors, total: urls.length, indexed: snapIndexed, notIndexed: snapNotIndexed });
15176
13876
  } catch (err) {
15177
13877
  const errorMsg = err instanceof Error ? err.message : String(err);
15178
13878
  db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq21(runs.id, runId)).run();
15179
- log3.error("inspect.failed", { runId, projectId, error: errorMsg });
13879
+ log4.error("inspect.failed", { runId, projectId, error: errorMsg });
15180
13880
  throw err;
15181
13881
  }
15182
13882
  }
@@ -15184,7 +13884,7 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
15184
13884
  // src/bing-inspect-sitemap.ts
15185
13885
  import crypto22 from "crypto";
15186
13886
  import { eq as eq22, desc as desc9 } from "drizzle-orm";
15187
- var log4 = createLogger("BingInspectSitemap");
13887
+ var log5 = createLogger("BingInspectSitemap");
15188
13888
  function parseBingDate2(value) {
15189
13889
  if (!value) return null;
15190
13890
  const match = /\/Date\((-?\d+)[^)]*\)\//.exec(value);
@@ -15215,16 +13915,16 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
15215
13915
  throw new Error('No Bing site configured. Run "canonry bing set-site <project> <url>" first.');
15216
13916
  }
15217
13917
  const sitemapUrl = opts.sitemapUrl ?? `https://${project.canonicalDomain}/sitemap.xml`;
15218
- log4.info("sitemap.fetch", { runId, projectId, sitemapUrl });
13918
+ log5.info("sitemap.fetch", { runId, projectId, sitemapUrl });
15219
13919
  const sitemapUrls = await fetchAndParseSitemap(sitemapUrl);
15220
- log4.info("sitemap.parsed", { runId, projectId, urlCount: sitemapUrls.length, sitemapUrl });
13920
+ log5.info("sitemap.parsed", { runId, projectId, urlCount: sitemapUrls.length, sitemapUrl });
15221
13921
  if (sitemapUrls.length === 0) {
15222
13922
  throw new Error("No URLs found in sitemap");
15223
13923
  }
15224
13924
  const trackedRows = db.select({ url: bingUrlInspections.url }).from(bingUrlInspections).where(eq22(bingUrlInspections.projectId, projectId)).all();
15225
13925
  const trackedUrls = new Set(trackedRows.map((r) => r.url));
15226
13926
  const discovered = sitemapUrls.filter((u) => !trackedUrls.has(u));
15227
- log4.info("sitemap.diff", {
13927
+ log5.info("sitemap.diff", {
15228
13928
  runId,
15229
13929
  projectId,
15230
13930
  sitemapTotal: sitemapUrls.length,
@@ -15239,9 +13939,9 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
15239
13939
  blockedUrls.add(issue.Url);
15240
13940
  }
15241
13941
  }
15242
- log4.info("crawl-issues.loaded", { runId, projectId, blockedCount: blockedUrls.size });
13942
+ log5.info("crawl-issues.loaded", { runId, projectId, blockedCount: blockedUrls.size });
15243
13943
  } catch (err) {
15244
- log4.warn("crawl-issues.lookup-failed", {
13944
+ log5.warn("crawl-issues.lookup-failed", {
15245
13945
  runId,
15246
13946
  projectId,
15247
13947
  error: err instanceof Error ? err.message : String(err)
@@ -15285,7 +13985,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
15285
13985
  discoveryDate
15286
13986
  }).run();
15287
13987
  inspected++;
15288
- log4.info("inspect.url-done", {
13988
+ log5.info("inspect.url-done", {
15289
13989
  runId,
15290
13990
  projectId,
15291
13991
  url: pageUrl,
@@ -15293,7 +13993,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
15293
13993
  });
15294
13994
  } catch (err) {
15295
13995
  errors++;
15296
- log4.error("inspect.url-failed", {
13996
+ log5.error("inspect.url-failed", {
15297
13997
  runId,
15298
13998
  projectId,
15299
13999
  url: pageUrl,
@@ -15348,7 +14048,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
15348
14048
  }).run();
15349
14049
  const status = errors === sitemapUrls.length ? RunStatuses.failed : errors > 0 ? RunStatuses.partial : RunStatuses.completed;
15350
14050
  db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq22(runs.id, runId)).run();
15351
- log4.info("inspect.completed", {
14051
+ log5.info("inspect.completed", {
15352
14052
  runId,
15353
14053
  projectId,
15354
14054
  inspected,
@@ -15362,16 +14062,16 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
15362
14062
  } catch (err) {
15363
14063
  const errorMsg = err instanceof Error ? err.message : String(err);
15364
14064
  db.update(runs).set({ status: RunStatuses.failed, error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq22(runs.id, runId)).run();
15365
- log4.error("inspect.failed", { runId, projectId, error: errorMsg });
14065
+ log5.error("inspect.failed", { runId, projectId, error: errorMsg });
15366
14066
  throw err;
15367
14067
  }
15368
14068
  }
15369
14069
 
15370
14070
  // src/commoncrawl-sync.ts
15371
14071
  import crypto23 from "crypto";
15372
- import path11 from "path";
14072
+ import path10 from "path";
15373
14073
  import { and as and11, eq as eq23, sql as sql8 } from "drizzle-orm";
15374
- var log5 = createLogger("CommonCrawlSync");
14074
+ var log6 = createLogger("CommonCrawlSync");
15375
14075
  var INSERT_CHUNK_SIZE = 1e4;
15376
14076
  function defaultDeps() {
15377
14077
  return {
@@ -15398,9 +14098,9 @@ async function executeReleaseSync(db, syncId, opts) {
15398
14098
  error: null
15399
14099
  }).where(eq23(ccReleaseSyncs.id, syncId)).run();
15400
14100
  const paths = ccReleasePaths(release);
15401
- const releaseCacheDir = path11.join(deps.cacheDir, release);
15402
- const vertexPath = path11.join(releaseCacheDir, paths.vertexFilename);
15403
- const edgesPath = path11.join(releaseCacheDir, paths.edgesFilename);
14101
+ const releaseCacheDir = path10.join(deps.cacheDir, release);
14102
+ const vertexPath = path10.join(releaseCacheDir, paths.vertexFilename);
14103
+ const edgesPath = path10.join(releaseCacheDir, paths.edgesFilename);
15404
14104
  const [vertex, edges] = await Promise.all([
15405
14105
  deps.downloadFile({ url: paths.vertexUrl, destPath: vertexPath }),
15406
14106
  deps.downloadFile({ url: paths.edgesUrl, destPath: edgesPath })
@@ -15496,7 +14196,7 @@ async function executeReleaseSync(db, syncId, opts) {
15496
14196
  updatedAt: finishedAt,
15497
14197
  error: null
15498
14198
  }).where(eq23(ccReleaseSyncs.id, syncId)).run();
15499
- log5.info("sync.completed", {
14199
+ log6.info("sync.completed", {
15500
14200
  syncId,
15501
14201
  release,
15502
14202
  projectsProcessed: allProjects.length,
@@ -15508,7 +14208,7 @@ async function executeReleaseSync(db, syncId, opts) {
15508
14208
  try {
15509
14209
  deps.enqueueAutoExtract({ projectId: p.id, release });
15510
14210
  } catch (err) {
15511
- log5.error("auto-extract.enqueue-failed", {
14211
+ log6.error("auto-extract.enqueue-failed", {
15512
14212
  syncId,
15513
14213
  release,
15514
14214
  projectId: p.id,
@@ -15526,7 +14226,7 @@ async function executeReleaseSync(db, syncId, opts) {
15526
14226
  phaseDetail: null,
15527
14227
  updatedAt: finishedAt
15528
14228
  }).where(eq23(ccReleaseSyncs.id, syncId)).run();
15529
- log5.error("sync.failed", { syncId, release, error: errorMsg });
14229
+ log6.error("sync.failed", { syncId, release, error: errorMsg });
15530
14230
  throw err;
15531
14231
  }
15532
14232
  }
@@ -15560,9 +14260,9 @@ function computeSummary(rows) {
15560
14260
 
15561
14261
  // src/backlink-extract.ts
15562
14262
  import crypto24 from "crypto";
15563
- import fs9 from "fs";
14263
+ import fs8 from "fs";
15564
14264
  import { and as and12, desc as desc10, eq as eq24 } from "drizzle-orm";
15565
- var log6 = createLogger("BacklinkExtract");
14265
+ var log7 = createLogger("BacklinkExtract");
15566
14266
  function defaultDeps2() {
15567
14267
  return {
15568
14268
  queryBacklinks,
@@ -15589,7 +14289,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
15589
14289
  if (!sync.vertexPath || !sync.edgesPath) {
15590
14290
  throw new Error(`Release ${sync.release} is missing cached file paths`);
15591
14291
  }
15592
- if (!fs9.existsSync(sync.vertexPath) || !fs9.existsSync(sync.edgesPath)) {
14292
+ if (!fs8.existsSync(sync.vertexPath) || !fs8.existsSync(sync.edgesPath)) {
15593
14293
  throw new Error(
15594
14294
  `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.`
15595
14295
  );
@@ -15648,7 +14348,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
15648
14348
  });
15649
14349
  const finishedAt = deps.now().toISOString();
15650
14350
  db.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq24(runs.id, runId)).run();
15651
- log6.info("extract.completed", { runId, projectId, release, rows: rows.length });
14351
+ log7.info("extract.completed", { runId, projectId, release, rows: rows.length });
15652
14352
  } catch (err) {
15653
14353
  const errorMsg = err instanceof Error ? err.message : String(err);
15654
14354
  const finishedAt = deps.now().toISOString();
@@ -15657,7 +14357,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
15657
14357
  error: errorMsg,
15658
14358
  finishedAt
15659
14359
  }).where(eq24(runs.id, runId)).run();
15660
- log6.error("extract.failed", { runId, projectId, error: errorMsg });
14360
+ log7.error("extract.failed", { runId, projectId, error: errorMsg });
15661
14361
  throw err;
15662
14362
  }
15663
14363
  }
@@ -15730,7 +14430,7 @@ var ProviderRegistry = class {
15730
14430
  // src/scheduler.ts
15731
14431
  import cron from "node-cron";
15732
14432
  import { eq as eq25 } from "drizzle-orm";
15733
- var log7 = createLogger("Scheduler");
14433
+ var log8 = createLogger("Scheduler");
15734
14434
  var Scheduler = class {
15735
14435
  db;
15736
14436
  callbacks;
@@ -15746,11 +14446,11 @@ var Scheduler = class {
15746
14446
  const missedRunAt = schedule.nextRunAt;
15747
14447
  this.registerCronTask(schedule);
15748
14448
  if (missedRunAt && new Date(missedRunAt) < /* @__PURE__ */ new Date()) {
15749
- log7.info("run.catch-up", { projectId: schedule.projectId, missedRunAt });
14449
+ log8.info("run.catch-up", { projectId: schedule.projectId, missedRunAt });
15750
14450
  this.triggerRun(schedule.id, schedule.projectId);
15751
14451
  }
15752
14452
  }
15753
- log7.info("started", { scheduleCount: allSchedules.length });
14453
+ log8.info("started", { scheduleCount: allSchedules.length });
15754
14454
  }
15755
14455
  /** Stop all cron tasks for graceful shutdown. */
15756
14456
  stop() {
@@ -15782,12 +14482,12 @@ var Scheduler = class {
15782
14482
  stopTask(projectId, task, verb) {
15783
14483
  task.stop();
15784
14484
  task.destroy();
15785
- log7.info(`task.${verb.toLowerCase()}`, { projectId });
14485
+ log8.info(`task.${verb.toLowerCase()}`, { projectId });
15786
14486
  }
15787
14487
  registerCronTask(schedule) {
15788
14488
  const { id: scheduleId, projectId, cronExpr, timezone } = schedule;
15789
14489
  if (!cron.validate(cronExpr)) {
15790
- log7.error("cron.invalid", { projectId, cronExpr });
14490
+ log8.error("cron.invalid", { projectId, cronExpr });
15791
14491
  return;
15792
14492
  }
15793
14493
  const task = cron.schedule(cronExpr, () => {
@@ -15801,14 +14501,14 @@ var Scheduler = class {
15801
14501
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
15802
14502
  }).where(eq25(schedules.id, scheduleId)).run();
15803
14503
  const label = schedule.preset ?? cronExpr;
15804
- log7.info("cron.registered", { projectId, schedule: label, timezone });
14504
+ log8.info("cron.registered", { projectId, schedule: label, timezone });
15805
14505
  }
15806
14506
  triggerRun(scheduleId, projectId) {
15807
14507
  try {
15808
14508
  const now = (/* @__PURE__ */ new Date()).toISOString();
15809
14509
  const currentSchedule = this.db.select().from(schedules).where(eq25(schedules.id, scheduleId)).get();
15810
14510
  if (!currentSchedule || currentSchedule.enabled !== 1) {
15811
- log7.warn("schedule.stale", { scheduleId, projectId, msg: "schedule no longer exists or is disabled" });
14511
+ log8.warn("schedule.stale", { scheduleId, projectId, msg: "schedule no longer exists or is disabled" });
15812
14512
  this.remove(projectId);
15813
14513
  return;
15814
14514
  }
@@ -15816,7 +14516,7 @@ var Scheduler = class {
15816
14516
  const nextRunAt = task?.getNextRun()?.toISOString() ?? null;
15817
14517
  const project = this.db.select().from(projects).where(eq25(projects.id, projectId)).get();
15818
14518
  if (!project) {
15819
- log7.error("project.not-found", { projectId, msg: "skipping scheduled run" });
14519
+ log8.error("project.not-found", { projectId, msg: "skipping scheduled run" });
15820
14520
  this.remove(projectId);
15821
14521
  return;
15822
14522
  }
@@ -15825,7 +14525,7 @@ var Scheduler = class {
15825
14525
  if (project.defaultLocation) {
15826
14526
  const loc = projectLocations.find((l) => l.label === project.defaultLocation);
15827
14527
  if (!loc) {
15828
- log7.warn("default-location.stale", { scheduleId, projectId, label: project.defaultLocation });
14528
+ log8.warn("default-location.stale", { scheduleId, projectId, label: project.defaultLocation });
15829
14529
  return;
15830
14530
  }
15831
14531
  resolvedLocation = loc;
@@ -15839,7 +14539,7 @@ var Scheduler = class {
15839
14539
  location: locationLabel
15840
14540
  });
15841
14541
  if (queueResult.conflict) {
15842
- log7.info("run.skipped-active", { projectName: project.name, activeRunId: queueResult.activeRunId });
14542
+ log8.info("run.skipped-active", { projectName: project.name, activeRunId: queueResult.activeRunId });
15843
14543
  this.db.update(schedules).set({
15844
14544
  nextRunAt,
15845
14545
  updatedAt: now
@@ -15854,10 +14554,10 @@ var Scheduler = class {
15854
14554
  }).where(eq25(schedules.id, currentSchedule.id)).run();
15855
14555
  const scheduleProviders = parseJsonColumn(currentSchedule.providers, []);
15856
14556
  const providers = scheduleProviders.length > 0 ? scheduleProviders : void 0;
15857
- log7.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
14557
+ log8.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
15858
14558
  this.callbacks.onRunCreated(runId, projectId, providers, resolvedLocation);
15859
14559
  } catch (err) {
15860
- log7.error("trigger.error", { scheduleId, projectId, error: err instanceof Error ? err.message : String(err) });
14560
+ log8.error("trigger.error", { scheduleId, projectId, error: err instanceof Error ? err.message : String(err) });
15861
14561
  }
15862
14562
  }
15863
14563
  };
@@ -15865,7 +14565,7 @@ var Scheduler = class {
15865
14565
  // src/notifier.ts
15866
14566
  import { eq as eq26, desc as desc11, and as and13, or as or2 } from "drizzle-orm";
15867
14567
  import crypto25 from "crypto";
15868
- var log8 = createLogger("Notifier");
14568
+ var log9 = createLogger("Notifier");
15869
14569
  var Notifier = class {
15870
14570
  db;
15871
14571
  serverUrl;
@@ -15875,26 +14575,26 @@ var Notifier = class {
15875
14575
  }
15876
14576
  /** Called after a run completes (success, partial, or failed). */
15877
14577
  async onRunCompleted(runId, projectId) {
15878
- log8.info("run.completed", { runId, projectId });
14578
+ log9.info("run.completed", { runId, projectId });
15879
14579
  const notifs = this.db.select().from(notifications).where(eq26(notifications.projectId, projectId)).all().filter((n) => n.enabled === 1);
15880
14580
  if (notifs.length === 0) {
15881
- log8.info("notifications.none-enabled", { projectId });
14581
+ log9.info("notifications.none-enabled", { projectId });
15882
14582
  return;
15883
14583
  }
15884
- log8.info("notifications.found", { projectId, count: notifs.length });
14584
+ log9.info("notifications.found", { projectId, count: notifs.length });
15885
14585
  const run = this.db.select().from(runs).where(eq26(runs.id, runId)).get();
15886
14586
  if (!run) {
15887
- log8.error("run.not-found", { runId, msg: "skipping notification dispatch" });
14587
+ log9.error("run.not-found", { runId, msg: "skipping notification dispatch" });
15888
14588
  return;
15889
14589
  }
15890
14590
  const project = this.db.select().from(projects).where(eq26(projects.id, projectId)).get();
15891
14591
  if (!project) {
15892
- log8.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
14592
+ log9.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
15893
14593
  return;
15894
14594
  }
15895
14595
  const transitions = this.computeTransitions(runId, projectId);
15896
14596
  const events = [];
15897
- log8.info("run.status", { runId: run.id, status: run.status, projectId });
14597
+ log9.info("run.status", { runId: run.id, status: run.status, projectId });
15898
14598
  if (run.status === "completed" || run.status === "partial") {
15899
14599
  events.push("run.completed");
15900
14600
  }
@@ -15910,7 +14610,7 @@ var Notifier = class {
15910
14610
  if (!config.url) continue;
15911
14611
  const subscribedEvents = config.events;
15912
14612
  const matchingEvents = events.filter((e) => subscribedEvents.includes(e));
15913
- log8.info("notification.match", { notificationId: notif.id, subscribedEvents, matchedEvents: matchingEvents });
14613
+ log9.info("notification.match", { notificationId: notif.id, subscribedEvents, matchedEvents: matchingEvents });
15914
14614
  if (matchingEvents.length === 0) continue;
15915
14615
  for (const event of matchingEvents) {
15916
14616
  const relevantTransitions = event === "citation.lost" ? lostTransitions : event === "citation.gained" ? gainedTransitions : transitions;
@@ -16012,23 +14712,23 @@ var Notifier = class {
16012
14712
  const targetLabel = redactNotificationUrl(url).urlDisplay;
16013
14713
  const targetCheck = await resolveWebhookTarget(url);
16014
14714
  if (!targetCheck.ok) {
16015
- log8.error("webhook.ssrf-blocked", { url: targetLabel, reason: targetCheck.message });
14715
+ log9.error("webhook.ssrf-blocked", { url: targetLabel, reason: targetCheck.message });
16016
14716
  this.logDelivery(projectId, notificationId, payload.event, "failed", `SSRF: ${targetCheck.message}`);
16017
14717
  return;
16018
14718
  }
16019
- log8.info("webhook.send", { event: payload.event, url: targetLabel });
14719
+ log9.info("webhook.send", { event: payload.event, url: targetLabel });
16020
14720
  const maxRetries = 3;
16021
14721
  const delays = [1e3, 4e3, 16e3];
16022
14722
  for (let attempt = 0; attempt < maxRetries; attempt++) {
16023
14723
  try {
16024
14724
  const response = await deliverWebhook(targetCheck.target, payload, webhookSecret);
16025
14725
  if (response.status >= 200 && response.status < 300) {
16026
- log8.info("webhook.delivered", { event: payload.event, url: targetLabel, httpStatus: response.status });
14726
+ log9.info("webhook.delivered", { event: payload.event, url: targetLabel, httpStatus: response.status });
16027
14727
  this.logDelivery(projectId, notificationId, payload.event, "sent", null);
16028
14728
  return;
16029
14729
  }
16030
14730
  const errorDetail = response.error ?? `HTTP ${response.status}`;
16031
- log8.warn("webhook.attempt-failed", { event: payload.event, url: targetLabel, attempt: attempt + 1, maxRetries, httpStatus: response.status, error: errorDetail });
14731
+ log9.warn("webhook.attempt-failed", { event: payload.event, url: targetLabel, attempt: attempt + 1, maxRetries, httpStatus: response.status, error: errorDetail });
16032
14732
  if (attempt === maxRetries - 1) {
16033
14733
  this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
16034
14734
  }
@@ -16036,7 +14736,7 @@ var Notifier = class {
16036
14736
  const errorDetail = err instanceof Error ? err.message : String(err);
16037
14737
  if (attempt === maxRetries - 1) {
16038
14738
  this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
16039
- log8.error("webhook.exhausted", { event: payload.event, url: targetLabel, maxRetries, error: errorDetail });
14739
+ log9.error("webhook.exhausted", { event: payload.event, url: targetLabel, maxRetries, error: errorDetail });
16040
14740
  }
16041
14741
  }
16042
14742
  if (attempt < maxRetries - 1) {
@@ -16059,7 +14759,7 @@ var Notifier = class {
16059
14759
  };
16060
14760
 
16061
14761
  // src/run-coordinator.ts
16062
- var log9 = createLogger("RunCoordinator");
14762
+ var log10 = createLogger("RunCoordinator");
16063
14763
  var RunCoordinator = class {
16064
14764
  constructor(notifier, intelligenceService, onInsightsGenerated, onAeroEvent) {
16065
14765
  this.notifier = notifier;
@@ -16081,23 +14781,23 @@ var RunCoordinator = class {
16081
14781
  try {
16082
14782
  await this.onInsightsGenerated(runId, projectId, result);
16083
14783
  } catch (err) {
16084
- log9.error("insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
14784
+ log10.error("insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
16085
14785
  }
16086
14786
  }
16087
14787
  }
16088
14788
  } catch (err) {
16089
- log9.error("intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
14789
+ log10.error("intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
16090
14790
  }
16091
14791
  try {
16092
14792
  await this.notifier.onRunCompleted(runId, projectId);
16093
14793
  } catch (err) {
16094
- log9.error("notifier.failed", { runId, error: err instanceof Error ? err.message : String(err) });
14794
+ log10.error("notifier.failed", { runId, error: err instanceof Error ? err.message : String(err) });
16095
14795
  }
16096
14796
  if (this.onAeroEvent) {
16097
14797
  try {
16098
14798
  await this.onAeroEvent({ runId, projectId, insightCount, criticalOrHigh });
16099
14799
  } catch (err) {
16100
- log9.error("aero.failed", { runId, error: err instanceof Error ? err.message : String(err) });
14800
+ log10.error("aero.failed", { runId, error: err instanceof Error ? err.message : String(err) });
16101
14801
  }
16102
14802
  }
16103
14803
  }
@@ -16108,8 +14808,8 @@ import crypto27 from "crypto";
16108
14808
  import { eq as eq28 } from "drizzle-orm";
16109
14809
 
16110
14810
  // src/agent/session.ts
16111
- import fs12 from "fs";
16112
- import path14 from "path";
14811
+ import fs11 from "fs";
14812
+ import path13 from "path";
16113
14813
  import { Agent } from "@mariozechner/pi-agent-core";
16114
14814
  import { registerBuiltInApiProviders } from "@mariozechner/pi-ai";
16115
14815
 
@@ -16210,26 +14910,26 @@ function buildAgentProvidersResponse(config) {
16210
14910
  }
16211
14911
 
16212
14912
  // src/agent/skill-paths.ts
16213
- import fs10 from "fs";
16214
- import path12 from "path";
14913
+ import fs9 from "fs";
14914
+ import path11 from "path";
16215
14915
  import { fileURLToPath } from "url";
16216
14916
  function resolveAeroSkillDir(pkgDir) {
16217
- const here = pkgDir ?? path12.dirname(fileURLToPath(import.meta.url));
14917
+ const here = pkgDir ?? path11.dirname(fileURLToPath(import.meta.url));
16218
14918
  const candidates = [
16219
- path12.join(here, "../assets/agent-workspace/skills/aero"),
16220
- path12.join(here, "../../assets/agent-workspace/skills/aero"),
16221
- path12.join(here, "../../../../skills/aero")
14919
+ path11.join(here, "../assets/agent-workspace/skills/aero"),
14920
+ path11.join(here, "../../assets/agent-workspace/skills/aero"),
14921
+ path11.join(here, "../../../../skills/aero")
16222
14922
  ];
16223
14923
  for (const candidate of candidates) {
16224
- if (fs10.existsSync(path12.join(candidate, "SKILL.md"))) return candidate;
14924
+ if (fs9.existsSync(path11.join(candidate, "SKILL.md"))) return candidate;
16225
14925
  }
16226
14926
  throw new Error(`Aero skill not found. Searched:
16227
14927
  ${candidates.join("\n ")}`);
16228
14928
  }
16229
14929
 
16230
14930
  // src/agent/skill-tools.ts
16231
- import fs11 from "fs";
16232
- import path13 from "path";
14931
+ import fs10 from "fs";
14932
+ import path12 from "path";
16233
14933
  import { Type } from "@sinclair/typebox";
16234
14934
  var MAX_DOC_CHARS = 2e4;
16235
14935
  function textResult(details) {
@@ -16250,13 +14950,13 @@ function parseDescription(body) {
16250
14950
  return "(no description)";
16251
14951
  }
16252
14952
  function scanSkillDocs(skillDir) {
16253
- const refsDir = path13.join(skillDir ?? resolveAeroSkillDir(), "references");
16254
- if (!fs11.existsSync(refsDir)) return [];
14953
+ const refsDir = path12.join(skillDir ?? resolveAeroSkillDir(), "references");
14954
+ if (!fs10.existsSync(refsDir)) return [];
16255
14955
  const entries = [];
16256
- for (const file of fs11.readdirSync(refsDir)) {
14956
+ for (const file of fs10.readdirSync(refsDir)) {
16257
14957
  if (!file.endsWith(".md")) continue;
16258
- const filePath = path13.join(refsDir, file);
16259
- const body = fs11.readFileSync(filePath, "utf-8");
14958
+ const filePath = path12.join(refsDir, file);
14959
+ const body = fs10.readFileSync(filePath, "utf-8");
16260
14960
  entries.push({
16261
14961
  slug: file.replace(/\.md$/, ""),
16262
14962
  description: parseDescription(body),
@@ -16299,8 +14999,8 @@ function buildReadSkillDocTool() {
16299
14999
  availableSlugs: docs.map((d) => d.slug)
16300
15000
  });
16301
15001
  }
16302
- const filePath = path13.join(skillDir, "references", `${match.slug}.md`);
16303
- const content = fs11.readFileSync(filePath, "utf-8");
15002
+ const filePath = path12.join(skillDir, "references", `${match.slug}.md`);
15003
+ const content = fs10.readFileSync(filePath, "utf-8");
16304
15004
  if (content.length > MAX_DOC_CHARS) {
16305
15005
  return textResult({
16306
15006
  slug: match.slug,
@@ -16854,10 +15554,10 @@ function ensureBuiltinsRegistered() {
16854
15554
  }
16855
15555
  function loadAeroSystemPrompt(pkgDir) {
16856
15556
  const skillDir = resolveAeroSkillDir(pkgDir);
16857
- const skillBody = fs12.readFileSync(path14.join(skillDir, "SKILL.md"), "utf-8");
16858
- const soulPath = path14.join(skillDir, "soul.md");
16859
- if (!fs12.existsSync(soulPath)) return skillBody;
16860
- const soulBody = fs12.readFileSync(soulPath, "utf-8");
15557
+ const skillBody = fs11.readFileSync(path13.join(skillDir, "SKILL.md"), "utf-8");
15558
+ const soulPath = path13.join(skillDir, "soul.md");
15559
+ if (!fs11.existsSync(soulPath)) return skillBody;
15560
+ const soulBody = fs11.readFileSync(soulPath, "utf-8");
16861
15561
  return `${soulBody.trimEnd()}
16862
15562
 
16863
15563
  ---
@@ -17041,7 +15741,7 @@ async function compactMessages(args) {
17041
15741
  }
17042
15742
 
17043
15743
  // src/agent/session-registry.ts
17044
- var log10 = createLogger("SessionRegistry");
15744
+ var log11 = createLogger("SessionRegistry");
17045
15745
  var MAX_HYDRATE_NOTES = 20;
17046
15746
  var MAX_HYDRATE_BYTES = 32 * 1024;
17047
15747
  function escapeMemoryFragment(value) {
@@ -17272,13 +15972,13 @@ ${lines.join("\n")}
17272
15972
  agent.state.messages = result.messages;
17273
15973
  agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, row.systemPrompt);
17274
15974
  this.save(projectName);
17275
- log10.info("compaction.completed", {
15975
+ log11.info("compaction.completed", {
17276
15976
  projectName,
17277
15977
  removedCount: result.removedCount,
17278
15978
  summaryBytes: Buffer.byteLength(result.summary, "utf8")
17279
15979
  });
17280
15980
  } catch (err) {
17281
- log10.error("compaction.failed", {
15981
+ log11.error("compaction.failed", {
17282
15982
  projectName,
17283
15983
  error: err instanceof Error ? err.message : String(err)
17284
15984
  });
@@ -17375,7 +16075,7 @@ ${lines.join("\n")}
17375
16075
  await agent.prompt(msgs);
17376
16076
  this.save(projectName);
17377
16077
  } catch (err) {
17378
- log10.error("drain.failed", {
16078
+ log11.error("drain.failed", {
17379
16079
  projectName,
17380
16080
  error: err instanceof Error ? err.message : String(err)
17381
16081
  });
@@ -17646,593 +16346,6 @@ function registerAgentRoutes(app, opts) {
17646
16346
  );
17647
16347
  }
17648
16348
 
17649
- // src/client.ts
17650
- function createApiClient() {
17651
- const config = loadConfig();
17652
- const basePathResolved = !!config.basePath || "CANONRY_BASE_PATH" in process.env;
17653
- return new ApiClient(config.apiUrl, config.apiKey, { skipProbe: basePathResolved });
17654
- }
17655
- var ApiClient = class {
17656
- baseUrl;
17657
- originUrl;
17658
- apiKey;
17659
- probePromise = null;
17660
- probeSkipped;
17661
- constructor(baseUrl, apiKey, opts) {
17662
- this.originUrl = baseUrl.replace(/\/$/, "");
17663
- this.baseUrl = this.originUrl + "/api/v1";
17664
- this.apiKey = apiKey;
17665
- this.probeSkipped = opts?.skipProbe ?? false;
17666
- }
17667
- /**
17668
- * On first API call, probe /health to auto-discover basePath when the user
17669
- * hasn't configured one locally. This lets `canonry run` in a separate shell
17670
- * discover that the server is running at e.g. /canonry/ without requiring
17671
- * config.yaml edits or CANONRY_BASE_PATH in every shell.
17672
- */
17673
- probeBasePath() {
17674
- if (this.probeSkipped) return Promise.resolve();
17675
- if (!this.probePromise) {
17676
- this.probePromise = (async () => {
17677
- try {
17678
- const origin = new URL(this.originUrl).origin;
17679
- const res = await fetch(`${origin}/health`, {
17680
- signal: AbortSignal.timeout(2e3)
17681
- });
17682
- if (res.ok) {
17683
- const body = await res.json();
17684
- if (body.basePath && typeof body.basePath === "string") {
17685
- const normalized = "/" + body.basePath.replace(/^\/|\/$/g, "");
17686
- if (normalized !== "/") {
17687
- this.originUrl = origin + normalized;
17688
- this.baseUrl = this.originUrl + "/api/v1";
17689
- }
17690
- }
17691
- }
17692
- } catch {
17693
- }
17694
- })();
17695
- }
17696
- return this.probePromise;
17697
- }
17698
- async request(method, path16, body) {
17699
- await this.probeBasePath();
17700
- const url = `${this.baseUrl}${path16}`;
17701
- const serializedBody = body != null ? JSON.stringify(body) : void 0;
17702
- const headers = {
17703
- "Authorization": `Bearer ${this.apiKey}`,
17704
- "Accept": "application/json",
17705
- ...serializedBody != null ? { "Content-Type": "application/json" } : {}
17706
- };
17707
- let res;
17708
- try {
17709
- res = await fetch(url, {
17710
- method,
17711
- headers,
17712
- body: serializedBody
17713
- });
17714
- } catch (err) {
17715
- if (err instanceof CliError) throw err;
17716
- const msg = err instanceof Error ? err.message : String(err);
17717
- if (msg.includes("fetch failed") || msg.includes("ECONNREFUSED") || msg.includes("connect ECONNREFUSED")) {
17718
- throw new CliError({
17719
- code: "CONNECTION_ERROR",
17720
- message: `Could not connect to canonry server at ${this.baseUrl.replace("/api/v1", "")}. Start it with "canonry serve" (or "canonry serve &" to run in background).`,
17721
- exitCode: EXIT_SYSTEM_ERROR
17722
- });
17723
- }
17724
- throw new CliError({ code: "CONNECTION_ERROR", message: msg, exitCode: EXIT_SYSTEM_ERROR });
17725
- }
17726
- if (!res.ok) {
17727
- let errorBody;
17728
- try {
17729
- errorBody = await res.json();
17730
- } catch {
17731
- errorBody = { error: { code: "UNKNOWN", message: res.statusText } };
17732
- }
17733
- const errorObj = errorBody && typeof errorBody === "object" && "error" in errorBody && errorBody.error && typeof errorBody.error === "object" ? errorBody.error : null;
17734
- const msg = errorObj?.message ? String(errorObj.message) : `HTTP ${res.status}: ${res.statusText}`;
17735
- const code = errorObj?.code ? String(errorObj.code) : "API_ERROR";
17736
- const exitCode = res.status >= 500 ? EXIT_SYSTEM_ERROR : EXIT_USER_ERROR;
17737
- throw new CliError({ code, message: msg, exitCode, details: { httpStatus: res.status } });
17738
- }
17739
- if (res.status === 204) {
17740
- return void 0;
17741
- }
17742
- return await res.json();
17743
- }
17744
- async getAgentTranscript(project) {
17745
- return this.request(
17746
- "GET",
17747
- `/projects/${encodeURIComponent(project)}/agent/transcript`
17748
- );
17749
- }
17750
- async resetAgentTranscript(project) {
17751
- await this.request(
17752
- "DELETE",
17753
- `/projects/${encodeURIComponent(project)}/agent/transcript`
17754
- );
17755
- }
17756
- async listAgentProviders(project) {
17757
- return this.request(
17758
- "GET",
17759
- `/projects/${encodeURIComponent(project)}/agent/providers`
17760
- );
17761
- }
17762
- async listAgentMemory(project) {
17763
- return this.request(
17764
- "GET",
17765
- `/projects/${encodeURIComponent(project)}/agent/memory`
17766
- );
17767
- }
17768
- async setAgentMemory(project, body) {
17769
- return this.request(
17770
- "PUT",
17771
- `/projects/${encodeURIComponent(project)}/agent/memory`,
17772
- body
17773
- );
17774
- }
17775
- async forgetAgentMemory(project, key) {
17776
- return this.request(
17777
- "DELETE",
17778
- `/projects/${encodeURIComponent(project)}/agent/memory`,
17779
- { key }
17780
- );
17781
- }
17782
- /**
17783
- * POST a request whose response body the caller intends to consume as a
17784
- * stream (e.g. the Aero agent SSE endpoint). Shares the probe + auth +
17785
- * structured-error behavior of `request()`; the caller reads `res.body`
17786
- * and releases the response when done.
17787
- */
17788
- async streamPost(path16, body, signal) {
17789
- await this.probeBasePath();
17790
- const url = `${this.baseUrl}${path16}`;
17791
- const headers = {
17792
- Authorization: `Bearer ${this.apiKey}`,
17793
- "Content-Type": "application/json",
17794
- Accept: "text/event-stream"
17795
- };
17796
- let res;
17797
- try {
17798
- res = await fetch(url, {
17799
- method: "POST",
17800
- headers,
17801
- body: JSON.stringify(body ?? {}),
17802
- signal
17803
- });
17804
- } catch (err) {
17805
- if (err instanceof CliError) throw err;
17806
- const msg = err instanceof Error ? err.message : String(err);
17807
- if (msg.includes("fetch failed") || msg.includes("ECONNREFUSED") || msg.includes("connect ECONNREFUSED")) {
17808
- throw new CliError({
17809
- code: "CONNECTION_ERROR",
17810
- message: `Could not connect to canonry server at ${this.baseUrl.replace("/api/v1", "")}. Start it with "canonry serve" (or "canonry serve &" to run in background).`,
17811
- exitCode: EXIT_SYSTEM_ERROR
17812
- });
17813
- }
17814
- throw new CliError({ code: "CONNECTION_ERROR", message: msg, exitCode: EXIT_SYSTEM_ERROR });
17815
- }
17816
- if (!res.ok || !res.body) {
17817
- let errorBody;
17818
- try {
17819
- errorBody = await res.json();
17820
- } catch {
17821
- errorBody = { error: { code: "UNKNOWN", message: res.statusText } };
17822
- }
17823
- const errorObj = errorBody && typeof errorBody === "object" && "error" in errorBody && errorBody.error ? errorBody.error : null;
17824
- const msg = errorObj?.message ? String(errorObj.message) : `HTTP ${res.status}: ${res.statusText}`;
17825
- const code = errorObj?.code ? String(errorObj.code) : "API_ERROR";
17826
- const exitCode = res.status >= 500 ? EXIT_SYSTEM_ERROR : EXIT_USER_ERROR;
17827
- throw new CliError({ code, message: msg, exitCode, details: { httpStatus: res.status } });
17828
- }
17829
- return res;
17830
- }
17831
- async putProject(name, body) {
17832
- return this.request("PUT", `/projects/${encodeURIComponent(name)}`, body);
17833
- }
17834
- async listProjects() {
17835
- return this.request("GET", "/projects");
17836
- }
17837
- async getProject(name) {
17838
- return this.request("GET", `/projects/${encodeURIComponent(name)}`);
17839
- }
17840
- async deleteProject(name) {
17841
- await this.request("DELETE", `/projects/${encodeURIComponent(name)}`);
17842
- }
17843
- async putKeywords(project, keywords2) {
17844
- await this.request("PUT", `/projects/${encodeURIComponent(project)}/keywords`, { keywords: keywords2 });
17845
- }
17846
- async listKeywords(project) {
17847
- return this.request("GET", `/projects/${encodeURIComponent(project)}/keywords`);
17848
- }
17849
- async deleteKeywords(project, keywords2) {
17850
- await this.request("DELETE", `/projects/${encodeURIComponent(project)}/keywords`, { keywords: keywords2 });
17851
- }
17852
- async appendKeywords(project, keywords2) {
17853
- await this.request("POST", `/projects/${encodeURIComponent(project)}/keywords`, { keywords: keywords2 });
17854
- }
17855
- async putCompetitors(project, competitors2) {
17856
- await this.request("PUT", `/projects/${encodeURIComponent(project)}/competitors`, { competitors: competitors2 });
17857
- }
17858
- async listCompetitors(project) {
17859
- return this.request("GET", `/projects/${encodeURIComponent(project)}/competitors`);
17860
- }
17861
- async triggerRun(project, body) {
17862
- return this.request("POST", `/projects/${encodeURIComponent(project)}/runs`, body ?? {});
17863
- }
17864
- async listRuns(project, limit) {
17865
- const query = limit != null ? `?limit=${encodeURIComponent(String(limit))}` : "";
17866
- return this.request("GET", `/projects/${encodeURIComponent(project)}/runs${query}`);
17867
- }
17868
- async getLatestRun(project) {
17869
- return this.request("GET", `/projects/${encodeURIComponent(project)}/runs/latest`);
17870
- }
17871
- async getRun(id) {
17872
- return this.request("GET", `/runs/${encodeURIComponent(id)}`);
17873
- }
17874
- async cancelRun(id) {
17875
- return this.request("POST", `/runs/${encodeURIComponent(id)}/cancel`);
17876
- }
17877
- async getTimeline(project) {
17878
- return this.request("GET", `/projects/${encodeURIComponent(project)}/timeline`);
17879
- }
17880
- async getHistory(project) {
17881
- return this.request("GET", `/projects/${encodeURIComponent(project)}/history`);
17882
- }
17883
- async getExport(project) {
17884
- return this.request("GET", `/projects/${encodeURIComponent(project)}/export`);
17885
- }
17886
- async apply(config) {
17887
- return this.request("POST", "/apply", config);
17888
- }
17889
- async getStatus(project) {
17890
- return this.request("GET", `/projects/${encodeURIComponent(project)}`);
17891
- }
17892
- async getSettings() {
17893
- return this.request("GET", "/settings");
17894
- }
17895
- async createSnapshot(body) {
17896
- return this.request("POST", "/snapshot", body);
17897
- }
17898
- async updateProvider(name, body) {
17899
- return this.request("PUT", `/settings/providers/${encodeURIComponent(name)}`, body);
17900
- }
17901
- async putSchedule(project, body) {
17902
- return this.request("PUT", `/projects/${encodeURIComponent(project)}/schedule`, body);
17903
- }
17904
- async getSchedule(project) {
17905
- return this.request("GET", `/projects/${encodeURIComponent(project)}/schedule`);
17906
- }
17907
- async deleteSchedule(project) {
17908
- await this.request("DELETE", `/projects/${encodeURIComponent(project)}/schedule`);
17909
- }
17910
- async createNotification(project, body) {
17911
- return this.request("POST", `/projects/${encodeURIComponent(project)}/notifications`, body);
17912
- }
17913
- async listNotifications(project) {
17914
- return this.request("GET", `/projects/${encodeURIComponent(project)}/notifications`);
17915
- }
17916
- async deleteNotification(project, id) {
17917
- await this.request("DELETE", `/projects/${encodeURIComponent(project)}/notifications/${encodeURIComponent(id)}`);
17918
- }
17919
- async testNotification(project, id) {
17920
- return this.request("POST", `/projects/${encodeURIComponent(project)}/notifications/${encodeURIComponent(id)}/test`);
17921
- }
17922
- async addLocation(project, body) {
17923
- return this.request("POST", `/projects/${encodeURIComponent(project)}/locations`, body);
17924
- }
17925
- async listLocations(project) {
17926
- return this.request("GET", `/projects/${encodeURIComponent(project)}/locations`);
17927
- }
17928
- async removeLocation(project, label) {
17929
- await this.request("DELETE", `/projects/${encodeURIComponent(project)}/locations/${encodeURIComponent(label)}`);
17930
- }
17931
- async setDefaultLocation(project, label) {
17932
- return this.request("PUT", `/projects/${encodeURIComponent(project)}/locations/default`, { label });
17933
- }
17934
- async getTelemetry() {
17935
- return this.request("GET", "/telemetry");
17936
- }
17937
- async updateTelemetry(enabled) {
17938
- return this.request("PUT", "/telemetry", { enabled });
17939
- }
17940
- async generateKeywords(project, provider, count) {
17941
- return this.request(
17942
- "POST",
17943
- `/projects/${encodeURIComponent(project)}/keywords/generate`,
17944
- { provider, count }
17945
- );
17946
- }
17947
- // Google connection management
17948
- async googleConnect(project, body) {
17949
- return this.request("POST", `/projects/${encodeURIComponent(project)}/google/connect`, body);
17950
- }
17951
- async googleConnections(project) {
17952
- return this.request("GET", `/projects/${encodeURIComponent(project)}/google/connections`);
17953
- }
17954
- async googleDisconnect(project, type) {
17955
- await this.request("DELETE", `/projects/${encodeURIComponent(project)}/google/connections/${encodeURIComponent(type)}`);
17956
- }
17957
- async googleProperties(project) {
17958
- return this.request("GET", `/projects/${encodeURIComponent(project)}/google/properties`);
17959
- }
17960
- async googleSetProperty(project, type, propertyId) {
17961
- return this.request("PUT", `/projects/${encodeURIComponent(project)}/google/connections/${encodeURIComponent(type)}/property`, { propertyId });
17962
- }
17963
- async googleSetSitemap(project, type, sitemapUrl) {
17964
- return this.request("PUT", `/projects/${encodeURIComponent(project)}/google/connections/${encodeURIComponent(type)}/sitemap`, { sitemapUrl });
17965
- }
17966
- // GSC data
17967
- async gscSync(project, body) {
17968
- return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/sync`, body ?? {});
17969
- }
17970
- async gscPerformance(project, params) {
17971
- const qs = params ? "?" + new URLSearchParams(params).toString() : "";
17972
- return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/performance${qs}`);
17973
- }
17974
- async gscInspect(project, url) {
17975
- return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/inspect`, { url });
17976
- }
17977
- async gscInspections(project, params) {
17978
- const qs = params ? "?" + new URLSearchParams(params).toString() : "";
17979
- return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/inspections${qs}`);
17980
- }
17981
- async gscDeindexed(project) {
17982
- return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/deindexed`);
17983
- }
17984
- async gscCoverage(project) {
17985
- return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/coverage`);
17986
- }
17987
- async gscCoverageHistory(project, params) {
17988
- const qs = params?.limit != null ? `?limit=${params.limit}` : "";
17989
- return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/coverage/history${qs}`);
17990
- }
17991
- async gscInspectSitemap(project, body) {
17992
- return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/inspect-sitemap`, body ?? {});
17993
- }
17994
- async gscSitemaps(project) {
17995
- return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/sitemaps`);
17996
- }
17997
- async gscDiscoverSitemaps(project) {
17998
- return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/discover-sitemaps`, {});
17999
- }
18000
- // Analytics
18001
- async getAnalyticsMetrics(project, window) {
18002
- const qs = window ? `?window=${encodeURIComponent(window)}` : "";
18003
- return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/metrics${qs}`);
18004
- }
18005
- async getAnalyticsGaps(project, window) {
18006
- const qs = window ? `?window=${encodeURIComponent(window)}` : "";
18007
- return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/gaps${qs}`);
18008
- }
18009
- async getAnalyticsSources(project, window) {
18010
- const qs = window ? `?window=${encodeURIComponent(window)}` : "";
18011
- return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/sources${qs}`);
18012
- }
18013
- // Google Indexing API
18014
- async googleRequestIndexing(project, body) {
18015
- return this.request("POST", `/projects/${encodeURIComponent(project)}/google/indexing/request`, body);
18016
- }
18017
- // Bing Webmaster Tools
18018
- async bingConnect(project, body) {
18019
- return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/connect`, body);
18020
- }
18021
- async bingDisconnect(project) {
18022
- await this.request("DELETE", `/projects/${encodeURIComponent(project)}/bing/disconnect`);
18023
- }
18024
- async bingStatus(project) {
18025
- return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/status`);
18026
- }
18027
- async bingSites(project) {
18028
- return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/sites`);
18029
- }
18030
- async bingSetSite(project, siteUrl) {
18031
- return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/set-site`, { siteUrl });
18032
- }
18033
- async bingCoverage(project) {
18034
- return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/coverage`);
18035
- }
18036
- async bingCoverageHistory(project, params) {
18037
- const qs = params?.limit != null ? `?limit=${params.limit}` : "";
18038
- return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/coverage/history${qs}`);
18039
- }
18040
- async bingInspections(project, params) {
18041
- const qs = params ? "?" + new URLSearchParams(params).toString() : "";
18042
- return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/inspections${qs}`);
18043
- }
18044
- async bingInspectUrl(project, url) {
18045
- return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/inspect-url`, { url });
18046
- }
18047
- async bingInspectSitemap(project, body) {
18048
- return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/inspect-sitemap`, body ?? {});
18049
- }
18050
- async bingRequestIndexing(project, body) {
18051
- return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/request-indexing`, body);
18052
- }
18053
- async bingPerformance(project, params) {
18054
- const qs = params ? "?" + new URLSearchParams(params).toString() : "";
18055
- return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/performance${qs}`);
18056
- }
18057
- // CDP browser provider
18058
- async getCdpStatus() {
18059
- return this.request("GET", "/cdp/status");
18060
- }
18061
- async cdpScreenshot(query, targets) {
18062
- return this.request("POST", "/cdp/screenshot", { query, targets });
18063
- }
18064
- async getBrowserDiff(project, runId) {
18065
- return this.request("GET", `/projects/${encodeURIComponent(project)}/runs/${encodeURIComponent(runId)}/browser-diff`);
18066
- }
18067
- // Google Analytics 4
18068
- async gaConnect(project, body) {
18069
- return this.request("POST", `/projects/${encodeURIComponent(project)}/ga/connect`, body);
18070
- }
18071
- async gaDisconnect(project) {
18072
- await this.request("DELETE", `/projects/${encodeURIComponent(project)}/ga/disconnect`);
18073
- }
18074
- async gaStatus(project) {
18075
- return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/status`);
18076
- }
18077
- async gaSync(project, body) {
18078
- return this.request("POST", `/projects/${encodeURIComponent(project)}/ga/sync`, body ?? {});
18079
- }
18080
- async gaTraffic(project, params) {
18081
- const qs = params ? "?" + new URLSearchParams(params).toString() : "";
18082
- return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/traffic${qs}`);
18083
- }
18084
- async gaCoverage(project) {
18085
- return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/coverage`);
18086
- }
18087
- async gaAiReferralHistory(project, params) {
18088
- const qs = params ? "?" + new URLSearchParams(params).toString() : "";
18089
- return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/ai-referral-history${qs}`);
18090
- }
18091
- async gaSocialReferralHistory(project, params) {
18092
- const qs = params ? "?" + new URLSearchParams(params).toString() : "";
18093
- return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/social-referral-history${qs}`);
18094
- }
18095
- async gaSocialReferralTrend(project) {
18096
- return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/social-referral-trend`);
18097
- }
18098
- async gaAttributionTrend(project) {
18099
- return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/attribution-trend`);
18100
- }
18101
- async gaSessionHistory(project, params) {
18102
- const qs = params ? "?" + new URLSearchParams(params).toString() : "";
18103
- return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/session-history${qs}`);
18104
- }
18105
- async wordpressConnect(project, body) {
18106
- return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/connect`, body);
18107
- }
18108
- async wordpressDisconnect(project) {
18109
- await this.request("DELETE", `/projects/${encodeURIComponent(project)}/wordpress/disconnect`);
18110
- }
18111
- async wordpressStatus(project) {
18112
- return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/status`);
18113
- }
18114
- async wordpressPages(project, env) {
18115
- const qs = env ? `?env=${encodeURIComponent(env)}` : "";
18116
- return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/pages${qs}`);
18117
- }
18118
- async wordpressPage(project, slug, env) {
18119
- const params = new URLSearchParams({ slug });
18120
- if (env) params.set("env", env);
18121
- return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/page?${params.toString()}`);
18122
- }
18123
- async wordpressCreatePage(project, body) {
18124
- return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/pages`, body);
18125
- }
18126
- async wordpressUpdatePage(project, body) {
18127
- return this.request("PUT", `/projects/${encodeURIComponent(project)}/wordpress/page`, body);
18128
- }
18129
- async wordpressSetMeta(project, body) {
18130
- return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/page/meta`, body);
18131
- }
18132
- async wordpressBulkSetMeta(project, body) {
18133
- return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/pages/meta/bulk`, body);
18134
- }
18135
- async wordpressSchema(project, slug, env) {
18136
- const params = new URLSearchParams({ slug });
18137
- if (env) params.set("env", env);
18138
- return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/schema?${params.toString()}`);
18139
- }
18140
- async wordpressSetSchema(project, body) {
18141
- return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/schema/manual`, body);
18142
- }
18143
- async wordpressSchemaDeploy(project, body) {
18144
- return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/schema/deploy`, body);
18145
- }
18146
- async wordpressSchemaStatus(project, env) {
18147
- const params = new URLSearchParams();
18148
- if (env) params.set("env", env);
18149
- const qs = params.toString();
18150
- return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/schema/status${qs ? `?${qs}` : ""}`);
18151
- }
18152
- async wordpressOnboard(project, body) {
18153
- return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/onboard`, body);
18154
- }
18155
- async wordpressLlmsTxt(project, env) {
18156
- const qs = env ? `?env=${encodeURIComponent(env)}` : "";
18157
- return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/llms-txt${qs}`);
18158
- }
18159
- async wordpressSetLlmsTxt(project, body) {
18160
- return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/llms-txt/manual`, body);
18161
- }
18162
- async wordpressAudit(project, env) {
18163
- const qs = env ? `?env=${encodeURIComponent(env)}` : "";
18164
- return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/audit${qs}`);
18165
- }
18166
- async wordpressDiff(project, slug) {
18167
- const params = new URLSearchParams({ slug });
18168
- return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/diff?${params.toString()}`);
18169
- }
18170
- async wordpressStagingStatus(project) {
18171
- return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/staging/status`);
18172
- }
18173
- async wordpressStagingPush(project) {
18174
- return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/staging/push`);
18175
- }
18176
- // ── Intelligence ──────────────────────────────────────────────────────
18177
- async getInsights(project, opts) {
18178
- const params = new URLSearchParams();
18179
- if (opts?.dismissed) params.set("dismissed", "true");
18180
- if (opts?.runId) params.set("runId", opts.runId);
18181
- const qs = params.toString();
18182
- return this.request("GET", `/projects/${encodeURIComponent(project)}/insights${qs ? `?${qs}` : ""}`);
18183
- }
18184
- async dismissInsight(project, id) {
18185
- return this.request("POST", `/projects/${encodeURIComponent(project)}/insights/${encodeURIComponent(id)}/dismiss`);
18186
- }
18187
- async getHealth(project) {
18188
- return this.request("GET", `/projects/${encodeURIComponent(project)}/health/latest`);
18189
- }
18190
- async getHealthHistory(project, limit) {
18191
- const qs = limit ? `?limit=${limit}` : "";
18192
- return this.request("GET", `/projects/${encodeURIComponent(project)}/health/history${qs}`);
18193
- }
18194
- // --- Backlinks ---------------------------------------------------------
18195
- async backlinksStatus() {
18196
- return this.request("GET", "/backlinks/status");
18197
- }
18198
- async backlinksInstall() {
18199
- return this.request("POST", "/backlinks/install");
18200
- }
18201
- async backlinksTriggerSync(release) {
18202
- return this.request("POST", "/backlinks/syncs", { release });
18203
- }
18204
- async backlinksLatestSync() {
18205
- return this.request("GET", "/backlinks/syncs/latest");
18206
- }
18207
- async backlinksListSyncs() {
18208
- return this.request("GET", "/backlinks/syncs");
18209
- }
18210
- async backlinksCachedReleases() {
18211
- return this.request("GET", "/backlinks/releases");
18212
- }
18213
- async backlinksPruneCache(release) {
18214
- return this.request("DELETE", `/backlinks/cache/${encodeURIComponent(release)}`);
18215
- }
18216
- async backlinksExtract(project, release) {
18217
- return this.request("POST", `/projects/${encodeURIComponent(project)}/backlinks/extract`, release ? { release } : {});
18218
- }
18219
- async backlinksSummary(project, release) {
18220
- const qs = release ? `?release=${encodeURIComponent(release)}` : "";
18221
- return this.request("GET", `/projects/${encodeURIComponent(project)}/backlinks/summary${qs}`);
18222
- }
18223
- async backlinksDomains(project, opts = {}) {
18224
- const qs = new URLSearchParams();
18225
- if (opts.limit !== void 0) qs.set("limit", String(opts.limit));
18226
- if (opts.offset !== void 0) qs.set("offset", String(opts.offset));
18227
- if (opts.release) qs.set("release", opts.release);
18228
- const suffix = qs.toString() ? `?${qs.toString()}` : "";
18229
- return this.request("GET", `/projects/${encodeURIComponent(project)}/backlinks/domains${suffix}`);
18230
- }
18231
- async backlinksHistory(project) {
18232
- return this.request("GET", `/projects/${encodeURIComponent(project)}/backlinks/history`);
18233
- }
18234
- };
18235
-
18236
16349
  // src/snapshot-service.ts
18237
16350
  import { runAeoAudit } from "@ainyc/aeo-audit";
18238
16351
 
@@ -18255,13 +16368,13 @@ function extractHostname(domain) {
18255
16368
  function fetchWithPinnedAddress(target) {
18256
16369
  return new Promise((resolve) => {
18257
16370
  const port = target.url.port ? Number(target.url.port) : 443;
18258
- const path16 = target.url.pathname + target.url.search;
16371
+ const path15 = target.url.pathname + target.url.search;
18259
16372
  const req = https2.request(
18260
16373
  {
18261
16374
  hostname: target.address,
18262
16375
  family: target.family,
18263
16376
  port,
18264
- path: path16,
16377
+ path: path15,
18265
16378
  method: "GET",
18266
16379
  timeout: FETCH_TIMEOUT_MS,
18267
16380
  servername: target.url.hostname,
@@ -18353,7 +16466,7 @@ function formatAuditFactorScore(factor) {
18353
16466
  }
18354
16467
 
18355
16468
  // src/snapshot-service.ts
18356
- var log11 = createLogger("Snapshot");
16469
+ var log12 = createLogger("Snapshot");
18357
16470
  var ANALYSIS_PROVIDER_PRIORITY = ["openai", "claude", "gemini", "perplexity", "local"];
18358
16471
  var SNAPSHOT_QUERY_COUNT = 6;
18359
16472
  var ProviderExecutionGate2 = class {
@@ -18496,7 +16609,7 @@ var SnapshotService = class {
18496
16609
  return mapAuditReport(report);
18497
16610
  } catch (err) {
18498
16611
  const message = err instanceof Error ? err.message : String(err);
18499
- log11.warn("audit.failed", { homepageUrl, error: message });
16612
+ log12.warn("audit.failed", { homepageUrl, error: message });
18500
16613
  return {
18501
16614
  url: homepageUrl,
18502
16615
  finalUrl: homepageUrl,
@@ -18526,7 +16639,7 @@ var SnapshotService = class {
18526
16639
  phrases: parsedPhrases
18527
16640
  };
18528
16641
  } catch (err) {
18529
- log11.warn("profile.generation-failed", {
16642
+ log12.warn("profile.generation-failed", {
18530
16643
  domain: ctx.domain,
18531
16644
  provider: ctx.analysisProvider.adapter.name,
18532
16645
  error: err instanceof Error ? err.message : String(err)
@@ -18668,7 +16781,7 @@ var SnapshotService = class {
18668
16781
  recommendedActions: uniqueStrings(parsed.recommendedActions ?? []).slice(0, 4)
18669
16782
  };
18670
16783
  } catch (err) {
18671
- log11.warn("response.analysis-failed", {
16784
+ log12.warn("response.analysis-failed", {
18672
16785
  provider: ctx.analysisProvider.adapter.name,
18673
16786
  error: err instanceof Error ? err.message : String(err)
18674
16787
  });
@@ -18953,7 +17066,7 @@ function clipText(value, length) {
18953
17066
  // src/server.ts
18954
17067
  var _require2 = createRequire3(import.meta.url);
18955
17068
  var { version: PKG_VERSION } = _require2("../package.json");
18956
- var log12 = createLogger("Server");
17069
+ var log13 = createLogger("Server");
18957
17070
  var DEFAULT_QUOTA = {
18958
17071
  maxConcurrency: 2,
18959
17072
  maxRequestsPerMinute: 10,
@@ -19040,7 +17153,7 @@ function applyLegacyCredentials(rows, config) {
19040
17153
  }
19041
17154
  if (migratedGoogle > 0) {
19042
17155
  saveConfigPatch({ google: config.google });
19043
- log12.info("credentials.migrated", { type: "google", count: migratedGoogle });
17156
+ log13.info("credentials.migrated", { type: "google", count: migratedGoogle });
19044
17157
  }
19045
17158
  let migratedGa4 = 0;
19046
17159
  for (const row of rows.ga4) {
@@ -19058,7 +17171,7 @@ function applyLegacyCredentials(rows, config) {
19058
17171
  }
19059
17172
  if (migratedGa4 > 0) {
19060
17173
  saveConfigPatch({ ga4: config.ga4 });
19061
- log12.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
17174
+ log13.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
19062
17175
  }
19063
17176
  }
19064
17177
  async function createServer(opts) {
@@ -19090,11 +17203,11 @@ async function createServer(opts) {
19090
17203
  applyLegacyCredentials(legacyRows, opts.config);
19091
17204
  dropLegacyCredentialColumns(opts.db);
19092
17205
  } catch (err) {
19093
- log12.warn("credentials.migration.failed", {
17206
+ log13.warn("credentials.migration.failed", {
19094
17207
  error: err instanceof Error ? err.message : String(err)
19095
17208
  });
19096
17209
  }
19097
- log12.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
17210
+ log13.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
19098
17211
  const p = providers[k];
19099
17212
  return p?.apiKey || p?.baseUrl || p?.vertexProject;
19100
17213
  }) });
@@ -19154,8 +17267,8 @@ async function createServer(opts) {
19154
17267
  );
19155
17268
  jobRunner.onRunCompleted = (runId, projectId) => runCoordinator.onRunCompleted(runId, projectId);
19156
17269
  const snapshotService = new SnapshotService(registry);
19157
- const orphanedOpenClawDir = path15.join(os6.homedir(), ".openclaw-aero");
19158
- if (fs13.existsSync(orphanedOpenClawDir)) {
17270
+ const orphanedOpenClawDir = path14.join(os5.homedir(), ".openclaw-aero");
17271
+ if (fs12.existsSync(orphanedOpenClawDir)) {
19159
17272
  app.log.warn(
19160
17273
  { path: orphanedOpenClawDir },
19161
17274
  "OpenClaw gateway is no longer used. Remove ~/.openclaw-aero/ manually to reclaim the directory."
@@ -19749,10 +17862,10 @@ async function createServer(opts) {
19749
17862
  return snapshotService.createReport(input);
19750
17863
  }
19751
17864
  });
19752
- const dirname = path15.dirname(fileURLToPath2(import.meta.url));
19753
- const assetsDir = path15.join(dirname, "..", "assets");
19754
- if (fs13.existsSync(assetsDir)) {
19755
- const indexPath = path15.join(assetsDir, "index.html");
17865
+ const dirname = path14.dirname(fileURLToPath2(import.meta.url));
17866
+ const assetsDir = path14.join(dirname, "..", "assets");
17867
+ if (fs12.existsSync(assetsDir)) {
17868
+ const indexPath = path14.join(assetsDir, "index.html");
19756
17869
  const injectConfig = (html) => {
19757
17870
  const clientConfig = {};
19758
17871
  if (basePath) clientConfig.basePath = basePath;
@@ -19770,8 +17883,8 @@ async function createServer(opts) {
19770
17883
  index: false
19771
17884
  });
19772
17885
  const serveIndex = (_request, reply) => {
19773
- if (fs13.existsSync(indexPath)) {
19774
- const html = fs13.readFileSync(indexPath, "utf-8");
17886
+ if (fs12.existsSync(indexPath)) {
17887
+ const html = fs12.readFileSync(indexPath, "utf-8");
19775
17888
  return reply.type("text/html").send(injectConfig(html));
19776
17889
  }
19777
17890
  return reply.status(404).send({ error: "Dashboard not built" });
@@ -19791,8 +17904,8 @@ async function createServer(opts) {
19791
17904
  if (basePath && !url.startsWith(basePath)) {
19792
17905
  return reply.status(404).send({ error: "Not found", path: request.url });
19793
17906
  }
19794
- if (fs13.existsSync(indexPath)) {
19795
- const html = fs13.readFileSync(indexPath, "utf-8");
17907
+ if (fs12.existsSync(indexPath)) {
17908
+ const html = fs12.readFileSync(indexPath, "utf-8");
19796
17909
  return reply.type("text/html").send(injectConfig(html));
19797
17910
  }
19798
17911
  return reply.status(404).send({ error: "Not found" });
@@ -19861,31 +17974,11 @@ function parseKeywordResponse(raw, count) {
19861
17974
  }
19862
17975
 
19863
17976
  export {
19864
- getConfigDir,
19865
- getConfigPath,
19866
- loadConfig,
19867
- saveConfig,
19868
- saveConfigPatch,
19869
- configExists,
19870
17977
  isTelemetryEnabled,
19871
17978
  getOrCreateAnonymousId,
19872
17979
  isFirstRun,
19873
17980
  showFirstRunNotice,
19874
17981
  trackEvent,
19875
- EXIT_SYSTEM_ERROR,
19876
- CliError,
19877
- usageError,
19878
- isEndpointMissing,
19879
- printCliError,
19880
- providerQuotaPolicySchema,
19881
- ProviderNames,
19882
- resolveProviderInput,
19883
- notificationEventSchema,
19884
- effectiveDomains,
19885
- RunStatuses,
19886
- RunKinds,
19887
- determineAnswerMentioned,
19888
- CcReleaseSyncStatuses,
19889
17982
  reparseStoredResult2 as reparseStoredResult,
19890
17983
  reparseStoredResult3 as reparseStoredResult2,
19891
17984
  reparseStoredResult as reparseStoredResult3,
@@ -19893,7 +17986,6 @@ export {
19893
17986
  determineCitationState,
19894
17987
  computeCompetitorOverlap,
19895
17988
  extractRecommendedCompetitors,
19896
- createApiClient,
19897
17989
  setGoogleAuthConfig,
19898
17990
  formatAuditFactorScore,
19899
17991
  listAgentProviders,