@ainyc/canonry 4.72.3 → 4.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +17 -0
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +56 -3
- package/assets/assets/{BacklinksPage-CjfpwZEH.js → BacklinksPage-ClgP7CUd.js} +1 -1
- package/assets/assets/{ChartPrimitives-Ckf2FrUy.js → ChartPrimitives-PGDrQBXP.js} +1 -1
- package/assets/assets/ProjectPage-xfLeh2vB.js +6 -0
- package/assets/assets/{RunRow-BuFyG0V_.js → RunRow-DL-lUm35.js} +1 -1
- package/assets/assets/{RunsPage-D-pr000K.js → RunsPage-BCL_lU-R.js} +1 -1
- package/assets/assets/{SettingsPage-CiaapCYn.js → SettingsPage-D67UQYJa.js} +1 -1
- package/assets/assets/{TrafficPage-B40xytJD.js → TrafficPage-DVRcPxCk.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-7hHem-gM.js → TrafficSourceDetailPage-JzX1fhGQ.js} +1 -1
- package/assets/assets/{extract-error-message-3GkDsu1h.js → extract-error-message-Cia_CilL.js} +1 -1
- package/assets/assets/index-CFVX11lK.css +1 -0
- package/assets/assets/{index-BVdH2O9w.js → index-DHg9_-PB.js} +118 -118
- package/assets/assets/{server-traffic-CsgPsudZ.js → server-traffic-GBmLS3L7.js} +1 -1
- package/assets/assets/{trash-2-B8Ipf9rI.js → trash-2-Bk7PYGBN.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-JXFNERK4.js → chunk-A7JX3FZB.js} +1094 -995
- package/dist/{chunk-SIB4NMEH.js → chunk-MRC4JMIH.js} +369 -176
- package/dist/{chunk-ZUBBADMR.js → chunk-W6GBIRFA.js} +162 -1
- package/dist/{chunk-HSX32G47.js → chunk-ZRZHIS22.js} +414 -73
- package/dist/cli.js +236 -30
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-ZW3ARLJT.js → intelligence-service-GPO2VMEC.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +8 -8
- package/assets/assets/ProjectPage-DZeplYeC.js +0 -6
- package/assets/assets/index-B3nENtU0.css +0 -1
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
loadConfig,
|
|
10
10
|
loadConfigRaw,
|
|
11
11
|
saveConfigPatch
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-W6GBIRFA.js";
|
|
13
13
|
import {
|
|
14
14
|
CC_CACHE_DIR,
|
|
15
15
|
DUCKDB_SPEC,
|
|
@@ -94,8 +94,10 @@ import {
|
|
|
94
94
|
resolveWebhookTarget,
|
|
95
95
|
runs,
|
|
96
96
|
schedules,
|
|
97
|
+
siteAuditPages,
|
|
98
|
+
siteAuditSnapshots,
|
|
97
99
|
usageCounters
|
|
98
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-ZRZHIS22.js";
|
|
99
101
|
import {
|
|
100
102
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
101
103
|
AGENT_PROVIDER_IDS,
|
|
@@ -133,6 +135,7 @@ import {
|
|
|
133
135
|
determineAnswerMentioned,
|
|
134
136
|
effectiveBrandNames,
|
|
135
137
|
effectiveDomains,
|
|
138
|
+
factorStatusFromScore,
|
|
136
139
|
isAgentProviderId,
|
|
137
140
|
isBrowserProvider,
|
|
138
141
|
isRetryableHttpError,
|
|
@@ -145,7 +148,7 @@ import {
|
|
|
145
148
|
validationError,
|
|
146
149
|
winnabilityClassLabel,
|
|
147
150
|
withRetry
|
|
148
|
-
} from "./chunk-
|
|
151
|
+
} from "./chunk-A7JX3FZB.js";
|
|
149
152
|
|
|
150
153
|
// src/telemetry.ts
|
|
151
154
|
import crypto from "crypto";
|
|
@@ -433,11 +436,11 @@ function checkLatestVersionForServer(opts) {
|
|
|
433
436
|
|
|
434
437
|
// src/server.ts
|
|
435
438
|
import { createRequire as createRequire3 } from "module";
|
|
436
|
-
import
|
|
439
|
+
import crypto18 from "crypto";
|
|
437
440
|
import fs8 from "fs";
|
|
438
441
|
import path9 from "path";
|
|
439
442
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
440
|
-
import { and as and13, eq as
|
|
443
|
+
import { and as and13, eq as eq18 } from "drizzle-orm";
|
|
441
444
|
import Fastify from "fastify";
|
|
442
445
|
import os5 from "os";
|
|
443
446
|
|
|
@@ -5162,8 +5165,158 @@ function buildDiscoveryInsightTitle(input) {
|
|
|
5162
5165
|
return parts.join(" \u2022 ");
|
|
5163
5166
|
}
|
|
5164
5167
|
|
|
5168
|
+
// src/execute-site-audit.ts
|
|
5169
|
+
import crypto12 from "crypto";
|
|
5170
|
+
import { eq as eq10 } from "drizzle-orm";
|
|
5171
|
+
import { runSitemapAudit } from "@ainyc/aeo-audit";
|
|
5172
|
+
var log11 = createLogger("SiteAudit");
|
|
5173
|
+
var SITE_AUDIT_DEFAULT_PAGE_LIMIT = 500;
|
|
5174
|
+
var SITE_AUDIT_MAX_PAGE_LIMIT = 2e3;
|
|
5175
|
+
function toHomepageUrl(canonicalDomain) {
|
|
5176
|
+
const trimmed = canonicalDomain.trim().replace(/\/+$/, "");
|
|
5177
|
+
return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
5178
|
+
}
|
|
5179
|
+
function clampSiteAuditLimit(limit) {
|
|
5180
|
+
if (limit == null || !Number.isFinite(limit)) return SITE_AUDIT_DEFAULT_PAGE_LIMIT;
|
|
5181
|
+
return Math.max(1, Math.min(SITE_AUDIT_MAX_PAGE_LIMIT, Math.floor(limit)));
|
|
5182
|
+
}
|
|
5183
|
+
function toPageFactor(factor) {
|
|
5184
|
+
return {
|
|
5185
|
+
id: factor.id,
|
|
5186
|
+
name: factor.name,
|
|
5187
|
+
weight: factor.weight,
|
|
5188
|
+
score: factor.score
|
|
5189
|
+
};
|
|
5190
|
+
}
|
|
5191
|
+
function computeFactorAverages(pages) {
|
|
5192
|
+
const byId = /* @__PURE__ */ new Map();
|
|
5193
|
+
for (const page of pages) {
|
|
5194
|
+
if (page.status !== "success" || !page.factors) continue;
|
|
5195
|
+
for (const factor of page.factors) {
|
|
5196
|
+
let entry = byId.get(factor.id);
|
|
5197
|
+
if (!entry) {
|
|
5198
|
+
entry = { name: factor.name, weight: factor.weight, scores: [], pass: 0, partial: 0, fail: 0 };
|
|
5199
|
+
byId.set(factor.id, entry);
|
|
5200
|
+
}
|
|
5201
|
+
entry.scores.push(factor.score);
|
|
5202
|
+
const status = factorStatusFromScore(factor.score);
|
|
5203
|
+
if (status === "pass") entry.pass++;
|
|
5204
|
+
else if (status === "partial") entry.partial++;
|
|
5205
|
+
else entry.fail++;
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
const summaries = [];
|
|
5209
|
+
for (const [id, entry] of byId) {
|
|
5210
|
+
const avgScore = entry.scores.length ? Math.round(entry.scores.reduce((sum, score) => sum + score, 0) / entry.scores.length) : 0;
|
|
5211
|
+
summaries.push({
|
|
5212
|
+
id,
|
|
5213
|
+
name: entry.name,
|
|
5214
|
+
weight: entry.weight,
|
|
5215
|
+
avgScore,
|
|
5216
|
+
status: factorStatusFromScore(avgScore),
|
|
5217
|
+
pagesPassing: entry.pass,
|
|
5218
|
+
pagesPartial: entry.partial,
|
|
5219
|
+
pagesFailing: entry.fail
|
|
5220
|
+
});
|
|
5221
|
+
}
|
|
5222
|
+
summaries.sort((a, b) => b.weight - a.weight || a.name.localeCompare(b.name));
|
|
5223
|
+
return summaries;
|
|
5224
|
+
}
|
|
5225
|
+
async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
5226
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5227
|
+
db.update(runs).set({ status: "running", startedAt }).where(eq10(runs.id, runId)).run();
|
|
5228
|
+
try {
|
|
5229
|
+
const project = db.select().from(projects).where(eq10(projects.id, projectId)).get();
|
|
5230
|
+
if (!project) {
|
|
5231
|
+
throw new Error(`Project not found: ${projectId}`);
|
|
5232
|
+
}
|
|
5233
|
+
const homepageUrl = toHomepageUrl(project.canonicalDomain);
|
|
5234
|
+
const limit = clampSiteAuditLimit(opts.limit);
|
|
5235
|
+
log11.info("start", { runId, projectId, homepageUrl, sitemapUrl: opts.sitemapUrl ?? null, limit });
|
|
5236
|
+
const report = await runSitemapAudit(homepageUrl, { sitemapUrl: opts.sitemapUrl, limit });
|
|
5237
|
+
const successCount = report.pages.filter((page) => page.status === "success").length;
|
|
5238
|
+
const pagesErrored = report.pages.filter((page) => page.status === "error").length;
|
|
5239
|
+
const auditable = report.pagesDiscovered - report.pagesSkipped;
|
|
5240
|
+
if (auditable > report.pagesAudited) {
|
|
5241
|
+
log11.info("truncated", {
|
|
5242
|
+
runId,
|
|
5243
|
+
projectId,
|
|
5244
|
+
auditable,
|
|
5245
|
+
audited: report.pagesAudited,
|
|
5246
|
+
dropped: auditable - report.pagesAudited,
|
|
5247
|
+
limit
|
|
5248
|
+
});
|
|
5249
|
+
}
|
|
5250
|
+
if (successCount === 0) {
|
|
5251
|
+
throw new Error(
|
|
5252
|
+
`Site audit could not successfully audit any of ${report.pagesAudited} page(s) from ${report.sitemapUrl}.`
|
|
5253
|
+
);
|
|
5254
|
+
}
|
|
5255
|
+
const factorAverages = computeFactorAverages(report.pages);
|
|
5256
|
+
const status = pagesErrored === 0 ? "completed" : "partial";
|
|
5257
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5258
|
+
db.transaction((tx) => {
|
|
5259
|
+
tx.insert(siteAuditSnapshots).values({
|
|
5260
|
+
id: crypto12.randomUUID(),
|
|
5261
|
+
projectId,
|
|
5262
|
+
runId,
|
|
5263
|
+
sitemapUrl: report.sitemapUrl,
|
|
5264
|
+
auditedAt: report.auditedAt,
|
|
5265
|
+
aggregateScore: report.aggregateScore,
|
|
5266
|
+
pagesDiscovered: report.pagesDiscovered,
|
|
5267
|
+
pagesAudited: report.pagesAudited,
|
|
5268
|
+
pagesSkipped: report.pagesSkipped,
|
|
5269
|
+
pagesErrored,
|
|
5270
|
+
factorAverages,
|
|
5271
|
+
// aeo-audit v3 enriches these (topIssues, avgGrade-free); keep only the
|
|
5272
|
+
// fields our DTO exposes so the stored JSON stays lean.
|
|
5273
|
+
crossCuttingIssues: report.crossCuttingIssues.map((issue) => ({
|
|
5274
|
+
factorId: issue.factorId,
|
|
5275
|
+
factorName: issue.factorName,
|
|
5276
|
+
avgScore: issue.avgScore,
|
|
5277
|
+
affectedPages: issue.affectedPages,
|
|
5278
|
+
totalPages: issue.totalPages,
|
|
5279
|
+
affectedPct: issue.totalPages > 0 ? Math.round(issue.affectedPages / issue.totalPages * 100) : 0,
|
|
5280
|
+
topRecommendations: issue.topRecommendations
|
|
5281
|
+
})),
|
|
5282
|
+
// v3 prioritizedFixes are structured PrioritizedFix objects; persist the
|
|
5283
|
+
// ready-to-display one-line summary to keep the DTO a string list.
|
|
5284
|
+
prioritizedFixes: report.prioritizedFixes.map((fix) => fix.summary),
|
|
5285
|
+
createdAt: finishedAt
|
|
5286
|
+
}).run();
|
|
5287
|
+
for (const page of report.pages) {
|
|
5288
|
+
tx.insert(siteAuditPages).values({
|
|
5289
|
+
id: crypto12.randomUUID(),
|
|
5290
|
+
projectId,
|
|
5291
|
+
runId,
|
|
5292
|
+
url: page.url,
|
|
5293
|
+
overallScore: page.overallScore,
|
|
5294
|
+
status: page.status,
|
|
5295
|
+
error: page.error ?? null,
|
|
5296
|
+
factors: (page.factors ?? []).map(toPageFactor),
|
|
5297
|
+
createdAt: finishedAt
|
|
5298
|
+
}).run();
|
|
5299
|
+
}
|
|
5300
|
+
tx.update(runs).set({ status, finishedAt }).where(eq10(runs.id, runId)).run();
|
|
5301
|
+
});
|
|
5302
|
+
log11.info("completed", {
|
|
5303
|
+
runId,
|
|
5304
|
+
projectId,
|
|
5305
|
+
status,
|
|
5306
|
+
score: report.aggregateScore,
|
|
5307
|
+
audited: report.pagesAudited,
|
|
5308
|
+
errored: pagesErrored
|
|
5309
|
+
});
|
|
5310
|
+
} catch (err) {
|
|
5311
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
5312
|
+
db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq10(runs.id, runId)).run();
|
|
5313
|
+
log11.error("failed", { runId, projectId, error: errorMsg });
|
|
5314
|
+
throw err;
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
5317
|
+
|
|
5165
5318
|
// src/commands/backfill.ts
|
|
5166
|
-
import { and as and9, eq as
|
|
5319
|
+
import { and as and9, eq as eq11, inArray as inArray4, isNull, sql as sql4 } from "drizzle-orm";
|
|
5167
5320
|
var SNAPSHOT_BATCH_SIZE = 500;
|
|
5168
5321
|
async function backfillAnswerVisibilityCommand(opts) {
|
|
5169
5322
|
const config = loadConfig();
|
|
@@ -5171,7 +5324,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
5171
5324
|
migrate(db);
|
|
5172
5325
|
const projectFilter = opts?.project?.trim();
|
|
5173
5326
|
const isDryRun = opts?.dryRun === true;
|
|
5174
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
5327
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq11(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
5175
5328
|
let examined = 0;
|
|
5176
5329
|
let updated = 0;
|
|
5177
5330
|
let wouldUpdate = 0;
|
|
@@ -5180,9 +5333,9 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
5180
5333
|
let providerErrors = 0;
|
|
5181
5334
|
if (scopedProjects.length > 0) {
|
|
5182
5335
|
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and9(
|
|
5183
|
-
|
|
5336
|
+
eq11(runs.kind, RunKinds["answer-visibility"]),
|
|
5184
5337
|
inArray4(runs.projectId, scopedProjects.map((project) => project.id))
|
|
5185
|
-
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(
|
|
5338
|
+
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq11(runs.kind, RunKinds["answer-visibility"])).all();
|
|
5186
5339
|
const runIdsByProject = /* @__PURE__ */ new Map();
|
|
5187
5340
|
for (const run of runRows) {
|
|
5188
5341
|
const existing = runIdsByProject.get(run.projectId);
|
|
@@ -5190,7 +5343,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
5190
5343
|
else runIdsByProject.set(run.projectId, [run.id]);
|
|
5191
5344
|
}
|
|
5192
5345
|
for (const project of scopedProjects) {
|
|
5193
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(
|
|
5346
|
+
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq11(competitors.projectId, project.id)).all().map((row) => row.domain);
|
|
5194
5347
|
const runIds = runIdsByProject.get(project.id) ?? [];
|
|
5195
5348
|
if (runIds.length === 0) continue;
|
|
5196
5349
|
const projectDomains = effectiveDomains({
|
|
@@ -5278,7 +5431,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
5278
5431
|
} else {
|
|
5279
5432
|
db.transaction((tx) => {
|
|
5280
5433
|
for (const update of pendingUpdates) {
|
|
5281
|
-
tx.update(querySnapshots).set(update.patch).where(
|
|
5434
|
+
tx.update(querySnapshots).set(update.patch).where(eq11(querySnapshots.id, update.id)).run();
|
|
5282
5435
|
}
|
|
5283
5436
|
});
|
|
5284
5437
|
updated += pendingUpdates.length;
|
|
@@ -5327,7 +5480,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
|
|
|
5327
5480
|
function backfillNormalizedPaths(db, opts) {
|
|
5328
5481
|
const baseConditions = [];
|
|
5329
5482
|
if (opts?.projectId) {
|
|
5330
|
-
baseConditions.push(
|
|
5483
|
+
baseConditions.push(eq11(gaTrafficSnapshots.projectId, opts.projectId));
|
|
5331
5484
|
}
|
|
5332
5485
|
const rows = db.select({
|
|
5333
5486
|
id: gaTrafficSnapshots.id,
|
|
@@ -5348,7 +5501,7 @@ function backfillNormalizedPaths(db, opts) {
|
|
|
5348
5501
|
unchanged++;
|
|
5349
5502
|
continue;
|
|
5350
5503
|
}
|
|
5351
|
-
tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(
|
|
5504
|
+
tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq11(gaTrafficSnapshots.id, row.id)).run();
|
|
5352
5505
|
updated++;
|
|
5353
5506
|
}
|
|
5354
5507
|
});
|
|
@@ -5362,7 +5515,7 @@ async function backfillNormalizedPathsCommand(opts) {
|
|
|
5362
5515
|
const projectFilter = opts?.project?.trim();
|
|
5363
5516
|
let projectId;
|
|
5364
5517
|
if (projectFilter) {
|
|
5365
|
-
const project = db.select({ id: projects.id }).from(projects).where(
|
|
5518
|
+
const project = db.select({ id: projects.id }).from(projects).where(eq11(projects.name, projectFilter)).get();
|
|
5366
5519
|
if (!project) {
|
|
5367
5520
|
const result2 = {
|
|
5368
5521
|
project: projectFilter,
|
|
@@ -5399,7 +5552,7 @@ async function backfillNormalizedPathsCommand(opts) {
|
|
|
5399
5552
|
function backfillAiReferralPaths(db, opts) {
|
|
5400
5553
|
const baseConditions = [];
|
|
5401
5554
|
if (opts?.projectId) {
|
|
5402
|
-
baseConditions.push(
|
|
5555
|
+
baseConditions.push(eq11(gaAiReferrals.projectId, opts.projectId));
|
|
5403
5556
|
}
|
|
5404
5557
|
const rows = db.select({
|
|
5405
5558
|
id: gaAiReferrals.id,
|
|
@@ -5420,7 +5573,7 @@ function backfillAiReferralPaths(db, opts) {
|
|
|
5420
5573
|
unchanged++;
|
|
5421
5574
|
continue;
|
|
5422
5575
|
}
|
|
5423
|
-
tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(
|
|
5576
|
+
tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(eq11(gaAiReferrals.id, row.id)).run();
|
|
5424
5577
|
updated++;
|
|
5425
5578
|
}
|
|
5426
5579
|
});
|
|
@@ -5434,7 +5587,7 @@ async function backfillAiReferralPathsCommand(opts) {
|
|
|
5434
5587
|
const projectFilter = opts?.project?.trim();
|
|
5435
5588
|
let projectId;
|
|
5436
5589
|
if (projectFilter) {
|
|
5437
|
-
const project = db.select({ id: projects.id }).from(projects).where(
|
|
5590
|
+
const project = db.select({ id: projects.id }).from(projects).where(eq11(projects.name, projectFilter)).get();
|
|
5438
5591
|
if (!project) {
|
|
5439
5592
|
const result2 = {
|
|
5440
5593
|
project: projectFilter,
|
|
@@ -5470,10 +5623,10 @@ async function backfillAiReferralPathsCommand(opts) {
|
|
|
5470
5623
|
}
|
|
5471
5624
|
function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
5472
5625
|
const isDryRun = opts?.dryRun === true;
|
|
5473
|
-
const project = db.select().from(projects).where(
|
|
5626
|
+
const project = db.select().from(projects).where(eq11(projects.id, projectId)).get();
|
|
5474
5627
|
if (!project) return { examined: 0, updated: 0, mentioned: 0 };
|
|
5475
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(
|
|
5476
|
-
const runRows = db.select({ id: runs.id }).from(runs).where(and9(
|
|
5628
|
+
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq11(competitors.projectId, projectId)).all().map((row) => row.domain);
|
|
5629
|
+
const runRows = db.select({ id: runs.id }).from(runs).where(and9(eq11(runs.kind, RunKinds["answer-visibility"]), eq11(runs.projectId, projectId))).all();
|
|
5477
5630
|
const runIds = runRows.map((r) => r.id);
|
|
5478
5631
|
let examined = 0;
|
|
5479
5632
|
let updated = 0;
|
|
@@ -5545,7 +5698,7 @@ function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
|
5545
5698
|
} else {
|
|
5546
5699
|
db.transaction((tx) => {
|
|
5547
5700
|
for (const update of pendingUpdates) {
|
|
5548
|
-
tx.update(querySnapshots).set(update.patch).where(
|
|
5701
|
+
tx.update(querySnapshots).set(update.patch).where(eq11(querySnapshots.id, update.id)).run();
|
|
5549
5702
|
}
|
|
5550
5703
|
});
|
|
5551
5704
|
updated += pendingUpdates.length;
|
|
@@ -5560,7 +5713,7 @@ async function backfillAnswerMentionsCommand(opts) {
|
|
|
5560
5713
|
migrate(db);
|
|
5561
5714
|
const projectFilter = opts?.project?.trim();
|
|
5562
5715
|
const isDryRun = opts?.dryRun === true;
|
|
5563
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
5716
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq11(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
5564
5717
|
let examined = 0;
|
|
5565
5718
|
let updated = 0;
|
|
5566
5719
|
let wouldUpdate = 0;
|
|
@@ -5620,7 +5773,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
5620
5773
|
return result;
|
|
5621
5774
|
}
|
|
5622
5775
|
async function backfillInsightsCommand(project, opts) {
|
|
5623
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
5776
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-GPO2VMEC.js");
|
|
5624
5777
|
const config = loadConfig();
|
|
5625
5778
|
const db = createClient(config.database);
|
|
5626
5779
|
migrate(db);
|
|
@@ -5779,7 +5932,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
5779
5932
|
const config = loadConfig();
|
|
5780
5933
|
const db = createClient(config.database);
|
|
5781
5934
|
migrate(db);
|
|
5782
|
-
const project = db.select().from(projects).where(
|
|
5935
|
+
const project = db.select().from(projects).where(eq11(projects.name, opts.project)).get();
|
|
5783
5936
|
if (!project) {
|
|
5784
5937
|
throw new Error(`Project "${opts.project}" not found`);
|
|
5785
5938
|
}
|
|
@@ -5791,7 +5944,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
5791
5944
|
`);
|
|
5792
5945
|
}
|
|
5793
5946
|
const events = db.select({ createdAt: auditLog.createdAt, action: auditLog.action, diff: auditLog.diff }).from(auditLog).where(and9(
|
|
5794
|
-
|
|
5947
|
+
eq11(auditLog.projectId, project.id),
|
|
5795
5948
|
inArray4(auditLog.action, ["keywords.appended", "keywords.deleted", "queries.appended", "queries.deleted", "queries.replaced"])
|
|
5796
5949
|
)).orderBy(auditLog.createdAt).all();
|
|
5797
5950
|
const history = replayQueryAuditLog(events);
|
|
@@ -5799,8 +5952,8 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
5799
5952
|
runId: runs.id,
|
|
5800
5953
|
createdAt: runs.createdAt,
|
|
5801
5954
|
location: runs.location
|
|
5802
|
-
}).from(runs).innerJoin(querySnapshots,
|
|
5803
|
-
|
|
5955
|
+
}).from(runs).innerJoin(querySnapshots, eq11(querySnapshots.runId, runs.id)).where(and9(
|
|
5956
|
+
eq11(runs.projectId, project.id),
|
|
5804
5957
|
isNull(querySnapshots.queryId),
|
|
5805
5958
|
isNull(querySnapshots.queryText)
|
|
5806
5959
|
)).groupBy(runs.id).orderBy(runs.createdAt).all();
|
|
@@ -5823,7 +5976,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
5823
5976
|
createdAt: querySnapshots.createdAt,
|
|
5824
5977
|
answerText: querySnapshots.answerText
|
|
5825
5978
|
}).from(querySnapshots).where(and9(
|
|
5826
|
-
|
|
5979
|
+
eq11(querySnapshots.runId, run.runId),
|
|
5827
5980
|
isNull(querySnapshots.queryId),
|
|
5828
5981
|
isNull(querySnapshots.queryText)
|
|
5829
5982
|
)).orderBy(querySnapshots.provider, querySnapshots.createdAt).all();
|
|
@@ -5889,7 +6042,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
5889
6042
|
if (!isDryRun && updates.length > 0) {
|
|
5890
6043
|
db.transaction((tx) => {
|
|
5891
6044
|
for (const u of updates) {
|
|
5892
|
-
tx.update(querySnapshots).set({ queryText: u.queryText }).where(
|
|
6045
|
+
tx.update(querySnapshots).set({ queryText: u.queryText }).where(eq11(querySnapshots.id, u.id)).run();
|
|
5893
6046
|
}
|
|
5894
6047
|
});
|
|
5895
6048
|
}
|
|
@@ -5963,7 +6116,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
5963
6116
|
const projectFilter = opts?.project?.trim();
|
|
5964
6117
|
const isDryRun = opts?.dryRun === true;
|
|
5965
6118
|
const isJson = isMachineFormat(opts?.format);
|
|
5966
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
6119
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq11(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
5967
6120
|
if (scopedProjects.length === 0) {
|
|
5968
6121
|
if (projectFilter && !isJson) {
|
|
5969
6122
|
process.stderr.write(`No project named "${projectFilter}".
|
|
@@ -5989,7 +6142,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
5989
6142
|
byBot: {}
|
|
5990
6143
|
};
|
|
5991
6144
|
const unknownCountRow = db.select({ n: sql4`count(*)` }).from(rawEventSamples).where(and9(
|
|
5992
|
-
|
|
6145
|
+
eq11(rawEventSamples.eventType, "unknown"),
|
|
5993
6146
|
inArray4(rawEventSamples.projectId, projectIds)
|
|
5994
6147
|
)).get();
|
|
5995
6148
|
result.unknownBefore = Number(unknownCountRow?.n ?? 0);
|
|
@@ -6002,7 +6155,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
6002
6155
|
pathNormalized: rawEventSamples.pathNormalized,
|
|
6003
6156
|
status: rawEventSamples.status
|
|
6004
6157
|
}).from(rawEventSamples).where(and9(
|
|
6005
|
-
|
|
6158
|
+
eq11(rawEventSamples.eventType, "unknown"),
|
|
6006
6159
|
inArray4(rawEventSamples.projectId, projectIds)
|
|
6007
6160
|
)).all();
|
|
6008
6161
|
result.examined = unknownSamples.length;
|
|
@@ -6041,7 +6194,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
6041
6194
|
result.reclassified++;
|
|
6042
6195
|
result.byBot[classified.botId] = (result.byBot[classified.botId] ?? 0) + 1;
|
|
6043
6196
|
if (isDryRun) continue;
|
|
6044
|
-
db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(
|
|
6197
|
+
db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(eq11(rawEventSamples.id, snap.id)).run();
|
|
6045
6198
|
const tsHour = new Date(snap.ts);
|
|
6046
6199
|
tsHour.setUTCMinutes(0, 0, 0);
|
|
6047
6200
|
if (userFetch) {
|
|
@@ -6106,7 +6259,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
6106
6259
|
}
|
|
6107
6260
|
if (!isDryRun) {
|
|
6108
6261
|
const afterRow = db.select({ n: sql4`count(*)` }).from(rawEventSamples).where(and9(
|
|
6109
|
-
|
|
6262
|
+
eq11(rawEventSamples.eventType, "unknown"),
|
|
6110
6263
|
inArray4(rawEventSamples.projectId, projectIds)
|
|
6111
6264
|
)).get();
|
|
6112
6265
|
result.unknownAfter = Number(afterRow?.n ?? 0);
|
|
@@ -6141,7 +6294,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
|
|
|
6141
6294
|
}
|
|
6142
6295
|
|
|
6143
6296
|
// src/commands/skills.ts
|
|
6144
|
-
import
|
|
6297
|
+
import crypto13 from "crypto";
|
|
6145
6298
|
import fs4 from "fs";
|
|
6146
6299
|
import os4 from "os";
|
|
6147
6300
|
import path5 from "path";
|
|
@@ -6196,7 +6349,7 @@ function walkRelative(dir, prefix = "") {
|
|
|
6196
6349
|
return out.sort();
|
|
6197
6350
|
}
|
|
6198
6351
|
function sha256File(filePath) {
|
|
6199
|
-
return
|
|
6352
|
+
return crypto13.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
|
|
6200
6353
|
}
|
|
6201
6354
|
function readSkillManifest(skillDir) {
|
|
6202
6355
|
try {
|
|
@@ -6519,10 +6672,10 @@ var ProviderRegistry = class {
|
|
|
6519
6672
|
};
|
|
6520
6673
|
|
|
6521
6674
|
// src/scheduler.ts
|
|
6522
|
-
import
|
|
6675
|
+
import crypto14 from "crypto";
|
|
6523
6676
|
import cron from "node-cron";
|
|
6524
|
-
import { and as and10, eq as
|
|
6525
|
-
var
|
|
6677
|
+
import { and as and10, eq as eq12, inArray as inArray5 } from "drizzle-orm";
|
|
6678
|
+
var log12 = createLogger("Scheduler");
|
|
6526
6679
|
function taskKey(projectId, kind) {
|
|
6527
6680
|
return `${projectId}::${kind}`;
|
|
6528
6681
|
}
|
|
@@ -6536,16 +6689,16 @@ var Scheduler = class {
|
|
|
6536
6689
|
}
|
|
6537
6690
|
/** Load all enabled schedules from DB and register cron jobs. */
|
|
6538
6691
|
start() {
|
|
6539
|
-
const allSchedules = this.db.select().from(schedules).where(
|
|
6692
|
+
const allSchedules = this.db.select().from(schedules).where(eq12(schedules.enabled, true)).all();
|
|
6540
6693
|
for (const schedule of allSchedules) {
|
|
6541
6694
|
const missedRunAt = schedule.nextRunAt;
|
|
6542
6695
|
this.registerCronTask(schedule);
|
|
6543
6696
|
if (missedRunAt && new Date(missedRunAt) < /* @__PURE__ */ new Date()) {
|
|
6544
|
-
|
|
6697
|
+
log12.info("run.catch-up", { projectId: schedule.projectId, kind: schedule.kind, missedRunAt });
|
|
6545
6698
|
this.triggerRun(schedule.id, schedule.projectId, schedule.kind);
|
|
6546
6699
|
}
|
|
6547
6700
|
}
|
|
6548
|
-
|
|
6701
|
+
log12.info("started", { scheduleCount: allSchedules.length });
|
|
6549
6702
|
}
|
|
6550
6703
|
/** Stop all cron tasks for graceful shutdown. */
|
|
6551
6704
|
stop() {
|
|
@@ -6566,7 +6719,7 @@ var Scheduler = class {
|
|
|
6566
6719
|
this.stopTask(key, existing, "Stopped");
|
|
6567
6720
|
this.tasks.delete(key);
|
|
6568
6721
|
}
|
|
6569
|
-
const schedule = this.db.select().from(schedules).where(and10(
|
|
6722
|
+
const schedule = this.db.select().from(schedules).where(and10(eq12(schedules.projectId, projectId), eq12(schedules.kind, kind))).get();
|
|
6570
6723
|
if (schedule && schedule.enabled) {
|
|
6571
6724
|
this.registerCronTask(schedule);
|
|
6572
6725
|
}
|
|
@@ -6589,13 +6742,13 @@ var Scheduler = class {
|
|
|
6589
6742
|
stopTask(key, task, verb) {
|
|
6590
6743
|
void task.stop();
|
|
6591
6744
|
void task.destroy();
|
|
6592
|
-
|
|
6745
|
+
log12.info(`task.${verb.toLowerCase()}`, { key });
|
|
6593
6746
|
}
|
|
6594
6747
|
registerCronTask(schedule) {
|
|
6595
6748
|
const { id: scheduleId, projectId, cronExpr, timezone } = schedule;
|
|
6596
6749
|
const kind = schedule.kind;
|
|
6597
6750
|
if (!cron.validate(cronExpr)) {
|
|
6598
|
-
|
|
6751
|
+
log12.error("cron.invalid", { projectId, kind, cronExpr });
|
|
6599
6752
|
return;
|
|
6600
6753
|
}
|
|
6601
6754
|
const task = cron.schedule(cronExpr, () => {
|
|
@@ -6607,51 +6760,51 @@ var Scheduler = class {
|
|
|
6607
6760
|
this.db.update(schedules).set({
|
|
6608
6761
|
nextRunAt: nextRunFromCron(cronExpr, timezone),
|
|
6609
6762
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6610
|
-
}).where(
|
|
6763
|
+
}).where(eq12(schedules.id, scheduleId)).run();
|
|
6611
6764
|
const label = schedule.preset ?? cronExpr;
|
|
6612
|
-
|
|
6765
|
+
log12.info("cron.registered", { projectId, kind, schedule: label, timezone });
|
|
6613
6766
|
}
|
|
6614
6767
|
triggerRun(scheduleId, projectId, kind) {
|
|
6615
6768
|
try {
|
|
6616
6769
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6617
|
-
const currentSchedule = this.db.select().from(schedules).where(
|
|
6770
|
+
const currentSchedule = this.db.select().from(schedules).where(eq12(schedules.id, scheduleId)).get();
|
|
6618
6771
|
if (!currentSchedule || !currentSchedule.enabled) {
|
|
6619
|
-
|
|
6772
|
+
log12.warn("schedule.stale", { scheduleId, projectId, kind, msg: "schedule no longer exists or is disabled" });
|
|
6620
6773
|
this.remove(projectId, kind);
|
|
6621
6774
|
return;
|
|
6622
6775
|
}
|
|
6623
6776
|
const nextRunAt = nextRunFromCron(currentSchedule.cronExpr, currentSchedule.timezone);
|
|
6624
|
-
const project = this.db.select().from(projects).where(
|
|
6777
|
+
const project = this.db.select().from(projects).where(eq12(projects.id, projectId)).get();
|
|
6625
6778
|
if (!project) {
|
|
6626
|
-
|
|
6779
|
+
log12.error("project.not-found", { projectId, kind, msg: "skipping scheduled run" });
|
|
6627
6780
|
this.remove(projectId, kind);
|
|
6628
6781
|
return;
|
|
6629
6782
|
}
|
|
6630
6783
|
if (kind === SchedulableRunKinds["traffic-sync"]) {
|
|
6631
6784
|
const sourceId = currentSchedule.sourceId;
|
|
6632
6785
|
if (!sourceId) {
|
|
6633
|
-
|
|
6786
|
+
log12.warn("traffic-sync.missing-source", { scheduleId, projectId });
|
|
6634
6787
|
return;
|
|
6635
6788
|
}
|
|
6636
6789
|
if (!this.callbacks.onTrafficSyncRequested) {
|
|
6637
|
-
|
|
6790
|
+
log12.warn("traffic-sync.no-callback", { scheduleId, projectId, msg: "host did not register onTrafficSyncRequested" });
|
|
6638
6791
|
return;
|
|
6639
6792
|
}
|
|
6640
6793
|
this.db.update(schedules).set({
|
|
6641
6794
|
lastRunAt: now,
|
|
6642
6795
|
nextRunAt,
|
|
6643
6796
|
updatedAt: now
|
|
6644
|
-
}).where(
|
|
6645
|
-
|
|
6797
|
+
}).where(eq12(schedules.id, currentSchedule.id)).run();
|
|
6798
|
+
log12.info("traffic-sync.triggered", { projectName: project.name, sourceId });
|
|
6646
6799
|
this.callbacks.onTrafficSyncRequested(project.name, sourceId);
|
|
6647
6800
|
return;
|
|
6648
6801
|
}
|
|
6649
6802
|
if (kind === SchedulableRunKinds["gbp-sync"]) {
|
|
6650
6803
|
if (!this.callbacks.onGbpSyncRequested) {
|
|
6651
|
-
|
|
6804
|
+
log12.warn("gbp-sync.no-callback", { scheduleId, projectId, msg: "host did not register onGbpSyncRequested" });
|
|
6652
6805
|
return;
|
|
6653
6806
|
}
|
|
6654
|
-
const runId2 =
|
|
6807
|
+
const runId2 = crypto14.randomUUID();
|
|
6655
6808
|
this.db.insert(runs).values({
|
|
6656
6809
|
id: runId2,
|
|
6657
6810
|
projectId,
|
|
@@ -6664,45 +6817,78 @@ var Scheduler = class {
|
|
|
6664
6817
|
lastRunAt: now,
|
|
6665
6818
|
nextRunAt,
|
|
6666
6819
|
updatedAt: now
|
|
6667
|
-
}).where(
|
|
6668
|
-
|
|
6820
|
+
}).where(eq12(schedules.id, currentSchedule.id)).run();
|
|
6821
|
+
log12.info("gbp-sync.triggered", { runId: runId2, projectName: project.name });
|
|
6669
6822
|
this.callbacks.onGbpSyncRequested(runId2, projectId);
|
|
6670
6823
|
return;
|
|
6671
6824
|
}
|
|
6672
6825
|
if (kind === SchedulableRunKinds["data-refresh"]) {
|
|
6673
6826
|
if (!this.callbacks.onDataRefreshRequested) {
|
|
6674
|
-
|
|
6827
|
+
log12.warn("data-refresh.no-callback", { scheduleId, projectId, msg: "host did not register onDataRefreshRequested" });
|
|
6675
6828
|
return;
|
|
6676
6829
|
}
|
|
6677
6830
|
this.db.update(schedules).set({
|
|
6678
6831
|
lastRunAt: now,
|
|
6679
6832
|
nextRunAt,
|
|
6680
6833
|
updatedAt: now
|
|
6681
|
-
}).where(
|
|
6682
|
-
|
|
6834
|
+
}).where(eq12(schedules.id, currentSchedule.id)).run();
|
|
6835
|
+
log12.info("data-refresh.triggered", { projectName: project.name });
|
|
6683
6836
|
this.callbacks.onDataRefreshRequested(project.name);
|
|
6684
6837
|
return;
|
|
6685
6838
|
}
|
|
6686
6839
|
if (kind === SchedulableRunKinds["backlinks-sync"]) {
|
|
6687
6840
|
if (!this.callbacks.onBacklinksSyncRequested) {
|
|
6688
|
-
|
|
6841
|
+
log12.warn("backlinks-sync.no-callback", { scheduleId, projectId, msg: "host did not register onBacklinksSyncRequested" });
|
|
6689
6842
|
return;
|
|
6690
6843
|
}
|
|
6691
6844
|
this.db.update(schedules).set({
|
|
6692
6845
|
lastRunAt: now,
|
|
6693
6846
|
nextRunAt,
|
|
6694
6847
|
updatedAt: now
|
|
6695
|
-
}).where(
|
|
6696
|
-
|
|
6848
|
+
}).where(eq12(schedules.id, currentSchedule.id)).run();
|
|
6849
|
+
log12.info("backlinks-sync.triggered", { projectName: project.name });
|
|
6697
6850
|
this.callbacks.onBacklinksSyncRequested(project.name);
|
|
6698
6851
|
return;
|
|
6699
6852
|
}
|
|
6853
|
+
if (kind === SchedulableRunKinds["site-audit"]) {
|
|
6854
|
+
if (!this.callbacks.onSiteAuditRequested) {
|
|
6855
|
+
log12.warn("site-audit.no-callback", { scheduleId, projectId, msg: "host did not register onSiteAuditRequested" });
|
|
6856
|
+
return;
|
|
6857
|
+
}
|
|
6858
|
+
const active = this.db.select({ id: runs.id }).from(runs).where(and10(
|
|
6859
|
+
eq12(runs.projectId, projectId),
|
|
6860
|
+
eq12(runs.kind, RunKinds["site-audit"]),
|
|
6861
|
+
inArray5(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
6862
|
+
)).get();
|
|
6863
|
+
if (active) {
|
|
6864
|
+
log12.info("site-audit.skipped-active", { projectName: project.name, activeRunId: active.id });
|
|
6865
|
+
this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq12(schedules.id, currentSchedule.id)).run();
|
|
6866
|
+
return;
|
|
6867
|
+
}
|
|
6868
|
+
const runId2 = crypto14.randomUUID();
|
|
6869
|
+
this.db.insert(runs).values({
|
|
6870
|
+
id: runId2,
|
|
6871
|
+
projectId,
|
|
6872
|
+
kind: RunKinds["site-audit"],
|
|
6873
|
+
status: RunStatuses.queued,
|
|
6874
|
+
trigger: RunTriggers.scheduled,
|
|
6875
|
+
createdAt: now
|
|
6876
|
+
}).run();
|
|
6877
|
+
this.db.update(schedules).set({
|
|
6878
|
+
lastRunAt: now,
|
|
6879
|
+
nextRunAt,
|
|
6880
|
+
updatedAt: now
|
|
6881
|
+
}).where(eq12(schedules.id, currentSchedule.id)).run();
|
|
6882
|
+
log12.info("site-audit.triggered", { runId: runId2, projectName: project.name });
|
|
6883
|
+
this.callbacks.onSiteAuditRequested(runId2, projectId);
|
|
6884
|
+
return;
|
|
6885
|
+
}
|
|
6700
6886
|
const projectLocations = project.locations;
|
|
6701
6887
|
let resolvedLocation;
|
|
6702
6888
|
if (project.defaultLocation) {
|
|
6703
6889
|
const loc = projectLocations.find((l) => l.label === project.defaultLocation);
|
|
6704
6890
|
if (!loc) {
|
|
6705
|
-
|
|
6891
|
+
log12.warn("default-location.stale", { scheduleId, projectId, label: project.defaultLocation });
|
|
6706
6892
|
return;
|
|
6707
6893
|
}
|
|
6708
6894
|
resolvedLocation = loc;
|
|
@@ -6716,11 +6902,11 @@ var Scheduler = class {
|
|
|
6716
6902
|
location: locationLabel
|
|
6717
6903
|
});
|
|
6718
6904
|
if (queueResult.conflict) {
|
|
6719
|
-
|
|
6905
|
+
log12.info("run.skipped-active", { projectName: project.name, activeRunId: queueResult.activeRunId });
|
|
6720
6906
|
this.db.update(schedules).set({
|
|
6721
6907
|
nextRunAt,
|
|
6722
6908
|
updatedAt: now
|
|
6723
|
-
}).where(
|
|
6909
|
+
}).where(eq12(schedules.id, currentSchedule.id)).run();
|
|
6724
6910
|
return;
|
|
6725
6911
|
}
|
|
6726
6912
|
const runId = queueResult.runId;
|
|
@@ -6728,19 +6914,19 @@ var Scheduler = class {
|
|
|
6728
6914
|
lastRunAt: now,
|
|
6729
6915
|
nextRunAt,
|
|
6730
6916
|
updatedAt: now
|
|
6731
|
-
}).where(
|
|
6917
|
+
}).where(eq12(schedules.id, currentSchedule.id)).run();
|
|
6732
6918
|
const scheduleProviders = currentSchedule.providers;
|
|
6733
6919
|
const providers = scheduleProviders.length > 0 ? scheduleProviders : void 0;
|
|
6734
|
-
|
|
6920
|
+
log12.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
|
|
6735
6921
|
this.callbacks.onRunCreated(runId, projectId, providers, resolvedLocation);
|
|
6736
6922
|
} catch (err) {
|
|
6737
|
-
|
|
6923
|
+
log12.error("trigger.error", { scheduleId, projectId, kind, error: err instanceof Error ? err.message : String(err) });
|
|
6738
6924
|
}
|
|
6739
6925
|
}
|
|
6740
6926
|
};
|
|
6741
6927
|
|
|
6742
6928
|
// src/data-refresh.ts
|
|
6743
|
-
var
|
|
6929
|
+
var log13 = createLogger("DataRefresh");
|
|
6744
6930
|
async function refreshAllIntegrations(client, projectName) {
|
|
6745
6931
|
const integrations = [
|
|
6746
6932
|
{ name: "gsc", run: () => client.gscSync(projectName, {}) },
|
|
@@ -6752,19 +6938,19 @@ async function refreshAllIntegrations(client, projectName) {
|
|
|
6752
6938
|
results.forEach((result, idx) => {
|
|
6753
6939
|
const integration = integrations[idx].name;
|
|
6754
6940
|
if (result.status === "fulfilled") {
|
|
6755
|
-
|
|
6941
|
+
log13.info("integration.refreshed", { projectName, integration });
|
|
6756
6942
|
} else {
|
|
6757
6943
|
const reason = result.reason;
|
|
6758
6944
|
const message = reason instanceof Error ? reason.message : String(reason);
|
|
6759
|
-
|
|
6945
|
+
log13.warn("integration.refresh-failed", { projectName, integration, error: message });
|
|
6760
6946
|
}
|
|
6761
6947
|
});
|
|
6762
6948
|
}
|
|
6763
6949
|
|
|
6764
6950
|
// src/notifier.ts
|
|
6765
|
-
import { eq as
|
|
6766
|
-
import
|
|
6767
|
-
var
|
|
6951
|
+
import { eq as eq13, desc as desc5, and as and11, inArray as inArray6, or } from "drizzle-orm";
|
|
6952
|
+
import crypto15 from "crypto";
|
|
6953
|
+
var log14 = createLogger("Notifier");
|
|
6768
6954
|
var Notifier = class {
|
|
6769
6955
|
db;
|
|
6770
6956
|
serverUrl;
|
|
@@ -6774,26 +6960,26 @@ var Notifier = class {
|
|
|
6774
6960
|
}
|
|
6775
6961
|
/** Called after a run completes (success, partial, or failed). */
|
|
6776
6962
|
async onRunCompleted(runId, projectId) {
|
|
6777
|
-
|
|
6778
|
-
const notifs = this.db.select().from(notifications).where(
|
|
6963
|
+
log14.info("run.completed", { runId, projectId });
|
|
6964
|
+
const notifs = this.db.select().from(notifications).where(eq13(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
6779
6965
|
if (notifs.length === 0) {
|
|
6780
|
-
|
|
6966
|
+
log14.info("notifications.none-enabled", { projectId });
|
|
6781
6967
|
return;
|
|
6782
6968
|
}
|
|
6783
|
-
|
|
6784
|
-
const run = this.db.select().from(runs).where(
|
|
6969
|
+
log14.info("notifications.found", { projectId, count: notifs.length });
|
|
6970
|
+
const run = this.db.select().from(runs).where(eq13(runs.id, runId)).get();
|
|
6785
6971
|
if (!run) {
|
|
6786
|
-
|
|
6972
|
+
log14.error("run.not-found", { runId, msg: "skipping notification dispatch" });
|
|
6787
6973
|
return;
|
|
6788
6974
|
}
|
|
6789
|
-
const project = this.db.select().from(projects).where(
|
|
6975
|
+
const project = this.db.select().from(projects).where(eq13(projects.id, projectId)).get();
|
|
6790
6976
|
if (!project) {
|
|
6791
|
-
|
|
6977
|
+
log14.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
|
|
6792
6978
|
return;
|
|
6793
6979
|
}
|
|
6794
6980
|
const transitions = this.computeTransitions(runId, projectId);
|
|
6795
6981
|
const events = [];
|
|
6796
|
-
|
|
6982
|
+
log14.info("run.status", { runId: run.id, status: run.status, projectId });
|
|
6797
6983
|
if (run.status === "completed" || run.status === "partial") {
|
|
6798
6984
|
events.push("run.completed");
|
|
6799
6985
|
}
|
|
@@ -6809,7 +6995,7 @@ var Notifier = class {
|
|
|
6809
6995
|
if (!config.url) continue;
|
|
6810
6996
|
const subscribedEvents = config.events;
|
|
6811
6997
|
const matchingEvents = events.filter((e) => subscribedEvents.includes(e));
|
|
6812
|
-
|
|
6998
|
+
log14.info("notification.match", { notificationId: notif.id, subscribedEvents, matchedEvents: matchingEvents });
|
|
6813
6999
|
if (matchingEvents.length === 0) continue;
|
|
6814
7000
|
for (const event of matchingEvents) {
|
|
6815
7001
|
const relevantTransitions = event === "citation.lost" ? lostTransitions : event === "citation.gained" ? gainedTransitions : transitions;
|
|
@@ -6833,11 +7019,11 @@ var Notifier = class {
|
|
|
6833
7019
|
if (criticalInsights.length > 0) insightEvents.push("insight.critical");
|
|
6834
7020
|
if (highInsights.length > 0) insightEvents.push("insight.high");
|
|
6835
7021
|
if (insightEvents.length === 0) return;
|
|
6836
|
-
const notifs = this.db.select().from(notifications).where(
|
|
7022
|
+
const notifs = this.db.select().from(notifications).where(eq13(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
6837
7023
|
if (notifs.length === 0) return;
|
|
6838
|
-
const run = this.db.select().from(runs).where(
|
|
7024
|
+
const run = this.db.select().from(runs).where(eq13(runs.id, runId)).get();
|
|
6839
7025
|
if (!run) return;
|
|
6840
|
-
const project = this.db.select().from(projects).where(
|
|
7026
|
+
const project = this.db.select().from(projects).where(eq13(projects.id, projectId)).get();
|
|
6841
7027
|
if (!project) return;
|
|
6842
7028
|
for (const notif of notifs) {
|
|
6843
7029
|
const config = notif.config;
|
|
@@ -6867,12 +7053,12 @@ var Notifier = class {
|
|
|
6867
7053
|
}
|
|
6868
7054
|
}
|
|
6869
7055
|
computeTransitions(runId, projectId) {
|
|
6870
|
-
const thisRun = this.db.select().from(runs).where(
|
|
7056
|
+
const thisRun = this.db.select().from(runs).where(eq13(runs.id, runId)).get();
|
|
6871
7057
|
if (!thisRun) return [];
|
|
6872
7058
|
const groupSiblings = this.db.select().from(runs).where(and11(
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
7059
|
+
eq13(runs.projectId, projectId),
|
|
7060
|
+
eq13(runs.kind, thisRun.kind),
|
|
7061
|
+
eq13(runs.createdAt, thisRun.createdAt)
|
|
6876
7062
|
)).all();
|
|
6877
7063
|
const stillPending = groupSiblings.some((r) => r.status === "queued" || r.status === "running");
|
|
6878
7064
|
if (stillPending) return [];
|
|
@@ -6888,7 +7074,7 @@ var Notifier = class {
|
|
|
6888
7074
|
return candidate.id > best.id ? candidate : best;
|
|
6889
7075
|
});
|
|
6890
7076
|
if (winner.id !== runId) return [];
|
|
6891
|
-
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(
|
|
7077
|
+
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq13(projects.id, projectId)).get();
|
|
6892
7078
|
const locationCount = Math.max(
|
|
6893
7079
|
1,
|
|
6894
7080
|
(projectLocations?.locations ?? []).length
|
|
@@ -6896,9 +7082,9 @@ var Notifier = class {
|
|
|
6896
7082
|
const RECENT_FETCH_LIMIT = Math.max(8, locationCount * 4);
|
|
6897
7083
|
const recentRuns = this.db.select().from(runs).where(
|
|
6898
7084
|
and11(
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
or(
|
|
7085
|
+
eq13(runs.projectId, projectId),
|
|
7086
|
+
eq13(runs.kind, thisRun.kind),
|
|
7087
|
+
or(eq13(runs.status, "completed"), eq13(runs.status, "partial"))
|
|
6902
7088
|
)
|
|
6903
7089
|
).orderBy(desc5(runs.createdAt), desc5(runs.id)).limit(RECENT_FETCH_LIMIT).all();
|
|
6904
7090
|
const groups = groupRunsByCreatedAt(recentRuns);
|
|
@@ -6915,13 +7101,13 @@ var Notifier = class {
|
|
|
6915
7101
|
provider: querySnapshots.provider,
|
|
6916
7102
|
location: querySnapshots.location,
|
|
6917
7103
|
citationState: querySnapshots.citationState
|
|
6918
|
-
}).from(querySnapshots).leftJoin(queries,
|
|
7104
|
+
}).from(querySnapshots).leftJoin(queries, eq13(querySnapshots.queryId, queries.id)).where(inArray6(querySnapshots.runId, currentRunIds)).all();
|
|
6919
7105
|
const previousSnapshots = this.db.select({
|
|
6920
7106
|
queryId: querySnapshots.queryId,
|
|
6921
7107
|
provider: querySnapshots.provider,
|
|
6922
7108
|
location: querySnapshots.location,
|
|
6923
7109
|
citationState: querySnapshots.citationState
|
|
6924
|
-
}).from(querySnapshots).where(
|
|
7110
|
+
}).from(querySnapshots).where(inArray6(querySnapshots.runId, previousRunIds)).all();
|
|
6925
7111
|
const prevMap = /* @__PURE__ */ new Map();
|
|
6926
7112
|
for (const s of previousSnapshots) {
|
|
6927
7113
|
if (s.queryId == null) continue;
|
|
@@ -6948,23 +7134,23 @@ var Notifier = class {
|
|
|
6948
7134
|
const targetLabel = redactNotificationUrl(url).urlDisplay;
|
|
6949
7135
|
const targetCheck = await resolveWebhookTarget(url);
|
|
6950
7136
|
if (!targetCheck.ok) {
|
|
6951
|
-
|
|
7137
|
+
log14.error("webhook.ssrf-blocked", { url: targetLabel, reason: targetCheck.message });
|
|
6952
7138
|
this.logDelivery(projectId, notificationId, payload.event, "failed", `SSRF: ${targetCheck.message}`);
|
|
6953
7139
|
return;
|
|
6954
7140
|
}
|
|
6955
|
-
|
|
7141
|
+
log14.info("webhook.send", { event: payload.event, url: targetLabel });
|
|
6956
7142
|
const maxRetries = 3;
|
|
6957
7143
|
const delays = [1e3, 4e3, 16e3];
|
|
6958
7144
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
6959
7145
|
try {
|
|
6960
7146
|
const response = await deliverWebhook(targetCheck.target, payload, webhookSecret);
|
|
6961
7147
|
if (response.status >= 200 && response.status < 300) {
|
|
6962
|
-
|
|
7148
|
+
log14.info("webhook.delivered", { event: payload.event, url: targetLabel, httpStatus: response.status });
|
|
6963
7149
|
this.logDelivery(projectId, notificationId, payload.event, "sent", null);
|
|
6964
7150
|
return;
|
|
6965
7151
|
}
|
|
6966
7152
|
const errorDetail = response.error ?? `HTTP ${response.status}`;
|
|
6967
|
-
|
|
7153
|
+
log14.warn("webhook.attempt-failed", { event: payload.event, url: targetLabel, attempt: attempt + 1, maxRetries, httpStatus: response.status, error: errorDetail });
|
|
6968
7154
|
if (attempt === maxRetries - 1) {
|
|
6969
7155
|
this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
|
|
6970
7156
|
}
|
|
@@ -6972,7 +7158,7 @@ var Notifier = class {
|
|
|
6972
7158
|
const errorDetail = err instanceof Error ? err.message : String(err);
|
|
6973
7159
|
if (attempt === maxRetries - 1) {
|
|
6974
7160
|
this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
|
|
6975
|
-
|
|
7161
|
+
log14.error("webhook.exhausted", { event: payload.event, url: targetLabel, maxRetries, error: errorDetail });
|
|
6976
7162
|
}
|
|
6977
7163
|
}
|
|
6978
7164
|
if (attempt < maxRetries - 1) {
|
|
@@ -6982,7 +7168,7 @@ var Notifier = class {
|
|
|
6982
7168
|
}
|
|
6983
7169
|
logDelivery(projectId, notificationId, event, status, error) {
|
|
6984
7170
|
this.db.insert(auditLog).values({
|
|
6985
|
-
id:
|
|
7171
|
+
id: crypto15.randomUUID(),
|
|
6986
7172
|
projectId,
|
|
6987
7173
|
actor: "scheduler",
|
|
6988
7174
|
action: `notification.${status}`,
|
|
@@ -6995,8 +7181,8 @@ var Notifier = class {
|
|
|
6995
7181
|
};
|
|
6996
7182
|
|
|
6997
7183
|
// src/run-coordinator.ts
|
|
6998
|
-
import { eq as
|
|
6999
|
-
var
|
|
7184
|
+
import { eq as eq14 } from "drizzle-orm";
|
|
7185
|
+
var log15 = createLogger("RunCoordinator");
|
|
7000
7186
|
var RunCoordinator = class {
|
|
7001
7187
|
constructor(db, notifier, intelligenceService, onInsightsGenerated, onAeroEvent) {
|
|
7002
7188
|
this.db = db;
|
|
@@ -7006,10 +7192,10 @@ var RunCoordinator = class {
|
|
|
7006
7192
|
this.onAeroEvent = onAeroEvent;
|
|
7007
7193
|
}
|
|
7008
7194
|
async onRunCompleted(runId, projectId) {
|
|
7009
|
-
const runRow = this.db.select().from(runs).where(
|
|
7195
|
+
const runRow = this.db.select().from(runs).where(eq14(runs.id, runId)).get();
|
|
7010
7196
|
const kind = runRow?.kind ?? RunKinds["answer-visibility"];
|
|
7011
7197
|
if (runRow?.trigger === RunTriggers.probe) {
|
|
7012
|
-
|
|
7198
|
+
log15.info("probe.skip-side-effects", { runId, projectId, kind });
|
|
7013
7199
|
return;
|
|
7014
7200
|
}
|
|
7015
7201
|
let insightCount = 0;
|
|
@@ -7026,12 +7212,12 @@ var RunCoordinator = class {
|
|
|
7026
7212
|
try {
|
|
7027
7213
|
await this.onInsightsGenerated(runId, projectId, result);
|
|
7028
7214
|
} catch (err) {
|
|
7029
|
-
|
|
7215
|
+
log15.error("insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
7030
7216
|
}
|
|
7031
7217
|
}
|
|
7032
7218
|
}
|
|
7033
7219
|
} catch (err) {
|
|
7034
|
-
|
|
7220
|
+
log15.error("intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
7035
7221
|
}
|
|
7036
7222
|
} else if (kind === RunKinds["gbp-sync"]) {
|
|
7037
7223
|
try {
|
|
@@ -7044,17 +7230,17 @@ var RunCoordinator = class {
|
|
|
7044
7230
|
try {
|
|
7045
7231
|
await this.onInsightsGenerated(runId, projectId, analysisResultFromInsights(gbpInsights));
|
|
7046
7232
|
} catch (err) {
|
|
7047
|
-
|
|
7233
|
+
log15.error("gbp-insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
7048
7234
|
}
|
|
7049
7235
|
}
|
|
7050
7236
|
} catch (err) {
|
|
7051
|
-
|
|
7237
|
+
log15.error("gbp-intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
7052
7238
|
}
|
|
7053
7239
|
}
|
|
7054
7240
|
try {
|
|
7055
7241
|
await this.notifier.onRunCompleted(runId, projectId);
|
|
7056
7242
|
} catch (err) {
|
|
7057
|
-
|
|
7243
|
+
log15.error("notifier.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
7058
7244
|
}
|
|
7059
7245
|
if (this.onAeroEvent) {
|
|
7060
7246
|
try {
|
|
@@ -7067,7 +7253,7 @@ var RunCoordinator = class {
|
|
|
7067
7253
|
};
|
|
7068
7254
|
await this.onAeroEvent(ctx);
|
|
7069
7255
|
} catch (err) {
|
|
7070
|
-
|
|
7256
|
+
log15.error("aero.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
7071
7257
|
}
|
|
7072
7258
|
}
|
|
7073
7259
|
}
|
|
@@ -7082,7 +7268,7 @@ var RunCoordinator = class {
|
|
|
7082
7268
|
* so the Aero queue is never starved of a follow-up.
|
|
7083
7269
|
*/
|
|
7084
7270
|
buildDiscoveryAeroContext(runId, projectId, status, error) {
|
|
7085
|
-
const session = this.db.select().from(discoverySessions).where(
|
|
7271
|
+
const session = this.db.select().from(discoverySessions).where(eq14(discoverySessions.runId, runId)).get();
|
|
7086
7272
|
const competitorMap = session ? session.competitorMap : [];
|
|
7087
7273
|
return {
|
|
7088
7274
|
kind: RunKinds["aeo-discover-probe"],
|
|
@@ -7122,8 +7308,8 @@ function analysisResultFromInsights(insights2) {
|
|
|
7122
7308
|
}
|
|
7123
7309
|
|
|
7124
7310
|
// src/agent/session-registry.ts
|
|
7125
|
-
import
|
|
7126
|
-
import { eq as
|
|
7311
|
+
import crypto17 from "crypto";
|
|
7312
|
+
import { eq as eq16 } from "drizzle-orm";
|
|
7127
7313
|
|
|
7128
7314
|
// src/agent/session.ts
|
|
7129
7315
|
import fs7 from "fs";
|
|
@@ -7511,8 +7697,8 @@ function resolveSessionProviderAndModel(config, opts) {
|
|
|
7511
7697
|
}
|
|
7512
7698
|
|
|
7513
7699
|
// src/agent/memory-store.ts
|
|
7514
|
-
import
|
|
7515
|
-
import { and as and12, desc as desc6, eq as
|
|
7700
|
+
import crypto16 from "crypto";
|
|
7701
|
+
import { and as and12, desc as desc6, eq as eq15, like, sql as sql5 } from "drizzle-orm";
|
|
7516
7702
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
7517
7703
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
7518
7704
|
function rowToDto(row) {
|
|
@@ -7526,7 +7712,7 @@ function rowToDto(row) {
|
|
|
7526
7712
|
};
|
|
7527
7713
|
}
|
|
7528
7714
|
function listMemoryEntries(db, projectId, opts = {}) {
|
|
7529
|
-
const query = db.select().from(agentMemory).where(
|
|
7715
|
+
const query = db.select().from(agentMemory).where(eq15(agentMemory.projectId, projectId)).orderBy(desc6(agentMemory.updatedAt));
|
|
7530
7716
|
const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
|
|
7531
7717
|
return rows.map(rowToDto);
|
|
7532
7718
|
}
|
|
@@ -7540,7 +7726,7 @@ function upsertMemoryEntry(db, args) {
|
|
|
7540
7726
|
throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
|
|
7541
7727
|
}
|
|
7542
7728
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7543
|
-
const id =
|
|
7729
|
+
const id = crypto16.randomUUID();
|
|
7544
7730
|
db.insert(agentMemory).values({
|
|
7545
7731
|
id,
|
|
7546
7732
|
projectId: args.projectId,
|
|
@@ -7557,12 +7743,12 @@ function upsertMemoryEntry(db, args) {
|
|
|
7557
7743
|
updatedAt: now
|
|
7558
7744
|
}
|
|
7559
7745
|
}).run();
|
|
7560
|
-
const row = db.select().from(agentMemory).where(and12(
|
|
7746
|
+
const row = db.select().from(agentMemory).where(and12(eq15(agentMemory.projectId, args.projectId), eq15(agentMemory.key, args.key))).get();
|
|
7561
7747
|
if (!row) throw new Error("memory upsert produced no row");
|
|
7562
7748
|
return rowToDto(row);
|
|
7563
7749
|
}
|
|
7564
7750
|
function deleteMemoryEntry(db, projectId, key) {
|
|
7565
|
-
const result = db.delete(agentMemory).where(and12(
|
|
7751
|
+
const result = db.delete(agentMemory).where(and12(eq15(agentMemory.projectId, projectId), eq15(agentMemory.key, key))).run();
|
|
7566
7752
|
const changes = result.changes ?? 0;
|
|
7567
7753
|
return changes > 0;
|
|
7568
7754
|
}
|
|
@@ -7577,7 +7763,7 @@ function writeCompactionNote(db, args) {
|
|
|
7577
7763
|
}
|
|
7578
7764
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7579
7765
|
const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
|
|
7580
|
-
const id =
|
|
7766
|
+
const id = crypto16.randomUUID();
|
|
7581
7767
|
let inserted;
|
|
7582
7768
|
db.transaction((tx) => {
|
|
7583
7769
|
tx.insert(agentMemory).values({
|
|
@@ -7592,7 +7778,7 @@ function writeCompactionNote(db, args) {
|
|
|
7592
7778
|
const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
|
|
7593
7779
|
const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
|
|
7594
7780
|
and12(
|
|
7595
|
-
|
|
7781
|
+
eq15(agentMemory.projectId, args.projectId),
|
|
7596
7782
|
like(agentMemory.key, `${sessionPrefix}%`)
|
|
7597
7783
|
)
|
|
7598
7784
|
).orderBy(desc6(agentMemory.updatedAt)).all();
|
|
@@ -7600,7 +7786,7 @@ function writeCompactionNote(db, args) {
|
|
|
7600
7786
|
if (stale.length > 0) {
|
|
7601
7787
|
tx.delete(agentMemory).where(sql5`${agentMemory.id} IN (${sql5.join(stale.map((s) => sql5`${s}`), sql5`, `)})`).run();
|
|
7602
7788
|
}
|
|
7603
|
-
const row = tx.select().from(agentMemory).where(and12(
|
|
7789
|
+
const row = tx.select().from(agentMemory).where(and12(eq15(agentMemory.projectId, args.projectId), eq15(agentMemory.key, key))).get();
|
|
7604
7790
|
if (row) inserted = rowToDto(row);
|
|
7605
7791
|
});
|
|
7606
7792
|
if (!inserted) throw new Error("compaction note write produced no row");
|
|
@@ -7733,7 +7919,7 @@ async function compactMessages(args) {
|
|
|
7733
7919
|
}
|
|
7734
7920
|
|
|
7735
7921
|
// src/agent/session-registry.ts
|
|
7736
|
-
var
|
|
7922
|
+
var log16 = createLogger("SessionRegistry");
|
|
7737
7923
|
var MAX_HYDRATE_NOTES = 20;
|
|
7738
7924
|
var MAX_HYDRATE_BYTES = 32 * 1024;
|
|
7739
7925
|
function escapeMemoryFragment(value) {
|
|
@@ -7782,7 +7968,7 @@ var SessionRegistry = class {
|
|
|
7782
7968
|
modelProvider: effectiveProvider,
|
|
7783
7969
|
modelId: effectiveModelId,
|
|
7784
7970
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7785
|
-
}).where(
|
|
7971
|
+
}).where(eq16(agentSessions.projectId, projectId)).run();
|
|
7786
7972
|
}
|
|
7787
7973
|
const agent2 = createAeroSession({
|
|
7788
7974
|
projectName,
|
|
@@ -7960,13 +8146,13 @@ ${lines.join("\n")}
|
|
|
7960
8146
|
agent.state.messages = result.messages;
|
|
7961
8147
|
agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, row.systemPrompt);
|
|
7962
8148
|
this.save(projectName);
|
|
7963
|
-
|
|
8149
|
+
log16.info("compaction.completed", {
|
|
7964
8150
|
projectName,
|
|
7965
8151
|
removedCount: result.removedCount,
|
|
7966
8152
|
summaryBytes: Buffer.byteLength(result.summary, "utf8")
|
|
7967
8153
|
});
|
|
7968
8154
|
} catch (err) {
|
|
7969
|
-
|
|
8155
|
+
log16.error("compaction.failed", {
|
|
7970
8156
|
projectName,
|
|
7971
8157
|
error: err instanceof Error ? err.message : String(err)
|
|
7972
8158
|
});
|
|
@@ -7996,7 +8182,7 @@ ${lines.join("\n")}
|
|
|
7996
8182
|
modelProvider: nextProvider,
|
|
7997
8183
|
modelId: nextModelId,
|
|
7998
8184
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7999
|
-
}).where(
|
|
8185
|
+
}).where(eq16(agentSessions.projectId, projectId)).run();
|
|
8000
8186
|
}
|
|
8001
8187
|
/** Persist a session's transcript back to the DB. Call after any run settles. */
|
|
8002
8188
|
save(projectName) {
|
|
@@ -8063,7 +8249,7 @@ ${lines.join("\n")}
|
|
|
8063
8249
|
await agent.prompt(msgs);
|
|
8064
8250
|
this.save(projectName);
|
|
8065
8251
|
} catch (err) {
|
|
8066
|
-
|
|
8252
|
+
log16.error("drain.failed", {
|
|
8067
8253
|
projectName,
|
|
8068
8254
|
error: err instanceof Error ? err.message : String(err)
|
|
8069
8255
|
});
|
|
@@ -8158,17 +8344,17 @@ ${lines.join("\n")}
|
|
|
8158
8344
|
return id;
|
|
8159
8345
|
}
|
|
8160
8346
|
tryResolveProjectId(projectName) {
|
|
8161
|
-
const row = this.opts.db.select({ id: projects.id }).from(projects).where(
|
|
8347
|
+
const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq16(projects.name, projectName)).get();
|
|
8162
8348
|
return row?.id;
|
|
8163
8349
|
}
|
|
8164
8350
|
loadRow(projectId) {
|
|
8165
|
-
const row = this.opts.db.select().from(agentSessions).where(
|
|
8351
|
+
const row = this.opts.db.select().from(agentSessions).where(eq16(agentSessions.projectId, projectId)).get();
|
|
8166
8352
|
return row ?? null;
|
|
8167
8353
|
}
|
|
8168
8354
|
insertRow(params) {
|
|
8169
8355
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8170
8356
|
this.opts.db.insert(agentSessions).values({
|
|
8171
|
-
id:
|
|
8357
|
+
id: crypto17.randomUUID(),
|
|
8172
8358
|
projectId: params.projectId,
|
|
8173
8359
|
systemPrompt: params.systemPrompt,
|
|
8174
8360
|
modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
|
|
@@ -8181,14 +8367,14 @@ ${lines.join("\n")}
|
|
|
8181
8367
|
}
|
|
8182
8368
|
updateRow(projectId, patch) {
|
|
8183
8369
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8184
|
-
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(
|
|
8370
|
+
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq16(agentSessions.projectId, projectId)).run();
|
|
8185
8371
|
}
|
|
8186
8372
|
};
|
|
8187
8373
|
|
|
8188
8374
|
// src/agent/agent-routes.ts
|
|
8189
|
-
import { eq as
|
|
8375
|
+
import { eq as eq17 } from "drizzle-orm";
|
|
8190
8376
|
function resolveProject(db, name) {
|
|
8191
|
-
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(
|
|
8377
|
+
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq17(projects.name, name)).get();
|
|
8192
8378
|
if (!row) throw notFound("project", name);
|
|
8193
8379
|
return row;
|
|
8194
8380
|
}
|
|
@@ -8197,7 +8383,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
8197
8383
|
"/projects/:name/agent/transcript",
|
|
8198
8384
|
async (request) => {
|
|
8199
8385
|
const project = resolveProject(opts.db, request.params.name);
|
|
8200
|
-
const row = opts.db.select().from(agentSessions).where(
|
|
8386
|
+
const row = opts.db.select().from(agentSessions).where(eq17(agentSessions.projectId, project.id)).get();
|
|
8201
8387
|
if (!row) {
|
|
8202
8388
|
return { messages: [], modelProvider: null, modelId: null, updatedAt: null };
|
|
8203
8389
|
}
|
|
@@ -8221,7 +8407,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
8221
8407
|
async (request) => {
|
|
8222
8408
|
const project = resolveProject(opts.db, request.params.name);
|
|
8223
8409
|
opts.sessionRegistry.reset(project.name);
|
|
8224
|
-
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
8410
|
+
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq17(agentSessions.projectId, project.id)).run();
|
|
8225
8411
|
return { status: "reset" };
|
|
8226
8412
|
}
|
|
8227
8413
|
);
|
|
@@ -8661,7 +8847,7 @@ function formatAuditFactorScore(factor) {
|
|
|
8661
8847
|
}
|
|
8662
8848
|
|
|
8663
8849
|
// src/snapshot-service.ts
|
|
8664
|
-
var
|
|
8850
|
+
var log17 = createLogger("Snapshot");
|
|
8665
8851
|
var ANALYSIS_PROVIDER_PRIORITY = ["openai", "claude", "gemini", "perplexity", "local"];
|
|
8666
8852
|
var SNAPSHOT_QUERY_COUNT = 6;
|
|
8667
8853
|
var ProviderExecutionGate2 = class {
|
|
@@ -8804,13 +8990,12 @@ var SnapshotService = class {
|
|
|
8804
8990
|
return mapAuditReport(report);
|
|
8805
8991
|
} catch (err) {
|
|
8806
8992
|
const message = err instanceof Error ? err.message : String(err);
|
|
8807
|
-
|
|
8993
|
+
log17.warn("audit.failed", { homepageUrl, error: message });
|
|
8808
8994
|
return {
|
|
8809
8995
|
url: homepageUrl,
|
|
8810
8996
|
finalUrl: homepageUrl,
|
|
8811
8997
|
auditedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8812
8998
|
overallScore: 0,
|
|
8813
|
-
overallGrade: "N/A",
|
|
8814
8999
|
summary: `Technical audit unavailable: ${message}`,
|
|
8815
9000
|
factors: []
|
|
8816
9001
|
};
|
|
@@ -8834,7 +9019,7 @@ var SnapshotService = class {
|
|
|
8834
9019
|
queries: parsedQueries
|
|
8835
9020
|
};
|
|
8836
9021
|
} catch (err) {
|
|
8837
|
-
|
|
9022
|
+
log17.warn("profile.generation-failed", {
|
|
8838
9023
|
domain: ctx.domain,
|
|
8839
9024
|
provider: ctx.analysisProvider.adapter.name,
|
|
8840
9025
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -8976,7 +9161,7 @@ var SnapshotService = class {
|
|
|
8976
9161
|
recommendedActions: uniqueStrings(parsed.recommendedActions ?? []).slice(0, 4)
|
|
8977
9162
|
};
|
|
8978
9163
|
} catch (err) {
|
|
8979
|
-
|
|
9164
|
+
log17.warn("response.analysis-failed", {
|
|
8980
9165
|
provider: ctx.analysisProvider.adapter.name,
|
|
8981
9166
|
error: err instanceof Error ? err.message : String(err)
|
|
8982
9167
|
});
|
|
@@ -9036,7 +9221,7 @@ function buildBatchAnalysisPrompt(ctx) {
|
|
|
9036
9221
|
`Services: ${ctx.profile.services.join(", ") || "unknown"}`,
|
|
9037
9222
|
`Category terms: ${ctx.profile.categoryTerms.join(", ") || "unknown"}`,
|
|
9038
9223
|
`Manual competitor hints: ${ctx.manualCompetitors.join(", ") || "none"}`,
|
|
9039
|
-
`Technical audit: ${ctx.audit.overallScore}/100
|
|
9224
|
+
`Technical audit: ${ctx.audit.overallScore}/100 \u2014 ${ctx.audit.summary}`,
|
|
9040
9225
|
"",
|
|
9041
9226
|
"Responses JSON:",
|
|
9042
9227
|
JSON.stringify(ctx.responses, null, 2)
|
|
@@ -9047,7 +9232,7 @@ function buildFallbackBatchAssessment(companyName, audit) {
|
|
|
9047
9232
|
assessments: [],
|
|
9048
9233
|
whatThisMeans: [
|
|
9049
9234
|
`${companyName} needs category-level visibility, not just branded comprehension.`,
|
|
9050
|
-
`The technical baseline is ${audit.overallScore}/100
|
|
9235
|
+
`The technical baseline is ${audit.overallScore}/100, so weak site signals may be making AI systems prefer better-structured alternatives.`
|
|
9051
9236
|
],
|
|
9052
9237
|
recommendedActions: buildFallbackRecommendedActions(audit)
|
|
9053
9238
|
};
|
|
@@ -9191,7 +9376,6 @@ function mapAuditReport(report) {
|
|
|
9191
9376
|
finalUrl: report.finalUrl,
|
|
9192
9377
|
auditedAt: report.auditedAt,
|
|
9193
9378
|
overallScore: report.overallScore,
|
|
9194
|
-
overallGrade: report.overallGrade,
|
|
9195
9379
|
summary: report.summary,
|
|
9196
9380
|
factors: report.factors.map(mapAuditFactor)
|
|
9197
9381
|
};
|
|
@@ -9202,8 +9386,6 @@ function mapAuditFactor(factor) {
|
|
|
9202
9386
|
name: factor.name,
|
|
9203
9387
|
weight: factor.weight,
|
|
9204
9388
|
score: factor.score,
|
|
9205
|
-
grade: factor.grade,
|
|
9206
|
-
status: factor.status,
|
|
9207
9389
|
findings: factor.findings.map((finding) => ({
|
|
9208
9390
|
type: finding.type,
|
|
9209
9391
|
message: finding.message
|
|
@@ -9261,7 +9443,7 @@ function clipText(value, length) {
|
|
|
9261
9443
|
// src/server.ts
|
|
9262
9444
|
var _require3 = createRequire3(import.meta.url);
|
|
9263
9445
|
var { version: PKG_VERSION2 } = _require3("../package.json");
|
|
9264
|
-
var
|
|
9446
|
+
var log18 = createLogger("Server");
|
|
9265
9447
|
var DEFAULT_QUOTA = {
|
|
9266
9448
|
maxConcurrency: 2,
|
|
9267
9449
|
maxRequestsPerMinute: 10,
|
|
@@ -9291,14 +9473,14 @@ function summarizeProviderConfig(provider, config) {
|
|
|
9291
9473
|
};
|
|
9292
9474
|
}
|
|
9293
9475
|
function hashApiKey(key) {
|
|
9294
|
-
return
|
|
9476
|
+
return crypto18.createHash("sha256").update(key).digest("hex");
|
|
9295
9477
|
}
|
|
9296
9478
|
var DASHBOARD_SCRYPT_KEYLEN = 64;
|
|
9297
9479
|
var DASHBOARD_SCRYPT_COST = 1 << 15;
|
|
9298
9480
|
var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
|
|
9299
9481
|
function hashDashboardPassword(password) {
|
|
9300
|
-
const salt =
|
|
9301
|
-
const derived =
|
|
9482
|
+
const salt = crypto18.randomBytes(16);
|
|
9483
|
+
const derived = crypto18.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
|
|
9302
9484
|
N: DASHBOARD_SCRYPT_COST,
|
|
9303
9485
|
maxmem: DASHBOARD_SCRYPT_MAXMEM
|
|
9304
9486
|
});
|
|
@@ -9319,18 +9501,18 @@ function verifyDashboardPassword(password, storedHash) {
|
|
|
9319
9501
|
} catch {
|
|
9320
9502
|
return { ok: false, needsRehash: false };
|
|
9321
9503
|
}
|
|
9322
|
-
const derived =
|
|
9504
|
+
const derived = crypto18.scryptSync(password, salt, expected.length, {
|
|
9323
9505
|
N: DASHBOARD_SCRYPT_COST,
|
|
9324
9506
|
maxmem: DASHBOARD_SCRYPT_MAXMEM
|
|
9325
9507
|
});
|
|
9326
9508
|
if (derived.length !== expected.length) return { ok: false, needsRehash: false };
|
|
9327
|
-
return { ok:
|
|
9509
|
+
return { ok: crypto18.timingSafeEqual(derived, expected), needsRehash: false };
|
|
9328
9510
|
}
|
|
9329
9511
|
if (/^[a-f0-9]{64}$/i.test(storedHash)) {
|
|
9330
9512
|
const candidate = Buffer.from(hashApiKey(password), "hex");
|
|
9331
9513
|
const expected = Buffer.from(storedHash, "hex");
|
|
9332
9514
|
if (candidate.length !== expected.length) return { ok: false, needsRehash: false };
|
|
9333
|
-
const ok =
|
|
9515
|
+
const ok = crypto18.timingSafeEqual(candidate, expected);
|
|
9334
9516
|
return { ok, needsRehash: ok };
|
|
9335
9517
|
}
|
|
9336
9518
|
return { ok: false, needsRehash: false };
|
|
@@ -9389,7 +9571,7 @@ function applyLegacyCredentials(rows, config) {
|
|
|
9389
9571
|
}
|
|
9390
9572
|
if (migratedGoogle > 0) {
|
|
9391
9573
|
saveConfigPatch({ google: config.google });
|
|
9392
|
-
|
|
9574
|
+
log18.info("credentials.migrated", { type: "google", count: migratedGoogle });
|
|
9393
9575
|
}
|
|
9394
9576
|
let migratedGa4 = 0;
|
|
9395
9577
|
for (const row of rows.ga4) {
|
|
@@ -9407,7 +9589,7 @@ function applyLegacyCredentials(rows, config) {
|
|
|
9407
9589
|
}
|
|
9408
9590
|
if (migratedGa4 > 0) {
|
|
9409
9591
|
saveConfigPatch({ ga4: config.ga4 });
|
|
9410
|
-
|
|
9592
|
+
log18.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
|
|
9411
9593
|
}
|
|
9412
9594
|
}
|
|
9413
9595
|
async function createServer(opts) {
|
|
@@ -9439,11 +9621,11 @@ async function createServer(opts) {
|
|
|
9439
9621
|
applyLegacyCredentials(legacyRows, opts.config);
|
|
9440
9622
|
dropLegacyCredentialColumns(opts.db);
|
|
9441
9623
|
} catch (err) {
|
|
9442
|
-
|
|
9624
|
+
log18.warn("credentials.migration.failed", {
|
|
9443
9625
|
error: err instanceof Error ? err.message : String(err)
|
|
9444
9626
|
});
|
|
9445
9627
|
}
|
|
9446
|
-
|
|
9628
|
+
log18.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
|
|
9447
9629
|
const p = providers[k];
|
|
9448
9630
|
return p?.apiKey || p?.baseUrl || p?.vertexProject;
|
|
9449
9631
|
}) });
|
|
@@ -9492,7 +9674,7 @@ async function createServer(opts) {
|
|
|
9492
9674
|
intelligenceService,
|
|
9493
9675
|
(runId, projectId, result) => notifier.dispatchInsightWebhooks(runId, projectId, result),
|
|
9494
9676
|
async (ctx) => {
|
|
9495
|
-
const project = opts.db.select({ name: projects.name }).from(projects).where(
|
|
9677
|
+
const project = opts.db.select({ name: projects.name }).from(projects).where(eq18(projects.id, ctx.projectId)).get();
|
|
9496
9678
|
if (!project) return;
|
|
9497
9679
|
let content;
|
|
9498
9680
|
if (ctx.kind === RunKinds["aeo-discover-probe"]) {
|
|
@@ -9535,6 +9717,11 @@ async function createServer(opts) {
|
|
|
9535
9717
|
app.log.error({ runId, err }, "GBP sync failed");
|
|
9536
9718
|
});
|
|
9537
9719
|
};
|
|
9720
|
+
const runSiteAudit = (runId, projectId, auditOpts) => {
|
|
9721
|
+
executeSiteAudit(opts.db, runId, projectId, auditOpts ?? {}).then(() => runCoordinator.onRunCompleted(runId, projectId)).catch((err) => {
|
|
9722
|
+
app.log.error({ runId, err }, "Site audit failed");
|
|
9723
|
+
});
|
|
9724
|
+
};
|
|
9538
9725
|
const scheduler = new Scheduler(opts.db, {
|
|
9539
9726
|
onRunCreated: (runId, projectId, providers2, location) => {
|
|
9540
9727
|
jobRunner.executeRun(runId, projectId, providers2, location).catch((err) => {
|
|
@@ -9560,8 +9747,8 @@ async function createServer(opts) {
|
|
|
9560
9747
|
});
|
|
9561
9748
|
if (!probed) return;
|
|
9562
9749
|
const alreadySynced = opts.db.select().from(ccReleaseSyncs).where(and13(
|
|
9563
|
-
|
|
9564
|
-
|
|
9750
|
+
eq18(ccReleaseSyncs.release, probed.release),
|
|
9751
|
+
eq18(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
|
|
9565
9752
|
)).limit(1).get();
|
|
9566
9753
|
if (alreadySynced) {
|
|
9567
9754
|
app.log.info({ projectName, release: probed.release }, "Scheduled backlinks sync: already up to date, skipping");
|
|
@@ -9574,6 +9761,9 @@ async function createServer(opts) {
|
|
|
9574
9761
|
);
|
|
9575
9762
|
});
|
|
9576
9763
|
})();
|
|
9764
|
+
},
|
|
9765
|
+
onSiteAuditRequested: (runId, projectId) => {
|
|
9766
|
+
runSiteAudit(runId, projectId);
|
|
9577
9767
|
}
|
|
9578
9768
|
});
|
|
9579
9769
|
const providerSummary = API_ADAPTERS.map((adapter) => ({
|
|
@@ -9691,7 +9881,7 @@ async function createServer(opts) {
|
|
|
9691
9881
|
return removed;
|
|
9692
9882
|
}
|
|
9693
9883
|
};
|
|
9694
|
-
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ??
|
|
9884
|
+
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto18.randomBytes(32).toString("hex");
|
|
9695
9885
|
const googleConnectionStore = {
|
|
9696
9886
|
listConnections: (domain) => listGoogleConnections(opts.config, domain),
|
|
9697
9887
|
getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
|
|
@@ -9737,11 +9927,11 @@ async function createServer(opts) {
|
|
|
9737
9927
|
const apiPrefix = basePath ? `${basePath}api/v1` : "/api/v1";
|
|
9738
9928
|
if (opts.config.apiKey) {
|
|
9739
9929
|
const keyHash = hashApiKey(opts.config.apiKey);
|
|
9740
|
-
const existing = opts.db.select().from(apiKeys).where(
|
|
9930
|
+
const existing = opts.db.select().from(apiKeys).where(eq18(apiKeys.keyHash, keyHash)).get();
|
|
9741
9931
|
if (!existing) {
|
|
9742
9932
|
const prefix = opts.config.apiKey.slice(0, 12);
|
|
9743
9933
|
opts.db.insert(apiKeys).values({
|
|
9744
|
-
id: `key_${
|
|
9934
|
+
id: `key_${crypto18.randomBytes(8).toString("hex")}`,
|
|
9745
9935
|
name: "default",
|
|
9746
9936
|
keyHash,
|
|
9747
9937
|
keyPrefix: prefix,
|
|
@@ -9765,7 +9955,7 @@ async function createServer(opts) {
|
|
|
9765
9955
|
};
|
|
9766
9956
|
const createSession = (apiKeyId) => {
|
|
9767
9957
|
pruneExpiredSessions();
|
|
9768
|
-
const sessionId =
|
|
9958
|
+
const sessionId = crypto18.randomBytes(32).toString("hex");
|
|
9769
9959
|
sessions.set(sessionId, {
|
|
9770
9960
|
apiKeyId,
|
|
9771
9961
|
expiresAt: Date.now() + SESSION_TTL_MS
|
|
@@ -9789,7 +9979,7 @@ async function createServer(opts) {
|
|
|
9789
9979
|
};
|
|
9790
9980
|
const getDefaultApiKey = () => {
|
|
9791
9981
|
if (!opts.config.apiKey) return void 0;
|
|
9792
|
-
return opts.db.select().from(apiKeys).where(
|
|
9982
|
+
return opts.db.select().from(apiKeys).where(eq18(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
|
|
9793
9983
|
};
|
|
9794
9984
|
const createPasswordSession = (reply) => {
|
|
9795
9985
|
const key = getDefaultApiKey();
|
|
@@ -9851,12 +10041,12 @@ async function createServer(opts) {
|
|
|
9851
10041
|
return reply.send({ authenticated: true });
|
|
9852
10042
|
}
|
|
9853
10043
|
if (apiKey) {
|
|
9854
|
-
const key = opts.db.select().from(apiKeys).where(
|
|
10044
|
+
const key = opts.db.select().from(apiKeys).where(eq18(apiKeys.keyHash, hashApiKey(apiKey))).get();
|
|
9855
10045
|
if (!key || key.revokedAt) {
|
|
9856
10046
|
const err2 = authInvalid();
|
|
9857
10047
|
return reply.status(err2.statusCode).send(err2.toJSON());
|
|
9858
10048
|
}
|
|
9859
|
-
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
10049
|
+
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq18(apiKeys.id, key.id)).run();
|
|
9860
10050
|
const sessionId = createSession(key.id);
|
|
9861
10051
|
reply.header("set-cookie", serializeSessionCookie({
|
|
9862
10052
|
name: SESSION_COOKIE_NAME,
|
|
@@ -10010,7 +10200,7 @@ async function createServer(opts) {
|
|
|
10010
10200
|
deps: {
|
|
10011
10201
|
enqueueAutoExtract: ({ projectId, release: r }) => {
|
|
10012
10202
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10013
|
-
const runId =
|
|
10203
|
+
const runId = crypto18.randomUUID();
|
|
10014
10204
|
opts.db.insert(runs).values({
|
|
10015
10205
|
id: runId,
|
|
10016
10206
|
projectId,
|
|
@@ -10048,6 +10238,9 @@ async function createServer(opts) {
|
|
|
10048
10238
|
app.log.error({ runId: input.runId, err }, "Discovery run failed");
|
|
10049
10239
|
});
|
|
10050
10240
|
},
|
|
10241
|
+
onSiteAuditRequested: (runId, projectId, auditOpts) => {
|
|
10242
|
+
runSiteAudit(runId, projectId, auditOpts);
|
|
10243
|
+
},
|
|
10051
10244
|
onBacklinksPruneCache: (release) => {
|
|
10052
10245
|
try {
|
|
10053
10246
|
pruneCachedRelease(release);
|
|
@@ -10093,7 +10286,7 @@ async function createServer(opts) {
|
|
|
10093
10286
|
...inspectOpts,
|
|
10094
10287
|
config: opts.config
|
|
10095
10288
|
}).then(() => {
|
|
10096
|
-
const finished = opts.db.select({ status: runs.status }).from(runs).where(
|
|
10289
|
+
const finished = opts.db.select({ status: runs.status }).from(runs).where(eq18(runs.id, runId)).get();
|
|
10097
10290
|
if (finished?.status === RunStatuses.completed || finished?.status === RunStatuses.partial) {
|
|
10098
10291
|
return maybeRefreshGscCoverage(opts.db, opts.config, projectId);
|
|
10099
10292
|
}
|
|
@@ -10181,7 +10374,7 @@ async function createServer(opts) {
|
|
|
10181
10374
|
const targetProjectIds = affectedProjectIds.length > 0 ? affectedProjectIds : [null];
|
|
10182
10375
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10183
10376
|
opts.db.insert(auditLog).values(targetProjectIds.map((projectId) => ({
|
|
10184
|
-
id:
|
|
10377
|
+
id: crypto18.randomUUID(),
|
|
10185
10378
|
projectId,
|
|
10186
10379
|
actor: "api",
|
|
10187
10380
|
action: existing ? "provider.updated" : "provider.created",
|