@ainyc/canonry 2.5.1 → 2.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/assets/assets/{index-agELvqT1.js → index-U1lA1GKP.js} +102 -102
- package/assets/index.html +1 -1
- package/bin/canonry-mcp.mjs +2 -0
- package/dist/chunk-FPZUQADO.js +2152 -0
- package/dist/{chunk-CFS35BKX.js → chunk-MGBXRWLX.js} +368 -2196
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/{chunk-32YTAZBL.js → chunk-PYHANJ3B.js} +3 -5
- package/dist/cli.js +505 -91
- package/dist/index.js +6 -3
- package/dist/{intelligence-service-U7YQ4NXV.js → intelligence-service-2ZABHNR4.js} +2 -1
- package/dist/mcp.js +848 -0
- package/package.json +7 -5
|
@@ -1,3 +1,57 @@
|
|
|
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
|
+
competitorBatchRequestSchema,
|
|
22
|
+
configExists,
|
|
23
|
+
deliveryFailed,
|
|
24
|
+
determineAnswerMentioned,
|
|
25
|
+
effectiveDomains,
|
|
26
|
+
extractAnswerMentions,
|
|
27
|
+
findDuplicateLocationLabels,
|
|
28
|
+
hasLocationLabel,
|
|
29
|
+
internalError,
|
|
30
|
+
isAgentProviderId,
|
|
31
|
+
isBrowserProvider,
|
|
32
|
+
keywordGenerateRequestSchema,
|
|
33
|
+
loadConfig,
|
|
34
|
+
locationContextSchema,
|
|
35
|
+
missingDependency,
|
|
36
|
+
normalizeProjectDomain,
|
|
37
|
+
notFound,
|
|
38
|
+
notImplemented,
|
|
39
|
+
parseWindow,
|
|
40
|
+
projectConfigSchema,
|
|
41
|
+
projectUpsertRequestSchema,
|
|
42
|
+
providerError,
|
|
43
|
+
runInProgress,
|
|
44
|
+
runNotCancellable,
|
|
45
|
+
runTriggerRequestSchema,
|
|
46
|
+
saveConfigPatch,
|
|
47
|
+
scheduleUpsertRequestSchema,
|
|
48
|
+
snapshotRequestSchema,
|
|
49
|
+
unsupportedKind,
|
|
50
|
+
validationError,
|
|
51
|
+
visibilityStateFromAnswerMentioned,
|
|
52
|
+
windowCutoff,
|
|
53
|
+
wordpressEnvSchema
|
|
54
|
+
} from "./chunk-FPZUQADO.js";
|
|
1
55
|
import {
|
|
2
56
|
IntelligenceService,
|
|
3
57
|
agentMemory,
|
|
@@ -30,172 +84,7 @@ import {
|
|
|
30
84
|
runs,
|
|
31
85
|
schedules,
|
|
32
86
|
usageCounters
|
|
33
|
-
} from "./chunk-
|
|
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
|
-
}
|
|
87
|
+
} from "./chunk-PYHANJ3B.js";
|
|
199
88
|
|
|
200
89
|
// src/telemetry.ts
|
|
201
90
|
import crypto from "crypto";
|
|
@@ -269,1259 +158,15 @@ function trackEvent(event, properties) {
|
|
|
269
158
|
}).finally(() => clearTimeout(timeout));
|
|
270
159
|
}
|
|
271
160
|
|
|
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
161
|
// src/server.ts
|
|
348
162
|
import { createRequire as createRequire3 } from "module";
|
|
349
163
|
import crypto28 from "crypto";
|
|
350
|
-
import
|
|
351
|
-
import
|
|
164
|
+
import fs12 from "fs";
|
|
165
|
+
import path14 from "path";
|
|
352
166
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
353
167
|
import { eq as eq30 } from "drizzle-orm";
|
|
354
168
|
import Fastify from "fastify";
|
|
355
169
|
|
|
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
170
|
// ../api-routes/src/auth.ts
|
|
1526
171
|
import crypto2 from "crypto";
|
|
1527
172
|
import { eq } from "drizzle-orm";
|
|
@@ -2017,10 +662,16 @@ async function keywordRoutes(app, opts) {
|
|
|
2017
662
|
});
|
|
2018
663
|
app.post("/projects/:name/keywords/generate", async (request, reply) => {
|
|
2019
664
|
const project = resolveProject(app.db, request.params.name);
|
|
2020
|
-
const
|
|
2021
|
-
if (!
|
|
2022
|
-
throw validationError(
|
|
665
|
+
const parsed = keywordGenerateRequestSchema.safeParse(request.body);
|
|
666
|
+
if (!parsed.success) {
|
|
667
|
+
throw validationError("Invalid keyword generation request", {
|
|
668
|
+
issues: parsed.error.issues.map((issue) => ({
|
|
669
|
+
path: issue.path.join("."),
|
|
670
|
+
message: issue.message
|
|
671
|
+
}))
|
|
672
|
+
});
|
|
2023
673
|
}
|
|
674
|
+
const body = parsed.data;
|
|
2024
675
|
const provider = body.provider.trim().toLowerCase();
|
|
2025
676
|
const validNames = opts.validProviderNames ?? [];
|
|
2026
677
|
if (validNames.length && !validNames.includes(provider)) {
|
|
@@ -2029,10 +680,7 @@ async function keywordRoutes(app, opts) {
|
|
|
2029
680
|
validProviders: validNames
|
|
2030
681
|
});
|
|
2031
682
|
}
|
|
2032
|
-
|
|
2033
|
-
throw validationError('"count" must be an integer');
|
|
2034
|
-
}
|
|
2035
|
-
const count = Math.min(Math.max(body.count ?? 5, 1), 20);
|
|
683
|
+
const count = body.count ?? 5;
|
|
2036
684
|
if (!opts.onGenerateKeywords) {
|
|
2037
685
|
throw notImplemented("Key phrase generation is not supported in this deployment");
|
|
2038
686
|
}
|
|
@@ -2091,6 +739,79 @@ async function competitorRoutes(app) {
|
|
|
2091
739
|
const rows = app.db.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
|
|
2092
740
|
return reply.send(rows.map((r) => ({ id: r.id, domain: r.domain, createdAt: r.createdAt })));
|
|
2093
741
|
});
|
|
742
|
+
app.post("/projects/:name/competitors", async (request, reply) => {
|
|
743
|
+
const project = resolveProject(app.db, request.params.name);
|
|
744
|
+
const body = parseCompetitorBatch(request.body);
|
|
745
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
746
|
+
const requested = uniqueStrings(body.competitors);
|
|
747
|
+
app.db.transaction((tx) => {
|
|
748
|
+
const existing = tx.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
|
|
749
|
+
const existingSet = new Set(existing.map((c) => c.domain));
|
|
750
|
+
const added = requested.filter((domain) => !existingSet.has(domain));
|
|
751
|
+
if (added.length === 0) return;
|
|
752
|
+
for (const domain of added) {
|
|
753
|
+
tx.insert(competitors).values({
|
|
754
|
+
id: crypto6.randomUUID(),
|
|
755
|
+
projectId: project.id,
|
|
756
|
+
domain,
|
|
757
|
+
createdAt: now
|
|
758
|
+
}).onConflictDoNothing({
|
|
759
|
+
target: [competitors.projectId, competitors.domain]
|
|
760
|
+
}).run();
|
|
761
|
+
}
|
|
762
|
+
writeAuditLog(tx, {
|
|
763
|
+
projectId: project.id,
|
|
764
|
+
actor: "api",
|
|
765
|
+
action: "competitors.appended",
|
|
766
|
+
entityType: "competitor",
|
|
767
|
+
diff: { added }
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
const rows = app.db.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
|
|
771
|
+
return reply.send(rows.map((r) => ({ id: r.id, domain: r.domain, createdAt: r.createdAt })));
|
|
772
|
+
});
|
|
773
|
+
app.delete("/projects/:name/competitors", async (request, reply) => {
|
|
774
|
+
const project = resolveProject(app.db, request.params.name);
|
|
775
|
+
const body = parseCompetitorBatch(request.body);
|
|
776
|
+
const requested = new Set(uniqueStrings(body.competitors));
|
|
777
|
+
app.db.transaction((tx) => {
|
|
778
|
+
const existing = tx.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
|
|
779
|
+
const rowsToDelete = existing.filter((c) => requested.has(c.domain));
|
|
780
|
+
if (rowsToDelete.length === 0) return;
|
|
781
|
+
for (const row of rowsToDelete) {
|
|
782
|
+
tx.delete(competitors).where(eq5(competitors.id, row.id)).run();
|
|
783
|
+
}
|
|
784
|
+
writeAuditLog(tx, {
|
|
785
|
+
projectId: project.id,
|
|
786
|
+
actor: "api",
|
|
787
|
+
action: "competitors.deleted",
|
|
788
|
+
entityType: "competitor",
|
|
789
|
+
diff: { deleted: rowsToDelete.map((row) => row.domain) }
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
const rows = app.db.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
|
|
793
|
+
return reply.send(rows.map((r) => ({ id: r.id, domain: r.domain, createdAt: r.createdAt })));
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
function parseCompetitorBatch(value) {
|
|
797
|
+
const result = competitorBatchRequestSchema.safeParse(value);
|
|
798
|
+
if (result.success) return result.data;
|
|
799
|
+
throw validationError("Invalid competitor batch request", {
|
|
800
|
+
issues: result.error.issues.map((issue) => ({
|
|
801
|
+
path: issue.path.join("."),
|
|
802
|
+
message: issue.message
|
|
803
|
+
}))
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
function uniqueStrings(values) {
|
|
807
|
+
const seen = /* @__PURE__ */ new Set();
|
|
808
|
+
const result = [];
|
|
809
|
+
for (const value of values) {
|
|
810
|
+
if (seen.has(value)) continue;
|
|
811
|
+
seen.add(value);
|
|
812
|
+
result.push(value);
|
|
813
|
+
}
|
|
814
|
+
return result;
|
|
2094
815
|
}
|
|
2095
816
|
|
|
2096
817
|
// ../api-routes/src/runs.ts
|
|
@@ -2132,11 +853,11 @@ function queueRunIfProjectIdle(db, params) {
|
|
|
2132
853
|
async function runRoutes(app, opts) {
|
|
2133
854
|
app.post("/projects/:name/runs", async (request, reply) => {
|
|
2134
855
|
const project = resolveProject(app.db, request.params.name);
|
|
2135
|
-
const
|
|
2136
|
-
if (kind !== "answer-visibility") throw unsupportedKind(kind);
|
|
856
|
+
const body = parseRunTriggerRequest(request.body ?? {});
|
|
2137
857
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2138
|
-
const
|
|
2139
|
-
const
|
|
858
|
+
const kind = body.kind ?? RunKinds["answer-visibility"];
|
|
859
|
+
const trigger = body.trigger ?? RunTriggers.manual;
|
|
860
|
+
const rawProviders = body.providers;
|
|
2140
861
|
if (rawProviders?.length) {
|
|
2141
862
|
const normalized = rawProviders.map((p) => p.trim().toLowerCase()).filter(Boolean);
|
|
2142
863
|
const validNames = opts.validProviderNames ?? [];
|
|
@@ -2154,13 +875,13 @@ async function runRoutes(app, opts) {
|
|
|
2154
875
|
const providers = rawProviders?.length ? rawProviders : void 0;
|
|
2155
876
|
let resolvedLocation;
|
|
2156
877
|
const projectLocations = parseJsonColumn(project.locations, []);
|
|
2157
|
-
if (
|
|
878
|
+
if (body.noLocation) {
|
|
2158
879
|
resolvedLocation = null;
|
|
2159
|
-
} else if (
|
|
2160
|
-
} else if (
|
|
2161
|
-
const loc = projectLocations.find((l) => l.label ===
|
|
880
|
+
} else if (body.allLocations) {
|
|
881
|
+
} else if (body.location) {
|
|
882
|
+
const loc = projectLocations.find((l) => l.label === body.location);
|
|
2162
883
|
if (!loc) {
|
|
2163
|
-
throw validationError(`Location "${
|
|
884
|
+
throw validationError(`Location "${body.location}" not found. Configure it first.`);
|
|
2164
885
|
}
|
|
2165
886
|
resolvedLocation = loc;
|
|
2166
887
|
} else if (project.defaultLocation) {
|
|
@@ -2170,7 +891,7 @@ async function runRoutes(app, opts) {
|
|
|
2170
891
|
}
|
|
2171
892
|
resolvedLocation = loc;
|
|
2172
893
|
}
|
|
2173
|
-
if (
|
|
894
|
+
if (body.allLocations) {
|
|
2174
895
|
if (projectLocations.length === 0) {
|
|
2175
896
|
throw validationError("No locations configured for this project");
|
|
2176
897
|
}
|
|
@@ -2339,6 +1060,16 @@ async function runRoutes(app, opts) {
|
|
|
2339
1060
|
return reply.send(loadRunDetail(app, run));
|
|
2340
1061
|
});
|
|
2341
1062
|
}
|
|
1063
|
+
function parseRunTriggerRequest(value) {
|
|
1064
|
+
const result = runTriggerRequestSchema.safeParse(value);
|
|
1065
|
+
if (result.success) return result.data;
|
|
1066
|
+
throw validationError("Invalid run trigger request", {
|
|
1067
|
+
issues: result.error.issues.map((issue) => ({
|
|
1068
|
+
path: issue.path.join("."),
|
|
1069
|
+
message: issue.message
|
|
1070
|
+
}))
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
2342
1073
|
function formatRun(row) {
|
|
2343
1074
|
return {
|
|
2344
1075
|
id: row.id,
|
|
@@ -2550,7 +1281,7 @@ async function deliverWebhook(target, payload, webhookSecret) {
|
|
|
2550
1281
|
const body = JSON.stringify(payload);
|
|
2551
1282
|
const isHttps = target.url.protocol === "https:";
|
|
2552
1283
|
const port = target.url.port ? Number(target.url.port) : isHttps ? 443 : 80;
|
|
2553
|
-
const
|
|
1284
|
+
const path15 = `${target.url.pathname}${target.url.search}`;
|
|
2554
1285
|
const headers = {
|
|
2555
1286
|
"Content-Length": String(Buffer.byteLength(body)),
|
|
2556
1287
|
"Content-Type": "application/json",
|
|
@@ -2566,7 +1297,7 @@ async function deliverWebhook(target, payload, webhookSecret) {
|
|
|
2566
1297
|
headers,
|
|
2567
1298
|
hostname: target.address,
|
|
2568
1299
|
method: "POST",
|
|
2569
|
-
path:
|
|
1300
|
+
path: path15,
|
|
2570
1301
|
port,
|
|
2571
1302
|
timeout: REQUEST_TIMEOUT_MS
|
|
2572
1303
|
};
|
|
@@ -3604,7 +2335,7 @@ var booleanSchema = { type: "boolean" };
|
|
|
3604
2335
|
var integerSchema = { type: "integer" };
|
|
3605
2336
|
var objectSchema = { type: "object", additionalProperties: true };
|
|
3606
2337
|
var stringArraySchema = { type: "array", items: stringSchema };
|
|
3607
|
-
var
|
|
2338
|
+
var googleConnectionTypeSchema = { type: "string", enum: ["gsc", "ga4"] };
|
|
3608
2339
|
var locationSchema = {
|
|
3609
2340
|
type: "object",
|
|
3610
2341
|
required: ["label", "city", "region", "country"],
|
|
@@ -3656,7 +2387,7 @@ var googleTypeParameter = {
|
|
|
3656
2387
|
in: "path",
|
|
3657
2388
|
required: true,
|
|
3658
2389
|
description: "Google connection type.",
|
|
3659
|
-
schema:
|
|
2390
|
+
schema: googleConnectionTypeSchema
|
|
3660
2391
|
};
|
|
3661
2392
|
var projectRunIdParameter = {
|
|
3662
2393
|
name: "runId",
|
|
@@ -3964,32 +2695,81 @@ var routeCatalog = [
|
|
|
3964
2695
|
type: "object",
|
|
3965
2696
|
required: ["provider"],
|
|
3966
2697
|
properties: {
|
|
3967
|
-
provider: { type: "string", enum: ["gemini", "openai", "claude", "perplexity", "local"] },
|
|
3968
|
-
count: integerSchema
|
|
2698
|
+
provider: { type: "string", enum: ["gemini", "openai", "claude", "perplexity", "local"] },
|
|
2699
|
+
count: integerSchema
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
},
|
|
2705
|
+
responses: {
|
|
2706
|
+
200: { description: "Keyword suggestions returned." },
|
|
2707
|
+
501: { description: "Keyword generation is not available." }
|
|
2708
|
+
}
|
|
2709
|
+
},
|
|
2710
|
+
{
|
|
2711
|
+
method: "get",
|
|
2712
|
+
path: "/api/v1/projects/{name}/competitors",
|
|
2713
|
+
summary: "List competitors",
|
|
2714
|
+
tags: ["competitors"],
|
|
2715
|
+
parameters: [nameParameter],
|
|
2716
|
+
responses: {
|
|
2717
|
+
200: { description: "Competitors returned." }
|
|
2718
|
+
}
|
|
2719
|
+
},
|
|
2720
|
+
{
|
|
2721
|
+
method: "put",
|
|
2722
|
+
path: "/api/v1/projects/{name}/competitors",
|
|
2723
|
+
summary: "Replace competitors",
|
|
2724
|
+
tags: ["competitors"],
|
|
2725
|
+
parameters: [nameParameter],
|
|
2726
|
+
requestBody: {
|
|
2727
|
+
required: true,
|
|
2728
|
+
content: {
|
|
2729
|
+
"application/json": {
|
|
2730
|
+
schema: {
|
|
2731
|
+
type: "object",
|
|
2732
|
+
required: ["competitors"],
|
|
2733
|
+
properties: {
|
|
2734
|
+
competitors: stringArraySchema
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
},
|
|
2740
|
+
responses: {
|
|
2741
|
+
200: { description: "Competitors replaced." }
|
|
2742
|
+
}
|
|
2743
|
+
},
|
|
2744
|
+
{
|
|
2745
|
+
method: "post",
|
|
2746
|
+
path: "/api/v1/projects/{name}/competitors",
|
|
2747
|
+
summary: "Append competitors",
|
|
2748
|
+
tags: ["competitors"],
|
|
2749
|
+
parameters: [nameParameter],
|
|
2750
|
+
requestBody: {
|
|
2751
|
+
required: true,
|
|
2752
|
+
content: {
|
|
2753
|
+
"application/json": {
|
|
2754
|
+
schema: {
|
|
2755
|
+
type: "object",
|
|
2756
|
+
required: ["competitors"],
|
|
2757
|
+
properties: {
|
|
2758
|
+
competitors: stringArraySchema
|
|
3969
2759
|
}
|
|
3970
2760
|
}
|
|
3971
2761
|
}
|
|
3972
2762
|
}
|
|
3973
2763
|
},
|
|
3974
2764
|
responses: {
|
|
3975
|
-
200: { description: "
|
|
3976
|
-
|
|
3977
|
-
}
|
|
3978
|
-
},
|
|
3979
|
-
{
|
|
3980
|
-
method: "get",
|
|
3981
|
-
path: "/api/v1/projects/{name}/competitors",
|
|
3982
|
-
summary: "List competitors",
|
|
3983
|
-
tags: ["competitors"],
|
|
3984
|
-
parameters: [nameParameter],
|
|
3985
|
-
responses: {
|
|
3986
|
-
200: { description: "Competitors returned." }
|
|
2765
|
+
200: { description: "Competitors appended." },
|
|
2766
|
+
400: { description: "Invalid competitor append request." }
|
|
3987
2767
|
}
|
|
3988
2768
|
},
|
|
3989
2769
|
{
|
|
3990
|
-
method: "
|
|
2770
|
+
method: "delete",
|
|
3991
2771
|
path: "/api/v1/projects/{name}/competitors",
|
|
3992
|
-
summary: "
|
|
2772
|
+
summary: "Delete specific competitors",
|
|
3993
2773
|
tags: ["competitors"],
|
|
3994
2774
|
parameters: [nameParameter],
|
|
3995
2775
|
requestBody: {
|
|
@@ -4007,7 +2787,8 @@ var routeCatalog = [
|
|
|
4007
2787
|
}
|
|
4008
2788
|
},
|
|
4009
2789
|
responses: {
|
|
4010
|
-
200: { description: "
|
|
2790
|
+
200: { description: "Remaining competitors returned." },
|
|
2791
|
+
400: { description: "Invalid competitor delete request." }
|
|
4011
2792
|
}
|
|
4012
2793
|
},
|
|
4013
2794
|
{
|
|
@@ -4649,7 +3430,7 @@ var routeCatalog = [
|
|
|
4649
3430
|
type: "object",
|
|
4650
3431
|
required: ["type"],
|
|
4651
3432
|
properties: {
|
|
4652
|
-
type:
|
|
3433
|
+
type: googleConnectionTypeSchema,
|
|
4653
3434
|
propertyId: stringSchema,
|
|
4654
3435
|
publicUrl: stringSchema
|
|
4655
3436
|
}
|
|
@@ -6071,8 +4852,8 @@ async function openApiRoutes(app, opts = {}) {
|
|
|
6071
4852
|
return reply.type("application/json").send(buildOpenApiDocument(opts));
|
|
6072
4853
|
});
|
|
6073
4854
|
}
|
|
6074
|
-
function buildOperationId(method,
|
|
6075
|
-
const parts =
|
|
4855
|
+
function buildOperationId(method, path15) {
|
|
4856
|
+
const parts = path15.split("/").filter(Boolean).map((part) => {
|
|
6076
4857
|
if (part.startsWith("{") && part.endsWith("}")) {
|
|
6077
4858
|
return `by-${part.slice(1, -1)}`;
|
|
6078
4859
|
}
|
|
@@ -6594,7 +5375,7 @@ async function exchangeCode(clientId, clientSecret, code, redirectUri) {
|
|
|
6594
5375
|
const parsed = JSON.parse(body);
|
|
6595
5376
|
if (parsed.error) detail = parsed.error;
|
|
6596
5377
|
if (parsed.error_description) {
|
|
6597
|
-
const sanitized = parsed.error_description.replace(new RegExp(
|
|
5378
|
+
const sanitized = parsed.error_description.replace(new RegExp(escapeRegExp(clientId), "g"), "***").replace(new RegExp(escapeRegExp(clientSecret), "g"), "***").replace(new RegExp(escapeRegExp(code), "g"), "***");
|
|
6598
5379
|
detail += detail ? `: ${sanitized}` : sanitized;
|
|
6599
5380
|
}
|
|
6600
5381
|
} catch {
|
|
@@ -6604,7 +5385,7 @@ async function exchangeCode(clientId, clientSecret, code, redirectUri) {
|
|
|
6604
5385
|
}
|
|
6605
5386
|
return await res.json();
|
|
6606
5387
|
}
|
|
6607
|
-
function
|
|
5388
|
+
function escapeRegExp(str) {
|
|
6608
5389
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6609
5390
|
}
|
|
6610
5391
|
async function refreshAccessToken(clientId, clientSecret, currentRefreshToken) {
|
|
@@ -6632,7 +5413,7 @@ async function refreshAccessToken(clientId, clientSecret, currentRefreshToken) {
|
|
|
6632
5413
|
const parsed = JSON.parse(body);
|
|
6633
5414
|
if (parsed.error) detail = parsed.error;
|
|
6634
5415
|
if (parsed.error_description) {
|
|
6635
|
-
const sanitized = parsed.error_description.replace(new RegExp(
|
|
5416
|
+
const sanitized = parsed.error_description.replace(new RegExp(escapeRegExp(clientId), "g"), "***").replace(new RegExp(escapeRegExp(clientSecret), "g"), "***").replace(new RegExp(escapeRegExp(currentRefreshToken), "g"), "***");
|
|
6636
5417
|
detail += detail ? `: ${sanitized}` : sanitized;
|
|
6637
5418
|
}
|
|
6638
5419
|
} catch {
|
|
@@ -6946,13 +5727,13 @@ async function getAccessToken(clientEmail, privateKey) {
|
|
|
6946
5727
|
const body = await res.text().catch(() => "");
|
|
6947
5728
|
ga4Log("error", "token.failed", { httpStatus: res.status });
|
|
6948
5729
|
const detail = body.length <= 200 ? body : `${body.slice(0, 200)}... [truncated]`;
|
|
6949
|
-
const sanitizedDetail = detail.replace(new RegExp(
|
|
5730
|
+
const sanitizedDetail = detail.replace(new RegExp(escapeRegExp2(clientEmail), "g"), "***").replace(new RegExp(escapeRegExp2(privateKey.slice(0, 32)), "g"), "***");
|
|
6950
5731
|
throw new GA4ApiError(`Failed to get access token: ${sanitizedDetail}`, res.status);
|
|
6951
5732
|
}
|
|
6952
5733
|
const data = await res.json();
|
|
6953
5734
|
return data.access_token;
|
|
6954
5735
|
}
|
|
6955
|
-
function
|
|
5736
|
+
function escapeRegExp2(str) {
|
|
6956
5737
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6957
5738
|
}
|
|
6958
5739
|
async function runReport(accessToken, propertyId, request) {
|
|
@@ -8082,7 +6863,7 @@ function bingClientLog(level, action, ctx) {
|
|
|
8082
6863
|
const stream = level === "error" ? process.stderr : process.stdout;
|
|
8083
6864
|
stream.write(JSON.stringify(entry) + "\n");
|
|
8084
6865
|
}
|
|
8085
|
-
function
|
|
6866
|
+
function escapeRegExp3(str) {
|
|
8086
6867
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8087
6868
|
}
|
|
8088
6869
|
async function bingFetch(apiKey, endpoint, opts) {
|
|
@@ -8110,7 +6891,7 @@ async function bingFetch(apiKey, endpoint, opts) {
|
|
|
8110
6891
|
const body = await res.text();
|
|
8111
6892
|
bingClientLog("error", "http.error", { endpoint, method, httpStatus: res.status });
|
|
8112
6893
|
let detail = body.length <= 500 ? body : `${body.slice(0, 500)}... [truncated]`;
|
|
8113
|
-
detail = detail.replace(new RegExp(
|
|
6894
|
+
detail = detail.replace(new RegExp(escapeRegExp3(apiKey), "g"), "***");
|
|
8114
6895
|
throw new BingApiError(`Bing API error (${res.status}): ${detail}`, res.status);
|
|
8115
6896
|
}
|
|
8116
6897
|
const text = await res.text();
|
|
@@ -8654,12 +7435,12 @@ async function bingRoutes(app, opts) {
|
|
|
8654
7435
|
}
|
|
8655
7436
|
|
|
8656
7437
|
// ../api-routes/src/cdp.ts
|
|
8657
|
-
import
|
|
8658
|
-
import
|
|
8659
|
-
import
|
|
7438
|
+
import fs from "fs";
|
|
7439
|
+
import path from "path";
|
|
7440
|
+
import os from "os";
|
|
8660
7441
|
import { eq as eq16, and as and5 } from "drizzle-orm";
|
|
8661
7442
|
function getScreenshotDir() {
|
|
8662
|
-
return
|
|
7443
|
+
return path.join(os.homedir(), ".canonry", "screenshots");
|
|
8663
7444
|
}
|
|
8664
7445
|
async function cdpRoutes(app, opts) {
|
|
8665
7446
|
app.get("/screenshots/:snapshotId", async (request, reply) => {
|
|
@@ -8669,17 +7450,17 @@ async function cdpRoutes(app, opts) {
|
|
|
8669
7450
|
const err = notFound("Screenshot", snapshotId);
|
|
8670
7451
|
return reply.code(err.statusCode).send(err.toJSON());
|
|
8671
7452
|
}
|
|
8672
|
-
const base =
|
|
8673
|
-
const fullPath =
|
|
8674
|
-
if (!fullPath.startsWith(base +
|
|
7453
|
+
const base = path.resolve(getScreenshotDir());
|
|
7454
|
+
const fullPath = path.resolve(path.join(base, snapshot.screenshotPath));
|
|
7455
|
+
if (!fullPath.startsWith(base + path.sep) && fullPath !== base) {
|
|
8675
7456
|
const err = notFound("Screenshot", snapshotId);
|
|
8676
7457
|
return reply.code(err.statusCode).send(err.toJSON());
|
|
8677
7458
|
}
|
|
8678
|
-
if (!
|
|
7459
|
+
if (!fs.existsSync(fullPath)) {
|
|
8679
7460
|
const err = notFound("Screenshot file", snapshotId);
|
|
8680
7461
|
return reply.code(err.statusCode).send(err.toJSON());
|
|
8681
7462
|
}
|
|
8682
|
-
const stream =
|
|
7463
|
+
const stream = fs.createReadStream(fullPath);
|
|
8683
7464
|
return reply.type("image/png").send(stream);
|
|
8684
7465
|
});
|
|
8685
7466
|
app.put("/settings/cdp", async (request, reply) => {
|
|
@@ -9684,10 +8465,10 @@ function buildAuthErrorMessage(res, responseText) {
|
|
|
9684
8465
|
}
|
|
9685
8466
|
return "WordPress credentials are invalid or lack permission for this action";
|
|
9686
8467
|
}
|
|
9687
|
-
async function fetchJson(connection, siteUrl,
|
|
8468
|
+
async function fetchJson(connection, siteUrl, path15, init) {
|
|
9688
8469
|
if (siteUrl.startsWith("http:")) {
|
|
9689
8470
|
}
|
|
9690
|
-
const res = await fetch(`${normalizeSiteUrl(siteUrl)}${
|
|
8471
|
+
const res = await fetch(`${normalizeSiteUrl(siteUrl)}${path15}`, {
|
|
9691
8472
|
...init,
|
|
9692
8473
|
headers: {
|
|
9693
8474
|
"Authorization": `Basic ${encodeBasicAuth(connection.username, connection.appPassword)}`,
|
|
@@ -10202,12 +8983,12 @@ var CANONRY_SCHEMA_START = "<!-- canonry:schema:start -->";
|
|
|
10202
8983
|
var CANONRY_SCHEMA_END = "<!-- canonry:schema:end -->";
|
|
10203
8984
|
function stripCanonrySchema(content) {
|
|
10204
8985
|
const regex = new RegExp(
|
|
10205
|
-
`${
|
|
8986
|
+
`${escapeRegExp4(CANONRY_SCHEMA_START)}[\\s\\S]*?${escapeRegExp4(CANONRY_SCHEMA_END)}`,
|
|
10206
8987
|
"g"
|
|
10207
8988
|
);
|
|
10208
8989
|
return content.replace(regex, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
10209
8990
|
}
|
|
10210
|
-
function
|
|
8991
|
+
function escapeRegExp4(str) {
|
|
10211
8992
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10212
8993
|
}
|
|
10213
8994
|
function injectCanonrySchema(content, schemas) {
|
|
@@ -10314,7 +9095,7 @@ async function getSchemaStatus(connection, env) {
|
|
|
10314
9095
|
const thirdPartySchemas = [];
|
|
10315
9096
|
if (hasCanonryMarker) {
|
|
10316
9097
|
const markerRegex = new RegExp(
|
|
10317
|
-
`${
|
|
9098
|
+
`${escapeRegExp4(CANONRY_SCHEMA_START)}([\\s\\S]*?)${escapeRegExp4(CANONRY_SCHEMA_END)}`
|
|
10318
9099
|
);
|
|
10319
9100
|
const match = markerRegex.exec(rawContent);
|
|
10320
9101
|
if (match?.[1]) {
|
|
@@ -11108,13 +9889,13 @@ import crypto18 from "crypto";
|
|
|
11108
9889
|
import { and as and7, asc as asc2, desc as desc8, eq as eq18, sql as sql5 } from "drizzle-orm";
|
|
11109
9890
|
|
|
11110
9891
|
// ../integration-commoncrawl/src/constants.ts
|
|
11111
|
-
import
|
|
11112
|
-
import
|
|
9892
|
+
import os2 from "os";
|
|
9893
|
+
import path2 from "path";
|
|
11113
9894
|
var CC_BASE_URL = "https://data.commoncrawl.org/projects/hyperlinkgraph";
|
|
11114
|
-
var PLUGIN_DIR =
|
|
11115
|
-
var PLUGIN_PKG_JSON =
|
|
9895
|
+
var PLUGIN_DIR = path2.join(os2.homedir(), ".canonry", "plugins");
|
|
9896
|
+
var PLUGIN_PKG_JSON = path2.join(PLUGIN_DIR, "package.json");
|
|
11116
9897
|
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 ??
|
|
9898
|
+
var CC_CACHE_DIR = process.env.CANONRY_CC_CACHE_DIR ?? path2.join(os2.homedir(), ".canonry", "cache", "commoncrawl");
|
|
11118
9899
|
var RELEASE_ID_REGEX = /^cc-main-(\d{4})-(jan-feb-mar|apr-may-jun|jul-aug-sep|oct-nov-dec)$/;
|
|
11119
9900
|
function ccReleasePaths(release) {
|
|
11120
9901
|
const base = `${CC_BASE_URL}/${release}/domain`;
|
|
@@ -11144,8 +9925,8 @@ function isValidReleaseId(id) {
|
|
|
11144
9925
|
// ../integration-commoncrawl/src/downloader.ts
|
|
11145
9926
|
import { createHash } from "crypto";
|
|
11146
9927
|
import { createWriteStream } from "fs";
|
|
11147
|
-
import
|
|
11148
|
-
import
|
|
9928
|
+
import fs2 from "fs/promises";
|
|
9929
|
+
import path3 from "path";
|
|
11149
9930
|
import { pipeline } from "stream/promises";
|
|
11150
9931
|
import { Readable, Transform } from "stream";
|
|
11151
9932
|
async function downloadFile(opts) {
|
|
@@ -11153,7 +9934,7 @@ async function downloadFile(opts) {
|
|
|
11153
9934
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
11154
9935
|
const sidecarPath = `${opts.destPath}.sha256`;
|
|
11155
9936
|
try {
|
|
11156
|
-
const stat = await
|
|
9937
|
+
const stat = await fs2.stat(opts.destPath);
|
|
11157
9938
|
const sidecar = await readSidecar(sidecarPath);
|
|
11158
9939
|
const sha2562 = sidecar ?? await hashFile(opts.destPath);
|
|
11159
9940
|
if (!sidecar) await writeSidecar(sidecarPath, sha2562);
|
|
@@ -11161,7 +9942,7 @@ async function downloadFile(opts) {
|
|
|
11161
9942
|
} catch {
|
|
11162
9943
|
}
|
|
11163
9944
|
const partialPath = `${opts.destPath}.partial`;
|
|
11164
|
-
await
|
|
9945
|
+
await fs2.mkdir(path3.dirname(opts.destPath), { recursive: true });
|
|
11165
9946
|
await unlinkIfExists(partialPath);
|
|
11166
9947
|
const res = await fetchImpl(opts.url);
|
|
11167
9948
|
if (!res.ok || !res.body) {
|
|
@@ -11184,13 +9965,13 @@ async function downloadFile(opts) {
|
|
|
11184
9965
|
createWriteStream(partialPath)
|
|
11185
9966
|
);
|
|
11186
9967
|
const sha256 = hasher.digest("hex");
|
|
11187
|
-
await
|
|
9968
|
+
await fs2.rename(partialPath, opts.destPath);
|
|
11188
9969
|
await writeSidecar(sidecarPath, sha256);
|
|
11189
9970
|
return { bytes, sha256, cached: false, elapsedMs: Date.now() - start };
|
|
11190
9971
|
}
|
|
11191
9972
|
async function hashFile(filePath) {
|
|
11192
9973
|
const hasher = createHash("sha256");
|
|
11193
|
-
const handle = await
|
|
9974
|
+
const handle = await fs2.open(filePath, "r");
|
|
11194
9975
|
try {
|
|
11195
9976
|
const stream = handle.createReadStream();
|
|
11196
9977
|
for await (const chunk of stream) hasher.update(chunk);
|
|
@@ -11201,7 +9982,7 @@ async function hashFile(filePath) {
|
|
|
11201
9982
|
}
|
|
11202
9983
|
async function readSidecar(sidecarPath) {
|
|
11203
9984
|
try {
|
|
11204
|
-
const raw = await
|
|
9985
|
+
const raw = await fs2.readFile(sidecarPath, "utf8");
|
|
11205
9986
|
const trimmed = raw.trim();
|
|
11206
9987
|
return /^[0-9a-f]{64}$/i.test(trimmed) ? trimmed.toLowerCase() : null;
|
|
11207
9988
|
} catch {
|
|
@@ -11209,12 +9990,12 @@ async function readSidecar(sidecarPath) {
|
|
|
11209
9990
|
}
|
|
11210
9991
|
}
|
|
11211
9992
|
async function writeSidecar(sidecarPath, sha256) {
|
|
11212
|
-
await
|
|
9993
|
+
await fs2.writeFile(sidecarPath, `${sha256}
|
|
11213
9994
|
`);
|
|
11214
9995
|
}
|
|
11215
9996
|
async function unlinkIfExists(p) {
|
|
11216
9997
|
try {
|
|
11217
|
-
await
|
|
9998
|
+
await fs2.unlink(p);
|
|
11218
9999
|
} catch {
|
|
11219
10000
|
}
|
|
11220
10001
|
}
|
|
@@ -11225,20 +10006,20 @@ function parseContentLength(value) {
|
|
|
11225
10006
|
}
|
|
11226
10007
|
|
|
11227
10008
|
// ../integration-commoncrawl/src/plugin-resolver.ts
|
|
11228
|
-
import
|
|
10009
|
+
import fs3 from "fs";
|
|
11229
10010
|
import { createRequire as createRequire2 } from "module";
|
|
11230
|
-
import
|
|
10011
|
+
import path4 from "path";
|
|
11231
10012
|
function pluginDirFor(pkgJson) {
|
|
11232
|
-
return
|
|
10013
|
+
return path4.dirname(pkgJson);
|
|
11233
10014
|
}
|
|
11234
10015
|
function duckdbPkgJsonFor(pluginDir) {
|
|
11235
|
-
return
|
|
10016
|
+
return path4.join(pluginDir, "node_modules", "@duckdb", "node-api", "package.json");
|
|
11236
10017
|
}
|
|
11237
10018
|
function loadDuckdb(opts = {}) {
|
|
11238
10019
|
const pkgJson = opts.pluginPkgJson ?? PLUGIN_PKG_JSON;
|
|
11239
10020
|
const pluginDir = pluginDirFor(pkgJson);
|
|
11240
10021
|
const duckdbPkg = duckdbPkgJsonFor(pluginDir);
|
|
11241
|
-
if (!
|
|
10022
|
+
if (!fs3.existsSync(duckdbPkg)) {
|
|
11242
10023
|
throw missingDependency(
|
|
11243
10024
|
"@duckdb/node-api is not installed. Run `canonry backlinks install` to enable the backlinks feature.",
|
|
11244
10025
|
{ pluginDir }
|
|
@@ -11256,12 +10037,12 @@ function loadDuckdb(opts = {}) {
|
|
|
11256
10037
|
}
|
|
11257
10038
|
function isDuckdbInstalled(opts = {}) {
|
|
11258
10039
|
const pkgJson = opts.pluginPkgJson ?? PLUGIN_PKG_JSON;
|
|
11259
|
-
return
|
|
10040
|
+
return fs3.existsSync(duckdbPkgJsonFor(pluginDirFor(pkgJson)));
|
|
11260
10041
|
}
|
|
11261
10042
|
function readInstalledVersion(opts = {}) {
|
|
11262
10043
|
const pluginDir = opts.pluginPkgJson ? pluginDirFor(opts.pluginPkgJson) : PLUGIN_DIR;
|
|
11263
10044
|
try {
|
|
11264
|
-
const raw =
|
|
10045
|
+
const raw = fs3.readFileSync(duckdbPkgJsonFor(pluginDir), "utf8");
|
|
11265
10046
|
const pkg = JSON.parse(raw);
|
|
11266
10047
|
return pkg.version ?? null;
|
|
11267
10048
|
} catch {
|
|
@@ -11271,11 +10052,11 @@ function readInstalledVersion(opts = {}) {
|
|
|
11271
10052
|
|
|
11272
10053
|
// ../integration-commoncrawl/src/plugin-installer.ts
|
|
11273
10054
|
import { spawn } from "child_process";
|
|
11274
|
-
import
|
|
11275
|
-
import
|
|
10055
|
+
import fs4 from "fs/promises";
|
|
10056
|
+
import path5 from "path";
|
|
11276
10057
|
async function installDuckdb(opts = {}) {
|
|
11277
10058
|
const pluginDir = opts.pluginDir ?? PLUGIN_DIR;
|
|
11278
|
-
const pluginPkgJson =
|
|
10059
|
+
const pluginPkgJson = path5.join(pluginDir, "package.json");
|
|
11279
10060
|
const spec = opts.spec ?? DUCKDB_SPEC;
|
|
11280
10061
|
const pkgManager = opts.packageManager ?? "npm";
|
|
11281
10062
|
await ensurePluginDir(pluginDir, pluginPkgJson);
|
|
@@ -11291,12 +10072,12 @@ async function installDuckdb(opts = {}) {
|
|
|
11291
10072
|
return { alreadyPresent: false, version, path: pluginDir };
|
|
11292
10073
|
}
|
|
11293
10074
|
async function ensurePluginDir(pluginDir = PLUGIN_DIR, pluginPkgJson = PLUGIN_PKG_JSON) {
|
|
11294
|
-
await
|
|
10075
|
+
await fs4.mkdir(pluginDir, { recursive: true });
|
|
11295
10076
|
try {
|
|
11296
|
-
await
|
|
10077
|
+
await fs4.access(pluginPkgJson);
|
|
11297
10078
|
} catch {
|
|
11298
10079
|
const contents = JSON.stringify({ name: "canonry-plugins", private: true, dependencies: {} }, null, 2);
|
|
11299
|
-
await
|
|
10080
|
+
await fs4.writeFile(pluginPkgJson, `${contents}
|
|
11300
10081
|
`);
|
|
11301
10082
|
}
|
|
11302
10083
|
}
|
|
@@ -11389,8 +10170,8 @@ function quote(s) {
|
|
|
11389
10170
|
}
|
|
11390
10171
|
|
|
11391
10172
|
// ../integration-commoncrawl/src/cache.ts
|
|
11392
|
-
import
|
|
11393
|
-
import
|
|
10173
|
+
import fs5 from "fs";
|
|
10174
|
+
import path6 from "path";
|
|
11394
10175
|
function cacheRoot(opts = {}) {
|
|
11395
10176
|
return opts.cacheDir ?? CC_CACHE_DIR;
|
|
11396
10177
|
}
|
|
@@ -11400,18 +10181,18 @@ function directoryBytesAndLastUsed(dir) {
|
|
|
11400
10181
|
const walk = (p) => {
|
|
11401
10182
|
let stat;
|
|
11402
10183
|
try {
|
|
11403
|
-
stat =
|
|
10184
|
+
stat = fs5.statSync(p);
|
|
11404
10185
|
} catch {
|
|
11405
10186
|
return;
|
|
11406
10187
|
}
|
|
11407
10188
|
if (stat.isDirectory()) {
|
|
11408
10189
|
let entries;
|
|
11409
10190
|
try {
|
|
11410
|
-
entries =
|
|
10191
|
+
entries = fs5.readdirSync(p);
|
|
11411
10192
|
} catch {
|
|
11412
10193
|
return;
|
|
11413
10194
|
}
|
|
11414
|
-
for (const e of entries) walk(
|
|
10195
|
+
for (const e of entries) walk(path6.join(p, e));
|
|
11415
10196
|
} else if (stat.isFile()) {
|
|
11416
10197
|
bytes += stat.size;
|
|
11417
10198
|
const mtime = Math.max(stat.mtimeMs, stat.atimeMs);
|
|
@@ -11426,13 +10207,13 @@ function directoryBytesAndLastUsed(dir) {
|
|
|
11426
10207
|
}
|
|
11427
10208
|
function listCachedReleases(opts = {}) {
|
|
11428
10209
|
const root = cacheRoot(opts);
|
|
11429
|
-
if (!
|
|
11430
|
-
const entries =
|
|
10210
|
+
if (!fs5.existsSync(root)) return [];
|
|
10211
|
+
const entries = fs5.readdirSync(root, { withFileTypes: true });
|
|
11431
10212
|
const result = [];
|
|
11432
10213
|
for (const entry of entries) {
|
|
11433
10214
|
if (!entry.isDirectory()) continue;
|
|
11434
10215
|
if (!RELEASE_ID_REGEX.test(entry.name)) continue;
|
|
11435
|
-
const dir =
|
|
10216
|
+
const dir = path6.join(root, entry.name);
|
|
11436
10217
|
const stats = directoryBytesAndLastUsed(dir);
|
|
11437
10218
|
result.push({ release: entry.name, bytes: stats.bytes, lastUsedAt: stats.lastUsedAt });
|
|
11438
10219
|
}
|
|
@@ -11443,8 +10224,8 @@ function pruneCachedRelease(release, opts = {}) {
|
|
|
11443
10224
|
if (!RELEASE_ID_REGEX.test(release)) {
|
|
11444
10225
|
throw new Error(`Invalid release id: ${release}`);
|
|
11445
10226
|
}
|
|
11446
|
-
const dir =
|
|
11447
|
-
|
|
10227
|
+
const dir = path6.join(cacheRoot(opts), release);
|
|
10228
|
+
fs5.rmSync(dir, { recursive: true, force: true });
|
|
11448
10229
|
}
|
|
11449
10230
|
|
|
11450
10231
|
// ../api-routes/src/backlinks.ts
|
|
@@ -11787,7 +10568,7 @@ async function apiRoutes(app, opts) {
|
|
|
11787
10568
|
}
|
|
11788
10569
|
|
|
11789
10570
|
// src/server.ts
|
|
11790
|
-
import
|
|
10571
|
+
import os5 from "os";
|
|
11791
10572
|
|
|
11792
10573
|
// ../provider-gemini/src/normalize.ts
|
|
11793
10574
|
import { GoogleGenAI } from "@google/genai";
|
|
@@ -13175,8 +11956,8 @@ var localAdapter = {
|
|
|
13175
11956
|
};
|
|
13176
11957
|
|
|
13177
11958
|
// ../provider-cdp/src/adapter.ts
|
|
13178
|
-
import
|
|
13179
|
-
import
|
|
11959
|
+
import path8 from "path";
|
|
11960
|
+
import os3 from "os";
|
|
13180
11961
|
|
|
13181
11962
|
// ../provider-cdp/src/connection.ts
|
|
13182
11963
|
import CDP from "chrome-remote-interface";
|
|
@@ -13540,12 +12321,12 @@ function sleep2(ms) {
|
|
|
13540
12321
|
}
|
|
13541
12322
|
|
|
13542
12323
|
// ../provider-cdp/src/screenshot.ts
|
|
13543
|
-
import
|
|
13544
|
-
import
|
|
12324
|
+
import fs6 from "fs";
|
|
12325
|
+
import path7 from "path";
|
|
13545
12326
|
async function captureElementScreenshot(client, selector, outputPath) {
|
|
13546
|
-
const dir =
|
|
13547
|
-
if (!
|
|
13548
|
-
|
|
12327
|
+
const dir = path7.dirname(outputPath);
|
|
12328
|
+
if (!fs6.existsSync(dir)) {
|
|
12329
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
13549
12330
|
}
|
|
13550
12331
|
let clip;
|
|
13551
12332
|
try {
|
|
@@ -13579,7 +12360,7 @@ async function captureElementScreenshot(client, selector, outputPath) {
|
|
|
13579
12360
|
}
|
|
13580
12361
|
const { data } = await client.Page.captureScreenshot(screenshotParams);
|
|
13581
12362
|
const buffer = Buffer.from(data, "base64");
|
|
13582
|
-
|
|
12363
|
+
fs6.writeFileSync(outputPath, buffer);
|
|
13583
12364
|
return outputPath;
|
|
13584
12365
|
}
|
|
13585
12366
|
|
|
@@ -13640,7 +12421,7 @@ function getConnection(config) {
|
|
|
13640
12421
|
return conn;
|
|
13641
12422
|
}
|
|
13642
12423
|
function getScreenshotDir2() {
|
|
13643
|
-
return
|
|
12424
|
+
return path8.join(os3.homedir(), ".canonry", "screenshots");
|
|
13644
12425
|
}
|
|
13645
12426
|
var cdpChatgptAdapter = {
|
|
13646
12427
|
name: "cdp:chatgpt",
|
|
@@ -13704,7 +12485,7 @@ var cdpChatgptAdapter = {
|
|
|
13704
12485
|
const answerText = await target.extractAnswer(client);
|
|
13705
12486
|
const groundingSources = await target.extractCitations(client);
|
|
13706
12487
|
const screenshotId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
13707
|
-
const screenshotPath =
|
|
12488
|
+
const screenshotPath = path8.join(getScreenshotDir2(), `${screenshotId}.png`);
|
|
13708
12489
|
let capturedScreenshotPath;
|
|
13709
12490
|
try {
|
|
13710
12491
|
capturedScreenshotPath = await captureElementScreenshot(
|
|
@@ -14241,9 +13022,9 @@ function removeWordpressConnection(config, projectName) {
|
|
|
14241
13022
|
|
|
14242
13023
|
// src/job-runner.ts
|
|
14243
13024
|
import crypto19 from "crypto";
|
|
14244
|
-
import
|
|
14245
|
-
import
|
|
14246
|
-
import
|
|
13025
|
+
import fs7 from "fs";
|
|
13026
|
+
import path9 from "path";
|
|
13027
|
+
import os4 from "os";
|
|
14247
13028
|
import { and as and8, eq as eq19, inArray as inArray3, sql as sql6 } from "drizzle-orm";
|
|
14248
13029
|
|
|
14249
13030
|
// src/citation-utils.ts
|
|
@@ -14627,12 +13408,12 @@ var JobRunner = class {
|
|
|
14627
13408
|
competitorDomains
|
|
14628
13409
|
);
|
|
14629
13410
|
let screenshotRelPath = null;
|
|
14630
|
-
if (raw.screenshotPath &&
|
|
13411
|
+
if (raw.screenshotPath && fs7.existsSync(raw.screenshotPath)) {
|
|
14631
13412
|
const snapshotId = crypto19.randomUUID();
|
|
14632
|
-
const screenshotDir =
|
|
14633
|
-
if (!
|
|
14634
|
-
const destPath =
|
|
14635
|
-
|
|
13413
|
+
const screenshotDir = path9.join(os4.homedir(), ".canonry", "screenshots", runId);
|
|
13414
|
+
if (!fs7.existsSync(screenshotDir)) fs7.mkdirSync(screenshotDir, { recursive: true });
|
|
13415
|
+
const destPath = path9.join(screenshotDir, `${snapshotId}.png`);
|
|
13416
|
+
fs7.renameSync(raw.screenshotPath, destPath);
|
|
14636
13417
|
screenshotRelPath = `${runId}/${snapshotId}.png`;
|
|
14637
13418
|
this.db.insert(querySnapshots).values({
|
|
14638
13419
|
id: snapshotId,
|
|
@@ -15421,7 +14202,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
15421
14202
|
|
|
15422
14203
|
// src/commoncrawl-sync.ts
|
|
15423
14204
|
import crypto23 from "crypto";
|
|
15424
|
-
import
|
|
14205
|
+
import path10 from "path";
|
|
15425
14206
|
import { and as and11, eq as eq23, sql as sql8 } from "drizzle-orm";
|
|
15426
14207
|
var log6 = createLogger("CommonCrawlSync");
|
|
15427
14208
|
var INSERT_CHUNK_SIZE = 1e4;
|
|
@@ -15450,9 +14231,9 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
15450
14231
|
error: null
|
|
15451
14232
|
}).where(eq23(ccReleaseSyncs.id, syncId)).run();
|
|
15452
14233
|
const paths = ccReleasePaths(release);
|
|
15453
|
-
const releaseCacheDir =
|
|
15454
|
-
const vertexPath =
|
|
15455
|
-
const edgesPath =
|
|
14234
|
+
const releaseCacheDir = path10.join(deps.cacheDir, release);
|
|
14235
|
+
const vertexPath = path10.join(releaseCacheDir, paths.vertexFilename);
|
|
14236
|
+
const edgesPath = path10.join(releaseCacheDir, paths.edgesFilename);
|
|
15456
14237
|
const [vertex, edges] = await Promise.all([
|
|
15457
14238
|
deps.downloadFile({ url: paths.vertexUrl, destPath: vertexPath }),
|
|
15458
14239
|
deps.downloadFile({ url: paths.edgesUrl, destPath: edgesPath })
|
|
@@ -15612,7 +14393,7 @@ function computeSummary(rows) {
|
|
|
15612
14393
|
|
|
15613
14394
|
// src/backlink-extract.ts
|
|
15614
14395
|
import crypto24 from "crypto";
|
|
15615
|
-
import
|
|
14396
|
+
import fs8 from "fs";
|
|
15616
14397
|
import { and as and12, desc as desc10, eq as eq24 } from "drizzle-orm";
|
|
15617
14398
|
var log7 = createLogger("BacklinkExtract");
|
|
15618
14399
|
function defaultDeps2() {
|
|
@@ -15641,7 +14422,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
15641
14422
|
if (!sync.vertexPath || !sync.edgesPath) {
|
|
15642
14423
|
throw new Error(`Release ${sync.release} is missing cached file paths`);
|
|
15643
14424
|
}
|
|
15644
|
-
if (!
|
|
14425
|
+
if (!fs8.existsSync(sync.vertexPath) || !fs8.existsSync(sync.edgesPath)) {
|
|
15645
14426
|
throw new Error(
|
|
15646
14427
|
`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.`
|
|
15647
14428
|
);
|
|
@@ -16160,8 +14941,8 @@ import crypto27 from "crypto";
|
|
|
16160
14941
|
import { eq as eq28 } from "drizzle-orm";
|
|
16161
14942
|
|
|
16162
14943
|
// src/agent/session.ts
|
|
16163
|
-
import
|
|
16164
|
-
import
|
|
14944
|
+
import fs11 from "fs";
|
|
14945
|
+
import path13 from "path";
|
|
16165
14946
|
import { Agent } from "@mariozechner/pi-agent-core";
|
|
16166
14947
|
import { registerBuiltInApiProviders } from "@mariozechner/pi-ai";
|
|
16167
14948
|
|
|
@@ -16262,26 +15043,26 @@ function buildAgentProvidersResponse(config) {
|
|
|
16262
15043
|
}
|
|
16263
15044
|
|
|
16264
15045
|
// src/agent/skill-paths.ts
|
|
16265
|
-
import
|
|
16266
|
-
import
|
|
15046
|
+
import fs9 from "fs";
|
|
15047
|
+
import path11 from "path";
|
|
16267
15048
|
import { fileURLToPath } from "url";
|
|
16268
15049
|
function resolveAeroSkillDir(pkgDir) {
|
|
16269
|
-
const here = pkgDir ??
|
|
15050
|
+
const here = pkgDir ?? path11.dirname(fileURLToPath(import.meta.url));
|
|
16270
15051
|
const candidates = [
|
|
16271
|
-
|
|
16272
|
-
|
|
16273
|
-
|
|
15052
|
+
path11.join(here, "../assets/agent-workspace/skills/aero"),
|
|
15053
|
+
path11.join(here, "../../assets/agent-workspace/skills/aero"),
|
|
15054
|
+
path11.join(here, "../../../../skills/aero")
|
|
16274
15055
|
];
|
|
16275
15056
|
for (const candidate of candidates) {
|
|
16276
|
-
if (
|
|
15057
|
+
if (fs9.existsSync(path11.join(candidate, "SKILL.md"))) return candidate;
|
|
16277
15058
|
}
|
|
16278
15059
|
throw new Error(`Aero skill not found. Searched:
|
|
16279
15060
|
${candidates.join("\n ")}`);
|
|
16280
15061
|
}
|
|
16281
15062
|
|
|
16282
15063
|
// src/agent/skill-tools.ts
|
|
16283
|
-
import
|
|
16284
|
-
import
|
|
15064
|
+
import fs10 from "fs";
|
|
15065
|
+
import path12 from "path";
|
|
16285
15066
|
import { Type } from "@sinclair/typebox";
|
|
16286
15067
|
var MAX_DOC_CHARS = 2e4;
|
|
16287
15068
|
function textResult(details) {
|
|
@@ -16302,13 +15083,13 @@ function parseDescription(body) {
|
|
|
16302
15083
|
return "(no description)";
|
|
16303
15084
|
}
|
|
16304
15085
|
function scanSkillDocs(skillDir) {
|
|
16305
|
-
const refsDir =
|
|
16306
|
-
if (!
|
|
15086
|
+
const refsDir = path12.join(skillDir ?? resolveAeroSkillDir(), "references");
|
|
15087
|
+
if (!fs10.existsSync(refsDir)) return [];
|
|
16307
15088
|
const entries = [];
|
|
16308
|
-
for (const file of
|
|
15089
|
+
for (const file of fs10.readdirSync(refsDir)) {
|
|
16309
15090
|
if (!file.endsWith(".md")) continue;
|
|
16310
|
-
const filePath =
|
|
16311
|
-
const body =
|
|
15091
|
+
const filePath = path12.join(refsDir, file);
|
|
15092
|
+
const body = fs10.readFileSync(filePath, "utf-8");
|
|
16312
15093
|
entries.push({
|
|
16313
15094
|
slug: file.replace(/\.md$/, ""),
|
|
16314
15095
|
description: parseDescription(body),
|
|
@@ -16351,8 +15132,8 @@ function buildReadSkillDocTool() {
|
|
|
16351
15132
|
availableSlugs: docs.map((d) => d.slug)
|
|
16352
15133
|
});
|
|
16353
15134
|
}
|
|
16354
|
-
const filePath =
|
|
16355
|
-
const content =
|
|
15135
|
+
const filePath = path12.join(skillDir, "references", `${match.slug}.md`);
|
|
15136
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
16356
15137
|
if (content.length > MAX_DOC_CHARS) {
|
|
16357
15138
|
return textResult({
|
|
16358
15139
|
slug: match.slug,
|
|
@@ -16747,7 +15528,7 @@ function buildAddCompetitorsTool(ctx) {
|
|
|
16747
15528
|
return {
|
|
16748
15529
|
name: "add_competitors",
|
|
16749
15530
|
label: "Add competitors",
|
|
16750
|
-
description: "Append competitor domains to the project.
|
|
15531
|
+
description: "Append competitor domains to the project. Existing competitors are skipped by the API.",
|
|
16751
15532
|
parameters: AddCompetitorsSchema,
|
|
16752
15533
|
execute: async (_toolCallId, params) => {
|
|
16753
15534
|
const existing = await ctx.client.listCompetitors(ctx.projectName);
|
|
@@ -16756,8 +15537,7 @@ function buildAddCompetitorsTool(ctx) {
|
|
|
16756
15537
|
if (newDomains.length === 0) {
|
|
16757
15538
|
return textResult2({ added: [], alreadyTracked: params.domains });
|
|
16758
15539
|
}
|
|
16759
|
-
|
|
16760
|
-
await ctx.client.putCompetitors(ctx.projectName, merged);
|
|
15540
|
+
await ctx.client.appendCompetitors(ctx.projectName, newDomains);
|
|
16761
15541
|
return textResult2({ added: newDomains, alreadyTracked: params.domains.filter((d) => existingDomains.has(d)) });
|
|
16762
15542
|
}
|
|
16763
15543
|
};
|
|
@@ -16906,10 +15686,10 @@ function ensureBuiltinsRegistered() {
|
|
|
16906
15686
|
}
|
|
16907
15687
|
function loadAeroSystemPrompt(pkgDir) {
|
|
16908
15688
|
const skillDir = resolveAeroSkillDir(pkgDir);
|
|
16909
|
-
const skillBody =
|
|
16910
|
-
const soulPath =
|
|
16911
|
-
if (!
|
|
16912
|
-
const soulBody =
|
|
15689
|
+
const skillBody = fs11.readFileSync(path13.join(skillDir, "SKILL.md"), "utf-8");
|
|
15690
|
+
const soulPath = path13.join(skillDir, "soul.md");
|
|
15691
|
+
if (!fs11.existsSync(soulPath)) return skillBody;
|
|
15692
|
+
const soulBody = fs11.readFileSync(soulPath, "utf-8");
|
|
16913
15693
|
return `${soulBody.trimEnd()}
|
|
16914
15694
|
|
|
16915
15695
|
---
|
|
@@ -17698,593 +16478,6 @@ function registerAgentRoutes(app, opts) {
|
|
|
17698
16478
|
);
|
|
17699
16479
|
}
|
|
17700
16480
|
|
|
17701
|
-
// src/client.ts
|
|
17702
|
-
function createApiClient() {
|
|
17703
|
-
const config = loadConfig();
|
|
17704
|
-
const basePathResolved = !!config.basePath || "CANONRY_BASE_PATH" in process.env;
|
|
17705
|
-
return new ApiClient(config.apiUrl, config.apiKey, { skipProbe: basePathResolved });
|
|
17706
|
-
}
|
|
17707
|
-
var ApiClient = class {
|
|
17708
|
-
baseUrl;
|
|
17709
|
-
originUrl;
|
|
17710
|
-
apiKey;
|
|
17711
|
-
probePromise = null;
|
|
17712
|
-
probeSkipped;
|
|
17713
|
-
constructor(baseUrl, apiKey, opts) {
|
|
17714
|
-
this.originUrl = baseUrl.replace(/\/$/, "");
|
|
17715
|
-
this.baseUrl = this.originUrl + "/api/v1";
|
|
17716
|
-
this.apiKey = apiKey;
|
|
17717
|
-
this.probeSkipped = opts?.skipProbe ?? false;
|
|
17718
|
-
}
|
|
17719
|
-
/**
|
|
17720
|
-
* On first API call, probe /health to auto-discover basePath when the user
|
|
17721
|
-
* hasn't configured one locally. This lets `canonry run` in a separate shell
|
|
17722
|
-
* discover that the server is running at e.g. /canonry/ without requiring
|
|
17723
|
-
* config.yaml edits or CANONRY_BASE_PATH in every shell.
|
|
17724
|
-
*/
|
|
17725
|
-
probeBasePath() {
|
|
17726
|
-
if (this.probeSkipped) return Promise.resolve();
|
|
17727
|
-
if (!this.probePromise) {
|
|
17728
|
-
this.probePromise = (async () => {
|
|
17729
|
-
try {
|
|
17730
|
-
const origin = new URL(this.originUrl).origin;
|
|
17731
|
-
const res = await fetch(`${origin}/health`, {
|
|
17732
|
-
signal: AbortSignal.timeout(2e3)
|
|
17733
|
-
});
|
|
17734
|
-
if (res.ok) {
|
|
17735
|
-
const body = await res.json();
|
|
17736
|
-
if (body.basePath && typeof body.basePath === "string") {
|
|
17737
|
-
const normalized = "/" + body.basePath.replace(/^\/|\/$/g, "");
|
|
17738
|
-
if (normalized !== "/") {
|
|
17739
|
-
this.originUrl = origin + normalized;
|
|
17740
|
-
this.baseUrl = this.originUrl + "/api/v1";
|
|
17741
|
-
}
|
|
17742
|
-
}
|
|
17743
|
-
}
|
|
17744
|
-
} catch {
|
|
17745
|
-
}
|
|
17746
|
-
})();
|
|
17747
|
-
}
|
|
17748
|
-
return this.probePromise;
|
|
17749
|
-
}
|
|
17750
|
-
async request(method, path16, body) {
|
|
17751
|
-
await this.probeBasePath();
|
|
17752
|
-
const url = `${this.baseUrl}${path16}`;
|
|
17753
|
-
const serializedBody = body != null ? JSON.stringify(body) : void 0;
|
|
17754
|
-
const headers = {
|
|
17755
|
-
"Authorization": `Bearer ${this.apiKey}`,
|
|
17756
|
-
"Accept": "application/json",
|
|
17757
|
-
...serializedBody != null ? { "Content-Type": "application/json" } : {}
|
|
17758
|
-
};
|
|
17759
|
-
let res;
|
|
17760
|
-
try {
|
|
17761
|
-
res = await fetch(url, {
|
|
17762
|
-
method,
|
|
17763
|
-
headers,
|
|
17764
|
-
body: serializedBody
|
|
17765
|
-
});
|
|
17766
|
-
} catch (err) {
|
|
17767
|
-
if (err instanceof CliError) throw err;
|
|
17768
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
17769
|
-
if (msg.includes("fetch failed") || msg.includes("ECONNREFUSED") || msg.includes("connect ECONNREFUSED")) {
|
|
17770
|
-
throw new CliError({
|
|
17771
|
-
code: "CONNECTION_ERROR",
|
|
17772
|
-
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).`,
|
|
17773
|
-
exitCode: EXIT_SYSTEM_ERROR
|
|
17774
|
-
});
|
|
17775
|
-
}
|
|
17776
|
-
throw new CliError({ code: "CONNECTION_ERROR", message: msg, exitCode: EXIT_SYSTEM_ERROR });
|
|
17777
|
-
}
|
|
17778
|
-
if (!res.ok) {
|
|
17779
|
-
let errorBody;
|
|
17780
|
-
try {
|
|
17781
|
-
errorBody = await res.json();
|
|
17782
|
-
} catch {
|
|
17783
|
-
errorBody = { error: { code: "UNKNOWN", message: res.statusText } };
|
|
17784
|
-
}
|
|
17785
|
-
const errorObj = errorBody && typeof errorBody === "object" && "error" in errorBody && errorBody.error && typeof errorBody.error === "object" ? errorBody.error : null;
|
|
17786
|
-
const msg = errorObj?.message ? String(errorObj.message) : `HTTP ${res.status}: ${res.statusText}`;
|
|
17787
|
-
const code = errorObj?.code ? String(errorObj.code) : "API_ERROR";
|
|
17788
|
-
const exitCode = res.status >= 500 ? EXIT_SYSTEM_ERROR : EXIT_USER_ERROR;
|
|
17789
|
-
throw new CliError({ code, message: msg, exitCode, details: { httpStatus: res.status } });
|
|
17790
|
-
}
|
|
17791
|
-
if (res.status === 204) {
|
|
17792
|
-
return void 0;
|
|
17793
|
-
}
|
|
17794
|
-
return await res.json();
|
|
17795
|
-
}
|
|
17796
|
-
async getAgentTranscript(project) {
|
|
17797
|
-
return this.request(
|
|
17798
|
-
"GET",
|
|
17799
|
-
`/projects/${encodeURIComponent(project)}/agent/transcript`
|
|
17800
|
-
);
|
|
17801
|
-
}
|
|
17802
|
-
async resetAgentTranscript(project) {
|
|
17803
|
-
await this.request(
|
|
17804
|
-
"DELETE",
|
|
17805
|
-
`/projects/${encodeURIComponent(project)}/agent/transcript`
|
|
17806
|
-
);
|
|
17807
|
-
}
|
|
17808
|
-
async listAgentProviders(project) {
|
|
17809
|
-
return this.request(
|
|
17810
|
-
"GET",
|
|
17811
|
-
`/projects/${encodeURIComponent(project)}/agent/providers`
|
|
17812
|
-
);
|
|
17813
|
-
}
|
|
17814
|
-
async listAgentMemory(project) {
|
|
17815
|
-
return this.request(
|
|
17816
|
-
"GET",
|
|
17817
|
-
`/projects/${encodeURIComponent(project)}/agent/memory`
|
|
17818
|
-
);
|
|
17819
|
-
}
|
|
17820
|
-
async setAgentMemory(project, body) {
|
|
17821
|
-
return this.request(
|
|
17822
|
-
"PUT",
|
|
17823
|
-
`/projects/${encodeURIComponent(project)}/agent/memory`,
|
|
17824
|
-
body
|
|
17825
|
-
);
|
|
17826
|
-
}
|
|
17827
|
-
async forgetAgentMemory(project, key) {
|
|
17828
|
-
return this.request(
|
|
17829
|
-
"DELETE",
|
|
17830
|
-
`/projects/${encodeURIComponent(project)}/agent/memory`,
|
|
17831
|
-
{ key }
|
|
17832
|
-
);
|
|
17833
|
-
}
|
|
17834
|
-
/**
|
|
17835
|
-
* POST a request whose response body the caller intends to consume as a
|
|
17836
|
-
* stream (e.g. the Aero agent SSE endpoint). Shares the probe + auth +
|
|
17837
|
-
* structured-error behavior of `request()`; the caller reads `res.body`
|
|
17838
|
-
* and releases the response when done.
|
|
17839
|
-
*/
|
|
17840
|
-
async streamPost(path16, body, signal) {
|
|
17841
|
-
await this.probeBasePath();
|
|
17842
|
-
const url = `${this.baseUrl}${path16}`;
|
|
17843
|
-
const headers = {
|
|
17844
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
17845
|
-
"Content-Type": "application/json",
|
|
17846
|
-
Accept: "text/event-stream"
|
|
17847
|
-
};
|
|
17848
|
-
let res;
|
|
17849
|
-
try {
|
|
17850
|
-
res = await fetch(url, {
|
|
17851
|
-
method: "POST",
|
|
17852
|
-
headers,
|
|
17853
|
-
body: JSON.stringify(body ?? {}),
|
|
17854
|
-
signal
|
|
17855
|
-
});
|
|
17856
|
-
} catch (err) {
|
|
17857
|
-
if (err instanceof CliError) throw err;
|
|
17858
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
17859
|
-
if (msg.includes("fetch failed") || msg.includes("ECONNREFUSED") || msg.includes("connect ECONNREFUSED")) {
|
|
17860
|
-
throw new CliError({
|
|
17861
|
-
code: "CONNECTION_ERROR",
|
|
17862
|
-
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).`,
|
|
17863
|
-
exitCode: EXIT_SYSTEM_ERROR
|
|
17864
|
-
});
|
|
17865
|
-
}
|
|
17866
|
-
throw new CliError({ code: "CONNECTION_ERROR", message: msg, exitCode: EXIT_SYSTEM_ERROR });
|
|
17867
|
-
}
|
|
17868
|
-
if (!res.ok || !res.body) {
|
|
17869
|
-
let errorBody;
|
|
17870
|
-
try {
|
|
17871
|
-
errorBody = await res.json();
|
|
17872
|
-
} catch {
|
|
17873
|
-
errorBody = { error: { code: "UNKNOWN", message: res.statusText } };
|
|
17874
|
-
}
|
|
17875
|
-
const errorObj = errorBody && typeof errorBody === "object" && "error" in errorBody && errorBody.error ? errorBody.error : null;
|
|
17876
|
-
const msg = errorObj?.message ? String(errorObj.message) : `HTTP ${res.status}: ${res.statusText}`;
|
|
17877
|
-
const code = errorObj?.code ? String(errorObj.code) : "API_ERROR";
|
|
17878
|
-
const exitCode = res.status >= 500 ? EXIT_SYSTEM_ERROR : EXIT_USER_ERROR;
|
|
17879
|
-
throw new CliError({ code, message: msg, exitCode, details: { httpStatus: res.status } });
|
|
17880
|
-
}
|
|
17881
|
-
return res;
|
|
17882
|
-
}
|
|
17883
|
-
async putProject(name, body) {
|
|
17884
|
-
return this.request("PUT", `/projects/${encodeURIComponent(name)}`, body);
|
|
17885
|
-
}
|
|
17886
|
-
async listProjects() {
|
|
17887
|
-
return this.request("GET", "/projects");
|
|
17888
|
-
}
|
|
17889
|
-
async getProject(name) {
|
|
17890
|
-
return this.request("GET", `/projects/${encodeURIComponent(name)}`);
|
|
17891
|
-
}
|
|
17892
|
-
async deleteProject(name) {
|
|
17893
|
-
await this.request("DELETE", `/projects/${encodeURIComponent(name)}`);
|
|
17894
|
-
}
|
|
17895
|
-
async putKeywords(project, keywords2) {
|
|
17896
|
-
await this.request("PUT", `/projects/${encodeURIComponent(project)}/keywords`, { keywords: keywords2 });
|
|
17897
|
-
}
|
|
17898
|
-
async listKeywords(project) {
|
|
17899
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/keywords`);
|
|
17900
|
-
}
|
|
17901
|
-
async deleteKeywords(project, keywords2) {
|
|
17902
|
-
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/keywords`, { keywords: keywords2 });
|
|
17903
|
-
}
|
|
17904
|
-
async appendKeywords(project, keywords2) {
|
|
17905
|
-
await this.request("POST", `/projects/${encodeURIComponent(project)}/keywords`, { keywords: keywords2 });
|
|
17906
|
-
}
|
|
17907
|
-
async putCompetitors(project, competitors2) {
|
|
17908
|
-
await this.request("PUT", `/projects/${encodeURIComponent(project)}/competitors`, { competitors: competitors2 });
|
|
17909
|
-
}
|
|
17910
|
-
async listCompetitors(project) {
|
|
17911
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/competitors`);
|
|
17912
|
-
}
|
|
17913
|
-
async triggerRun(project, body) {
|
|
17914
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/runs`, body ?? {});
|
|
17915
|
-
}
|
|
17916
|
-
async listRuns(project, limit) {
|
|
17917
|
-
const query = limit != null ? `?limit=${encodeURIComponent(String(limit))}` : "";
|
|
17918
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/runs${query}`);
|
|
17919
|
-
}
|
|
17920
|
-
async getLatestRun(project) {
|
|
17921
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/runs/latest`);
|
|
17922
|
-
}
|
|
17923
|
-
async getRun(id) {
|
|
17924
|
-
return this.request("GET", `/runs/${encodeURIComponent(id)}`);
|
|
17925
|
-
}
|
|
17926
|
-
async cancelRun(id) {
|
|
17927
|
-
return this.request("POST", `/runs/${encodeURIComponent(id)}/cancel`);
|
|
17928
|
-
}
|
|
17929
|
-
async getTimeline(project) {
|
|
17930
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/timeline`);
|
|
17931
|
-
}
|
|
17932
|
-
async getHistory(project) {
|
|
17933
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/history`);
|
|
17934
|
-
}
|
|
17935
|
-
async getExport(project) {
|
|
17936
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/export`);
|
|
17937
|
-
}
|
|
17938
|
-
async apply(config) {
|
|
17939
|
-
return this.request("POST", "/apply", config);
|
|
17940
|
-
}
|
|
17941
|
-
async getStatus(project) {
|
|
17942
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}`);
|
|
17943
|
-
}
|
|
17944
|
-
async getSettings() {
|
|
17945
|
-
return this.request("GET", "/settings");
|
|
17946
|
-
}
|
|
17947
|
-
async createSnapshot(body) {
|
|
17948
|
-
return this.request("POST", "/snapshot", body);
|
|
17949
|
-
}
|
|
17950
|
-
async updateProvider(name, body) {
|
|
17951
|
-
return this.request("PUT", `/settings/providers/${encodeURIComponent(name)}`, body);
|
|
17952
|
-
}
|
|
17953
|
-
async putSchedule(project, body) {
|
|
17954
|
-
return this.request("PUT", `/projects/${encodeURIComponent(project)}/schedule`, body);
|
|
17955
|
-
}
|
|
17956
|
-
async getSchedule(project) {
|
|
17957
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/schedule`);
|
|
17958
|
-
}
|
|
17959
|
-
async deleteSchedule(project) {
|
|
17960
|
-
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/schedule`);
|
|
17961
|
-
}
|
|
17962
|
-
async createNotification(project, body) {
|
|
17963
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/notifications`, body);
|
|
17964
|
-
}
|
|
17965
|
-
async listNotifications(project) {
|
|
17966
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/notifications`);
|
|
17967
|
-
}
|
|
17968
|
-
async deleteNotification(project, id) {
|
|
17969
|
-
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/notifications/${encodeURIComponent(id)}`);
|
|
17970
|
-
}
|
|
17971
|
-
async testNotification(project, id) {
|
|
17972
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/notifications/${encodeURIComponent(id)}/test`);
|
|
17973
|
-
}
|
|
17974
|
-
async addLocation(project, body) {
|
|
17975
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/locations`, body);
|
|
17976
|
-
}
|
|
17977
|
-
async listLocations(project) {
|
|
17978
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/locations`);
|
|
17979
|
-
}
|
|
17980
|
-
async removeLocation(project, label) {
|
|
17981
|
-
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/locations/${encodeURIComponent(label)}`);
|
|
17982
|
-
}
|
|
17983
|
-
async setDefaultLocation(project, label) {
|
|
17984
|
-
return this.request("PUT", `/projects/${encodeURIComponent(project)}/locations/default`, { label });
|
|
17985
|
-
}
|
|
17986
|
-
async getTelemetry() {
|
|
17987
|
-
return this.request("GET", "/telemetry");
|
|
17988
|
-
}
|
|
17989
|
-
async updateTelemetry(enabled) {
|
|
17990
|
-
return this.request("PUT", "/telemetry", { enabled });
|
|
17991
|
-
}
|
|
17992
|
-
async generateKeywords(project, provider, count) {
|
|
17993
|
-
return this.request(
|
|
17994
|
-
"POST",
|
|
17995
|
-
`/projects/${encodeURIComponent(project)}/keywords/generate`,
|
|
17996
|
-
{ provider, count }
|
|
17997
|
-
);
|
|
17998
|
-
}
|
|
17999
|
-
// Google connection management
|
|
18000
|
-
async googleConnect(project, body) {
|
|
18001
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/connect`, body);
|
|
18002
|
-
}
|
|
18003
|
-
async googleConnections(project) {
|
|
18004
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/connections`);
|
|
18005
|
-
}
|
|
18006
|
-
async googleDisconnect(project, type) {
|
|
18007
|
-
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/google/connections/${encodeURIComponent(type)}`);
|
|
18008
|
-
}
|
|
18009
|
-
async googleProperties(project) {
|
|
18010
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/properties`);
|
|
18011
|
-
}
|
|
18012
|
-
async googleSetProperty(project, type, propertyId) {
|
|
18013
|
-
return this.request("PUT", `/projects/${encodeURIComponent(project)}/google/connections/${encodeURIComponent(type)}/property`, { propertyId });
|
|
18014
|
-
}
|
|
18015
|
-
async googleSetSitemap(project, type, sitemapUrl) {
|
|
18016
|
-
return this.request("PUT", `/projects/${encodeURIComponent(project)}/google/connections/${encodeURIComponent(type)}/sitemap`, { sitemapUrl });
|
|
18017
|
-
}
|
|
18018
|
-
// GSC data
|
|
18019
|
-
async gscSync(project, body) {
|
|
18020
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/sync`, body ?? {});
|
|
18021
|
-
}
|
|
18022
|
-
async gscPerformance(project, params) {
|
|
18023
|
-
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
18024
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/performance${qs}`);
|
|
18025
|
-
}
|
|
18026
|
-
async gscInspect(project, url) {
|
|
18027
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/inspect`, { url });
|
|
18028
|
-
}
|
|
18029
|
-
async gscInspections(project, params) {
|
|
18030
|
-
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
18031
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/inspections${qs}`);
|
|
18032
|
-
}
|
|
18033
|
-
async gscDeindexed(project) {
|
|
18034
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/deindexed`);
|
|
18035
|
-
}
|
|
18036
|
-
async gscCoverage(project) {
|
|
18037
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/coverage`);
|
|
18038
|
-
}
|
|
18039
|
-
async gscCoverageHistory(project, params) {
|
|
18040
|
-
const qs = params?.limit != null ? `?limit=${params.limit}` : "";
|
|
18041
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/coverage/history${qs}`);
|
|
18042
|
-
}
|
|
18043
|
-
async gscInspectSitemap(project, body) {
|
|
18044
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/inspect-sitemap`, body ?? {});
|
|
18045
|
-
}
|
|
18046
|
-
async gscSitemaps(project) {
|
|
18047
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/sitemaps`);
|
|
18048
|
-
}
|
|
18049
|
-
async gscDiscoverSitemaps(project) {
|
|
18050
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/discover-sitemaps`, {});
|
|
18051
|
-
}
|
|
18052
|
-
// Analytics
|
|
18053
|
-
async getAnalyticsMetrics(project, window) {
|
|
18054
|
-
const qs = window ? `?window=${encodeURIComponent(window)}` : "";
|
|
18055
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/metrics${qs}`);
|
|
18056
|
-
}
|
|
18057
|
-
async getAnalyticsGaps(project, window) {
|
|
18058
|
-
const qs = window ? `?window=${encodeURIComponent(window)}` : "";
|
|
18059
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/gaps${qs}`);
|
|
18060
|
-
}
|
|
18061
|
-
async getAnalyticsSources(project, window) {
|
|
18062
|
-
const qs = window ? `?window=${encodeURIComponent(window)}` : "";
|
|
18063
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/sources${qs}`);
|
|
18064
|
-
}
|
|
18065
|
-
// Google Indexing API
|
|
18066
|
-
async googleRequestIndexing(project, body) {
|
|
18067
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/indexing/request`, body);
|
|
18068
|
-
}
|
|
18069
|
-
// Bing Webmaster Tools
|
|
18070
|
-
async bingConnect(project, body) {
|
|
18071
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/connect`, body);
|
|
18072
|
-
}
|
|
18073
|
-
async bingDisconnect(project) {
|
|
18074
|
-
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/bing/disconnect`);
|
|
18075
|
-
}
|
|
18076
|
-
async bingStatus(project) {
|
|
18077
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/status`);
|
|
18078
|
-
}
|
|
18079
|
-
async bingSites(project) {
|
|
18080
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/sites`);
|
|
18081
|
-
}
|
|
18082
|
-
async bingSetSite(project, siteUrl) {
|
|
18083
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/set-site`, { siteUrl });
|
|
18084
|
-
}
|
|
18085
|
-
async bingCoverage(project) {
|
|
18086
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/coverage`);
|
|
18087
|
-
}
|
|
18088
|
-
async bingCoverageHistory(project, params) {
|
|
18089
|
-
const qs = params?.limit != null ? `?limit=${params.limit}` : "";
|
|
18090
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/coverage/history${qs}`);
|
|
18091
|
-
}
|
|
18092
|
-
async bingInspections(project, params) {
|
|
18093
|
-
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
18094
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/inspections${qs}`);
|
|
18095
|
-
}
|
|
18096
|
-
async bingInspectUrl(project, url) {
|
|
18097
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/inspect-url`, { url });
|
|
18098
|
-
}
|
|
18099
|
-
async bingInspectSitemap(project, body) {
|
|
18100
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/inspect-sitemap`, body ?? {});
|
|
18101
|
-
}
|
|
18102
|
-
async bingRequestIndexing(project, body) {
|
|
18103
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/request-indexing`, body);
|
|
18104
|
-
}
|
|
18105
|
-
async bingPerformance(project, params) {
|
|
18106
|
-
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
18107
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/performance${qs}`);
|
|
18108
|
-
}
|
|
18109
|
-
// CDP browser provider
|
|
18110
|
-
async getCdpStatus() {
|
|
18111
|
-
return this.request("GET", "/cdp/status");
|
|
18112
|
-
}
|
|
18113
|
-
async cdpScreenshot(query, targets) {
|
|
18114
|
-
return this.request("POST", "/cdp/screenshot", { query, targets });
|
|
18115
|
-
}
|
|
18116
|
-
async getBrowserDiff(project, runId) {
|
|
18117
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/runs/${encodeURIComponent(runId)}/browser-diff`);
|
|
18118
|
-
}
|
|
18119
|
-
// Google Analytics 4
|
|
18120
|
-
async gaConnect(project, body) {
|
|
18121
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/ga/connect`, body);
|
|
18122
|
-
}
|
|
18123
|
-
async gaDisconnect(project) {
|
|
18124
|
-
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/ga/disconnect`);
|
|
18125
|
-
}
|
|
18126
|
-
async gaStatus(project) {
|
|
18127
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/status`);
|
|
18128
|
-
}
|
|
18129
|
-
async gaSync(project, body) {
|
|
18130
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/ga/sync`, body ?? {});
|
|
18131
|
-
}
|
|
18132
|
-
async gaTraffic(project, params) {
|
|
18133
|
-
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
18134
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/traffic${qs}`);
|
|
18135
|
-
}
|
|
18136
|
-
async gaCoverage(project) {
|
|
18137
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/coverage`);
|
|
18138
|
-
}
|
|
18139
|
-
async gaAiReferralHistory(project, params) {
|
|
18140
|
-
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
18141
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/ai-referral-history${qs}`);
|
|
18142
|
-
}
|
|
18143
|
-
async gaSocialReferralHistory(project, params) {
|
|
18144
|
-
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
18145
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/social-referral-history${qs}`);
|
|
18146
|
-
}
|
|
18147
|
-
async gaSocialReferralTrend(project) {
|
|
18148
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/social-referral-trend`);
|
|
18149
|
-
}
|
|
18150
|
-
async gaAttributionTrend(project) {
|
|
18151
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/attribution-trend`);
|
|
18152
|
-
}
|
|
18153
|
-
async gaSessionHistory(project, params) {
|
|
18154
|
-
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
18155
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/session-history${qs}`);
|
|
18156
|
-
}
|
|
18157
|
-
async wordpressConnect(project, body) {
|
|
18158
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/connect`, body);
|
|
18159
|
-
}
|
|
18160
|
-
async wordpressDisconnect(project) {
|
|
18161
|
-
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/wordpress/disconnect`);
|
|
18162
|
-
}
|
|
18163
|
-
async wordpressStatus(project) {
|
|
18164
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/status`);
|
|
18165
|
-
}
|
|
18166
|
-
async wordpressPages(project, env) {
|
|
18167
|
-
const qs = env ? `?env=${encodeURIComponent(env)}` : "";
|
|
18168
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/pages${qs}`);
|
|
18169
|
-
}
|
|
18170
|
-
async wordpressPage(project, slug, env) {
|
|
18171
|
-
const params = new URLSearchParams({ slug });
|
|
18172
|
-
if (env) params.set("env", env);
|
|
18173
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/page?${params.toString()}`);
|
|
18174
|
-
}
|
|
18175
|
-
async wordpressCreatePage(project, body) {
|
|
18176
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/pages`, body);
|
|
18177
|
-
}
|
|
18178
|
-
async wordpressUpdatePage(project, body) {
|
|
18179
|
-
return this.request("PUT", `/projects/${encodeURIComponent(project)}/wordpress/page`, body);
|
|
18180
|
-
}
|
|
18181
|
-
async wordpressSetMeta(project, body) {
|
|
18182
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/page/meta`, body);
|
|
18183
|
-
}
|
|
18184
|
-
async wordpressBulkSetMeta(project, body) {
|
|
18185
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/pages/meta/bulk`, body);
|
|
18186
|
-
}
|
|
18187
|
-
async wordpressSchema(project, slug, env) {
|
|
18188
|
-
const params = new URLSearchParams({ slug });
|
|
18189
|
-
if (env) params.set("env", env);
|
|
18190
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/schema?${params.toString()}`);
|
|
18191
|
-
}
|
|
18192
|
-
async wordpressSetSchema(project, body) {
|
|
18193
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/schema/manual`, body);
|
|
18194
|
-
}
|
|
18195
|
-
async wordpressSchemaDeploy(project, body) {
|
|
18196
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/schema/deploy`, body);
|
|
18197
|
-
}
|
|
18198
|
-
async wordpressSchemaStatus(project, env) {
|
|
18199
|
-
const params = new URLSearchParams();
|
|
18200
|
-
if (env) params.set("env", env);
|
|
18201
|
-
const qs = params.toString();
|
|
18202
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/schema/status${qs ? `?${qs}` : ""}`);
|
|
18203
|
-
}
|
|
18204
|
-
async wordpressOnboard(project, body) {
|
|
18205
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/onboard`, body);
|
|
18206
|
-
}
|
|
18207
|
-
async wordpressLlmsTxt(project, env) {
|
|
18208
|
-
const qs = env ? `?env=${encodeURIComponent(env)}` : "";
|
|
18209
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/llms-txt${qs}`);
|
|
18210
|
-
}
|
|
18211
|
-
async wordpressSetLlmsTxt(project, body) {
|
|
18212
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/llms-txt/manual`, body);
|
|
18213
|
-
}
|
|
18214
|
-
async wordpressAudit(project, env) {
|
|
18215
|
-
const qs = env ? `?env=${encodeURIComponent(env)}` : "";
|
|
18216
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/audit${qs}`);
|
|
18217
|
-
}
|
|
18218
|
-
async wordpressDiff(project, slug) {
|
|
18219
|
-
const params = new URLSearchParams({ slug });
|
|
18220
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/diff?${params.toString()}`);
|
|
18221
|
-
}
|
|
18222
|
-
async wordpressStagingStatus(project) {
|
|
18223
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/staging/status`);
|
|
18224
|
-
}
|
|
18225
|
-
async wordpressStagingPush(project) {
|
|
18226
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/staging/push`);
|
|
18227
|
-
}
|
|
18228
|
-
// ── Intelligence ──────────────────────────────────────────────────────
|
|
18229
|
-
async getInsights(project, opts) {
|
|
18230
|
-
const params = new URLSearchParams();
|
|
18231
|
-
if (opts?.dismissed) params.set("dismissed", "true");
|
|
18232
|
-
if (opts?.runId) params.set("runId", opts.runId);
|
|
18233
|
-
const qs = params.toString();
|
|
18234
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/insights${qs ? `?${qs}` : ""}`);
|
|
18235
|
-
}
|
|
18236
|
-
async dismissInsight(project, id) {
|
|
18237
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/insights/${encodeURIComponent(id)}/dismiss`);
|
|
18238
|
-
}
|
|
18239
|
-
async getHealth(project) {
|
|
18240
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/health/latest`);
|
|
18241
|
-
}
|
|
18242
|
-
async getHealthHistory(project, limit) {
|
|
18243
|
-
const qs = limit ? `?limit=${limit}` : "";
|
|
18244
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/health/history${qs}`);
|
|
18245
|
-
}
|
|
18246
|
-
// --- Backlinks ---------------------------------------------------------
|
|
18247
|
-
async backlinksStatus() {
|
|
18248
|
-
return this.request("GET", "/backlinks/status");
|
|
18249
|
-
}
|
|
18250
|
-
async backlinksInstall() {
|
|
18251
|
-
return this.request("POST", "/backlinks/install");
|
|
18252
|
-
}
|
|
18253
|
-
async backlinksTriggerSync(release) {
|
|
18254
|
-
return this.request("POST", "/backlinks/syncs", { release });
|
|
18255
|
-
}
|
|
18256
|
-
async backlinksLatestSync() {
|
|
18257
|
-
return this.request("GET", "/backlinks/syncs/latest");
|
|
18258
|
-
}
|
|
18259
|
-
async backlinksListSyncs() {
|
|
18260
|
-
return this.request("GET", "/backlinks/syncs");
|
|
18261
|
-
}
|
|
18262
|
-
async backlinksCachedReleases() {
|
|
18263
|
-
return this.request("GET", "/backlinks/releases");
|
|
18264
|
-
}
|
|
18265
|
-
async backlinksPruneCache(release) {
|
|
18266
|
-
return this.request("DELETE", `/backlinks/cache/${encodeURIComponent(release)}`);
|
|
18267
|
-
}
|
|
18268
|
-
async backlinksExtract(project, release) {
|
|
18269
|
-
return this.request("POST", `/projects/${encodeURIComponent(project)}/backlinks/extract`, release ? { release } : {});
|
|
18270
|
-
}
|
|
18271
|
-
async backlinksSummary(project, release) {
|
|
18272
|
-
const qs = release ? `?release=${encodeURIComponent(release)}` : "";
|
|
18273
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/backlinks/summary${qs}`);
|
|
18274
|
-
}
|
|
18275
|
-
async backlinksDomains(project, opts = {}) {
|
|
18276
|
-
const qs = new URLSearchParams();
|
|
18277
|
-
if (opts.limit !== void 0) qs.set("limit", String(opts.limit));
|
|
18278
|
-
if (opts.offset !== void 0) qs.set("offset", String(opts.offset));
|
|
18279
|
-
if (opts.release) qs.set("release", opts.release);
|
|
18280
|
-
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
18281
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/backlinks/domains${suffix}`);
|
|
18282
|
-
}
|
|
18283
|
-
async backlinksHistory(project) {
|
|
18284
|
-
return this.request("GET", `/projects/${encodeURIComponent(project)}/backlinks/history`);
|
|
18285
|
-
}
|
|
18286
|
-
};
|
|
18287
|
-
|
|
18288
16481
|
// src/snapshot-service.ts
|
|
18289
16482
|
import { runAeoAudit } from "@ainyc/aeo-audit";
|
|
18290
16483
|
|
|
@@ -18307,13 +16500,13 @@ function extractHostname(domain) {
|
|
|
18307
16500
|
function fetchWithPinnedAddress(target) {
|
|
18308
16501
|
return new Promise((resolve) => {
|
|
18309
16502
|
const port = target.url.port ? Number(target.url.port) : 443;
|
|
18310
|
-
const
|
|
16503
|
+
const path15 = target.url.pathname + target.url.search;
|
|
18311
16504
|
const req = https2.request(
|
|
18312
16505
|
{
|
|
18313
16506
|
hostname: target.address,
|
|
18314
16507
|
family: target.family,
|
|
18315
16508
|
port,
|
|
18316
|
-
path:
|
|
16509
|
+
path: path15,
|
|
18317
16510
|
method: "GET",
|
|
18318
16511
|
timeout: FETCH_TIMEOUT_MS,
|
|
18319
16512
|
servername: target.url.hostname,
|
|
@@ -18520,7 +16713,7 @@ var SnapshotService = class {
|
|
|
18520
16713
|
});
|
|
18521
16714
|
const enrichedResults = applyBatchAssessment(queryResults, batchAssessment);
|
|
18522
16715
|
const summary = buildSnapshotSummary(companyName, profile.phrases, providers, enrichedResults, audit, batchAssessment);
|
|
18523
|
-
const reportCompetitors =
|
|
16716
|
+
const reportCompetitors = uniqueStrings2([
|
|
18524
16717
|
...manualCompetitors,
|
|
18525
16718
|
...summary.topCompetitors.map((entry) => entry.name)
|
|
18526
16719
|
]);
|
|
@@ -18573,8 +16766,8 @@ var SnapshotService = class {
|
|
|
18573
16766
|
return {
|
|
18574
16767
|
industry: parsed.industry?.trim() || "Unknown",
|
|
18575
16768
|
summary: parsed.summary?.trim() || ctx.audit.summary,
|
|
18576
|
-
services:
|
|
18577
|
-
categoryTerms:
|
|
16769
|
+
services: uniqueStrings2(parsed.services ?? []).slice(0, 6),
|
|
16770
|
+
categoryTerms: uniqueStrings2(parsed.categoryTerms ?? []).slice(0, 8),
|
|
18578
16771
|
phrases: parsedPhrases
|
|
18579
16772
|
};
|
|
18580
16773
|
} catch (err) {
|
|
@@ -18642,9 +16835,9 @@ var SnapshotService = class {
|
|
|
18642
16835
|
accuracyNotes: null,
|
|
18643
16836
|
incorrectClaims: [],
|
|
18644
16837
|
recommendedCompetitors: preliminaryCompetitors,
|
|
18645
|
-
citedDomains:
|
|
16838
|
+
citedDomains: uniqueStrings2(normalized.citedDomains),
|
|
18646
16839
|
groundingSources: normalized.groundingSources,
|
|
18647
|
-
searchQueries:
|
|
16840
|
+
searchQueries: uniqueStrings2(normalized.searchQueries),
|
|
18648
16841
|
answerText: normalized.answerText,
|
|
18649
16842
|
error: null
|
|
18650
16843
|
};
|
|
@@ -18710,14 +16903,14 @@ var SnapshotService = class {
|
|
|
18710
16903
|
mentioned: assessment.mentioned,
|
|
18711
16904
|
describedAccurately: assessment.describedAccurately,
|
|
18712
16905
|
accuracyNotes: assessment.accuracyNotes ?? null,
|
|
18713
|
-
incorrectClaims:
|
|
16906
|
+
incorrectClaims: uniqueStrings2(assessment.incorrectClaims ?? []).slice(0, 5),
|
|
18714
16907
|
...hasReviewedCompetitors ? {
|
|
18715
|
-
recommendedCompetitors:
|
|
16908
|
+
recommendedCompetitors: uniqueStrings2(assessment.recommendedCompetitors ?? []).slice(0, 10)
|
|
18716
16909
|
} : {}
|
|
18717
16910
|
};
|
|
18718
16911
|
}),
|
|
18719
|
-
whatThisMeans:
|
|
18720
|
-
recommendedActions:
|
|
16912
|
+
whatThisMeans: uniqueStrings2(parsed.whatThisMeans ?? []).slice(0, 4),
|
|
16913
|
+
recommendedActions: uniqueStrings2(parsed.recommendedActions ?? []).slice(0, 4)
|
|
18721
16914
|
};
|
|
18722
16915
|
} catch (err) {
|
|
18723
16916
|
log12.warn("response.analysis-failed", {
|
|
@@ -18812,13 +17005,13 @@ function applyBatchAssessment(queryResults, batchAssessment) {
|
|
|
18812
17005
|
};
|
|
18813
17006
|
}
|
|
18814
17007
|
const reviewedCompetitors = assessment.recommendedCompetitors;
|
|
18815
|
-
const recommendedCompetitors = reviewedCompetitors !== void 0 ?
|
|
17008
|
+
const recommendedCompetitors = reviewedCompetitors !== void 0 ? uniqueStrings2(reviewedCompetitors) : result.recommendedCompetitors;
|
|
18816
17009
|
return {
|
|
18817
17010
|
...result,
|
|
18818
17011
|
mentioned: result.mentioned || assessment.mentioned === true,
|
|
18819
17012
|
describedAccurately: assessment.describedAccurately ?? (result.mentioned ? "unknown" : "not-mentioned"),
|
|
18820
17013
|
accuracyNotes: assessment.accuracyNotes ?? result.accuracyNotes ?? null,
|
|
18821
|
-
incorrectClaims:
|
|
17014
|
+
incorrectClaims: uniqueStrings2([
|
|
18822
17015
|
...result.incorrectClaims,
|
|
18823
17016
|
...assessment.incorrectClaims ?? []
|
|
18824
17017
|
]),
|
|
@@ -18848,7 +17041,7 @@ function buildSnapshotSummary(companyName, phrases, providers, queryResults, aud
|
|
|
18848
17041
|
const whatThisMeans = batchAssessment.whatThisMeans.length > 0 ? batchAssessment.whatThisMeans : [
|
|
18849
17042
|
defaultMeaning
|
|
18850
17043
|
];
|
|
18851
|
-
const combinedWhatThisMeans =
|
|
17044
|
+
const combinedWhatThisMeans = uniqueStrings2([...whatThisMeans, ...failureNote]).slice(0, 5);
|
|
18852
17045
|
const recommendedActions = batchAssessment.recommendedActions.length > 0 ? batchAssessment.recommendedActions : buildFallbackRecommendedActions(audit);
|
|
18853
17046
|
return {
|
|
18854
17047
|
totalQueries: phrases.length,
|
|
@@ -18891,7 +17084,7 @@ function buildFallbackRecommendedActions(audit) {
|
|
|
18891
17084
|
"Add machine-readable trust signals such as schema, FAQs, and llms.txt support.",
|
|
18892
17085
|
"Build comparison and proof content that makes the category fit unmistakable."
|
|
18893
17086
|
];
|
|
18894
|
-
return
|
|
17087
|
+
return uniqueStrings2([...weakestFactors, ...defaults]).slice(0, 4);
|
|
18895
17088
|
}
|
|
18896
17089
|
function citesTargetDomain(citedDomains, groundingSources, targetDomain) {
|
|
18897
17090
|
const normalizedTarget = extractHostname2(targetDomain);
|
|
@@ -18965,11 +17158,11 @@ function parseJsonObject(input) {
|
|
|
18965
17158
|
}
|
|
18966
17159
|
function normalizeStringList(values) {
|
|
18967
17160
|
const items = values.flatMap((value) => value.split(","));
|
|
18968
|
-
return
|
|
17161
|
+
return uniqueStrings2(
|
|
18969
17162
|
items.map((value) => value.trim()).filter(Boolean)
|
|
18970
17163
|
);
|
|
18971
17164
|
}
|
|
18972
|
-
function
|
|
17165
|
+
function uniqueStrings2(values) {
|
|
18973
17166
|
if (!Array.isArray(values)) return [];
|
|
18974
17167
|
return [...new Set(
|
|
18975
17168
|
values.filter((value) => typeof value === "string").map((value) => value.trim()).filter(Boolean)
|
|
@@ -19206,8 +17399,8 @@ async function createServer(opts) {
|
|
|
19206
17399
|
);
|
|
19207
17400
|
jobRunner.onRunCompleted = (runId, projectId) => runCoordinator.onRunCompleted(runId, projectId);
|
|
19208
17401
|
const snapshotService = new SnapshotService(registry);
|
|
19209
|
-
const orphanedOpenClawDir =
|
|
19210
|
-
if (
|
|
17402
|
+
const orphanedOpenClawDir = path14.join(os5.homedir(), ".openclaw-aero");
|
|
17403
|
+
if (fs12.existsSync(orphanedOpenClawDir)) {
|
|
19211
17404
|
app.log.warn(
|
|
19212
17405
|
{ path: orphanedOpenClawDir },
|
|
19213
17406
|
"OpenClaw gateway is no longer used. Remove ~/.openclaw-aero/ manually to reclaim the directory."
|
|
@@ -19801,10 +17994,10 @@ async function createServer(opts) {
|
|
|
19801
17994
|
return snapshotService.createReport(input);
|
|
19802
17995
|
}
|
|
19803
17996
|
});
|
|
19804
|
-
const dirname =
|
|
19805
|
-
const assetsDir =
|
|
19806
|
-
if (
|
|
19807
|
-
const indexPath =
|
|
17997
|
+
const dirname = path14.dirname(fileURLToPath2(import.meta.url));
|
|
17998
|
+
const assetsDir = path14.join(dirname, "..", "assets");
|
|
17999
|
+
if (fs12.existsSync(assetsDir)) {
|
|
18000
|
+
const indexPath = path14.join(assetsDir, "index.html");
|
|
19808
18001
|
const injectConfig = (html) => {
|
|
19809
18002
|
const clientConfig = {};
|
|
19810
18003
|
if (basePath) clientConfig.basePath = basePath;
|
|
@@ -19822,8 +18015,8 @@ async function createServer(opts) {
|
|
|
19822
18015
|
index: false
|
|
19823
18016
|
});
|
|
19824
18017
|
const serveIndex = (_request, reply) => {
|
|
19825
|
-
if (
|
|
19826
|
-
const html =
|
|
18018
|
+
if (fs12.existsSync(indexPath)) {
|
|
18019
|
+
const html = fs12.readFileSync(indexPath, "utf-8");
|
|
19827
18020
|
return reply.type("text/html").send(injectConfig(html));
|
|
19828
18021
|
}
|
|
19829
18022
|
return reply.status(404).send({ error: "Dashboard not built" });
|
|
@@ -19843,8 +18036,8 @@ async function createServer(opts) {
|
|
|
19843
18036
|
if (basePath && !url.startsWith(basePath)) {
|
|
19844
18037
|
return reply.status(404).send({ error: "Not found", path: request.url });
|
|
19845
18038
|
}
|
|
19846
|
-
if (
|
|
19847
|
-
const html =
|
|
18039
|
+
if (fs12.existsSync(indexPath)) {
|
|
18040
|
+
const html = fs12.readFileSync(indexPath, "utf-8");
|
|
19848
18041
|
return reply.type("text/html").send(injectConfig(html));
|
|
19849
18042
|
}
|
|
19850
18043
|
return reply.status(404).send({ error: "Not found" });
|
|
@@ -19913,31 +18106,11 @@ function parseKeywordResponse(raw, count) {
|
|
|
19913
18106
|
}
|
|
19914
18107
|
|
|
19915
18108
|
export {
|
|
19916
|
-
getConfigDir,
|
|
19917
|
-
getConfigPath,
|
|
19918
|
-
loadConfig,
|
|
19919
|
-
saveConfig,
|
|
19920
|
-
saveConfigPatch,
|
|
19921
|
-
configExists,
|
|
19922
18109
|
isTelemetryEnabled,
|
|
19923
18110
|
getOrCreateAnonymousId,
|
|
19924
18111
|
isFirstRun,
|
|
19925
18112
|
showFirstRunNotice,
|
|
19926
18113
|
trackEvent,
|
|
19927
|
-
EXIT_SYSTEM_ERROR,
|
|
19928
|
-
CliError,
|
|
19929
|
-
usageError,
|
|
19930
|
-
isEndpointMissing,
|
|
19931
|
-
printCliError,
|
|
19932
|
-
providerQuotaPolicySchema,
|
|
19933
|
-
ProviderNames,
|
|
19934
|
-
resolveProviderInput,
|
|
19935
|
-
notificationEventSchema,
|
|
19936
|
-
effectiveDomains,
|
|
19937
|
-
RunStatuses,
|
|
19938
|
-
RunKinds,
|
|
19939
|
-
determineAnswerMentioned,
|
|
19940
|
-
CcReleaseSyncStatuses,
|
|
19941
18114
|
reparseStoredResult2 as reparseStoredResult,
|
|
19942
18115
|
reparseStoredResult3 as reparseStoredResult2,
|
|
19943
18116
|
reparseStoredResult as reparseStoredResult3,
|
|
@@ -19945,7 +18118,6 @@ export {
|
|
|
19945
18118
|
determineCitationState,
|
|
19946
18119
|
computeCompetitorOverlap,
|
|
19947
18120
|
extractRecommendedCompetitors,
|
|
19948
|
-
createApiClient,
|
|
19949
18121
|
setGoogleAuthConfig,
|
|
19950
18122
|
formatAuditFactorScore,
|
|
19951
18123
|
listAgentProviders,
|