@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
|
@@ -0,0 +1,2152 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import { parse, stringify } from "yaml";
|
|
6
|
+
function normalizeGoogleConfig(config) {
|
|
7
|
+
if (!config.google) return;
|
|
8
|
+
config.google.connections = (config.google.connections ?? []).map((connection) => ({
|
|
9
|
+
...connection,
|
|
10
|
+
propertyId: connection.propertyId ?? null,
|
|
11
|
+
refreshToken: connection.refreshToken ?? null,
|
|
12
|
+
tokenExpiresAt: connection.tokenExpiresAt ?? null,
|
|
13
|
+
scopes: connection.scopes ?? []
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
function normalizeWordpressConfig(config) {
|
|
17
|
+
if (!config.wordpress) return;
|
|
18
|
+
config.wordpress.connections = (config.wordpress.connections ?? []).map((connection) => ({
|
|
19
|
+
...connection,
|
|
20
|
+
url: connection.url.replace(/\/$/, ""),
|
|
21
|
+
stagingUrl: connection.stagingUrl?.replace(/\/$/, ""),
|
|
22
|
+
defaultEnv: connection.defaultEnv ?? "live"
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
function getConfigDir() {
|
|
26
|
+
const override = process.env.CANONRY_CONFIG_DIR?.trim();
|
|
27
|
+
if (override) {
|
|
28
|
+
return override;
|
|
29
|
+
}
|
|
30
|
+
return path.join(os.homedir(), ".canonry");
|
|
31
|
+
}
|
|
32
|
+
function getConfigPath() {
|
|
33
|
+
return path.join(getConfigDir(), "config.yaml");
|
|
34
|
+
}
|
|
35
|
+
function loadConfig() {
|
|
36
|
+
const configPath = getConfigPath();
|
|
37
|
+
if (!fs.existsSync(configPath)) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Config not found at ${configPath}.
|
|
40
|
+
Run "canonry init" to set up interactively, or "canonry init --gemini-key <key>" for non-interactive setup.
|
|
41
|
+
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).`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
45
|
+
const parsed = parse(raw);
|
|
46
|
+
if (!parsed.apiUrl || !parsed.database || !parsed.apiKey) {
|
|
47
|
+
const missing = [
|
|
48
|
+
!parsed.apiUrl && "apiUrl",
|
|
49
|
+
!parsed.database && "database",
|
|
50
|
+
!parsed.apiKey && "apiKey"
|
|
51
|
+
].filter(Boolean).join(", ");
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Invalid config at ${configPath} \u2014 missing: ${missing}.
|
|
54
|
+
These fields are auto-generated. Run "canonry init" (or "canonry init --gemini-key <key>" for non-interactive setup) to create a valid config.
|
|
55
|
+
Do not write config.yaml by hand; use "canonry init", "canonry settings", or "canonry bootstrap" instead.`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (parsed.geminiApiKey && !parsed.providers?.gemini) {
|
|
59
|
+
parsed.providers = {
|
|
60
|
+
...parsed.providers,
|
|
61
|
+
gemini: {
|
|
62
|
+
apiKey: parsed.geminiApiKey,
|
|
63
|
+
model: parsed.geminiModel,
|
|
64
|
+
quota: parsed.geminiQuota
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
normalizeGoogleConfig(parsed);
|
|
69
|
+
normalizeWordpressConfig(parsed);
|
|
70
|
+
const portOverride = process.env.CANONRY_PORT?.trim();
|
|
71
|
+
if (portOverride) {
|
|
72
|
+
try {
|
|
73
|
+
const url = new URL(parsed.apiUrl);
|
|
74
|
+
url.port = portOverride;
|
|
75
|
+
parsed.apiUrl = url.origin;
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if ("CANONRY_BASE_PATH" in process.env) {
|
|
80
|
+
const val = process.env.CANONRY_BASE_PATH.trim();
|
|
81
|
+
parsed.basePath = val || void 0;
|
|
82
|
+
}
|
|
83
|
+
if (parsed.basePath) {
|
|
84
|
+
const normalizedBase = "/" + parsed.basePath.replace(/^\/|\/$/g, "");
|
|
85
|
+
try {
|
|
86
|
+
const url = new URL(parsed.apiUrl);
|
|
87
|
+
if (normalizedBase !== "/" && !url.pathname.startsWith(normalizedBase)) {
|
|
88
|
+
parsed.apiUrl = url.origin + normalizedBase;
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return parsed;
|
|
94
|
+
}
|
|
95
|
+
function loadConfigRaw() {
|
|
96
|
+
const configPath = getConfigPath();
|
|
97
|
+
if (!fs.existsSync(configPath)) return null;
|
|
98
|
+
try {
|
|
99
|
+
return parse(fs.readFileSync(configPath, "utf-8")) ?? null;
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function saveConfig(config) {
|
|
105
|
+
const configDir = getConfigDir();
|
|
106
|
+
if (!fs.existsSync(configDir)) {
|
|
107
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
108
|
+
}
|
|
109
|
+
const configPath = getConfigPath();
|
|
110
|
+
const onDisk = loadConfigRaw();
|
|
111
|
+
const merged = onDisk ? { ...onDisk } : {};
|
|
112
|
+
for (const [key, value] of Object.entries(config)) {
|
|
113
|
+
if (value !== void 0) {
|
|
114
|
+
merged[key] = value;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (onDisk) {
|
|
118
|
+
if (process.env.CANONRY_PORT?.trim() || onDisk.basePath) {
|
|
119
|
+
merged.apiUrl = onDisk.apiUrl;
|
|
120
|
+
}
|
|
121
|
+
if ("CANONRY_BASE_PATH" in process.env) {
|
|
122
|
+
if (onDisk.basePath !== void 0) {
|
|
123
|
+
merged.basePath = onDisk.basePath;
|
|
124
|
+
} else {
|
|
125
|
+
delete merged.basePath;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const yaml = stringify(merged);
|
|
130
|
+
fs.writeFileSync(configPath, yaml, { encoding: "utf-8", mode: 384 });
|
|
131
|
+
}
|
|
132
|
+
function saveConfigPatch(patch) {
|
|
133
|
+
const configDir = getConfigDir();
|
|
134
|
+
if (!fs.existsSync(configDir)) {
|
|
135
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
const configPath = getConfigPath();
|
|
138
|
+
let base = {};
|
|
139
|
+
if (fs.existsSync(configPath)) {
|
|
140
|
+
try {
|
|
141
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
142
|
+
base = parse(raw) ?? {};
|
|
143
|
+
} catch {
|
|
144
|
+
base = {};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const merged = { ...base, ...patch };
|
|
148
|
+
if (base.database) merged.database = base.database;
|
|
149
|
+
if (base.apiKey) merged.apiKey = base.apiKey;
|
|
150
|
+
if (base.anonymousId) merged.anonymousId = base.anonymousId;
|
|
151
|
+
if (base.dashboardPasswordHash) merged.dashboardPasswordHash = base.dashboardPasswordHash;
|
|
152
|
+
if (base.providers && patch.providers) {
|
|
153
|
+
merged.providers = { ...base.providers };
|
|
154
|
+
for (const [key, patchEntry] of Object.entries(patch.providers)) {
|
|
155
|
+
const baseEntry = base.providers[key] ?? {};
|
|
156
|
+
merged.providers[key] = { ...baseEntry, ...patchEntry };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const yaml = stringify(merged);
|
|
160
|
+
fs.writeFileSync(configPath, yaml, { encoding: "utf-8", mode: 384 });
|
|
161
|
+
}
|
|
162
|
+
function configExists() {
|
|
163
|
+
return fs.existsSync(getConfigPath());
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/cli-error.ts
|
|
167
|
+
var EXIT_USER_ERROR = 1;
|
|
168
|
+
var EXIT_SYSTEM_ERROR = 2;
|
|
169
|
+
var CliError = class extends Error {
|
|
170
|
+
code;
|
|
171
|
+
displayMessage;
|
|
172
|
+
details;
|
|
173
|
+
exitCode;
|
|
174
|
+
constructor(options) {
|
|
175
|
+
super(options.message);
|
|
176
|
+
this.name = "CliError";
|
|
177
|
+
this.code = options.code;
|
|
178
|
+
this.displayMessage = options.displayMessage;
|
|
179
|
+
this.details = options.details;
|
|
180
|
+
this.exitCode = options.exitCode ?? EXIT_USER_ERROR;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
function usageError(displayMessage, options) {
|
|
184
|
+
const firstLine = displayMessage.split("\n", 1)[0] ?? "Error: invalid command usage";
|
|
185
|
+
return new CliError({
|
|
186
|
+
code: "CLI_USAGE_ERROR",
|
|
187
|
+
message: options?.message ?? firstLine.replace(/^Error:\s*/, ""),
|
|
188
|
+
displayMessage,
|
|
189
|
+
details: options?.details
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function isEndpointMissing(err) {
|
|
193
|
+
if (!(err instanceof CliError)) return false;
|
|
194
|
+
const status = err.details?.httpStatus;
|
|
195
|
+
return status === 404 || status === 405;
|
|
196
|
+
}
|
|
197
|
+
function printCliError(err, format) {
|
|
198
|
+
if (format === "json") {
|
|
199
|
+
if (err instanceof CliError) {
|
|
200
|
+
console.error(
|
|
201
|
+
JSON.stringify(
|
|
202
|
+
{
|
|
203
|
+
error: {
|
|
204
|
+
code: err.code,
|
|
205
|
+
message: err.message,
|
|
206
|
+
...err.details ? { details: err.details } : {}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
null,
|
|
210
|
+
2
|
|
211
|
+
)
|
|
212
|
+
);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const message = err instanceof Error ? err.message : "An unexpected error occurred";
|
|
216
|
+
console.error(
|
|
217
|
+
JSON.stringify(
|
|
218
|
+
{
|
|
219
|
+
error: {
|
|
220
|
+
code: "CLI_ERROR",
|
|
221
|
+
message
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
null,
|
|
225
|
+
2
|
|
226
|
+
)
|
|
227
|
+
);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (err instanceof CliError && err.displayMessage) {
|
|
231
|
+
console.error(err.displayMessage);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (err instanceof Error) {
|
|
235
|
+
console.error(`Error: ${err.message}`);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
console.error("An unexpected error occurred");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ../contracts/src/errors.ts
|
|
242
|
+
var AppError = class extends Error {
|
|
243
|
+
code;
|
|
244
|
+
statusCode;
|
|
245
|
+
details;
|
|
246
|
+
constructor(code, message, statusCode, details) {
|
|
247
|
+
super(message);
|
|
248
|
+
this.name = "AppError";
|
|
249
|
+
this.code = code;
|
|
250
|
+
this.statusCode = statusCode;
|
|
251
|
+
this.details = details;
|
|
252
|
+
}
|
|
253
|
+
toJSON() {
|
|
254
|
+
return {
|
|
255
|
+
error: {
|
|
256
|
+
code: this.code,
|
|
257
|
+
message: this.message,
|
|
258
|
+
...this.details ? { details: this.details } : {}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
function notFound(entity, id) {
|
|
264
|
+
return new AppError("NOT_FOUND", `${entity} '${id}' not found`, 404);
|
|
265
|
+
}
|
|
266
|
+
function validationError(message, details) {
|
|
267
|
+
return new AppError("VALIDATION_ERROR", message, 400, details);
|
|
268
|
+
}
|
|
269
|
+
function authRequired() {
|
|
270
|
+
return new AppError("AUTH_REQUIRED", "Authentication required", 401);
|
|
271
|
+
}
|
|
272
|
+
function authInvalid() {
|
|
273
|
+
return new AppError("AUTH_INVALID", "Invalid API key", 401);
|
|
274
|
+
}
|
|
275
|
+
function providerError(message, details) {
|
|
276
|
+
return new AppError("PROVIDER_ERROR", message, 502, details);
|
|
277
|
+
}
|
|
278
|
+
function runInProgress(projectName) {
|
|
279
|
+
return new AppError("RUN_IN_PROGRESS", `A run is already in progress for '${projectName}'`, 409);
|
|
280
|
+
}
|
|
281
|
+
function runNotCancellable(runId, status) {
|
|
282
|
+
return new AppError("RUN_NOT_CANCELLABLE", `Run '${runId}' is already in terminal state '${status}' and cannot be cancelled`, 409);
|
|
283
|
+
}
|
|
284
|
+
function unsupportedKind(kind) {
|
|
285
|
+
return new AppError("UNSUPPORTED_KIND", `Kind '${kind}' is not supported in this version`, 400);
|
|
286
|
+
}
|
|
287
|
+
function notImplemented(message) {
|
|
288
|
+
return new AppError("NOT_IMPLEMENTED", message, 501);
|
|
289
|
+
}
|
|
290
|
+
function deliveryFailed(message) {
|
|
291
|
+
return new AppError("DELIVERY_FAILED", message, 502);
|
|
292
|
+
}
|
|
293
|
+
function agentBusy(projectName) {
|
|
294
|
+
return new AppError(
|
|
295
|
+
"AGENT_BUSY",
|
|
296
|
+
`Aero is already running a turn for '${projectName}'. Retry after the current turn settles.`,
|
|
297
|
+
409
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
function missingDependency(message, details) {
|
|
301
|
+
return new AppError("MISSING_DEPENDENCY", message, 422, details);
|
|
302
|
+
}
|
|
303
|
+
function internalError(message, details) {
|
|
304
|
+
return new AppError("INTERNAL_ERROR", message, 500, details);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ../contracts/src/run.ts
|
|
308
|
+
import { z as z2 } from "zod";
|
|
309
|
+
|
|
310
|
+
// ../contracts/src/provider.ts
|
|
311
|
+
import { z } from "zod";
|
|
312
|
+
var providerQuotaPolicySchema = z.object({
|
|
313
|
+
maxConcurrency: z.number().int().positive(),
|
|
314
|
+
maxRequestsPerMinute: z.number().int().positive(),
|
|
315
|
+
maxRequestsPerDay: z.number().int().positive()
|
|
316
|
+
});
|
|
317
|
+
var ProviderNames = {
|
|
318
|
+
gemini: "gemini",
|
|
319
|
+
openai: "openai",
|
|
320
|
+
claude: "claude",
|
|
321
|
+
perplexity: "perplexity",
|
|
322
|
+
local: "local",
|
|
323
|
+
cdpChatgpt: "cdp:chatgpt"
|
|
324
|
+
};
|
|
325
|
+
var providerNameSchema = z.string().min(1);
|
|
326
|
+
var apiProviderNameSchema = z.string().min(1);
|
|
327
|
+
function isBrowserProvider(name) {
|
|
328
|
+
return name.startsWith("cdp:");
|
|
329
|
+
}
|
|
330
|
+
var CDP_TARGETS = ["cdp:chatgpt"];
|
|
331
|
+
function resolveProviderInput(input) {
|
|
332
|
+
const lower = input.trim().toLowerCase();
|
|
333
|
+
if (lower === "cdp") {
|
|
334
|
+
return [...CDP_TARGETS];
|
|
335
|
+
}
|
|
336
|
+
return lower ? [lower] : [];
|
|
337
|
+
}
|
|
338
|
+
var locationContextSchema = z.object({
|
|
339
|
+
label: z.string().min(1),
|
|
340
|
+
city: z.string().min(1),
|
|
341
|
+
region: z.string().min(1),
|
|
342
|
+
country: z.string().length(2),
|
|
343
|
+
timezone: z.string().optional()
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// ../contracts/src/run.ts
|
|
347
|
+
var runStatusSchema = z2.enum(["queued", "running", "completed", "partial", "failed", "cancelled"]);
|
|
348
|
+
var RunStatuses = runStatusSchema.enum;
|
|
349
|
+
var runKindSchema = z2.enum([
|
|
350
|
+
"answer-visibility",
|
|
351
|
+
"site-audit",
|
|
352
|
+
"gsc-sync",
|
|
353
|
+
"inspect-sitemap",
|
|
354
|
+
"ga-sync",
|
|
355
|
+
"bing-inspect",
|
|
356
|
+
"bing-inspect-sitemap",
|
|
357
|
+
"backlink-extract"
|
|
358
|
+
]);
|
|
359
|
+
var RunKinds = runKindSchema.enum;
|
|
360
|
+
var runTriggerSchema = z2.enum(["manual", "scheduled", "config-apply"]);
|
|
361
|
+
var RunTriggers = runTriggerSchema.enum;
|
|
362
|
+
var citationStateSchema = z2.enum(["cited", "not-cited"]);
|
|
363
|
+
var CitationStates = citationStateSchema.enum;
|
|
364
|
+
var visibilityStateSchema = z2.enum(["visible", "not-visible"]);
|
|
365
|
+
var VisibilityStates = visibilityStateSchema.enum;
|
|
366
|
+
var computedTransitionSchema = z2.enum(["new", "cited", "lost", "emerging", "not-cited"]);
|
|
367
|
+
var ComputedTransitions = computedTransitionSchema.enum;
|
|
368
|
+
var runTriggerRequestSchema = z2.object({
|
|
369
|
+
kind: z2.literal(RunKinds["answer-visibility"]).optional(),
|
|
370
|
+
trigger: z2.literal(RunTriggers.manual).optional(),
|
|
371
|
+
providers: z2.array(providerNameSchema).optional(),
|
|
372
|
+
location: z2.string().min(1).optional(),
|
|
373
|
+
allLocations: z2.boolean().optional(),
|
|
374
|
+
noLocation: z2.boolean().optional()
|
|
375
|
+
}).refine(
|
|
376
|
+
(data) => Number(Boolean(data.location)) + Number(Boolean(data.allLocations)) + Number(Boolean(data.noLocation)) <= 1,
|
|
377
|
+
{ message: 'Only one of "location", "allLocations", or "noLocation" may be provided' }
|
|
378
|
+
);
|
|
379
|
+
var runDtoSchema = z2.object({
|
|
380
|
+
id: z2.string(),
|
|
381
|
+
projectId: z2.string(),
|
|
382
|
+
kind: runKindSchema,
|
|
383
|
+
status: runStatusSchema,
|
|
384
|
+
trigger: runTriggerSchema.default("manual"),
|
|
385
|
+
location: z2.string().nullable().optional(),
|
|
386
|
+
startedAt: z2.string().nullable().optional(),
|
|
387
|
+
finishedAt: z2.string().nullable().optional(),
|
|
388
|
+
error: z2.string().nullable().optional(),
|
|
389
|
+
createdAt: z2.string()
|
|
390
|
+
});
|
|
391
|
+
var groundingSourceSchema = z2.object({
|
|
392
|
+
uri: z2.string(),
|
|
393
|
+
title: z2.string()
|
|
394
|
+
});
|
|
395
|
+
var querySnapshotDtoSchema = z2.object({
|
|
396
|
+
id: z2.string(),
|
|
397
|
+
runId: z2.string(),
|
|
398
|
+
keywordId: z2.string(),
|
|
399
|
+
keyword: z2.string().optional(),
|
|
400
|
+
provider: providerNameSchema,
|
|
401
|
+
citationState: citationStateSchema,
|
|
402
|
+
answerMentioned: z2.boolean().optional(),
|
|
403
|
+
visibilityState: visibilityStateSchema.optional(),
|
|
404
|
+
transition: computedTransitionSchema.optional(),
|
|
405
|
+
answerText: z2.string().nullable().optional(),
|
|
406
|
+
citedDomains: z2.array(z2.string()).default([]),
|
|
407
|
+
competitorOverlap: z2.array(z2.string()).default([]),
|
|
408
|
+
recommendedCompetitors: z2.array(z2.string()).default([]),
|
|
409
|
+
matchedTerms: z2.array(z2.string()).default([]),
|
|
410
|
+
groundingSources: z2.array(groundingSourceSchema).default([]),
|
|
411
|
+
searchQueries: z2.array(z2.string()).default([]),
|
|
412
|
+
model: z2.string().nullable().optional(),
|
|
413
|
+
location: z2.string().nullable().optional(),
|
|
414
|
+
createdAt: z2.string()
|
|
415
|
+
});
|
|
416
|
+
var snapshotListResponseSchema = z2.object({
|
|
417
|
+
snapshots: z2.array(querySnapshotDtoSchema),
|
|
418
|
+
total: z2.number().int().nonnegative()
|
|
419
|
+
});
|
|
420
|
+
var snapshotDiffRowSchema = z2.object({
|
|
421
|
+
keywordId: z2.string().nullable(),
|
|
422
|
+
keyword: z2.string().nullable(),
|
|
423
|
+
run1State: citationStateSchema.nullable(),
|
|
424
|
+
run2State: citationStateSchema.nullable(),
|
|
425
|
+
run1AnswerMentioned: z2.boolean().nullable(),
|
|
426
|
+
run2AnswerMentioned: z2.boolean().nullable(),
|
|
427
|
+
run1VisibilityState: visibilityStateSchema.nullable(),
|
|
428
|
+
run2VisibilityState: visibilityStateSchema.nullable(),
|
|
429
|
+
changed: z2.boolean(),
|
|
430
|
+
visibilityChanged: z2.boolean()
|
|
431
|
+
});
|
|
432
|
+
var snapshotDiffResponseSchema = z2.object({
|
|
433
|
+
run1: z2.string(),
|
|
434
|
+
run2: z2.string(),
|
|
435
|
+
diff: z2.array(snapshotDiffRowSchema)
|
|
436
|
+
});
|
|
437
|
+
var runDetailDtoSchema = runDtoSchema.extend({
|
|
438
|
+
snapshots: z2.array(querySnapshotDtoSchema).optional()
|
|
439
|
+
});
|
|
440
|
+
var latestProjectRunDtoSchema = z2.object({
|
|
441
|
+
totalRuns: z2.number().int().nonnegative(),
|
|
442
|
+
run: runDetailDtoSchema.nullable()
|
|
443
|
+
});
|
|
444
|
+
var auditLogEntrySchema = z2.object({
|
|
445
|
+
id: z2.string(),
|
|
446
|
+
projectId: z2.string().nullable().optional(),
|
|
447
|
+
actor: z2.string(),
|
|
448
|
+
action: z2.string(),
|
|
449
|
+
entityType: z2.string(),
|
|
450
|
+
entityId: z2.string().nullable().optional(),
|
|
451
|
+
diff: z2.unknown().optional(),
|
|
452
|
+
createdAt: z2.string()
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// ../contracts/src/config-schema.ts
|
|
456
|
+
import { z as z5 } from "zod";
|
|
457
|
+
|
|
458
|
+
// ../contracts/src/notification.ts
|
|
459
|
+
import { z as z3 } from "zod";
|
|
460
|
+
var notificationEventSchema = z3.enum([
|
|
461
|
+
"citation.lost",
|
|
462
|
+
"citation.gained",
|
|
463
|
+
"run.completed",
|
|
464
|
+
"run.failed",
|
|
465
|
+
"insight.critical",
|
|
466
|
+
"insight.high"
|
|
467
|
+
]);
|
|
468
|
+
var notificationDtoSchema = z3.object({
|
|
469
|
+
id: z3.string(),
|
|
470
|
+
projectId: z3.string(),
|
|
471
|
+
channel: z3.literal("webhook"),
|
|
472
|
+
url: z3.string().url(),
|
|
473
|
+
urlDisplay: z3.string(),
|
|
474
|
+
urlHost: z3.string(),
|
|
475
|
+
events: z3.array(notificationEventSchema),
|
|
476
|
+
enabled: z3.boolean().default(true),
|
|
477
|
+
/** Opaque tag identifying the creator (e.g. `"agent"` for Aero webhooks). */
|
|
478
|
+
source: z3.string().optional(),
|
|
479
|
+
webhookSecret: z3.string().optional(),
|
|
480
|
+
createdAt: z3.string(),
|
|
481
|
+
updatedAt: z3.string()
|
|
482
|
+
});
|
|
483
|
+
var notificationCreateRequestSchema = z3.object({
|
|
484
|
+
channel: z3.literal("webhook"),
|
|
485
|
+
url: z3.string().url(),
|
|
486
|
+
events: z3.array(notificationEventSchema).min(1),
|
|
487
|
+
source: z3.string().optional()
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
// ../contracts/src/project.ts
|
|
491
|
+
import { z as z4 } from "zod";
|
|
492
|
+
var configSourceSchema = z4.enum(["cli", "api", "config-file"]);
|
|
493
|
+
function findDuplicateLocationLabels(locations) {
|
|
494
|
+
const seen = /* @__PURE__ */ new Set();
|
|
495
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
496
|
+
for (const location of locations) {
|
|
497
|
+
if (seen.has(location.label)) {
|
|
498
|
+
duplicates.add(location.label);
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
seen.add(location.label);
|
|
502
|
+
}
|
|
503
|
+
return [...duplicates];
|
|
504
|
+
}
|
|
505
|
+
function hasLocationLabel(locations, label) {
|
|
506
|
+
if (!label) return true;
|
|
507
|
+
return locations.some((location) => location.label === label);
|
|
508
|
+
}
|
|
509
|
+
var projectUpsertRequestSchema = z4.object({
|
|
510
|
+
displayName: z4.string().min(1),
|
|
511
|
+
canonicalDomain: z4.string().min(1),
|
|
512
|
+
ownedDomains: z4.array(z4.string().min(1)).optional(),
|
|
513
|
+
country: z4.string().length(2),
|
|
514
|
+
language: z4.string().min(2),
|
|
515
|
+
tags: z4.array(z4.string()).optional(),
|
|
516
|
+
labels: z4.record(z4.string(), z4.string()).optional(),
|
|
517
|
+
providers: z4.array(providerNameSchema).optional(),
|
|
518
|
+
locations: z4.array(locationContextSchema).optional(),
|
|
519
|
+
defaultLocation: z4.string().nullable().optional(),
|
|
520
|
+
autoExtractBacklinks: z4.boolean().optional(),
|
|
521
|
+
configSource: configSourceSchema.optional()
|
|
522
|
+
});
|
|
523
|
+
var projectDtoSchema = z4.object({
|
|
524
|
+
id: z4.string(),
|
|
525
|
+
name: z4.string(),
|
|
526
|
+
displayName: z4.string().optional(),
|
|
527
|
+
canonicalDomain: z4.string(),
|
|
528
|
+
ownedDomains: z4.array(z4.string()).default([]),
|
|
529
|
+
country: z4.string().length(2),
|
|
530
|
+
language: z4.string().min(2),
|
|
531
|
+
tags: z4.array(z4.string()).default([]),
|
|
532
|
+
labels: z4.record(z4.string(), z4.string()).default({}),
|
|
533
|
+
locations: z4.array(locationContextSchema).default([]),
|
|
534
|
+
defaultLocation: z4.string().nullable().optional(),
|
|
535
|
+
autoExtractBacklinks: z4.boolean().default(false),
|
|
536
|
+
configSource: configSourceSchema.default("cli"),
|
|
537
|
+
configRevision: z4.number().int().positive().default(1),
|
|
538
|
+
createdAt: z4.string().optional(),
|
|
539
|
+
updatedAt: z4.string().optional()
|
|
540
|
+
});
|
|
541
|
+
var keywordDtoSchema = z4.object({
|
|
542
|
+
id: z4.string(),
|
|
543
|
+
keyword: z4.string(),
|
|
544
|
+
createdAt: z4.string()
|
|
545
|
+
});
|
|
546
|
+
var keywordBatchRequestSchema = z4.object({
|
|
547
|
+
keywords: z4.array(z4.string().trim().min(1)).min(1)
|
|
548
|
+
});
|
|
549
|
+
var keywordGenerateRequestSchema = z4.object({
|
|
550
|
+
provider: providerNameSchema,
|
|
551
|
+
count: z4.number().int().min(1).max(20).optional()
|
|
552
|
+
});
|
|
553
|
+
var competitorDtoSchema = z4.object({
|
|
554
|
+
id: z4.string(),
|
|
555
|
+
domain: z4.string(),
|
|
556
|
+
createdAt: z4.string()
|
|
557
|
+
});
|
|
558
|
+
var competitorBatchRequestSchema = z4.object({
|
|
559
|
+
competitors: z4.array(z4.string().trim().min(1)).min(1)
|
|
560
|
+
});
|
|
561
|
+
function normalizeProjectDomain(input) {
|
|
562
|
+
let domain = input.trim().toLowerCase();
|
|
563
|
+
try {
|
|
564
|
+
if (domain.includes("://")) {
|
|
565
|
+
domain = new URL(domain).hostname.toLowerCase();
|
|
566
|
+
}
|
|
567
|
+
} catch {
|
|
568
|
+
}
|
|
569
|
+
return domain.replace(/^www\./, "");
|
|
570
|
+
}
|
|
571
|
+
function effectiveDomains(project) {
|
|
572
|
+
const all = [project.canonicalDomain, ...project.ownedDomains ?? []];
|
|
573
|
+
const seen = /* @__PURE__ */ new Set();
|
|
574
|
+
const result = [];
|
|
575
|
+
for (const d of all) {
|
|
576
|
+
const trimmed = d.trim();
|
|
577
|
+
if (!trimmed) continue;
|
|
578
|
+
const norm = normalizeProjectDomain(trimmed);
|
|
579
|
+
if (seen.has(norm)) continue;
|
|
580
|
+
seen.add(norm);
|
|
581
|
+
result.push(trimmed);
|
|
582
|
+
}
|
|
583
|
+
return result;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// ../contracts/src/config-schema.ts
|
|
587
|
+
var configMetadataSchema = z5.object({
|
|
588
|
+
name: z5.string().min(1).max(63).regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, {
|
|
589
|
+
message: "Name must be a lowercase slug (letters, numbers, hyphens)"
|
|
590
|
+
}),
|
|
591
|
+
labels: z5.record(z5.string(), z5.string()).optional().default({})
|
|
592
|
+
});
|
|
593
|
+
var configScheduleSchema = z5.object({
|
|
594
|
+
preset: z5.string().optional(),
|
|
595
|
+
cron: z5.string().optional(),
|
|
596
|
+
timezone: z5.string().optional().default("UTC"),
|
|
597
|
+
providers: z5.array(providerNameSchema).optional().default([])
|
|
598
|
+
}).refine(
|
|
599
|
+
(data) => data.preset && !data.cron || !data.preset && data.cron,
|
|
600
|
+
{ message: 'Exactly one of "preset" or "cron" must be provided' }
|
|
601
|
+
).optional();
|
|
602
|
+
var configNotificationSchema = z5.object({
|
|
603
|
+
channel: z5.literal("webhook"),
|
|
604
|
+
url: z5.string().url(),
|
|
605
|
+
events: z5.array(notificationEventSchema).min(1)
|
|
606
|
+
});
|
|
607
|
+
var configGoogleSchema = z5.object({
|
|
608
|
+
gsc: z5.object({
|
|
609
|
+
propertyUrl: z5.string()
|
|
610
|
+
}).optional(),
|
|
611
|
+
syncSchedule: z5.object({
|
|
612
|
+
preset: z5.string().optional(),
|
|
613
|
+
cron: z5.string().optional()
|
|
614
|
+
}).optional()
|
|
615
|
+
}).optional();
|
|
616
|
+
var configSpecSchema = z5.object({
|
|
617
|
+
displayName: z5.string().min(1),
|
|
618
|
+
canonicalDomain: z5.string().min(1),
|
|
619
|
+
ownedDomains: z5.array(z5.string().min(1)).optional().default([]),
|
|
620
|
+
country: z5.string().length(2),
|
|
621
|
+
language: z5.string().min(2),
|
|
622
|
+
keywords: z5.array(z5.string().min(1)).optional().default([]),
|
|
623
|
+
competitors: z5.array(z5.string().min(1)).optional().default([]),
|
|
624
|
+
providers: z5.array(providerNameSchema).optional().default([]),
|
|
625
|
+
locations: z5.array(locationContextSchema).optional().default([]),
|
|
626
|
+
defaultLocation: z5.string().optional(),
|
|
627
|
+
schedule: configScheduleSchema,
|
|
628
|
+
notifications: z5.array(configNotificationSchema).optional().default([]),
|
|
629
|
+
google: configGoogleSchema,
|
|
630
|
+
autoExtractBacklinks: z5.boolean().optional().default(false)
|
|
631
|
+
}).superRefine((spec, ctx) => {
|
|
632
|
+
const duplicateLabels = findDuplicateLocationLabels(spec.locations);
|
|
633
|
+
if (duplicateLabels.length > 0) {
|
|
634
|
+
ctx.addIssue({
|
|
635
|
+
code: "custom",
|
|
636
|
+
message: `Duplicate location labels are not allowed: ${duplicateLabels.join(", ")}`,
|
|
637
|
+
path: ["locations"]
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
if (!hasLocationLabel(spec.locations, spec.defaultLocation)) {
|
|
641
|
+
ctx.addIssue({
|
|
642
|
+
code: "custom",
|
|
643
|
+
message: `defaultLocation "${spec.defaultLocation}" must match a configured location label`,
|
|
644
|
+
path: ["defaultLocation"]
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
var projectConfigSchema = z5.object({
|
|
649
|
+
apiVersion: z5.literal("canonry/v1"),
|
|
650
|
+
kind: z5.literal("Project"),
|
|
651
|
+
metadata: configMetadataSchema,
|
|
652
|
+
spec: configSpecSchema
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
// ../contracts/src/google.ts
|
|
656
|
+
import { z as z6 } from "zod";
|
|
657
|
+
var googleConnectionTypeSchema = z6.enum(["gsc", "ga4"]);
|
|
658
|
+
var googleConnectionDtoSchema = z6.object({
|
|
659
|
+
id: z6.string(),
|
|
660
|
+
domain: z6.string(),
|
|
661
|
+
connectionType: googleConnectionTypeSchema,
|
|
662
|
+
propertyId: z6.string().nullable().optional(),
|
|
663
|
+
sitemapUrl: z6.string().nullable().optional(),
|
|
664
|
+
scopes: z6.array(z6.string()).default([]),
|
|
665
|
+
createdAt: z6.string(),
|
|
666
|
+
updatedAt: z6.string()
|
|
667
|
+
});
|
|
668
|
+
var gscSearchDataDtoSchema = z6.object({
|
|
669
|
+
date: z6.string(),
|
|
670
|
+
query: z6.string(),
|
|
671
|
+
page: z6.string(),
|
|
672
|
+
country: z6.string().nullable().optional(),
|
|
673
|
+
device: z6.string().nullable().optional(),
|
|
674
|
+
clicks: z6.number(),
|
|
675
|
+
impressions: z6.number(),
|
|
676
|
+
ctr: z6.number(),
|
|
677
|
+
position: z6.number()
|
|
678
|
+
});
|
|
679
|
+
var gscUrlInspectionDtoSchema = z6.object({
|
|
680
|
+
id: z6.string(),
|
|
681
|
+
url: z6.string(),
|
|
682
|
+
indexingState: z6.string().nullable().optional(),
|
|
683
|
+
verdict: z6.string().nullable().optional(),
|
|
684
|
+
coverageState: z6.string().nullable().optional(),
|
|
685
|
+
pageFetchState: z6.string().nullable().optional(),
|
|
686
|
+
robotsTxtState: z6.string().nullable().optional(),
|
|
687
|
+
crawlTime: z6.string().nullable().optional(),
|
|
688
|
+
lastCrawlResult: z6.string().nullable().optional(),
|
|
689
|
+
isMobileFriendly: z6.boolean().nullable().optional(),
|
|
690
|
+
richResults: z6.array(z6.string()).default([]),
|
|
691
|
+
inspectedAt: z6.string()
|
|
692
|
+
});
|
|
693
|
+
var indexTransitionSchema = z6.enum(["stable", "reindexed", "deindexed", "still-missing", "new"]);
|
|
694
|
+
var gscDeindexedRowSchema = z6.object({
|
|
695
|
+
url: z6.string(),
|
|
696
|
+
previousState: z6.string().nullable(),
|
|
697
|
+
currentState: z6.string().nullable(),
|
|
698
|
+
transitionDate: z6.string()
|
|
699
|
+
});
|
|
700
|
+
var gscReasonGroupSchema = z6.object({
|
|
701
|
+
reason: z6.string(),
|
|
702
|
+
count: z6.number(),
|
|
703
|
+
urls: z6.array(gscUrlInspectionDtoSchema).default([])
|
|
704
|
+
});
|
|
705
|
+
var gscCoverageSummaryDtoSchema = z6.object({
|
|
706
|
+
summary: z6.object({
|
|
707
|
+
total: z6.number(),
|
|
708
|
+
indexed: z6.number(),
|
|
709
|
+
notIndexed: z6.number(),
|
|
710
|
+
deindexed: z6.number(),
|
|
711
|
+
percentage: z6.number()
|
|
712
|
+
}),
|
|
713
|
+
lastInspectedAt: z6.string().nullable(),
|
|
714
|
+
indexed: z6.array(gscUrlInspectionDtoSchema).default([]),
|
|
715
|
+
notIndexed: z6.array(gscUrlInspectionDtoSchema).default([]),
|
|
716
|
+
deindexed: z6.array(gscDeindexedRowSchema).default([]),
|
|
717
|
+
reasonGroups: z6.array(gscReasonGroupSchema).default([])
|
|
718
|
+
});
|
|
719
|
+
var indexingNotificationDtoSchema = z6.object({
|
|
720
|
+
url: z6.string(),
|
|
721
|
+
type: z6.enum(["URL_UPDATED", "URL_DELETED"]),
|
|
722
|
+
notifiedAt: z6.string()
|
|
723
|
+
});
|
|
724
|
+
var indexingRequestResultDtoSchema = z6.object({
|
|
725
|
+
url: z6.string(),
|
|
726
|
+
type: z6.enum(["URL_UPDATED", "URL_DELETED"]),
|
|
727
|
+
notifiedAt: z6.string(),
|
|
728
|
+
status: z6.enum(["success", "error"]),
|
|
729
|
+
error: z6.string().optional()
|
|
730
|
+
});
|
|
731
|
+
var gscCoverageSnapshotDtoSchema = z6.object({
|
|
732
|
+
date: z6.string(),
|
|
733
|
+
indexed: z6.number(),
|
|
734
|
+
notIndexed: z6.number(),
|
|
735
|
+
reasonBreakdown: z6.record(z6.string(), z6.number()).default({})
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
// ../contracts/src/bing.ts
|
|
739
|
+
import { z as z7 } from "zod";
|
|
740
|
+
var bingConnectionDtoSchema = z7.object({
|
|
741
|
+
id: z7.string(),
|
|
742
|
+
domain: z7.string(),
|
|
743
|
+
siteUrl: z7.string().nullable().optional(),
|
|
744
|
+
createdAt: z7.string(),
|
|
745
|
+
updatedAt: z7.string()
|
|
746
|
+
});
|
|
747
|
+
var bingUrlInspectionDtoSchema = z7.object({
|
|
748
|
+
id: z7.string(),
|
|
749
|
+
url: z7.string(),
|
|
750
|
+
httpCode: z7.number().nullable().optional(),
|
|
751
|
+
inIndex: z7.boolean().nullable().optional(),
|
|
752
|
+
lastCrawledDate: z7.string().nullable().optional(),
|
|
753
|
+
inIndexDate: z7.string().nullable().optional(),
|
|
754
|
+
inspectedAt: z7.string(),
|
|
755
|
+
// Fields derived from GetUrlInfo response (more reliable than InIndex)
|
|
756
|
+
documentSize: z7.number().nullable().optional(),
|
|
757
|
+
anchorCount: z7.number().nullable().optional(),
|
|
758
|
+
discoveryDate: z7.string().nullable().optional()
|
|
759
|
+
});
|
|
760
|
+
var bingCoverageSummaryDtoSchema = z7.object({
|
|
761
|
+
summary: z7.object({
|
|
762
|
+
total: z7.number(),
|
|
763
|
+
indexed: z7.number(),
|
|
764
|
+
notIndexed: z7.number(),
|
|
765
|
+
unknown: z7.number().optional(),
|
|
766
|
+
percentage: z7.number()
|
|
767
|
+
}),
|
|
768
|
+
lastInspectedAt: z7.string().nullable(),
|
|
769
|
+
indexed: z7.array(bingUrlInspectionDtoSchema).default([]),
|
|
770
|
+
notIndexed: z7.array(bingUrlInspectionDtoSchema).default([]),
|
|
771
|
+
unknown: z7.array(bingUrlInspectionDtoSchema).default([]).optional()
|
|
772
|
+
});
|
|
773
|
+
var bingKeywordStatsDtoSchema = z7.object({
|
|
774
|
+
query: z7.string(),
|
|
775
|
+
impressions: z7.number(),
|
|
776
|
+
clicks: z7.number(),
|
|
777
|
+
ctr: z7.number(),
|
|
778
|
+
averagePosition: z7.number()
|
|
779
|
+
});
|
|
780
|
+
var bingCoverageSnapshotDtoSchema = z7.object({
|
|
781
|
+
date: z7.string(),
|
|
782
|
+
indexed: z7.number(),
|
|
783
|
+
notIndexed: z7.number(),
|
|
784
|
+
unknown: z7.number()
|
|
785
|
+
});
|
|
786
|
+
var bingSubmitResultDtoSchema = z7.object({
|
|
787
|
+
url: z7.string(),
|
|
788
|
+
status: z7.enum(["success", "error"]),
|
|
789
|
+
submittedAt: z7.string(),
|
|
790
|
+
error: z7.string().optional()
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// ../contracts/src/wordpress.ts
|
|
794
|
+
import { z as z8 } from "zod";
|
|
795
|
+
var wordpressEnvSchema = z8.enum(["live", "staging"]);
|
|
796
|
+
var wordpressConnectionDtoSchema = z8.object({
|
|
797
|
+
projectName: z8.string(),
|
|
798
|
+
url: z8.string(),
|
|
799
|
+
stagingUrl: z8.string().optional(),
|
|
800
|
+
username: z8.string(),
|
|
801
|
+
defaultEnv: wordpressEnvSchema,
|
|
802
|
+
createdAt: z8.string(),
|
|
803
|
+
updatedAt: z8.string()
|
|
804
|
+
});
|
|
805
|
+
var wordpressSiteStatusDtoSchema = z8.object({
|
|
806
|
+
url: z8.string(),
|
|
807
|
+
reachable: z8.boolean(),
|
|
808
|
+
pageCount: z8.number().nullable().optional(),
|
|
809
|
+
version: z8.string().nullable().optional(),
|
|
810
|
+
error: z8.string().nullable().optional(),
|
|
811
|
+
plugins: z8.array(z8.string()).optional(),
|
|
812
|
+
authenticatedUser: z8.object({
|
|
813
|
+
id: z8.number(),
|
|
814
|
+
slug: z8.string()
|
|
815
|
+
}).nullable().optional()
|
|
816
|
+
});
|
|
817
|
+
var wordpressStatusDtoSchema = z8.object({
|
|
818
|
+
connected: z8.boolean(),
|
|
819
|
+
projectName: z8.string(),
|
|
820
|
+
defaultEnv: wordpressEnvSchema,
|
|
821
|
+
live: wordpressSiteStatusDtoSchema.nullable(),
|
|
822
|
+
staging: wordpressSiteStatusDtoSchema.nullable(),
|
|
823
|
+
adminUrl: z8.string().nullable().optional()
|
|
824
|
+
});
|
|
825
|
+
var wordpressPageSummaryDtoSchema = z8.object({
|
|
826
|
+
id: z8.number(),
|
|
827
|
+
slug: z8.string(),
|
|
828
|
+
title: z8.string(),
|
|
829
|
+
status: z8.string(),
|
|
830
|
+
modifiedAt: z8.string().nullable().optional(),
|
|
831
|
+
link: z8.string().nullable().optional()
|
|
832
|
+
});
|
|
833
|
+
var wordpressSeoStateDtoSchema = z8.object({
|
|
834
|
+
title: z8.string().nullable(),
|
|
835
|
+
description: z8.string().nullable(),
|
|
836
|
+
noindex: z8.boolean().nullable(),
|
|
837
|
+
writable: z8.boolean().default(false),
|
|
838
|
+
writeTargets: z8.array(z8.string()).default([])
|
|
839
|
+
});
|
|
840
|
+
var wordpressSchemaBlockDtoSchema = z8.object({
|
|
841
|
+
type: z8.string(),
|
|
842
|
+
json: z8.record(z8.string(), z8.unknown())
|
|
843
|
+
});
|
|
844
|
+
var wordpressPageDetailDtoSchema = wordpressPageSummaryDtoSchema.extend({
|
|
845
|
+
env: wordpressEnvSchema,
|
|
846
|
+
content: z8.string(),
|
|
847
|
+
seo: wordpressSeoStateDtoSchema,
|
|
848
|
+
schemaBlocks: z8.array(wordpressSchemaBlockDtoSchema).default([])
|
|
849
|
+
});
|
|
850
|
+
var wordpressDiffPageDtoSchema = wordpressPageDetailDtoSchema.extend({
|
|
851
|
+
contentHash: z8.string(),
|
|
852
|
+
contentSnippet: z8.string()
|
|
853
|
+
});
|
|
854
|
+
var wordpressManualAssistDtoSchema = z8.object({
|
|
855
|
+
manualRequired: z8.literal(true),
|
|
856
|
+
targetUrl: z8.string(),
|
|
857
|
+
adminUrl: z8.string().nullable().optional(),
|
|
858
|
+
content: z8.string(),
|
|
859
|
+
nextSteps: z8.array(z8.string()).default([])
|
|
860
|
+
});
|
|
861
|
+
var wordpressAuditIssueDtoSchema = z8.object({
|
|
862
|
+
slug: z8.string(),
|
|
863
|
+
severity: z8.enum(["high", "medium", "low"]),
|
|
864
|
+
code: z8.enum([
|
|
865
|
+
"noindex",
|
|
866
|
+
"missing-seo-title",
|
|
867
|
+
"missing-meta-description",
|
|
868
|
+
"missing-schema",
|
|
869
|
+
"thin-content"
|
|
870
|
+
]),
|
|
871
|
+
message: z8.string()
|
|
872
|
+
});
|
|
873
|
+
var wordpressAuditPageDtoSchema = z8.object({
|
|
874
|
+
slug: z8.string(),
|
|
875
|
+
title: z8.string(),
|
|
876
|
+
status: z8.string(),
|
|
877
|
+
wordCount: z8.number(),
|
|
878
|
+
seo: wordpressSeoStateDtoSchema,
|
|
879
|
+
schemaPresent: z8.boolean(),
|
|
880
|
+
issues: z8.array(wordpressAuditIssueDtoSchema).default([])
|
|
881
|
+
});
|
|
882
|
+
var wordpressBulkMetaEntryResultDtoSchema = z8.object({
|
|
883
|
+
slug: z8.string(),
|
|
884
|
+
status: z8.enum(["applied", "skipped", "manual"]),
|
|
885
|
+
error: z8.string().optional(),
|
|
886
|
+
manualAssist: wordpressManualAssistDtoSchema.optional()
|
|
887
|
+
});
|
|
888
|
+
var wordpressBulkMetaResultDtoSchema = z8.object({
|
|
889
|
+
env: wordpressEnvSchema,
|
|
890
|
+
strategy: z8.enum(["plugin", "manual"]),
|
|
891
|
+
results: z8.array(wordpressBulkMetaEntryResultDtoSchema)
|
|
892
|
+
});
|
|
893
|
+
var wordpressSchemaDeployEntryResultDtoSchema = z8.object({
|
|
894
|
+
slug: z8.string(),
|
|
895
|
+
status: z8.enum(["deployed", "stripped", "skipped", "failed"]),
|
|
896
|
+
schemasInjected: z8.array(z8.string()).optional(),
|
|
897
|
+
manualAssist: wordpressManualAssistDtoSchema.optional(),
|
|
898
|
+
error: z8.string().optional()
|
|
899
|
+
});
|
|
900
|
+
var wordpressSchemaDeployResultDtoSchema = z8.object({
|
|
901
|
+
env: wordpressEnvSchema,
|
|
902
|
+
results: z8.array(wordpressSchemaDeployEntryResultDtoSchema)
|
|
903
|
+
});
|
|
904
|
+
var wordpressSchemaStatusPageDtoSchema = z8.object({
|
|
905
|
+
slug: z8.string(),
|
|
906
|
+
title: z8.string(),
|
|
907
|
+
canonrySchemas: z8.array(z8.string()),
|
|
908
|
+
thirdPartySchemas: z8.array(z8.string()),
|
|
909
|
+
hasCanonrySchema: z8.boolean()
|
|
910
|
+
});
|
|
911
|
+
var wordpressSchemaStatusResultDtoSchema = z8.object({
|
|
912
|
+
env: wordpressEnvSchema,
|
|
913
|
+
pages: z8.array(wordpressSchemaStatusPageDtoSchema)
|
|
914
|
+
});
|
|
915
|
+
var wordpressOnboardStepDtoSchema = z8.object({
|
|
916
|
+
name: z8.string(),
|
|
917
|
+
status: z8.enum(["completed", "skipped", "failed"]),
|
|
918
|
+
summary: z8.string().optional(),
|
|
919
|
+
error: z8.string().optional()
|
|
920
|
+
});
|
|
921
|
+
var wordpressOnboardResultDtoSchema = z8.object({
|
|
922
|
+
projectName: z8.string(),
|
|
923
|
+
steps: z8.array(wordpressOnboardStepDtoSchema)
|
|
924
|
+
});
|
|
925
|
+
var wordpressDiffDtoSchema = z8.object({
|
|
926
|
+
slug: z8.string(),
|
|
927
|
+
live: wordpressDiffPageDtoSchema,
|
|
928
|
+
staging: wordpressDiffPageDtoSchema,
|
|
929
|
+
hasDifferences: z8.boolean(),
|
|
930
|
+
differences: z8.object({
|
|
931
|
+
title: z8.boolean(),
|
|
932
|
+
slug: z8.boolean(),
|
|
933
|
+
content: z8.boolean(),
|
|
934
|
+
seoTitle: z8.boolean(),
|
|
935
|
+
seoDescription: z8.boolean(),
|
|
936
|
+
noindex: z8.boolean(),
|
|
937
|
+
schema: z8.boolean()
|
|
938
|
+
})
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
// ../contracts/src/providers.ts
|
|
942
|
+
var ProviderIds = {
|
|
943
|
+
claude: "claude",
|
|
944
|
+
openai: "openai",
|
|
945
|
+
gemini: "gemini",
|
|
946
|
+
perplexity: "perplexity",
|
|
947
|
+
local: "local",
|
|
948
|
+
cdpChatgpt: "cdp:chatgpt",
|
|
949
|
+
zai: "zai"
|
|
950
|
+
};
|
|
951
|
+
var PROVIDER_IDS = Object.values(ProviderIds);
|
|
952
|
+
var SweepProviderIds = {
|
|
953
|
+
claude: ProviderIds.claude,
|
|
954
|
+
openai: ProviderIds.openai,
|
|
955
|
+
gemini: ProviderIds.gemini,
|
|
956
|
+
perplexity: ProviderIds.perplexity,
|
|
957
|
+
local: ProviderIds.local,
|
|
958
|
+
cdpChatgpt: ProviderIds.cdpChatgpt
|
|
959
|
+
};
|
|
960
|
+
var SWEEP_PROVIDER_IDS = Object.values(SweepProviderIds);
|
|
961
|
+
var AgentProviderIds = {
|
|
962
|
+
claude: ProviderIds.claude,
|
|
963
|
+
openai: ProviderIds.openai,
|
|
964
|
+
gemini: ProviderIds.gemini,
|
|
965
|
+
zai: ProviderIds.zai
|
|
966
|
+
};
|
|
967
|
+
var AGENT_PROVIDER_IDS = Object.values(AgentProviderIds);
|
|
968
|
+
function isAgentProviderId(value) {
|
|
969
|
+
return AGENT_PROVIDER_IDS.includes(value);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// ../contracts/src/snapshot.ts
|
|
973
|
+
import { z as z9 } from "zod";
|
|
974
|
+
var snapshotAccuracySchema = z9.enum(["yes", "no", "unknown", "not-mentioned"]);
|
|
975
|
+
var snapshotRequestSchema = z9.object({
|
|
976
|
+
companyName: z9.string().min(1),
|
|
977
|
+
domain: z9.string().min(1),
|
|
978
|
+
phrases: z9.array(z9.string().min(1)).optional().default([]),
|
|
979
|
+
competitors: z9.array(z9.string().min(1)).optional().default([])
|
|
980
|
+
});
|
|
981
|
+
var snapshotCompetitorEntrySchema = z9.object({
|
|
982
|
+
name: z9.string(),
|
|
983
|
+
count: z9.number().int().nonnegative()
|
|
984
|
+
});
|
|
985
|
+
var snapshotAuditFactorSchema = z9.object({
|
|
986
|
+
id: z9.string(),
|
|
987
|
+
name: z9.string(),
|
|
988
|
+
weight: z9.number(),
|
|
989
|
+
score: z9.number(),
|
|
990
|
+
grade: z9.string(),
|
|
991
|
+
status: z9.enum(["pass", "partial", "fail"]),
|
|
992
|
+
findings: z9.array(z9.object({
|
|
993
|
+
type: z9.string(),
|
|
994
|
+
message: z9.string()
|
|
995
|
+
})).default([]),
|
|
996
|
+
recommendations: z9.array(z9.string()).default([])
|
|
997
|
+
});
|
|
998
|
+
var snapshotAuditSchema = z9.object({
|
|
999
|
+
url: z9.string(),
|
|
1000
|
+
finalUrl: z9.string(),
|
|
1001
|
+
auditedAt: z9.string(),
|
|
1002
|
+
overallScore: z9.number(),
|
|
1003
|
+
overallGrade: z9.string(),
|
|
1004
|
+
summary: z9.string(),
|
|
1005
|
+
factors: z9.array(snapshotAuditFactorSchema).default([])
|
|
1006
|
+
});
|
|
1007
|
+
var snapshotProfileSchema = z9.object({
|
|
1008
|
+
industry: z9.string(),
|
|
1009
|
+
summary: z9.string(),
|
|
1010
|
+
services: z9.array(z9.string()).default([]),
|
|
1011
|
+
categoryTerms: z9.array(z9.string()).default([])
|
|
1012
|
+
});
|
|
1013
|
+
var snapshotProviderResultSchema = z9.object({
|
|
1014
|
+
provider: z9.string(),
|
|
1015
|
+
displayName: z9.string(),
|
|
1016
|
+
model: z9.string().nullable().optional(),
|
|
1017
|
+
mentioned: z9.boolean(),
|
|
1018
|
+
cited: z9.boolean(),
|
|
1019
|
+
describedAccurately: snapshotAccuracySchema,
|
|
1020
|
+
accuracyNotes: z9.string().nullable().optional(),
|
|
1021
|
+
incorrectClaims: z9.array(z9.string()).default([]),
|
|
1022
|
+
recommendedCompetitors: z9.array(z9.string()).default([]),
|
|
1023
|
+
citedDomains: z9.array(z9.string()).default([]),
|
|
1024
|
+
groundingSources: z9.array(groundingSourceSchema).default([]),
|
|
1025
|
+
searchQueries: z9.array(z9.string()).default([]),
|
|
1026
|
+
answerText: z9.string(),
|
|
1027
|
+
error: z9.string().nullable().optional()
|
|
1028
|
+
});
|
|
1029
|
+
var snapshotQueryResultSchema = z9.object({
|
|
1030
|
+
phrase: z9.string(),
|
|
1031
|
+
providerResults: z9.array(snapshotProviderResultSchema).default([])
|
|
1032
|
+
});
|
|
1033
|
+
var snapshotSummarySchema = z9.object({
|
|
1034
|
+
totalQueries: z9.number().int().nonnegative(),
|
|
1035
|
+
totalProviders: z9.number().int().nonnegative(),
|
|
1036
|
+
totalComparisons: z9.number().int().nonnegative(),
|
|
1037
|
+
mentionCount: z9.number().int().nonnegative(),
|
|
1038
|
+
citationCount: z9.number().int().nonnegative(),
|
|
1039
|
+
topCompetitors: z9.array(snapshotCompetitorEntrySchema).default([]),
|
|
1040
|
+
visibilityGap: z9.string(),
|
|
1041
|
+
whatThisMeans: z9.array(z9.string()).default([]),
|
|
1042
|
+
recommendedActions: z9.array(z9.string()).default([])
|
|
1043
|
+
});
|
|
1044
|
+
var snapshotReportSchema = z9.object({
|
|
1045
|
+
companyName: z9.string(),
|
|
1046
|
+
domain: z9.string(),
|
|
1047
|
+
homepageUrl: z9.string(),
|
|
1048
|
+
generatedAt: z9.string(),
|
|
1049
|
+
phrases: z9.array(z9.string()).default([]),
|
|
1050
|
+
competitors: z9.array(z9.string()).default([]),
|
|
1051
|
+
profile: snapshotProfileSchema,
|
|
1052
|
+
audit: snapshotAuditSchema,
|
|
1053
|
+
queryResults: z9.array(snapshotQueryResultSchema).default([]),
|
|
1054
|
+
summary: snapshotSummarySchema
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
// ../contracts/src/schedule.ts
|
|
1058
|
+
import { z as z10 } from "zod";
|
|
1059
|
+
var scheduleDtoSchema = z10.object({
|
|
1060
|
+
id: z10.string(),
|
|
1061
|
+
projectId: z10.string(),
|
|
1062
|
+
cronExpr: z10.string(),
|
|
1063
|
+
preset: z10.string().nullable().optional(),
|
|
1064
|
+
timezone: z10.string().default("UTC"),
|
|
1065
|
+
enabled: z10.boolean().default(true),
|
|
1066
|
+
providers: z10.array(providerNameSchema).default([]),
|
|
1067
|
+
lastRunAt: z10.string().nullable().optional(),
|
|
1068
|
+
nextRunAt: z10.string().nullable().optional(),
|
|
1069
|
+
createdAt: z10.string(),
|
|
1070
|
+
updatedAt: z10.string()
|
|
1071
|
+
});
|
|
1072
|
+
var scheduleUpsertRequestSchema = z10.object({
|
|
1073
|
+
preset: z10.string().optional(),
|
|
1074
|
+
cron: z10.string().optional(),
|
|
1075
|
+
timezone: z10.string().optional().default("UTC"),
|
|
1076
|
+
enabled: z10.boolean().optional().default(true),
|
|
1077
|
+
providers: z10.array(providerNameSchema).optional().default([])
|
|
1078
|
+
}).refine(
|
|
1079
|
+
(data) => data.preset && !data.cron || !data.preset && data.cron,
|
|
1080
|
+
{ message: 'Exactly one of "preset" or "cron" must be provided' }
|
|
1081
|
+
);
|
|
1082
|
+
|
|
1083
|
+
// ../contracts/src/analytics.ts
|
|
1084
|
+
import { z as z11 } from "zod";
|
|
1085
|
+
var visibilityMetricModeSchema = z11.enum(["answer", "citation"]);
|
|
1086
|
+
var VisibilityMetricModes = visibilityMetricModeSchema.enum;
|
|
1087
|
+
function parseWindow(value) {
|
|
1088
|
+
if (value === "7d" || value === "30d" || value === "90d" || value === "all") return value;
|
|
1089
|
+
return "all";
|
|
1090
|
+
}
|
|
1091
|
+
function windowCutoff(window) {
|
|
1092
|
+
if (window === "all") return null;
|
|
1093
|
+
const days = window === "7d" ? 7 : window === "30d" ? 30 : 90;
|
|
1094
|
+
const d = /* @__PURE__ */ new Date();
|
|
1095
|
+
d.setDate(d.getDate() - days);
|
|
1096
|
+
return d.toISOString();
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// ../contracts/src/source-categories.ts
|
|
1100
|
+
var SOURCE_CATEGORY_RULES = [
|
|
1101
|
+
// Forums
|
|
1102
|
+
{ pattern: "reddit.com", category: "forum", label: "Reddit" },
|
|
1103
|
+
{ pattern: "quora.com", category: "forum", label: "Quora" },
|
|
1104
|
+
{ pattern: "stackexchange.com", category: "forum", label: "Stack Exchange" },
|
|
1105
|
+
{ pattern: "stackoverflow.com", category: "forum", label: "Stack Overflow" },
|
|
1106
|
+
{ pattern: "discourse.org", category: "forum", label: "Discourse" },
|
|
1107
|
+
// Social
|
|
1108
|
+
{ pattern: "linkedin.com", category: "social", label: "LinkedIn" },
|
|
1109
|
+
{ pattern: "twitter.com", category: "social", label: "X (Twitter)" },
|
|
1110
|
+
{ pattern: "x.com", category: "social", label: "X (Twitter)" },
|
|
1111
|
+
{ pattern: "facebook.com", category: "social", label: "Facebook" },
|
|
1112
|
+
{ pattern: "instagram.com", category: "social", label: "Instagram" },
|
|
1113
|
+
{ pattern: "threads.net", category: "social", label: "Threads" },
|
|
1114
|
+
{ pattern: "pinterest.com", category: "social", label: "Pinterest" },
|
|
1115
|
+
{ pattern: "tiktok.com", category: "social", label: "TikTok" },
|
|
1116
|
+
// Video
|
|
1117
|
+
{ pattern: "youtube.com", category: "video", label: "YouTube" },
|
|
1118
|
+
{ pattern: "youtu.be", category: "video", label: "YouTube" },
|
|
1119
|
+
{ pattern: "vimeo.com", category: "video", label: "Vimeo" },
|
|
1120
|
+
// News
|
|
1121
|
+
{ pattern: "nytimes.com", category: "news", label: "NY Times" },
|
|
1122
|
+
{ pattern: "bbc.com", category: "news", label: "BBC" },
|
|
1123
|
+
{ pattern: "bbc.co.uk", category: "news", label: "BBC" },
|
|
1124
|
+
{ pattern: "cnn.com", category: "news", label: "CNN" },
|
|
1125
|
+
{ pattern: "reuters.com", category: "news", label: "Reuters" },
|
|
1126
|
+
{ pattern: "apnews.com", category: "news", label: "AP News" },
|
|
1127
|
+
{ pattern: "theguardian.com", category: "news", label: "The Guardian" },
|
|
1128
|
+
{ pattern: "washingtonpost.com", category: "news", label: "Washington Post" },
|
|
1129
|
+
{ pattern: "wsj.com", category: "news", label: "WSJ" },
|
|
1130
|
+
{ pattern: "forbes.com", category: "news", label: "Forbes" },
|
|
1131
|
+
{ pattern: "techcrunch.com", category: "news", label: "TechCrunch" },
|
|
1132
|
+
{ pattern: "theverge.com", category: "news", label: "The Verge" },
|
|
1133
|
+
{ pattern: "wired.com", category: "news", label: "Wired" },
|
|
1134
|
+
{ pattern: "arstechnica.com", category: "news", label: "Ars Technica" },
|
|
1135
|
+
// Reference
|
|
1136
|
+
{ pattern: "wikipedia.org", category: "reference", label: "Wikipedia" },
|
|
1137
|
+
{ pattern: "wikimedia.org", category: "reference", label: "Wikimedia" },
|
|
1138
|
+
{ pattern: "britannica.com", category: "reference", label: "Britannica" },
|
|
1139
|
+
{ pattern: "merriam-webster.com", category: "reference", label: "Merriam-Webster" },
|
|
1140
|
+
// Blog / Content platforms
|
|
1141
|
+
{ pattern: "medium.com", category: "blog", label: "Medium" },
|
|
1142
|
+
{ pattern: "substack.com", category: "blog", label: "Substack" },
|
|
1143
|
+
{ pattern: "dev.to", category: "blog", label: "DEV Community" },
|
|
1144
|
+
{ pattern: "hashnode.dev", category: "blog", label: "Hashnode" },
|
|
1145
|
+
{ pattern: "wordpress.com", category: "blog", label: "WordPress" },
|
|
1146
|
+
{ pattern: "blogger.com", category: "blog", label: "Blogger" },
|
|
1147
|
+
{ pattern: "hubspot.com", category: "blog", label: "HubSpot" },
|
|
1148
|
+
// E-commerce
|
|
1149
|
+
{ pattern: "amazon.com", category: "ecommerce", label: "Amazon" },
|
|
1150
|
+
{ pattern: "amazon.co.uk", category: "ecommerce", label: "Amazon UK" },
|
|
1151
|
+
{ pattern: "shopify.com", category: "ecommerce", label: "Shopify" },
|
|
1152
|
+
{ pattern: "ebay.com", category: "ecommerce", label: "eBay" },
|
|
1153
|
+
// Academic
|
|
1154
|
+
{ pattern: "scholar.google.com", category: "academic", label: "Google Scholar" },
|
|
1155
|
+
{ pattern: "arxiv.org", category: "academic", label: "arXiv" },
|
|
1156
|
+
{ pattern: "pubmed.ncbi.nlm.nih.gov", category: "academic", label: "PubMed" },
|
|
1157
|
+
{ pattern: "researchgate.net", category: "academic", label: "ResearchGate" },
|
|
1158
|
+
{ pattern: ".edu", category: "academic", label: "Academic (.edu)" }
|
|
1159
|
+
];
|
|
1160
|
+
var CATEGORY_LABELS = {
|
|
1161
|
+
social: "Social Media",
|
|
1162
|
+
forum: "Forums & Q&A",
|
|
1163
|
+
news: "News & Media",
|
|
1164
|
+
reference: "Reference",
|
|
1165
|
+
blog: "Blogs & Content",
|
|
1166
|
+
ecommerce: "E-commerce",
|
|
1167
|
+
video: "Video",
|
|
1168
|
+
academic: "Academic",
|
|
1169
|
+
other: "Other"
|
|
1170
|
+
};
|
|
1171
|
+
function categorizeSource(uri) {
|
|
1172
|
+
let domain;
|
|
1173
|
+
try {
|
|
1174
|
+
const url = new URL(uri.startsWith("http") ? uri : `https://${uri}`);
|
|
1175
|
+
domain = url.hostname.replace(/^www\./, "");
|
|
1176
|
+
} catch {
|
|
1177
|
+
domain = uri.replace(/^https?:\/\//, "").replace(/^www\./, "").split("/")[0] ?? uri;
|
|
1178
|
+
}
|
|
1179
|
+
const domainLower = domain.toLowerCase();
|
|
1180
|
+
for (const rule of SOURCE_CATEGORY_RULES) {
|
|
1181
|
+
if (domainLower === rule.pattern || domainLower.endsWith(`.${rule.pattern}`) || rule.pattern.startsWith(".") && domainLower.endsWith(rule.pattern)) {
|
|
1182
|
+
return { category: rule.category, label: rule.label, domain };
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
return { category: "other", label: CATEGORY_LABELS.other, domain };
|
|
1186
|
+
}
|
|
1187
|
+
function categoryLabel(category) {
|
|
1188
|
+
return CATEGORY_LABELS[category];
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
// ../contracts/src/ga.ts
|
|
1192
|
+
import { z as z12 } from "zod";
|
|
1193
|
+
var ga4ConnectionDtoSchema = z12.object({
|
|
1194
|
+
id: z12.string(),
|
|
1195
|
+
projectId: z12.string(),
|
|
1196
|
+
propertyId: z12.string(),
|
|
1197
|
+
clientEmail: z12.string(),
|
|
1198
|
+
connected: z12.boolean(),
|
|
1199
|
+
createdAt: z12.string(),
|
|
1200
|
+
updatedAt: z12.string()
|
|
1201
|
+
});
|
|
1202
|
+
var ga4TrafficSnapshotDtoSchema = z12.object({
|
|
1203
|
+
date: z12.string(),
|
|
1204
|
+
landingPage: z12.string(),
|
|
1205
|
+
sessions: z12.number(),
|
|
1206
|
+
organicSessions: z12.number(),
|
|
1207
|
+
users: z12.number()
|
|
1208
|
+
});
|
|
1209
|
+
var ga4SourceDimensionSchema = z12.enum(["session", "first_user", "manual_utm"]);
|
|
1210
|
+
var ga4AiReferralDtoSchema = z12.object({
|
|
1211
|
+
source: z12.string(),
|
|
1212
|
+
medium: z12.string(),
|
|
1213
|
+
sessions: z12.number(),
|
|
1214
|
+
users: z12.number(),
|
|
1215
|
+
sourceDimension: ga4SourceDimensionSchema
|
|
1216
|
+
});
|
|
1217
|
+
var ga4SocialReferralDtoSchema = z12.object({
|
|
1218
|
+
source: z12.string(),
|
|
1219
|
+
medium: z12.string(),
|
|
1220
|
+
sessions: z12.number(),
|
|
1221
|
+
users: z12.number(),
|
|
1222
|
+
/** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
|
|
1223
|
+
channelGroup: z12.string()
|
|
1224
|
+
});
|
|
1225
|
+
var ga4TrafficSummaryDtoSchema = z12.object({
|
|
1226
|
+
totalSessions: z12.number(),
|
|
1227
|
+
totalOrganicSessions: z12.number(),
|
|
1228
|
+
totalUsers: z12.number(),
|
|
1229
|
+
topPages: z12.array(z12.object({
|
|
1230
|
+
landingPage: z12.string(),
|
|
1231
|
+
sessions: z12.number(),
|
|
1232
|
+
organicSessions: z12.number(),
|
|
1233
|
+
users: z12.number()
|
|
1234
|
+
})),
|
|
1235
|
+
aiReferrals: z12.array(ga4AiReferralDtoSchema),
|
|
1236
|
+
/** Deduped AI session total: MAX(sessions) per date+source+medium across attribution dimensions, then summed. */
|
|
1237
|
+
aiSessionsDeduped: z12.number(),
|
|
1238
|
+
/** Deduped AI user total: MAX(users) per date+source+medium across attribution dimensions, then summed. */
|
|
1239
|
+
aiUsersDeduped: z12.number(),
|
|
1240
|
+
socialReferrals: z12.array(ga4SocialReferralDtoSchema),
|
|
1241
|
+
/** Total social sessions (session-scoped, no cross-dimension dedup needed). */
|
|
1242
|
+
socialSessions: z12.number(),
|
|
1243
|
+
/** Total social users (session-scoped, no cross-dimension dedup needed). */
|
|
1244
|
+
socialUsers: z12.number(),
|
|
1245
|
+
/** Organic sessions as a percentage of total sessions (0–100, rounded). */
|
|
1246
|
+
organicSharePct: z12.number(),
|
|
1247
|
+
/** Deduped AI sessions as a percentage of total sessions (0–100, rounded). */
|
|
1248
|
+
aiSharePct: z12.number(),
|
|
1249
|
+
/** Social sessions as a percentage of total sessions (0–100, rounded). */
|
|
1250
|
+
socialSharePct: z12.number(),
|
|
1251
|
+
lastSyncedAt: z12.string().nullable()
|
|
1252
|
+
});
|
|
1253
|
+
var ga4AiReferralHistoryEntrySchema = z12.object({
|
|
1254
|
+
date: z12.string(),
|
|
1255
|
+
source: z12.string(),
|
|
1256
|
+
medium: z12.string(),
|
|
1257
|
+
sessions: z12.number(),
|
|
1258
|
+
users: z12.number(),
|
|
1259
|
+
/** Which GA4 dimension this row came from: session (sessionSource), first_user (firstUserSource), or manual_utm (utm_source parameter) */
|
|
1260
|
+
sourceDimension: ga4SourceDimensionSchema
|
|
1261
|
+
});
|
|
1262
|
+
var ga4SocialReferralHistoryEntrySchema = z12.object({
|
|
1263
|
+
date: z12.string(),
|
|
1264
|
+
source: z12.string(),
|
|
1265
|
+
medium: z12.string(),
|
|
1266
|
+
sessions: z12.number(),
|
|
1267
|
+
users: z12.number(),
|
|
1268
|
+
/** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
|
|
1269
|
+
channelGroup: z12.string()
|
|
1270
|
+
});
|
|
1271
|
+
var ga4SessionHistoryEntrySchema = z12.object({
|
|
1272
|
+
date: z12.string(),
|
|
1273
|
+
sessions: z12.number(),
|
|
1274
|
+
organicSessions: z12.number(),
|
|
1275
|
+
users: z12.number()
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
// ../contracts/src/answer-visibility.ts
|
|
1279
|
+
var GENERIC_TOKENS = /* @__PURE__ */ new Set([
|
|
1280
|
+
"agency",
|
|
1281
|
+
"app",
|
|
1282
|
+
"company",
|
|
1283
|
+
"corp",
|
|
1284
|
+
"group",
|
|
1285
|
+
"health",
|
|
1286
|
+
"inc",
|
|
1287
|
+
"llc",
|
|
1288
|
+
"online",
|
|
1289
|
+
"platform",
|
|
1290
|
+
"services",
|
|
1291
|
+
"site",
|
|
1292
|
+
"solutions",
|
|
1293
|
+
"software",
|
|
1294
|
+
"systems",
|
|
1295
|
+
"tech"
|
|
1296
|
+
]);
|
|
1297
|
+
function extractAnswerMentions(answerText, displayName, domains) {
|
|
1298
|
+
if (!answerText) return { mentioned: false, matchedTerms: [] };
|
|
1299
|
+
const matchedTerms = [];
|
|
1300
|
+
const lowerAnswer = answerText.toLowerCase();
|
|
1301
|
+
for (const domain of domains) {
|
|
1302
|
+
const normalizedDomain = normalizeProjectDomain(domain);
|
|
1303
|
+
if (!normalizedDomain || !normalizedDomain.includes(".")) continue;
|
|
1304
|
+
if (domainMentioned(lowerAnswer, normalizedDomain)) {
|
|
1305
|
+
matchedTerms.push(normalizedDomain);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
const normalizedDisplayName = normalizeText(displayName);
|
|
1309
|
+
if (normalizedDisplayName && normalizeText(answerText).includes(normalizedDisplayName)) {
|
|
1310
|
+
matchedTerms.push(displayName);
|
|
1311
|
+
}
|
|
1312
|
+
const tokens = collectDistinctiveTokens(displayName, domains);
|
|
1313
|
+
let tokenMatches = 0;
|
|
1314
|
+
const matchedTokens = [];
|
|
1315
|
+
for (const token of tokens) {
|
|
1316
|
+
if (new RegExp(`\\b${escapeRegExp(token)}\\b`).test(lowerAnswer)) {
|
|
1317
|
+
tokenMatches++;
|
|
1318
|
+
matchedTokens.push(token);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
const tokenThresholdMet = tokens.length > 0 && (tokens.length === 1 && tokenMatches >= 1 || tokenMatches >= Math.min(2, tokens.length));
|
|
1322
|
+
if (tokenThresholdMet) {
|
|
1323
|
+
matchedTerms.push(...matchedTokens);
|
|
1324
|
+
}
|
|
1325
|
+
const unique = [...new Set(matchedTerms)];
|
|
1326
|
+
const domainMatches = unique.filter((t) => t.includes("."));
|
|
1327
|
+
const dedupedFinal = unique.filter((term) => {
|
|
1328
|
+
if (term.includes(".")) return true;
|
|
1329
|
+
return !domainMatches.some((d) => d.toLowerCase().startsWith(term.toLowerCase() + "."));
|
|
1330
|
+
});
|
|
1331
|
+
return { mentioned: dedupedFinal.length > 0, matchedTerms: dedupedFinal };
|
|
1332
|
+
}
|
|
1333
|
+
function determineAnswerMentioned(answerText, displayName, domains) {
|
|
1334
|
+
return extractAnswerMentions(answerText, displayName, domains).mentioned;
|
|
1335
|
+
}
|
|
1336
|
+
function visibilityStateFromAnswerMentioned(answerMentioned) {
|
|
1337
|
+
return answerMentioned ? "visible" : "not-visible";
|
|
1338
|
+
}
|
|
1339
|
+
function brandKeyFromText(value) {
|
|
1340
|
+
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1341
|
+
}
|
|
1342
|
+
function domainMentioned(lowerAnswer, normalizedDomain) {
|
|
1343
|
+
const escapedDomain = escapeRegExp(normalizedDomain.toLowerCase());
|
|
1344
|
+
const patterns = [
|
|
1345
|
+
new RegExp(`(^|[^a-z0-9-])${escapedDomain}($|[^a-z0-9-])`),
|
|
1346
|
+
new RegExp(`https?://(?:www\\.)?${escapedDomain}(?:[/:?#]|$)`),
|
|
1347
|
+
new RegExp(`www\\.${escapedDomain}(?:[/:?#]|$)`)
|
|
1348
|
+
];
|
|
1349
|
+
return patterns.some((pattern) => pattern.test(lowerAnswer));
|
|
1350
|
+
}
|
|
1351
|
+
function collectDistinctiveTokens(displayName, domains) {
|
|
1352
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
1353
|
+
for (const token of extractDistinctiveTokens(displayName)) {
|
|
1354
|
+
tokens.add(token);
|
|
1355
|
+
}
|
|
1356
|
+
for (const domain of domains) {
|
|
1357
|
+
const hostname = normalizeProjectDomain(domain).split("/")[0] ?? "";
|
|
1358
|
+
for (const label of hostname.split(".").filter(Boolean)) {
|
|
1359
|
+
const token = label.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
1360
|
+
if (isDistinctiveToken(token)) tokens.add(token);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return [...tokens];
|
|
1364
|
+
}
|
|
1365
|
+
function extractDistinctiveTokens(value) {
|
|
1366
|
+
return normalizeText(value).split(" ").filter(isDistinctiveToken);
|
|
1367
|
+
}
|
|
1368
|
+
function isDistinctiveToken(token) {
|
|
1369
|
+
if (token.length < 4) return false;
|
|
1370
|
+
return !GENERIC_TOKENS.has(token);
|
|
1371
|
+
}
|
|
1372
|
+
function normalizeText(value) {
|
|
1373
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
1374
|
+
}
|
|
1375
|
+
function escapeRegExp(value) {
|
|
1376
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// ../contracts/src/agent.ts
|
|
1380
|
+
import { z as z13 } from "zod";
|
|
1381
|
+
var memorySourceSchema = z13.enum(["aero", "user", "compaction"]);
|
|
1382
|
+
var MemorySources = memorySourceSchema.enum;
|
|
1383
|
+
var AGENT_MEMORY_VALUE_MAX_BYTES = 2 * 1024;
|
|
1384
|
+
var AGENT_MEMORY_KEY_MAX_LENGTH = 128;
|
|
1385
|
+
var agentMemoryUpsertRequestSchema = z13.object({
|
|
1386
|
+
key: z13.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH),
|
|
1387
|
+
value: z13.string().min(1)
|
|
1388
|
+
});
|
|
1389
|
+
var agentMemoryDeleteRequestSchema = z13.object({
|
|
1390
|
+
key: z13.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH)
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
// ../contracts/src/backlinks.ts
|
|
1394
|
+
import { z as z14 } from "zod";
|
|
1395
|
+
var ccReleaseSyncStatusSchema = z14.enum(["queued", "downloading", "querying", "ready", "failed"]);
|
|
1396
|
+
var CcReleaseSyncStatuses = ccReleaseSyncStatusSchema.enum;
|
|
1397
|
+
var ccReleaseSyncDtoSchema = z14.object({
|
|
1398
|
+
id: z14.string(),
|
|
1399
|
+
release: z14.string(),
|
|
1400
|
+
status: ccReleaseSyncStatusSchema,
|
|
1401
|
+
phaseDetail: z14.string().nullable().optional(),
|
|
1402
|
+
vertexPath: z14.string().nullable().optional(),
|
|
1403
|
+
edgesPath: z14.string().nullable().optional(),
|
|
1404
|
+
vertexSha256: z14.string().nullable().optional(),
|
|
1405
|
+
edgesSha256: z14.string().nullable().optional(),
|
|
1406
|
+
vertexBytes: z14.number().int().nullable().optional(),
|
|
1407
|
+
edgesBytes: z14.number().int().nullable().optional(),
|
|
1408
|
+
projectsProcessed: z14.number().int().nullable().optional(),
|
|
1409
|
+
domainsDiscovered: z14.number().int().nullable().optional(),
|
|
1410
|
+
downloadStartedAt: z14.string().nullable().optional(),
|
|
1411
|
+
downloadFinishedAt: z14.string().nullable().optional(),
|
|
1412
|
+
queryStartedAt: z14.string().nullable().optional(),
|
|
1413
|
+
queryFinishedAt: z14.string().nullable().optional(),
|
|
1414
|
+
error: z14.string().nullable().optional(),
|
|
1415
|
+
createdAt: z14.string(),
|
|
1416
|
+
updatedAt: z14.string()
|
|
1417
|
+
});
|
|
1418
|
+
var backlinkDomainDtoSchema = z14.object({
|
|
1419
|
+
linkingDomain: z14.string(),
|
|
1420
|
+
numHosts: z14.number().int()
|
|
1421
|
+
});
|
|
1422
|
+
var backlinkSummaryDtoSchema = z14.object({
|
|
1423
|
+
projectId: z14.string(),
|
|
1424
|
+
release: z14.string(),
|
|
1425
|
+
targetDomain: z14.string(),
|
|
1426
|
+
totalLinkingDomains: z14.number().int(),
|
|
1427
|
+
totalHosts: z14.number().int(),
|
|
1428
|
+
top10HostsShare: z14.string(),
|
|
1429
|
+
queriedAt: z14.string()
|
|
1430
|
+
});
|
|
1431
|
+
var backlinkListResponseSchema = z14.object({
|
|
1432
|
+
summary: backlinkSummaryDtoSchema.nullable(),
|
|
1433
|
+
total: z14.number().int(),
|
|
1434
|
+
rows: z14.array(backlinkDomainDtoSchema)
|
|
1435
|
+
});
|
|
1436
|
+
var backlinkHistoryEntrySchema = z14.object({
|
|
1437
|
+
release: z14.string(),
|
|
1438
|
+
totalLinkingDomains: z14.number().int(),
|
|
1439
|
+
totalHosts: z14.number().int(),
|
|
1440
|
+
top10HostsShare: z14.string(),
|
|
1441
|
+
queriedAt: z14.string()
|
|
1442
|
+
});
|
|
1443
|
+
var backlinksInstallStatusDtoSchema = z14.object({
|
|
1444
|
+
duckdbInstalled: z14.boolean(),
|
|
1445
|
+
duckdbVersion: z14.string().nullable().optional(),
|
|
1446
|
+
duckdbSpec: z14.string(),
|
|
1447
|
+
pluginDir: z14.string()
|
|
1448
|
+
});
|
|
1449
|
+
var backlinksInstallResultDtoSchema = z14.object({
|
|
1450
|
+
installed: z14.boolean(),
|
|
1451
|
+
version: z14.string(),
|
|
1452
|
+
path: z14.string(),
|
|
1453
|
+
alreadyPresent: z14.boolean()
|
|
1454
|
+
});
|
|
1455
|
+
var ccAvailableReleaseSchema = z14.object({
|
|
1456
|
+
release: z14.string(),
|
|
1457
|
+
vertexUrl: z14.string(),
|
|
1458
|
+
edgesUrl: z14.string(),
|
|
1459
|
+
vertexBytes: z14.number().int().nullable(),
|
|
1460
|
+
edgesBytes: z14.number().int().nullable(),
|
|
1461
|
+
lastModified: z14.string().nullable()
|
|
1462
|
+
});
|
|
1463
|
+
var ccCachedReleaseSchema = z14.object({
|
|
1464
|
+
release: z14.string(),
|
|
1465
|
+
syncStatus: ccReleaseSyncStatusSchema.nullable(),
|
|
1466
|
+
bytes: z14.number().int(),
|
|
1467
|
+
lastUsedAt: z14.string().nullable()
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1470
|
+
// src/client.ts
|
|
1471
|
+
function createApiClient() {
|
|
1472
|
+
const config = loadConfig();
|
|
1473
|
+
const basePathResolved = !!config.basePath || "CANONRY_BASE_PATH" in process.env;
|
|
1474
|
+
return new ApiClient(config.apiUrl, config.apiKey, { skipProbe: basePathResolved });
|
|
1475
|
+
}
|
|
1476
|
+
var ApiClient = class {
|
|
1477
|
+
baseUrl;
|
|
1478
|
+
originUrl;
|
|
1479
|
+
apiKey;
|
|
1480
|
+
probePromise = null;
|
|
1481
|
+
probeSkipped;
|
|
1482
|
+
constructor(baseUrl, apiKey, opts) {
|
|
1483
|
+
this.originUrl = baseUrl.replace(/\/$/, "");
|
|
1484
|
+
this.baseUrl = this.originUrl + "/api/v1";
|
|
1485
|
+
this.apiKey = apiKey;
|
|
1486
|
+
this.probeSkipped = opts?.skipProbe ?? false;
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* On first API call, probe /health to auto-discover basePath when the user
|
|
1490
|
+
* hasn't configured one locally. This lets `canonry run` in a separate shell
|
|
1491
|
+
* discover that the server is running at e.g. /canonry/ without requiring
|
|
1492
|
+
* config.yaml edits or CANONRY_BASE_PATH in every shell.
|
|
1493
|
+
*/
|
|
1494
|
+
probeBasePath() {
|
|
1495
|
+
if (this.probeSkipped) return Promise.resolve();
|
|
1496
|
+
if (!this.probePromise) {
|
|
1497
|
+
this.probePromise = (async () => {
|
|
1498
|
+
try {
|
|
1499
|
+
const origin = new URL(this.originUrl).origin;
|
|
1500
|
+
const res = await fetch(`${origin}/health`, {
|
|
1501
|
+
signal: AbortSignal.timeout(2e3)
|
|
1502
|
+
});
|
|
1503
|
+
if (res.ok) {
|
|
1504
|
+
const body = await res.json();
|
|
1505
|
+
if (body.basePath && typeof body.basePath === "string") {
|
|
1506
|
+
const normalized = "/" + body.basePath.replace(/^\/|\/$/g, "");
|
|
1507
|
+
if (normalized !== "/") {
|
|
1508
|
+
this.originUrl = origin + normalized;
|
|
1509
|
+
this.baseUrl = this.originUrl + "/api/v1";
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
} catch {
|
|
1514
|
+
}
|
|
1515
|
+
})();
|
|
1516
|
+
}
|
|
1517
|
+
return this.probePromise;
|
|
1518
|
+
}
|
|
1519
|
+
async request(method, path2, body) {
|
|
1520
|
+
await this.probeBasePath();
|
|
1521
|
+
const url = `${this.baseUrl}${path2}`;
|
|
1522
|
+
const serializedBody = body != null ? JSON.stringify(body) : void 0;
|
|
1523
|
+
const headers = {
|
|
1524
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
1525
|
+
"Accept": "application/json",
|
|
1526
|
+
...serializedBody != null ? { "Content-Type": "application/json" } : {}
|
|
1527
|
+
};
|
|
1528
|
+
let res;
|
|
1529
|
+
try {
|
|
1530
|
+
res = await fetch(url, {
|
|
1531
|
+
method,
|
|
1532
|
+
headers,
|
|
1533
|
+
body: serializedBody
|
|
1534
|
+
});
|
|
1535
|
+
} catch (err) {
|
|
1536
|
+
if (err instanceof CliError) throw err;
|
|
1537
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1538
|
+
if (msg.includes("fetch failed") || msg.includes("ECONNREFUSED") || msg.includes("connect ECONNREFUSED")) {
|
|
1539
|
+
throw new CliError({
|
|
1540
|
+
code: "CONNECTION_ERROR",
|
|
1541
|
+
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).`,
|
|
1542
|
+
exitCode: EXIT_SYSTEM_ERROR
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
throw new CliError({ code: "CONNECTION_ERROR", message: msg, exitCode: EXIT_SYSTEM_ERROR });
|
|
1546
|
+
}
|
|
1547
|
+
if (!res.ok) {
|
|
1548
|
+
let errorBody;
|
|
1549
|
+
try {
|
|
1550
|
+
errorBody = await res.json();
|
|
1551
|
+
} catch {
|
|
1552
|
+
errorBody = { error: { code: "UNKNOWN", message: res.statusText } };
|
|
1553
|
+
}
|
|
1554
|
+
const errorObj = errorBody && typeof errorBody === "object" && "error" in errorBody && errorBody.error && typeof errorBody.error === "object" ? errorBody.error : null;
|
|
1555
|
+
const msg = errorObj?.message ? String(errorObj.message) : `HTTP ${res.status}: ${res.statusText}`;
|
|
1556
|
+
const code = errorObj?.code ? String(errorObj.code) : "API_ERROR";
|
|
1557
|
+
const exitCode = res.status >= 500 ? EXIT_SYSTEM_ERROR : EXIT_USER_ERROR;
|
|
1558
|
+
throw new CliError({
|
|
1559
|
+
code,
|
|
1560
|
+
message: msg,
|
|
1561
|
+
exitCode,
|
|
1562
|
+
details: {
|
|
1563
|
+
...errorObj?.details ?? {},
|
|
1564
|
+
httpStatus: res.status
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
if (res.status === 204) {
|
|
1569
|
+
return void 0;
|
|
1570
|
+
}
|
|
1571
|
+
return await res.json();
|
|
1572
|
+
}
|
|
1573
|
+
async getAgentTranscript(project) {
|
|
1574
|
+
return this.request(
|
|
1575
|
+
"GET",
|
|
1576
|
+
`/projects/${encodeURIComponent(project)}/agent/transcript`
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
async resetAgentTranscript(project) {
|
|
1580
|
+
await this.request(
|
|
1581
|
+
"DELETE",
|
|
1582
|
+
`/projects/${encodeURIComponent(project)}/agent/transcript`
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
async listAgentProviders(project) {
|
|
1586
|
+
return this.request(
|
|
1587
|
+
"GET",
|
|
1588
|
+
`/projects/${encodeURIComponent(project)}/agent/providers`
|
|
1589
|
+
);
|
|
1590
|
+
}
|
|
1591
|
+
async listAgentMemory(project) {
|
|
1592
|
+
return this.request(
|
|
1593
|
+
"GET",
|
|
1594
|
+
`/projects/${encodeURIComponent(project)}/agent/memory`
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1597
|
+
async setAgentMemory(project, body) {
|
|
1598
|
+
return this.request(
|
|
1599
|
+
"PUT",
|
|
1600
|
+
`/projects/${encodeURIComponent(project)}/agent/memory`,
|
|
1601
|
+
body
|
|
1602
|
+
);
|
|
1603
|
+
}
|
|
1604
|
+
async forgetAgentMemory(project, key) {
|
|
1605
|
+
return this.request(
|
|
1606
|
+
"DELETE",
|
|
1607
|
+
`/projects/${encodeURIComponent(project)}/agent/memory`,
|
|
1608
|
+
{ key }
|
|
1609
|
+
);
|
|
1610
|
+
}
|
|
1611
|
+
/**
|
|
1612
|
+
* POST a request whose response body the caller intends to consume as a
|
|
1613
|
+
* stream (e.g. the Aero agent SSE endpoint). Shares the probe + auth +
|
|
1614
|
+
* structured-error behavior of `request()`; the caller reads `res.body`
|
|
1615
|
+
* and releases the response when done.
|
|
1616
|
+
*/
|
|
1617
|
+
async streamPost(path2, body, signal) {
|
|
1618
|
+
await this.probeBasePath();
|
|
1619
|
+
const url = `${this.baseUrl}${path2}`;
|
|
1620
|
+
const headers = {
|
|
1621
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
1622
|
+
"Content-Type": "application/json",
|
|
1623
|
+
Accept: "text/event-stream"
|
|
1624
|
+
};
|
|
1625
|
+
let res;
|
|
1626
|
+
try {
|
|
1627
|
+
res = await fetch(url, {
|
|
1628
|
+
method: "POST",
|
|
1629
|
+
headers,
|
|
1630
|
+
body: JSON.stringify(body ?? {}),
|
|
1631
|
+
signal
|
|
1632
|
+
});
|
|
1633
|
+
} catch (err) {
|
|
1634
|
+
if (err instanceof CliError) throw err;
|
|
1635
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1636
|
+
if (msg.includes("fetch failed") || msg.includes("ECONNREFUSED") || msg.includes("connect ECONNREFUSED")) {
|
|
1637
|
+
throw new CliError({
|
|
1638
|
+
code: "CONNECTION_ERROR",
|
|
1639
|
+
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).`,
|
|
1640
|
+
exitCode: EXIT_SYSTEM_ERROR
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
throw new CliError({ code: "CONNECTION_ERROR", message: msg, exitCode: EXIT_SYSTEM_ERROR });
|
|
1644
|
+
}
|
|
1645
|
+
if (!res.ok || !res.body) {
|
|
1646
|
+
let errorBody;
|
|
1647
|
+
try {
|
|
1648
|
+
errorBody = await res.json();
|
|
1649
|
+
} catch {
|
|
1650
|
+
errorBody = { error: { code: "UNKNOWN", message: res.statusText } };
|
|
1651
|
+
}
|
|
1652
|
+
const errorObj = errorBody && typeof errorBody === "object" && "error" in errorBody && errorBody.error ? errorBody.error : null;
|
|
1653
|
+
const msg = errorObj?.message ? String(errorObj.message) : `HTTP ${res.status}: ${res.statusText}`;
|
|
1654
|
+
const code = errorObj?.code ? String(errorObj.code) : "API_ERROR";
|
|
1655
|
+
const exitCode = res.status >= 500 ? EXIT_SYSTEM_ERROR : EXIT_USER_ERROR;
|
|
1656
|
+
throw new CliError({ code, message: msg, exitCode, details: { httpStatus: res.status } });
|
|
1657
|
+
}
|
|
1658
|
+
return res;
|
|
1659
|
+
}
|
|
1660
|
+
async putProject(name, body) {
|
|
1661
|
+
return this.request("PUT", `/projects/${encodeURIComponent(name)}`, body);
|
|
1662
|
+
}
|
|
1663
|
+
async listProjects() {
|
|
1664
|
+
return this.request("GET", "/projects");
|
|
1665
|
+
}
|
|
1666
|
+
async getProject(name) {
|
|
1667
|
+
return this.request("GET", `/projects/${encodeURIComponent(name)}`);
|
|
1668
|
+
}
|
|
1669
|
+
async deleteProject(name) {
|
|
1670
|
+
await this.request("DELETE", `/projects/${encodeURIComponent(name)}`);
|
|
1671
|
+
}
|
|
1672
|
+
async putKeywords(project, keywords) {
|
|
1673
|
+
await this.request("PUT", `/projects/${encodeURIComponent(project)}/keywords`, { keywords });
|
|
1674
|
+
}
|
|
1675
|
+
async listKeywords(project) {
|
|
1676
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/keywords`);
|
|
1677
|
+
}
|
|
1678
|
+
async deleteKeywords(project, keywords) {
|
|
1679
|
+
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/keywords`, { keywords });
|
|
1680
|
+
}
|
|
1681
|
+
async appendKeywords(project, keywords) {
|
|
1682
|
+
await this.request("POST", `/projects/${encodeURIComponent(project)}/keywords`, { keywords });
|
|
1683
|
+
}
|
|
1684
|
+
async listCompetitors(project) {
|
|
1685
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/competitors`);
|
|
1686
|
+
}
|
|
1687
|
+
async appendCompetitors(project, competitors) {
|
|
1688
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/competitors`, { competitors });
|
|
1689
|
+
}
|
|
1690
|
+
async deleteCompetitors(project, competitors) {
|
|
1691
|
+
return this.request("DELETE", `/projects/${encodeURIComponent(project)}/competitors`, { competitors });
|
|
1692
|
+
}
|
|
1693
|
+
async triggerRun(project, body) {
|
|
1694
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/runs`, body ?? {});
|
|
1695
|
+
}
|
|
1696
|
+
async listRuns(project, limit) {
|
|
1697
|
+
const query = limit != null ? `?limit=${encodeURIComponent(String(limit))}` : "";
|
|
1698
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/runs${query}`);
|
|
1699
|
+
}
|
|
1700
|
+
async getLatestRun(project) {
|
|
1701
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/runs/latest`);
|
|
1702
|
+
}
|
|
1703
|
+
async getRun(id) {
|
|
1704
|
+
return this.request("GET", `/runs/${encodeURIComponent(id)}`);
|
|
1705
|
+
}
|
|
1706
|
+
async cancelRun(id) {
|
|
1707
|
+
return this.request("POST", `/runs/${encodeURIComponent(id)}/cancel`);
|
|
1708
|
+
}
|
|
1709
|
+
async getTimeline(project, location) {
|
|
1710
|
+
const qs = location != null ? `?location=${encodeURIComponent(location)}` : "";
|
|
1711
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/timeline${qs}`);
|
|
1712
|
+
}
|
|
1713
|
+
async getHistory(project) {
|
|
1714
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/history`);
|
|
1715
|
+
}
|
|
1716
|
+
async getSnapshots(project, opts) {
|
|
1717
|
+
const params = new URLSearchParams();
|
|
1718
|
+
if (opts?.limit != null) params.set("limit", String(opts.limit));
|
|
1719
|
+
if (opts?.offset != null) params.set("offset", String(opts.offset));
|
|
1720
|
+
if (opts?.location != null) params.set("location", opts.location);
|
|
1721
|
+
const qs = params.toString();
|
|
1722
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/snapshots${qs ? `?${qs}` : ""}`);
|
|
1723
|
+
}
|
|
1724
|
+
async getSnapshotDiff(project, run1, run2) {
|
|
1725
|
+
const params = new URLSearchParams({ run1, run2 });
|
|
1726
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/snapshots/diff?${params.toString()}`);
|
|
1727
|
+
}
|
|
1728
|
+
async getExport(project) {
|
|
1729
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/export`);
|
|
1730
|
+
}
|
|
1731
|
+
async apply(config) {
|
|
1732
|
+
return this.request("POST", "/apply", config);
|
|
1733
|
+
}
|
|
1734
|
+
async getStatus(project) {
|
|
1735
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}`);
|
|
1736
|
+
}
|
|
1737
|
+
async getSettings() {
|
|
1738
|
+
return this.request("GET", "/settings");
|
|
1739
|
+
}
|
|
1740
|
+
async createSnapshot(body) {
|
|
1741
|
+
return this.request("POST", "/snapshot", body);
|
|
1742
|
+
}
|
|
1743
|
+
async updateProvider(name, body) {
|
|
1744
|
+
return this.request("PUT", `/settings/providers/${encodeURIComponent(name)}`, body);
|
|
1745
|
+
}
|
|
1746
|
+
async putSchedule(project, body) {
|
|
1747
|
+
return this.request("PUT", `/projects/${encodeURIComponent(project)}/schedule`, body);
|
|
1748
|
+
}
|
|
1749
|
+
async getSchedule(project) {
|
|
1750
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/schedule`);
|
|
1751
|
+
}
|
|
1752
|
+
async deleteSchedule(project) {
|
|
1753
|
+
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/schedule`);
|
|
1754
|
+
}
|
|
1755
|
+
async createNotification(project, body) {
|
|
1756
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/notifications`, body);
|
|
1757
|
+
}
|
|
1758
|
+
async listNotifications(project) {
|
|
1759
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/notifications`);
|
|
1760
|
+
}
|
|
1761
|
+
async deleteNotification(project, id) {
|
|
1762
|
+
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/notifications/${encodeURIComponent(id)}`);
|
|
1763
|
+
}
|
|
1764
|
+
async testNotification(project, id) {
|
|
1765
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/notifications/${encodeURIComponent(id)}/test`);
|
|
1766
|
+
}
|
|
1767
|
+
async addLocation(project, body) {
|
|
1768
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/locations`, body);
|
|
1769
|
+
}
|
|
1770
|
+
async listLocations(project) {
|
|
1771
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/locations`);
|
|
1772
|
+
}
|
|
1773
|
+
async removeLocation(project, label) {
|
|
1774
|
+
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/locations/${encodeURIComponent(label)}`);
|
|
1775
|
+
}
|
|
1776
|
+
async setDefaultLocation(project, label) {
|
|
1777
|
+
return this.request("PUT", `/projects/${encodeURIComponent(project)}/locations/default`, { label });
|
|
1778
|
+
}
|
|
1779
|
+
async getTelemetry() {
|
|
1780
|
+
return this.request("GET", "/telemetry");
|
|
1781
|
+
}
|
|
1782
|
+
async updateTelemetry(enabled) {
|
|
1783
|
+
return this.request("PUT", "/telemetry", { enabled });
|
|
1784
|
+
}
|
|
1785
|
+
async generateKeywords(project, provider, count) {
|
|
1786
|
+
return this.request(
|
|
1787
|
+
"POST",
|
|
1788
|
+
`/projects/${encodeURIComponent(project)}/keywords/generate`,
|
|
1789
|
+
{ provider, count }
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1792
|
+
// Google connection management
|
|
1793
|
+
async googleConnect(project, body) {
|
|
1794
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/connect`, body);
|
|
1795
|
+
}
|
|
1796
|
+
async googleConnections(project) {
|
|
1797
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/connections`);
|
|
1798
|
+
}
|
|
1799
|
+
async googleDisconnect(project, type) {
|
|
1800
|
+
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/google/connections/${encodeURIComponent(type)}`);
|
|
1801
|
+
}
|
|
1802
|
+
async googleProperties(project) {
|
|
1803
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/properties`);
|
|
1804
|
+
}
|
|
1805
|
+
async googleSetProperty(project, type, propertyId) {
|
|
1806
|
+
return this.request("PUT", `/projects/${encodeURIComponent(project)}/google/connections/${encodeURIComponent(type)}/property`, { propertyId });
|
|
1807
|
+
}
|
|
1808
|
+
async googleSetSitemap(project, type, sitemapUrl) {
|
|
1809
|
+
return this.request("PUT", `/projects/${encodeURIComponent(project)}/google/connections/${encodeURIComponent(type)}/sitemap`, { sitemapUrl });
|
|
1810
|
+
}
|
|
1811
|
+
// GSC data
|
|
1812
|
+
async gscSync(project, body) {
|
|
1813
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/sync`, body ?? {});
|
|
1814
|
+
}
|
|
1815
|
+
async gscPerformance(project, params) {
|
|
1816
|
+
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
1817
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/performance${qs}`);
|
|
1818
|
+
}
|
|
1819
|
+
async gscInspect(project, url) {
|
|
1820
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/inspect`, { url });
|
|
1821
|
+
}
|
|
1822
|
+
async gscInspections(project, params) {
|
|
1823
|
+
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
1824
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/inspections${qs}`);
|
|
1825
|
+
}
|
|
1826
|
+
async gscDeindexed(project) {
|
|
1827
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/deindexed`);
|
|
1828
|
+
}
|
|
1829
|
+
async gscCoverage(project) {
|
|
1830
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/coverage`);
|
|
1831
|
+
}
|
|
1832
|
+
async gscCoverageHistory(project, params) {
|
|
1833
|
+
const qs = params?.limit != null ? `?limit=${params.limit}` : "";
|
|
1834
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/coverage/history${qs}`);
|
|
1835
|
+
}
|
|
1836
|
+
async gscInspectSitemap(project, body) {
|
|
1837
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/inspect-sitemap`, body ?? {});
|
|
1838
|
+
}
|
|
1839
|
+
async gscSitemaps(project) {
|
|
1840
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/sitemaps`);
|
|
1841
|
+
}
|
|
1842
|
+
async gscDiscoverSitemaps(project) {
|
|
1843
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/discover-sitemaps`, {});
|
|
1844
|
+
}
|
|
1845
|
+
// Analytics
|
|
1846
|
+
async getAnalyticsMetrics(project, window) {
|
|
1847
|
+
const qs = window ? `?window=${encodeURIComponent(window)}` : "";
|
|
1848
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/metrics${qs}`);
|
|
1849
|
+
}
|
|
1850
|
+
async getAnalyticsGaps(project, window) {
|
|
1851
|
+
const qs = window ? `?window=${encodeURIComponent(window)}` : "";
|
|
1852
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/gaps${qs}`);
|
|
1853
|
+
}
|
|
1854
|
+
async getAnalyticsSources(project, window) {
|
|
1855
|
+
const qs = window ? `?window=${encodeURIComponent(window)}` : "";
|
|
1856
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/sources${qs}`);
|
|
1857
|
+
}
|
|
1858
|
+
// Google Indexing API
|
|
1859
|
+
async googleRequestIndexing(project, body) {
|
|
1860
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/google/indexing/request`, body);
|
|
1861
|
+
}
|
|
1862
|
+
// Bing Webmaster Tools
|
|
1863
|
+
async bingConnect(project, body) {
|
|
1864
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/connect`, body);
|
|
1865
|
+
}
|
|
1866
|
+
async bingDisconnect(project) {
|
|
1867
|
+
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/bing/disconnect`);
|
|
1868
|
+
}
|
|
1869
|
+
async bingStatus(project) {
|
|
1870
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/status`);
|
|
1871
|
+
}
|
|
1872
|
+
async bingSites(project) {
|
|
1873
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/sites`);
|
|
1874
|
+
}
|
|
1875
|
+
async bingSetSite(project, siteUrl) {
|
|
1876
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/set-site`, { siteUrl });
|
|
1877
|
+
}
|
|
1878
|
+
async bingCoverage(project) {
|
|
1879
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/coverage`);
|
|
1880
|
+
}
|
|
1881
|
+
async bingCoverageHistory(project, params) {
|
|
1882
|
+
const qs = params?.limit != null ? `?limit=${params.limit}` : "";
|
|
1883
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/coverage/history${qs}`);
|
|
1884
|
+
}
|
|
1885
|
+
async bingInspections(project, params) {
|
|
1886
|
+
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
1887
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/inspections${qs}`);
|
|
1888
|
+
}
|
|
1889
|
+
async bingInspectUrl(project, url) {
|
|
1890
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/inspect-url`, { url });
|
|
1891
|
+
}
|
|
1892
|
+
async bingInspectSitemap(project, body) {
|
|
1893
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/inspect-sitemap`, body ?? {});
|
|
1894
|
+
}
|
|
1895
|
+
async bingRequestIndexing(project, body) {
|
|
1896
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/request-indexing`, body);
|
|
1897
|
+
}
|
|
1898
|
+
async bingPerformance(project, params) {
|
|
1899
|
+
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
1900
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/performance${qs}`);
|
|
1901
|
+
}
|
|
1902
|
+
// CDP browser provider
|
|
1903
|
+
async getCdpStatus() {
|
|
1904
|
+
return this.request("GET", "/cdp/status");
|
|
1905
|
+
}
|
|
1906
|
+
async cdpScreenshot(query, targets) {
|
|
1907
|
+
return this.request("POST", "/cdp/screenshot", { query, targets });
|
|
1908
|
+
}
|
|
1909
|
+
async getBrowserDiff(project, runId) {
|
|
1910
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/runs/${encodeURIComponent(runId)}/browser-diff`);
|
|
1911
|
+
}
|
|
1912
|
+
// Google Analytics 4
|
|
1913
|
+
async gaConnect(project, body) {
|
|
1914
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/ga/connect`, body);
|
|
1915
|
+
}
|
|
1916
|
+
async gaDisconnect(project) {
|
|
1917
|
+
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/ga/disconnect`);
|
|
1918
|
+
}
|
|
1919
|
+
async gaStatus(project) {
|
|
1920
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/status`);
|
|
1921
|
+
}
|
|
1922
|
+
async gaSync(project, body) {
|
|
1923
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/ga/sync`, body ?? {});
|
|
1924
|
+
}
|
|
1925
|
+
async gaTraffic(project, params) {
|
|
1926
|
+
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
1927
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/traffic${qs}`);
|
|
1928
|
+
}
|
|
1929
|
+
async gaCoverage(project) {
|
|
1930
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/coverage`);
|
|
1931
|
+
}
|
|
1932
|
+
async gaAiReferralHistory(project, params) {
|
|
1933
|
+
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
1934
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/ai-referral-history${qs}`);
|
|
1935
|
+
}
|
|
1936
|
+
async gaSocialReferralHistory(project, params) {
|
|
1937
|
+
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
1938
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/social-referral-history${qs}`);
|
|
1939
|
+
}
|
|
1940
|
+
async gaSocialReferralTrend(project) {
|
|
1941
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/social-referral-trend`);
|
|
1942
|
+
}
|
|
1943
|
+
async gaAttributionTrend(project) {
|
|
1944
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/attribution-trend`);
|
|
1945
|
+
}
|
|
1946
|
+
async gaSessionHistory(project, params) {
|
|
1947
|
+
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
1948
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/session-history${qs}`);
|
|
1949
|
+
}
|
|
1950
|
+
async wordpressConnect(project, body) {
|
|
1951
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/connect`, body);
|
|
1952
|
+
}
|
|
1953
|
+
async wordpressDisconnect(project) {
|
|
1954
|
+
await this.request("DELETE", `/projects/${encodeURIComponent(project)}/wordpress/disconnect`);
|
|
1955
|
+
}
|
|
1956
|
+
async wordpressStatus(project) {
|
|
1957
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/status`);
|
|
1958
|
+
}
|
|
1959
|
+
async wordpressPages(project, env) {
|
|
1960
|
+
const qs = env ? `?env=${encodeURIComponent(env)}` : "";
|
|
1961
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/pages${qs}`);
|
|
1962
|
+
}
|
|
1963
|
+
async wordpressPage(project, slug, env) {
|
|
1964
|
+
const params = new URLSearchParams({ slug });
|
|
1965
|
+
if (env) params.set("env", env);
|
|
1966
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/page?${params.toString()}`);
|
|
1967
|
+
}
|
|
1968
|
+
async wordpressCreatePage(project, body) {
|
|
1969
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/pages`, body);
|
|
1970
|
+
}
|
|
1971
|
+
async wordpressUpdatePage(project, body) {
|
|
1972
|
+
return this.request("PUT", `/projects/${encodeURIComponent(project)}/wordpress/page`, body);
|
|
1973
|
+
}
|
|
1974
|
+
async wordpressSetMeta(project, body) {
|
|
1975
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/page/meta`, body);
|
|
1976
|
+
}
|
|
1977
|
+
async wordpressBulkSetMeta(project, body) {
|
|
1978
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/pages/meta/bulk`, body);
|
|
1979
|
+
}
|
|
1980
|
+
async wordpressSchema(project, slug, env) {
|
|
1981
|
+
const params = new URLSearchParams({ slug });
|
|
1982
|
+
if (env) params.set("env", env);
|
|
1983
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/schema?${params.toString()}`);
|
|
1984
|
+
}
|
|
1985
|
+
async wordpressSetSchema(project, body) {
|
|
1986
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/schema/manual`, body);
|
|
1987
|
+
}
|
|
1988
|
+
async wordpressSchemaDeploy(project, body) {
|
|
1989
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/schema/deploy`, body);
|
|
1990
|
+
}
|
|
1991
|
+
async wordpressSchemaStatus(project, env) {
|
|
1992
|
+
const params = new URLSearchParams();
|
|
1993
|
+
if (env) params.set("env", env);
|
|
1994
|
+
const qs = params.toString();
|
|
1995
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/schema/status${qs ? `?${qs}` : ""}`);
|
|
1996
|
+
}
|
|
1997
|
+
async wordpressOnboard(project, body) {
|
|
1998
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/onboard`, body);
|
|
1999
|
+
}
|
|
2000
|
+
async wordpressLlmsTxt(project, env) {
|
|
2001
|
+
const qs = env ? `?env=${encodeURIComponent(env)}` : "";
|
|
2002
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/llms-txt${qs}`);
|
|
2003
|
+
}
|
|
2004
|
+
async wordpressSetLlmsTxt(project, body) {
|
|
2005
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/llms-txt/manual`, body);
|
|
2006
|
+
}
|
|
2007
|
+
async wordpressAudit(project, env) {
|
|
2008
|
+
const qs = env ? `?env=${encodeURIComponent(env)}` : "";
|
|
2009
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/audit${qs}`);
|
|
2010
|
+
}
|
|
2011
|
+
async wordpressDiff(project, slug) {
|
|
2012
|
+
const params = new URLSearchParams({ slug });
|
|
2013
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/diff?${params.toString()}`);
|
|
2014
|
+
}
|
|
2015
|
+
async wordpressStagingStatus(project) {
|
|
2016
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/staging/status`);
|
|
2017
|
+
}
|
|
2018
|
+
async wordpressStagingPush(project) {
|
|
2019
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/staging/push`);
|
|
2020
|
+
}
|
|
2021
|
+
// ── Intelligence ──────────────────────────────────────────────────────
|
|
2022
|
+
async getInsights(project, opts) {
|
|
2023
|
+
const params = new URLSearchParams();
|
|
2024
|
+
if (opts?.dismissed) params.set("dismissed", "true");
|
|
2025
|
+
if (opts?.runId) params.set("runId", opts.runId);
|
|
2026
|
+
const qs = params.toString();
|
|
2027
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/insights${qs ? `?${qs}` : ""}`);
|
|
2028
|
+
}
|
|
2029
|
+
async getInsight(project, id) {
|
|
2030
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/insights/${encodeURIComponent(id)}`);
|
|
2031
|
+
}
|
|
2032
|
+
async dismissInsight(project, id) {
|
|
2033
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/insights/${encodeURIComponent(id)}/dismiss`);
|
|
2034
|
+
}
|
|
2035
|
+
async getHealth(project) {
|
|
2036
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/health/latest`);
|
|
2037
|
+
}
|
|
2038
|
+
async getHealthHistory(project, limit) {
|
|
2039
|
+
const qs = limit ? `?limit=${limit}` : "";
|
|
2040
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/health/history${qs}`);
|
|
2041
|
+
}
|
|
2042
|
+
// --- Backlinks ---------------------------------------------------------
|
|
2043
|
+
async backlinksStatus() {
|
|
2044
|
+
return this.request("GET", "/backlinks/status");
|
|
2045
|
+
}
|
|
2046
|
+
async backlinksInstall() {
|
|
2047
|
+
return this.request("POST", "/backlinks/install");
|
|
2048
|
+
}
|
|
2049
|
+
async backlinksTriggerSync(release) {
|
|
2050
|
+
return this.request("POST", "/backlinks/syncs", { release });
|
|
2051
|
+
}
|
|
2052
|
+
async backlinksLatestSync() {
|
|
2053
|
+
return this.request("GET", "/backlinks/syncs/latest");
|
|
2054
|
+
}
|
|
2055
|
+
async backlinksListSyncs() {
|
|
2056
|
+
return this.request("GET", "/backlinks/syncs");
|
|
2057
|
+
}
|
|
2058
|
+
async backlinksCachedReleases() {
|
|
2059
|
+
return this.request("GET", "/backlinks/releases");
|
|
2060
|
+
}
|
|
2061
|
+
async backlinksPruneCache(release) {
|
|
2062
|
+
return this.request("DELETE", `/backlinks/cache/${encodeURIComponent(release)}`);
|
|
2063
|
+
}
|
|
2064
|
+
async backlinksExtract(project, release) {
|
|
2065
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/backlinks/extract`, release ? { release } : {});
|
|
2066
|
+
}
|
|
2067
|
+
async backlinksSummary(project, release) {
|
|
2068
|
+
const qs = release ? `?release=${encodeURIComponent(release)}` : "";
|
|
2069
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/backlinks/summary${qs}`);
|
|
2070
|
+
}
|
|
2071
|
+
async backlinksDomains(project, opts = {}) {
|
|
2072
|
+
const qs = new URLSearchParams();
|
|
2073
|
+
if (opts.limit !== void 0) qs.set("limit", String(opts.limit));
|
|
2074
|
+
if (opts.offset !== void 0) qs.set("offset", String(opts.offset));
|
|
2075
|
+
if (opts.release) qs.set("release", opts.release);
|
|
2076
|
+
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
2077
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/backlinks/domains${suffix}`);
|
|
2078
|
+
}
|
|
2079
|
+
async backlinksHistory(project) {
|
|
2080
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/backlinks/history`);
|
|
2081
|
+
}
|
|
2082
|
+
};
|
|
2083
|
+
|
|
2084
|
+
export {
|
|
2085
|
+
getConfigDir,
|
|
2086
|
+
getConfigPath,
|
|
2087
|
+
loadConfig,
|
|
2088
|
+
saveConfig,
|
|
2089
|
+
saveConfigPatch,
|
|
2090
|
+
configExists,
|
|
2091
|
+
EXIT_SYSTEM_ERROR,
|
|
2092
|
+
CliError,
|
|
2093
|
+
usageError,
|
|
2094
|
+
isEndpointMissing,
|
|
2095
|
+
printCliError,
|
|
2096
|
+
providerQuotaPolicySchema,
|
|
2097
|
+
ProviderNames,
|
|
2098
|
+
isBrowserProvider,
|
|
2099
|
+
resolveProviderInput,
|
|
2100
|
+
locationContextSchema,
|
|
2101
|
+
notificationEventSchema,
|
|
2102
|
+
notificationCreateRequestSchema,
|
|
2103
|
+
findDuplicateLocationLabels,
|
|
2104
|
+
hasLocationLabel,
|
|
2105
|
+
projectUpsertRequestSchema,
|
|
2106
|
+
keywordBatchRequestSchema,
|
|
2107
|
+
keywordGenerateRequestSchema,
|
|
2108
|
+
competitorBatchRequestSchema,
|
|
2109
|
+
normalizeProjectDomain,
|
|
2110
|
+
effectiveDomains,
|
|
2111
|
+
projectConfigSchema,
|
|
2112
|
+
AppError,
|
|
2113
|
+
notFound,
|
|
2114
|
+
validationError,
|
|
2115
|
+
authRequired,
|
|
2116
|
+
authInvalid,
|
|
2117
|
+
providerError,
|
|
2118
|
+
runInProgress,
|
|
2119
|
+
runNotCancellable,
|
|
2120
|
+
unsupportedKind,
|
|
2121
|
+
notImplemented,
|
|
2122
|
+
deliveryFailed,
|
|
2123
|
+
agentBusy,
|
|
2124
|
+
missingDependency,
|
|
2125
|
+
internalError,
|
|
2126
|
+
wordpressEnvSchema,
|
|
2127
|
+
AgentProviderIds,
|
|
2128
|
+
AGENT_PROVIDER_IDS,
|
|
2129
|
+
isAgentProviderId,
|
|
2130
|
+
RunStatuses,
|
|
2131
|
+
RunKinds,
|
|
2132
|
+
RunTriggers,
|
|
2133
|
+
runTriggerRequestSchema,
|
|
2134
|
+
snapshotRequestSchema,
|
|
2135
|
+
scheduleUpsertRequestSchema,
|
|
2136
|
+
parseWindow,
|
|
2137
|
+
windowCutoff,
|
|
2138
|
+
categorizeSource,
|
|
2139
|
+
categoryLabel,
|
|
2140
|
+
extractAnswerMentions,
|
|
2141
|
+
determineAnswerMentioned,
|
|
2142
|
+
visibilityStateFromAnswerMentioned,
|
|
2143
|
+
brandKeyFromText,
|
|
2144
|
+
MemorySources,
|
|
2145
|
+
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
2146
|
+
AGENT_MEMORY_KEY_MAX_LENGTH,
|
|
2147
|
+
agentMemoryUpsertRequestSchema,
|
|
2148
|
+
agentMemoryDeleteRequestSchema,
|
|
2149
|
+
CcReleaseSyncStatuses,
|
|
2150
|
+
createApiClient,
|
|
2151
|
+
ApiClient
|
|
2152
|
+
};
|