@ainyc/canonry 2.4.6 → 2.5.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/README.md +1 -1
- package/assets/agent-workspace/skills/canonry-setup/SKILL.md +3 -3
- package/assets/agent-workspace/skills/canonry-setup/references/canonry-cli.md +2 -0
- package/assets/agent-workspace/skills/canonry-setup/references/indexing.md +8 -0
- package/assets/assets/{index-Nrl3ecFY.js → index-agELvqT1.js} +14 -14
- package/assets/index.html +1 -1
- package/dist/{chunk-6UY2PETG.js → chunk-TIHU2YXW.js} +378 -128
- package/dist/cli.js +90 -9
- package/dist/index.js +1 -1
- package/package.json +7 -7
|
@@ -346,11 +346,11 @@ function printCliError(err, format) {
|
|
|
346
346
|
|
|
347
347
|
// src/server.ts
|
|
348
348
|
import { createRequire as createRequire3 } from "module";
|
|
349
|
-
import
|
|
349
|
+
import crypto28 from "crypto";
|
|
350
350
|
import fs13 from "fs";
|
|
351
351
|
import path15 from "path";
|
|
352
352
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
353
|
-
import { eq as
|
|
353
|
+
import { eq as eq30 } from "drizzle-orm";
|
|
354
354
|
import Fastify from "fastify";
|
|
355
355
|
|
|
356
356
|
// ../contracts/src/config-schema.ts
|
|
@@ -957,6 +957,7 @@ var runKindSchema = z8.enum([
|
|
|
957
957
|
"inspect-sitemap",
|
|
958
958
|
"ga-sync",
|
|
959
959
|
"bing-inspect",
|
|
960
|
+
"bing-inspect-sitemap",
|
|
960
961
|
"backlink-extract"
|
|
961
962
|
]);
|
|
962
963
|
var RunKinds = runKindSchema.enum;
|
|
@@ -5072,6 +5073,31 @@ var routeCatalog = [
|
|
|
5072
5073
|
404: { description: "Project or connection not found." }
|
|
5073
5074
|
}
|
|
5074
5075
|
},
|
|
5076
|
+
{
|
|
5077
|
+
method: "post",
|
|
5078
|
+
path: "/api/v1/projects/{name}/bing/inspect-sitemap",
|
|
5079
|
+
summary: "Inspect every URL in a sitemap through Bing Webmaster Tools",
|
|
5080
|
+
tags: ["bing"],
|
|
5081
|
+
parameters: [nameParameter],
|
|
5082
|
+
requestBody: {
|
|
5083
|
+
required: false,
|
|
5084
|
+
content: {
|
|
5085
|
+
"application/json": {
|
|
5086
|
+
schema: {
|
|
5087
|
+
type: "object",
|
|
5088
|
+
properties: {
|
|
5089
|
+
sitemapUrl: stringSchema
|
|
5090
|
+
}
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
}
|
|
5094
|
+
},
|
|
5095
|
+
responses: {
|
|
5096
|
+
200: { description: "Sitemap inspection run queued." },
|
|
5097
|
+
400: { description: "Bing is not configured for this project." },
|
|
5098
|
+
404: { description: "Project not found." }
|
|
5099
|
+
}
|
|
5100
|
+
},
|
|
5075
5101
|
{
|
|
5076
5102
|
method: "post",
|
|
5077
5103
|
path: "/api/v1/projects/{name}/bing/request-indexing",
|
|
@@ -8509,6 +8535,32 @@ async function bingRoutes(app, opts) {
|
|
|
8509
8535
|
throw e;
|
|
8510
8536
|
}
|
|
8511
8537
|
});
|
|
8538
|
+
app.post("/projects/:name/bing/inspect-sitemap", async (request) => {
|
|
8539
|
+
const store = requireConnectionStore();
|
|
8540
|
+
const project = resolveProject(app.db, request.params.name);
|
|
8541
|
+
const conn = requireConnection(store, project.canonicalDomain);
|
|
8542
|
+
if (!conn.siteUrl) {
|
|
8543
|
+
throw validationError('No Bing site configured. Run "canonry bing set-site <project> <url>" first.');
|
|
8544
|
+
}
|
|
8545
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8546
|
+
const runId = crypto15.randomUUID();
|
|
8547
|
+
app.db.insert(runs).values({
|
|
8548
|
+
id: runId,
|
|
8549
|
+
projectId: project.id,
|
|
8550
|
+
kind: RunKinds["bing-inspect-sitemap"],
|
|
8551
|
+
status: RunStatuses.queued,
|
|
8552
|
+
trigger: RunTriggers.manual,
|
|
8553
|
+
createdAt: now
|
|
8554
|
+
}).run();
|
|
8555
|
+
const { sitemapUrl } = request.body ?? {};
|
|
8556
|
+
if (opts.onInspectSitemapRequested) {
|
|
8557
|
+
opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl: sitemapUrl ?? void 0 });
|
|
8558
|
+
} else {
|
|
8559
|
+
bingLog("warn", "inspect-sitemap.no-callback", { domain: project.canonicalDomain, runId });
|
|
8560
|
+
}
|
|
8561
|
+
const run = app.db.select().from(runs).where(eq15(runs.id, runId)).get();
|
|
8562
|
+
return run;
|
|
8563
|
+
});
|
|
8512
8564
|
app.post("/projects/:name/bing/request-indexing", async (request) => {
|
|
8513
8565
|
const store = requireConnectionStore();
|
|
8514
8566
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -11693,7 +11745,8 @@ async function apiRoutes(app, opts) {
|
|
|
11693
11745
|
setTelemetryEnabled: opts.setTelemetryEnabled
|
|
11694
11746
|
});
|
|
11695
11747
|
await api.register(bingRoutes, {
|
|
11696
|
-
bingConnectionStore: opts.bingConnectionStore
|
|
11748
|
+
bingConnectionStore: opts.bingConnectionStore,
|
|
11749
|
+
onInspectSitemapRequested: opts.onBingInspectSitemapRequested
|
|
11697
11750
|
});
|
|
11698
11751
|
await api.register(googleRoutes, {
|
|
11699
11752
|
getGoogleAuthConfig: opts.getGoogleAuthConfig,
|
|
@@ -15128,11 +15181,197 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
|
|
|
15128
15181
|
}
|
|
15129
15182
|
}
|
|
15130
15183
|
|
|
15131
|
-
// src/
|
|
15184
|
+
// src/bing-inspect-sitemap.ts
|
|
15132
15185
|
import crypto22 from "crypto";
|
|
15186
|
+
import { eq as eq22, desc as desc9 } from "drizzle-orm";
|
|
15187
|
+
var log4 = createLogger("BingInspectSitemap");
|
|
15188
|
+
function parseBingDate2(value) {
|
|
15189
|
+
if (!value) return null;
|
|
15190
|
+
const match = /\/Date\((-?\d+)[^)]*\)\//.exec(value);
|
|
15191
|
+
if (!match) return null;
|
|
15192
|
+
const ms = parseInt(match[1], 10);
|
|
15193
|
+
if (ms <= 0) return null;
|
|
15194
|
+
return new Date(ms).toISOString();
|
|
15195
|
+
}
|
|
15196
|
+
function isBlockingIssueType2(issueType) {
|
|
15197
|
+
if (!issueType) return true;
|
|
15198
|
+
const trimmed = issueType.trim();
|
|
15199
|
+
if (!trimmed) return true;
|
|
15200
|
+
return trimmed.split(/\s+/).some((flag) => !/^(None|Seo(Issues|Concerns))$/i.test(flag));
|
|
15201
|
+
}
|
|
15202
|
+
async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
15203
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
15204
|
+
db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq22(runs.id, runId)).run();
|
|
15205
|
+
try {
|
|
15206
|
+
const project = db.select().from(projects).where(eq22(projects.id, projectId)).get();
|
|
15207
|
+
if (!project) {
|
|
15208
|
+
throw new Error(`Project not found: ${projectId}`);
|
|
15209
|
+
}
|
|
15210
|
+
const conn = opts.config.bing?.connections?.find((c) => c.domain === project.canonicalDomain);
|
|
15211
|
+
if (!conn) {
|
|
15212
|
+
throw new Error('No Bing connection found for this project. Run "canonry bing connect <project>" first.');
|
|
15213
|
+
}
|
|
15214
|
+
if (!conn.siteUrl) {
|
|
15215
|
+
throw new Error('No Bing site configured. Run "canonry bing set-site <project> <url>" first.');
|
|
15216
|
+
}
|
|
15217
|
+
const sitemapUrl = opts.sitemapUrl ?? `https://${project.canonicalDomain}/sitemap.xml`;
|
|
15218
|
+
log4.info("sitemap.fetch", { runId, projectId, sitemapUrl });
|
|
15219
|
+
const sitemapUrls = await fetchAndParseSitemap(sitemapUrl);
|
|
15220
|
+
log4.info("sitemap.parsed", { runId, projectId, urlCount: sitemapUrls.length, sitemapUrl });
|
|
15221
|
+
if (sitemapUrls.length === 0) {
|
|
15222
|
+
throw new Error("No URLs found in sitemap");
|
|
15223
|
+
}
|
|
15224
|
+
const trackedRows = db.select({ url: bingUrlInspections.url }).from(bingUrlInspections).where(eq22(bingUrlInspections.projectId, projectId)).all();
|
|
15225
|
+
const trackedUrls = new Set(trackedRows.map((r) => r.url));
|
|
15226
|
+
const discovered = sitemapUrls.filter((u) => !trackedUrls.has(u));
|
|
15227
|
+
log4.info("sitemap.diff", {
|
|
15228
|
+
runId,
|
|
15229
|
+
projectId,
|
|
15230
|
+
sitemapTotal: sitemapUrls.length,
|
|
15231
|
+
alreadyTracked: sitemapUrls.length - discovered.length,
|
|
15232
|
+
newlyDiscovered: discovered.length
|
|
15233
|
+
});
|
|
15234
|
+
let blockedUrls = /* @__PURE__ */ new Set();
|
|
15235
|
+
try {
|
|
15236
|
+
const issues = await getCrawlIssues(conn.apiKey, conn.siteUrl);
|
|
15237
|
+
for (const issue of issues) {
|
|
15238
|
+
if (issue.Url && isBlockingIssueType2(issue.IssueType ?? null)) {
|
|
15239
|
+
blockedUrls.add(issue.Url);
|
|
15240
|
+
}
|
|
15241
|
+
}
|
|
15242
|
+
log4.info("crawl-issues.loaded", { runId, projectId, blockedCount: blockedUrls.size });
|
|
15243
|
+
} catch (err) {
|
|
15244
|
+
log4.warn("crawl-issues.lookup-failed", {
|
|
15245
|
+
runId,
|
|
15246
|
+
projectId,
|
|
15247
|
+
error: err instanceof Error ? err.message : String(err)
|
|
15248
|
+
});
|
|
15249
|
+
blockedUrls = /* @__PURE__ */ new Set();
|
|
15250
|
+
}
|
|
15251
|
+
let inspected = 0;
|
|
15252
|
+
let errors = 0;
|
|
15253
|
+
for (const pageUrl of sitemapUrls) {
|
|
15254
|
+
try {
|
|
15255
|
+
const result = await getUrlInfo(conn.apiKey, conn.siteUrl, pageUrl);
|
|
15256
|
+
const inspectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
15257
|
+
const httpCode = result.HttpStatus ?? result.HttpCode ?? null;
|
|
15258
|
+
const lastCrawledDate = parseBingDate2(result.LastCrawledDate);
|
|
15259
|
+
const inIndexDate = parseBingDate2(result.InIndexDate);
|
|
15260
|
+
const discoveryDate = parseBingDate2(result.DiscoveryDate);
|
|
15261
|
+
let derivedInIndex = null;
|
|
15262
|
+
if (result.DocumentSize != null && result.DocumentSize > 0) {
|
|
15263
|
+
derivedInIndex = true;
|
|
15264
|
+
} else if (lastCrawledDate != null) {
|
|
15265
|
+
derivedInIndex = httpCode != null && httpCode >= 400 ? false : true;
|
|
15266
|
+
} else if (discoveryDate != null) {
|
|
15267
|
+
derivedInIndex = false;
|
|
15268
|
+
}
|
|
15269
|
+
if (derivedInIndex === true && blockedUrls.has(pageUrl)) {
|
|
15270
|
+
derivedInIndex = false;
|
|
15271
|
+
}
|
|
15272
|
+
db.insert(bingUrlInspections).values({
|
|
15273
|
+
id: crypto22.randomUUID(),
|
|
15274
|
+
projectId,
|
|
15275
|
+
url: pageUrl,
|
|
15276
|
+
httpCode,
|
|
15277
|
+
inIndex: derivedInIndex === true ? 1 : derivedInIndex === false ? 0 : null,
|
|
15278
|
+
lastCrawledDate,
|
|
15279
|
+
inIndexDate,
|
|
15280
|
+
inspectedAt,
|
|
15281
|
+
syncRunId: runId,
|
|
15282
|
+
createdAt: inspectedAt,
|
|
15283
|
+
documentSize: result.DocumentSize ?? null,
|
|
15284
|
+
anchorCount: result.AnchorCount ?? null,
|
|
15285
|
+
discoveryDate
|
|
15286
|
+
}).run();
|
|
15287
|
+
inspected++;
|
|
15288
|
+
log4.info("inspect.url-done", {
|
|
15289
|
+
runId,
|
|
15290
|
+
projectId,
|
|
15291
|
+
url: pageUrl,
|
|
15292
|
+
progress: `${inspected}/${sitemapUrls.length}`
|
|
15293
|
+
});
|
|
15294
|
+
} catch (err) {
|
|
15295
|
+
errors++;
|
|
15296
|
+
log4.error("inspect.url-failed", {
|
|
15297
|
+
runId,
|
|
15298
|
+
projectId,
|
|
15299
|
+
url: pageUrl,
|
|
15300
|
+
error: err instanceof Error ? err.message : String(err)
|
|
15301
|
+
});
|
|
15302
|
+
}
|
|
15303
|
+
if (inspected + errors < sitemapUrls.length) {
|
|
15304
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
15305
|
+
}
|
|
15306
|
+
}
|
|
15307
|
+
const allInspections = db.select().from(bingUrlInspections).where(eq22(bingUrlInspections.projectId, projectId)).orderBy(desc9(bingUrlInspections.inspectedAt)).all();
|
|
15308
|
+
const latestByUrl = /* @__PURE__ */ new Map();
|
|
15309
|
+
const definitiveByUrl = /* @__PURE__ */ new Map();
|
|
15310
|
+
for (const row of allInspections) {
|
|
15311
|
+
if (!latestByUrl.has(row.url)) latestByUrl.set(row.url, row);
|
|
15312
|
+
if (!definitiveByUrl.has(row.url) && row.inIndex != null) definitiveByUrl.set(row.url, row);
|
|
15313
|
+
}
|
|
15314
|
+
for (const [url, latest] of latestByUrl) {
|
|
15315
|
+
if (latest.inIndex == null) {
|
|
15316
|
+
const def = definitiveByUrl.get(url);
|
|
15317
|
+
if (def) latestByUrl.set(url, def);
|
|
15318
|
+
}
|
|
15319
|
+
}
|
|
15320
|
+
let snapIndexed = 0;
|
|
15321
|
+
let snapNotIndexed = 0;
|
|
15322
|
+
let snapUnknown = 0;
|
|
15323
|
+
for (const [, row] of latestByUrl) {
|
|
15324
|
+
if (row.inIndex === 1) snapIndexed++;
|
|
15325
|
+
else if (row.inIndex === 0) snapNotIndexed++;
|
|
15326
|
+
else snapUnknown++;
|
|
15327
|
+
}
|
|
15328
|
+
const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
15329
|
+
const snapNow = (/* @__PURE__ */ new Date()).toISOString();
|
|
15330
|
+
db.insert(bingCoverageSnapshots).values({
|
|
15331
|
+
id: crypto22.randomUUID(),
|
|
15332
|
+
projectId,
|
|
15333
|
+
syncRunId: runId,
|
|
15334
|
+
date: snapshotDate,
|
|
15335
|
+
indexed: snapIndexed,
|
|
15336
|
+
notIndexed: snapNotIndexed,
|
|
15337
|
+
unknown: snapUnknown,
|
|
15338
|
+
createdAt: snapNow
|
|
15339
|
+
}).onConflictDoUpdate({
|
|
15340
|
+
target: [bingCoverageSnapshots.projectId, bingCoverageSnapshots.date],
|
|
15341
|
+
set: {
|
|
15342
|
+
indexed: snapIndexed,
|
|
15343
|
+
notIndexed: snapNotIndexed,
|
|
15344
|
+
unknown: snapUnknown,
|
|
15345
|
+
createdAt: snapNow,
|
|
15346
|
+
syncRunId: runId
|
|
15347
|
+
}
|
|
15348
|
+
}).run();
|
|
15349
|
+
const status = errors === sitemapUrls.length ? RunStatuses.failed : errors > 0 ? RunStatuses.partial : RunStatuses.completed;
|
|
15350
|
+
db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq22(runs.id, runId)).run();
|
|
15351
|
+
log4.info("inspect.completed", {
|
|
15352
|
+
runId,
|
|
15353
|
+
projectId,
|
|
15354
|
+
inspected,
|
|
15355
|
+
errors,
|
|
15356
|
+
total: sitemapUrls.length,
|
|
15357
|
+
newlyDiscovered: discovered.length,
|
|
15358
|
+
indexed: snapIndexed,
|
|
15359
|
+
notIndexed: snapNotIndexed,
|
|
15360
|
+
unknown: snapUnknown
|
|
15361
|
+
});
|
|
15362
|
+
} catch (err) {
|
|
15363
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
15364
|
+
db.update(runs).set({ status: RunStatuses.failed, error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq22(runs.id, runId)).run();
|
|
15365
|
+
log4.error("inspect.failed", { runId, projectId, error: errorMsg });
|
|
15366
|
+
throw err;
|
|
15367
|
+
}
|
|
15368
|
+
}
|
|
15369
|
+
|
|
15370
|
+
// src/commoncrawl-sync.ts
|
|
15371
|
+
import crypto23 from "crypto";
|
|
15133
15372
|
import path11 from "path";
|
|
15134
|
-
import { and as and11, eq as
|
|
15135
|
-
var
|
|
15373
|
+
import { and as and11, eq as eq23, sql as sql8 } from "drizzle-orm";
|
|
15374
|
+
var log5 = createLogger("CommonCrawlSync");
|
|
15136
15375
|
var INSERT_CHUNK_SIZE = 1e4;
|
|
15137
15376
|
function defaultDeps() {
|
|
15138
15377
|
return {
|
|
@@ -15157,7 +15396,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
15157
15396
|
phaseDetail: "downloading vertices + edges",
|
|
15158
15397
|
updatedAt: downloadStartedAt,
|
|
15159
15398
|
error: null
|
|
15160
|
-
}).where(
|
|
15399
|
+
}).where(eq23(ccReleaseSyncs.id, syncId)).run();
|
|
15161
15400
|
const paths = ccReleasePaths(release);
|
|
15162
15401
|
const releaseCacheDir = path11.join(deps.cacheDir, release);
|
|
15163
15402
|
const vertexPath = path11.join(releaseCacheDir, paths.vertexFilename);
|
|
@@ -15180,7 +15419,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
15180
15419
|
vertexSha256: vertex.sha256,
|
|
15181
15420
|
edgesSha256: edges.sha256,
|
|
15182
15421
|
updatedAt: downloadFinishedAt
|
|
15183
|
-
}).where(
|
|
15422
|
+
}).where(eq23(ccReleaseSyncs.id, syncId)).run();
|
|
15184
15423
|
const allProjects = db.select().from(projects).all();
|
|
15185
15424
|
const targets = Array.from(new Set(allProjects.map((p) => p.canonicalDomain)));
|
|
15186
15425
|
let rows = [];
|
|
@@ -15196,15 +15435,15 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
15196
15435
|
}
|
|
15197
15436
|
const queriedAt = deps.now().toISOString();
|
|
15198
15437
|
db.transaction((tx) => {
|
|
15199
|
-
tx.delete(backlinkDomains).where(
|
|
15200
|
-
tx.delete(backlinkSummaries).where(
|
|
15438
|
+
tx.delete(backlinkDomains).where(eq23(backlinkDomains.releaseSyncId, syncId)).run();
|
|
15439
|
+
tx.delete(backlinkSummaries).where(eq23(backlinkSummaries.releaseSyncId, syncId)).run();
|
|
15201
15440
|
const expanded = [];
|
|
15202
15441
|
for (const r of rows) {
|
|
15203
15442
|
const projectIds = projectsByDomain.get(r.targetDomain);
|
|
15204
15443
|
if (!projectIds) continue;
|
|
15205
15444
|
for (const projectId of projectIds) {
|
|
15206
15445
|
expanded.push({
|
|
15207
|
-
id:
|
|
15446
|
+
id: crypto23.randomUUID(),
|
|
15208
15447
|
projectId,
|
|
15209
15448
|
releaseSyncId: syncId,
|
|
15210
15449
|
release,
|
|
@@ -15224,7 +15463,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
15224
15463
|
const projectRows = rowsByProject.get(p.id) ?? [];
|
|
15225
15464
|
const summary = computeSummary(projectRows);
|
|
15226
15465
|
tx.insert(backlinkSummaries).values({
|
|
15227
|
-
id:
|
|
15466
|
+
id: crypto23.randomUUID(),
|
|
15228
15467
|
projectId: p.id,
|
|
15229
15468
|
releaseSyncId: syncId,
|
|
15230
15469
|
release,
|
|
@@ -15256,8 +15495,8 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
15256
15495
|
domainsDiscovered: rows.length,
|
|
15257
15496
|
updatedAt: finishedAt,
|
|
15258
15497
|
error: null
|
|
15259
|
-
}).where(
|
|
15260
|
-
|
|
15498
|
+
}).where(eq23(ccReleaseSyncs.id, syncId)).run();
|
|
15499
|
+
log5.info("sync.completed", {
|
|
15261
15500
|
syncId,
|
|
15262
15501
|
release,
|
|
15263
15502
|
projectsProcessed: allProjects.length,
|
|
@@ -15269,7 +15508,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
15269
15508
|
try {
|
|
15270
15509
|
deps.enqueueAutoExtract({ projectId: p.id, release });
|
|
15271
15510
|
} catch (err) {
|
|
15272
|
-
|
|
15511
|
+
log5.error("auto-extract.enqueue-failed", {
|
|
15273
15512
|
syncId,
|
|
15274
15513
|
release,
|
|
15275
15514
|
projectId: p.id,
|
|
@@ -15286,8 +15525,8 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
15286
15525
|
error: errorMsg,
|
|
15287
15526
|
phaseDetail: null,
|
|
15288
15527
|
updatedAt: finishedAt
|
|
15289
|
-
}).where(
|
|
15290
|
-
|
|
15528
|
+
}).where(eq23(ccReleaseSyncs.id, syncId)).run();
|
|
15529
|
+
log5.error("sync.failed", { syncId, release, error: errorMsg });
|
|
15291
15530
|
throw err;
|
|
15292
15531
|
}
|
|
15293
15532
|
}
|
|
@@ -15320,10 +15559,10 @@ function computeSummary(rows) {
|
|
|
15320
15559
|
}
|
|
15321
15560
|
|
|
15322
15561
|
// src/backlink-extract.ts
|
|
15323
|
-
import
|
|
15562
|
+
import crypto24 from "crypto";
|
|
15324
15563
|
import fs9 from "fs";
|
|
15325
|
-
import { and as and12, desc as
|
|
15326
|
-
var
|
|
15564
|
+
import { and as and12, desc as desc10, eq as eq24 } from "drizzle-orm";
|
|
15565
|
+
var log6 = createLogger("BacklinkExtract");
|
|
15327
15566
|
function defaultDeps2() {
|
|
15328
15567
|
return {
|
|
15329
15568
|
queryBacklinks,
|
|
@@ -15334,13 +15573,13 @@ function defaultDeps2() {
|
|
|
15334
15573
|
async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
15335
15574
|
const deps = { ...defaultDeps2(), ...opts.deps };
|
|
15336
15575
|
const startedAt = deps.now().toISOString();
|
|
15337
|
-
db.update(runs).set({ status: RunStatuses.running, startedAt }).where(
|
|
15576
|
+
db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq24(runs.id, runId)).run();
|
|
15338
15577
|
try {
|
|
15339
|
-
const project = db.select().from(projects).where(
|
|
15578
|
+
const project = db.select().from(projects).where(eq24(projects.id, projectId)).get();
|
|
15340
15579
|
if (!project) {
|
|
15341
15580
|
throw new Error(`Project not found: ${projectId}`);
|
|
15342
15581
|
}
|
|
15343
|
-
const sync = opts.release ? db.select().from(ccReleaseSyncs).where(
|
|
15582
|
+
const sync = opts.release ? db.select().from(ccReleaseSyncs).where(eq24(ccReleaseSyncs.release, opts.release)).get() : db.select().from(ccReleaseSyncs).where(eq24(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).orderBy(desc10(ccReleaseSyncs.createdAt)).limit(1).get();
|
|
15344
15583
|
if (!sync) {
|
|
15345
15584
|
throw new Error("No ready release sync available \u2014 run `canonry backlinks sync` first");
|
|
15346
15585
|
}
|
|
@@ -15368,11 +15607,11 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
15368
15607
|
const targetDomain = project.canonicalDomain;
|
|
15369
15608
|
db.transaction((tx) => {
|
|
15370
15609
|
tx.delete(backlinkDomains).where(
|
|
15371
|
-
and12(
|
|
15610
|
+
and12(eq24(backlinkDomains.projectId, projectId), eq24(backlinkDomains.release, release))
|
|
15372
15611
|
).run();
|
|
15373
15612
|
if (rows.length > 0) {
|
|
15374
15613
|
const values = rows.map((r) => ({
|
|
15375
|
-
id:
|
|
15614
|
+
id: crypto24.randomUUID(),
|
|
15376
15615
|
projectId,
|
|
15377
15616
|
releaseSyncId: syncId,
|
|
15378
15617
|
release,
|
|
@@ -15385,7 +15624,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
15385
15624
|
}
|
|
15386
15625
|
const summary = computeSummary2(rows);
|
|
15387
15626
|
tx.insert(backlinkSummaries).values({
|
|
15388
|
-
id:
|
|
15627
|
+
id: crypto24.randomUUID(),
|
|
15389
15628
|
projectId,
|
|
15390
15629
|
releaseSyncId: syncId,
|
|
15391
15630
|
release,
|
|
@@ -15408,8 +15647,8 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
15408
15647
|
}).run();
|
|
15409
15648
|
});
|
|
15410
15649
|
const finishedAt = deps.now().toISOString();
|
|
15411
|
-
db.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(
|
|
15412
|
-
|
|
15650
|
+
db.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq24(runs.id, runId)).run();
|
|
15651
|
+
log6.info("extract.completed", { runId, projectId, release, rows: rows.length });
|
|
15413
15652
|
} catch (err) {
|
|
15414
15653
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
15415
15654
|
const finishedAt = deps.now().toISOString();
|
|
@@ -15417,8 +15656,8 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
15417
15656
|
status: RunStatuses.failed,
|
|
15418
15657
|
error: errorMsg,
|
|
15419
15658
|
finishedAt
|
|
15420
|
-
}).where(
|
|
15421
|
-
|
|
15659
|
+
}).where(eq24(runs.id, runId)).run();
|
|
15660
|
+
log6.error("extract.failed", { runId, projectId, error: errorMsg });
|
|
15422
15661
|
throw err;
|
|
15423
15662
|
}
|
|
15424
15663
|
}
|
|
@@ -15490,8 +15729,8 @@ var ProviderRegistry = class {
|
|
|
15490
15729
|
|
|
15491
15730
|
// src/scheduler.ts
|
|
15492
15731
|
import cron from "node-cron";
|
|
15493
|
-
import { eq as
|
|
15494
|
-
var
|
|
15732
|
+
import { eq as eq25 } from "drizzle-orm";
|
|
15733
|
+
var log7 = createLogger("Scheduler");
|
|
15495
15734
|
var Scheduler = class {
|
|
15496
15735
|
db;
|
|
15497
15736
|
callbacks;
|
|
@@ -15502,16 +15741,16 @@ var Scheduler = class {
|
|
|
15502
15741
|
}
|
|
15503
15742
|
/** Load all enabled schedules from DB and register cron jobs. */
|
|
15504
15743
|
start() {
|
|
15505
|
-
const allSchedules = this.db.select().from(schedules).where(
|
|
15744
|
+
const allSchedules = this.db.select().from(schedules).where(eq25(schedules.enabled, 1)).all();
|
|
15506
15745
|
for (const schedule of allSchedules) {
|
|
15507
15746
|
const missedRunAt = schedule.nextRunAt;
|
|
15508
15747
|
this.registerCronTask(schedule);
|
|
15509
15748
|
if (missedRunAt && new Date(missedRunAt) < /* @__PURE__ */ new Date()) {
|
|
15510
|
-
|
|
15749
|
+
log7.info("run.catch-up", { projectId: schedule.projectId, missedRunAt });
|
|
15511
15750
|
this.triggerRun(schedule.id, schedule.projectId);
|
|
15512
15751
|
}
|
|
15513
15752
|
}
|
|
15514
|
-
|
|
15753
|
+
log7.info("started", { scheduleCount: allSchedules.length });
|
|
15515
15754
|
}
|
|
15516
15755
|
/** Stop all cron tasks for graceful shutdown. */
|
|
15517
15756
|
stop() {
|
|
@@ -15527,7 +15766,7 @@ var Scheduler = class {
|
|
|
15527
15766
|
this.stopTask(projectId, existing, "Stopped");
|
|
15528
15767
|
this.tasks.delete(projectId);
|
|
15529
15768
|
}
|
|
15530
|
-
const schedule = this.db.select().from(schedules).where(
|
|
15769
|
+
const schedule = this.db.select().from(schedules).where(eq25(schedules.projectId, projectId)).get();
|
|
15531
15770
|
if (schedule && schedule.enabled === 1) {
|
|
15532
15771
|
this.registerCronTask(schedule);
|
|
15533
15772
|
}
|
|
@@ -15543,12 +15782,12 @@ var Scheduler = class {
|
|
|
15543
15782
|
stopTask(projectId, task, verb) {
|
|
15544
15783
|
task.stop();
|
|
15545
15784
|
task.destroy();
|
|
15546
|
-
|
|
15785
|
+
log7.info(`task.${verb.toLowerCase()}`, { projectId });
|
|
15547
15786
|
}
|
|
15548
15787
|
registerCronTask(schedule) {
|
|
15549
15788
|
const { id: scheduleId, projectId, cronExpr, timezone } = schedule;
|
|
15550
15789
|
if (!cron.validate(cronExpr)) {
|
|
15551
|
-
|
|
15790
|
+
log7.error("cron.invalid", { projectId, cronExpr });
|
|
15552
15791
|
return;
|
|
15553
15792
|
}
|
|
15554
15793
|
const task = cron.schedule(cronExpr, () => {
|
|
@@ -15560,24 +15799,24 @@ var Scheduler = class {
|
|
|
15560
15799
|
this.db.update(schedules).set({
|
|
15561
15800
|
nextRunAt: task.getNextRun()?.toISOString() ?? null,
|
|
15562
15801
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15563
|
-
}).where(
|
|
15802
|
+
}).where(eq25(schedules.id, scheduleId)).run();
|
|
15564
15803
|
const label = schedule.preset ?? cronExpr;
|
|
15565
|
-
|
|
15804
|
+
log7.info("cron.registered", { projectId, schedule: label, timezone });
|
|
15566
15805
|
}
|
|
15567
15806
|
triggerRun(scheduleId, projectId) {
|
|
15568
15807
|
try {
|
|
15569
15808
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15570
|
-
const currentSchedule = this.db.select().from(schedules).where(
|
|
15809
|
+
const currentSchedule = this.db.select().from(schedules).where(eq25(schedules.id, scheduleId)).get();
|
|
15571
15810
|
if (!currentSchedule || currentSchedule.enabled !== 1) {
|
|
15572
|
-
|
|
15811
|
+
log7.warn("schedule.stale", { scheduleId, projectId, msg: "schedule no longer exists or is disabled" });
|
|
15573
15812
|
this.remove(projectId);
|
|
15574
15813
|
return;
|
|
15575
15814
|
}
|
|
15576
15815
|
const task = this.tasks.get(projectId);
|
|
15577
15816
|
const nextRunAt = task?.getNextRun()?.toISOString() ?? null;
|
|
15578
|
-
const project = this.db.select().from(projects).where(
|
|
15817
|
+
const project = this.db.select().from(projects).where(eq25(projects.id, projectId)).get();
|
|
15579
15818
|
if (!project) {
|
|
15580
|
-
|
|
15819
|
+
log7.error("project.not-found", { projectId, msg: "skipping scheduled run" });
|
|
15581
15820
|
this.remove(projectId);
|
|
15582
15821
|
return;
|
|
15583
15822
|
}
|
|
@@ -15586,7 +15825,7 @@ var Scheduler = class {
|
|
|
15586
15825
|
if (project.defaultLocation) {
|
|
15587
15826
|
const loc = projectLocations.find((l) => l.label === project.defaultLocation);
|
|
15588
15827
|
if (!loc) {
|
|
15589
|
-
|
|
15828
|
+
log7.warn("default-location.stale", { scheduleId, projectId, label: project.defaultLocation });
|
|
15590
15829
|
return;
|
|
15591
15830
|
}
|
|
15592
15831
|
resolvedLocation = loc;
|
|
@@ -15600,11 +15839,11 @@ var Scheduler = class {
|
|
|
15600
15839
|
location: locationLabel
|
|
15601
15840
|
});
|
|
15602
15841
|
if (queueResult.conflict) {
|
|
15603
|
-
|
|
15842
|
+
log7.info("run.skipped-active", { projectName: project.name, activeRunId: queueResult.activeRunId });
|
|
15604
15843
|
this.db.update(schedules).set({
|
|
15605
15844
|
nextRunAt,
|
|
15606
15845
|
updatedAt: now
|
|
15607
|
-
}).where(
|
|
15846
|
+
}).where(eq25(schedules.id, currentSchedule.id)).run();
|
|
15608
15847
|
return;
|
|
15609
15848
|
}
|
|
15610
15849
|
const runId = queueResult.runId;
|
|
@@ -15612,21 +15851,21 @@ var Scheduler = class {
|
|
|
15612
15851
|
lastRunAt: now,
|
|
15613
15852
|
nextRunAt,
|
|
15614
15853
|
updatedAt: now
|
|
15615
|
-
}).where(
|
|
15854
|
+
}).where(eq25(schedules.id, currentSchedule.id)).run();
|
|
15616
15855
|
const scheduleProviders = parseJsonColumn(currentSchedule.providers, []);
|
|
15617
15856
|
const providers = scheduleProviders.length > 0 ? scheduleProviders : void 0;
|
|
15618
|
-
|
|
15857
|
+
log7.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
|
|
15619
15858
|
this.callbacks.onRunCreated(runId, projectId, providers, resolvedLocation);
|
|
15620
15859
|
} catch (err) {
|
|
15621
|
-
|
|
15860
|
+
log7.error("trigger.error", { scheduleId, projectId, error: err instanceof Error ? err.message : String(err) });
|
|
15622
15861
|
}
|
|
15623
15862
|
}
|
|
15624
15863
|
};
|
|
15625
15864
|
|
|
15626
15865
|
// src/notifier.ts
|
|
15627
|
-
import { eq as
|
|
15628
|
-
import
|
|
15629
|
-
var
|
|
15866
|
+
import { eq as eq26, desc as desc11, and as and13, or as or2 } from "drizzle-orm";
|
|
15867
|
+
import crypto25 from "crypto";
|
|
15868
|
+
var log8 = createLogger("Notifier");
|
|
15630
15869
|
var Notifier = class {
|
|
15631
15870
|
db;
|
|
15632
15871
|
serverUrl;
|
|
@@ -15636,26 +15875,26 @@ var Notifier = class {
|
|
|
15636
15875
|
}
|
|
15637
15876
|
/** Called after a run completes (success, partial, or failed). */
|
|
15638
15877
|
async onRunCompleted(runId, projectId) {
|
|
15639
|
-
|
|
15640
|
-
const notifs = this.db.select().from(notifications).where(
|
|
15878
|
+
log8.info("run.completed", { runId, projectId });
|
|
15879
|
+
const notifs = this.db.select().from(notifications).where(eq26(notifications.projectId, projectId)).all().filter((n) => n.enabled === 1);
|
|
15641
15880
|
if (notifs.length === 0) {
|
|
15642
|
-
|
|
15881
|
+
log8.info("notifications.none-enabled", { projectId });
|
|
15643
15882
|
return;
|
|
15644
15883
|
}
|
|
15645
|
-
|
|
15646
|
-
const run = this.db.select().from(runs).where(
|
|
15884
|
+
log8.info("notifications.found", { projectId, count: notifs.length });
|
|
15885
|
+
const run = this.db.select().from(runs).where(eq26(runs.id, runId)).get();
|
|
15647
15886
|
if (!run) {
|
|
15648
|
-
|
|
15887
|
+
log8.error("run.not-found", { runId, msg: "skipping notification dispatch" });
|
|
15649
15888
|
return;
|
|
15650
15889
|
}
|
|
15651
|
-
const project = this.db.select().from(projects).where(
|
|
15890
|
+
const project = this.db.select().from(projects).where(eq26(projects.id, projectId)).get();
|
|
15652
15891
|
if (!project) {
|
|
15653
|
-
|
|
15892
|
+
log8.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
|
|
15654
15893
|
return;
|
|
15655
15894
|
}
|
|
15656
15895
|
const transitions = this.computeTransitions(runId, projectId);
|
|
15657
15896
|
const events = [];
|
|
15658
|
-
|
|
15897
|
+
log8.info("run.status", { runId: run.id, status: run.status, projectId });
|
|
15659
15898
|
if (run.status === "completed" || run.status === "partial") {
|
|
15660
15899
|
events.push("run.completed");
|
|
15661
15900
|
}
|
|
@@ -15671,7 +15910,7 @@ var Notifier = class {
|
|
|
15671
15910
|
if (!config.url) continue;
|
|
15672
15911
|
const subscribedEvents = config.events;
|
|
15673
15912
|
const matchingEvents = events.filter((e) => subscribedEvents.includes(e));
|
|
15674
|
-
|
|
15913
|
+
log8.info("notification.match", { notificationId: notif.id, subscribedEvents, matchedEvents: matchingEvents });
|
|
15675
15914
|
if (matchingEvents.length === 0) continue;
|
|
15676
15915
|
for (const event of matchingEvents) {
|
|
15677
15916
|
const relevantTransitions = event === "citation.lost" ? lostTransitions : event === "citation.gained" ? gainedTransitions : transitions;
|
|
@@ -15695,11 +15934,11 @@ var Notifier = class {
|
|
|
15695
15934
|
if (criticalInsights.length > 0) insightEvents.push("insight.critical");
|
|
15696
15935
|
if (highInsights.length > 0) insightEvents.push("insight.high");
|
|
15697
15936
|
if (insightEvents.length === 0) return;
|
|
15698
|
-
const notifs = this.db.select().from(notifications).where(
|
|
15937
|
+
const notifs = this.db.select().from(notifications).where(eq26(notifications.projectId, projectId)).all().filter((n) => n.enabled === 1);
|
|
15699
15938
|
if (notifs.length === 0) return;
|
|
15700
|
-
const run = this.db.select().from(runs).where(
|
|
15939
|
+
const run = this.db.select().from(runs).where(eq26(runs.id, runId)).get();
|
|
15701
15940
|
if (!run) return;
|
|
15702
|
-
const project = this.db.select().from(projects).where(
|
|
15941
|
+
const project = this.db.select().from(projects).where(eq26(projects.id, projectId)).get();
|
|
15703
15942
|
if (!project) return;
|
|
15704
15943
|
for (const notif of notifs) {
|
|
15705
15944
|
const config = parseJsonColumn(notif.config, { url: "", events: [] });
|
|
@@ -15731,10 +15970,10 @@ var Notifier = class {
|
|
|
15731
15970
|
computeTransitions(runId, projectId) {
|
|
15732
15971
|
const recentRuns = this.db.select().from(runs).where(
|
|
15733
15972
|
and13(
|
|
15734
|
-
|
|
15735
|
-
or2(
|
|
15973
|
+
eq26(runs.projectId, projectId),
|
|
15974
|
+
or2(eq26(runs.status, "completed"), eq26(runs.status, "partial"))
|
|
15736
15975
|
)
|
|
15737
|
-
).orderBy(
|
|
15976
|
+
).orderBy(desc11(runs.createdAt)).limit(2).all();
|
|
15738
15977
|
if (recentRuns.length < 2) return [];
|
|
15739
15978
|
const currentRunId = recentRuns[0].id;
|
|
15740
15979
|
const previousRunId = recentRuns[1].id;
|
|
@@ -15744,12 +15983,12 @@ var Notifier = class {
|
|
|
15744
15983
|
keyword: keywords.keyword,
|
|
15745
15984
|
provider: querySnapshots.provider,
|
|
15746
15985
|
citationState: querySnapshots.citationState
|
|
15747
|
-
}).from(querySnapshots).leftJoin(keywords,
|
|
15986
|
+
}).from(querySnapshots).leftJoin(keywords, eq26(querySnapshots.keywordId, keywords.id)).where(eq26(querySnapshots.runId, currentRunId)).all();
|
|
15748
15987
|
const previousSnapshots = this.db.select({
|
|
15749
15988
|
keywordId: querySnapshots.keywordId,
|
|
15750
15989
|
provider: querySnapshots.provider,
|
|
15751
15990
|
citationState: querySnapshots.citationState
|
|
15752
|
-
}).from(querySnapshots).where(
|
|
15991
|
+
}).from(querySnapshots).where(eq26(querySnapshots.runId, previousRunId)).all();
|
|
15753
15992
|
const prevMap = /* @__PURE__ */ new Map();
|
|
15754
15993
|
for (const s of previousSnapshots) {
|
|
15755
15994
|
prevMap.set(`${s.keywordId}:${s.provider}`, s.citationState);
|
|
@@ -15773,23 +16012,23 @@ var Notifier = class {
|
|
|
15773
16012
|
const targetLabel = redactNotificationUrl(url).urlDisplay;
|
|
15774
16013
|
const targetCheck = await resolveWebhookTarget(url);
|
|
15775
16014
|
if (!targetCheck.ok) {
|
|
15776
|
-
|
|
16015
|
+
log8.error("webhook.ssrf-blocked", { url: targetLabel, reason: targetCheck.message });
|
|
15777
16016
|
this.logDelivery(projectId, notificationId, payload.event, "failed", `SSRF: ${targetCheck.message}`);
|
|
15778
16017
|
return;
|
|
15779
16018
|
}
|
|
15780
|
-
|
|
16019
|
+
log8.info("webhook.send", { event: payload.event, url: targetLabel });
|
|
15781
16020
|
const maxRetries = 3;
|
|
15782
16021
|
const delays = [1e3, 4e3, 16e3];
|
|
15783
16022
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
15784
16023
|
try {
|
|
15785
16024
|
const response = await deliverWebhook(targetCheck.target, payload, webhookSecret);
|
|
15786
16025
|
if (response.status >= 200 && response.status < 300) {
|
|
15787
|
-
|
|
16026
|
+
log8.info("webhook.delivered", { event: payload.event, url: targetLabel, httpStatus: response.status });
|
|
15788
16027
|
this.logDelivery(projectId, notificationId, payload.event, "sent", null);
|
|
15789
16028
|
return;
|
|
15790
16029
|
}
|
|
15791
16030
|
const errorDetail = response.error ?? `HTTP ${response.status}`;
|
|
15792
|
-
|
|
16031
|
+
log8.warn("webhook.attempt-failed", { event: payload.event, url: targetLabel, attempt: attempt + 1, maxRetries, httpStatus: response.status, error: errorDetail });
|
|
15793
16032
|
if (attempt === maxRetries - 1) {
|
|
15794
16033
|
this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
|
|
15795
16034
|
}
|
|
@@ -15797,7 +16036,7 @@ var Notifier = class {
|
|
|
15797
16036
|
const errorDetail = err instanceof Error ? err.message : String(err);
|
|
15798
16037
|
if (attempt === maxRetries - 1) {
|
|
15799
16038
|
this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
|
|
15800
|
-
|
|
16039
|
+
log8.error("webhook.exhausted", { event: payload.event, url: targetLabel, maxRetries, error: errorDetail });
|
|
15801
16040
|
}
|
|
15802
16041
|
}
|
|
15803
16042
|
if (attempt < maxRetries - 1) {
|
|
@@ -15807,7 +16046,7 @@ var Notifier = class {
|
|
|
15807
16046
|
}
|
|
15808
16047
|
logDelivery(projectId, notificationId, event, status, error) {
|
|
15809
16048
|
this.db.insert(auditLog).values({
|
|
15810
|
-
id:
|
|
16049
|
+
id: crypto25.randomUUID(),
|
|
15811
16050
|
projectId,
|
|
15812
16051
|
actor: "scheduler",
|
|
15813
16052
|
action: `notification.${status}`,
|
|
@@ -15820,7 +16059,7 @@ var Notifier = class {
|
|
|
15820
16059
|
};
|
|
15821
16060
|
|
|
15822
16061
|
// src/run-coordinator.ts
|
|
15823
|
-
var
|
|
16062
|
+
var log9 = createLogger("RunCoordinator");
|
|
15824
16063
|
var RunCoordinator = class {
|
|
15825
16064
|
constructor(notifier, intelligenceService, onInsightsGenerated, onAeroEvent) {
|
|
15826
16065
|
this.notifier = notifier;
|
|
@@ -15842,31 +16081,31 @@ var RunCoordinator = class {
|
|
|
15842
16081
|
try {
|
|
15843
16082
|
await this.onInsightsGenerated(runId, projectId, result);
|
|
15844
16083
|
} catch (err) {
|
|
15845
|
-
|
|
16084
|
+
log9.error("insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
15846
16085
|
}
|
|
15847
16086
|
}
|
|
15848
16087
|
}
|
|
15849
16088
|
} catch (err) {
|
|
15850
|
-
|
|
16089
|
+
log9.error("intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
15851
16090
|
}
|
|
15852
16091
|
try {
|
|
15853
16092
|
await this.notifier.onRunCompleted(runId, projectId);
|
|
15854
16093
|
} catch (err) {
|
|
15855
|
-
|
|
16094
|
+
log9.error("notifier.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
15856
16095
|
}
|
|
15857
16096
|
if (this.onAeroEvent) {
|
|
15858
16097
|
try {
|
|
15859
16098
|
await this.onAeroEvent({ runId, projectId, insightCount, criticalOrHigh });
|
|
15860
16099
|
} catch (err) {
|
|
15861
|
-
|
|
16100
|
+
log9.error("aero.failed", { runId, error: err instanceof Error ? err.message : String(err) });
|
|
15862
16101
|
}
|
|
15863
16102
|
}
|
|
15864
16103
|
}
|
|
15865
16104
|
};
|
|
15866
16105
|
|
|
15867
16106
|
// src/agent/session-registry.ts
|
|
15868
|
-
import
|
|
15869
|
-
import { eq as
|
|
16107
|
+
import crypto27 from "crypto";
|
|
16108
|
+
import { eq as eq28 } from "drizzle-orm";
|
|
15870
16109
|
|
|
15871
16110
|
// src/agent/session.ts
|
|
15872
16111
|
import fs12 from "fs";
|
|
@@ -16085,8 +16324,8 @@ function buildSkillDocTools() {
|
|
|
16085
16324
|
import { Type as Type2 } from "@sinclair/typebox";
|
|
16086
16325
|
|
|
16087
16326
|
// src/agent/memory-store.ts
|
|
16088
|
-
import
|
|
16089
|
-
import { and as and14, desc as
|
|
16327
|
+
import crypto26 from "crypto";
|
|
16328
|
+
import { and as and14, desc as desc12, eq as eq27, like, sql as sql9 } from "drizzle-orm";
|
|
16090
16329
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
16091
16330
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
16092
16331
|
function rowToDto(row) {
|
|
@@ -16100,7 +16339,7 @@ function rowToDto(row) {
|
|
|
16100
16339
|
};
|
|
16101
16340
|
}
|
|
16102
16341
|
function listMemoryEntries(db, projectId, opts = {}) {
|
|
16103
|
-
const query = db.select().from(agentMemory).where(
|
|
16342
|
+
const query = db.select().from(agentMemory).where(eq27(agentMemory.projectId, projectId)).orderBy(desc12(agentMemory.updatedAt));
|
|
16104
16343
|
const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
|
|
16105
16344
|
return rows.map(rowToDto);
|
|
16106
16345
|
}
|
|
@@ -16114,7 +16353,7 @@ function upsertMemoryEntry(db, args) {
|
|
|
16114
16353
|
throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
|
|
16115
16354
|
}
|
|
16116
16355
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16117
|
-
const id =
|
|
16356
|
+
const id = crypto26.randomUUID();
|
|
16118
16357
|
db.insert(agentMemory).values({
|
|
16119
16358
|
id,
|
|
16120
16359
|
projectId: args.projectId,
|
|
@@ -16131,12 +16370,12 @@ function upsertMemoryEntry(db, args) {
|
|
|
16131
16370
|
updatedAt: now
|
|
16132
16371
|
}
|
|
16133
16372
|
}).run();
|
|
16134
|
-
const row = db.select().from(agentMemory).where(and14(
|
|
16373
|
+
const row = db.select().from(agentMemory).where(and14(eq27(agentMemory.projectId, args.projectId), eq27(agentMemory.key, args.key))).get();
|
|
16135
16374
|
if (!row) throw new Error("memory upsert produced no row");
|
|
16136
16375
|
return rowToDto(row);
|
|
16137
16376
|
}
|
|
16138
16377
|
function deleteMemoryEntry(db, projectId, key) {
|
|
16139
|
-
const result = db.delete(agentMemory).where(and14(
|
|
16378
|
+
const result = db.delete(agentMemory).where(and14(eq27(agentMemory.projectId, projectId), eq27(agentMemory.key, key))).run();
|
|
16140
16379
|
const changes = result.changes ?? 0;
|
|
16141
16380
|
return changes > 0;
|
|
16142
16381
|
}
|
|
@@ -16151,7 +16390,7 @@ function writeCompactionNote(db, args) {
|
|
|
16151
16390
|
}
|
|
16152
16391
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16153
16392
|
const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
|
|
16154
|
-
const id =
|
|
16393
|
+
const id = crypto26.randomUUID();
|
|
16155
16394
|
let inserted;
|
|
16156
16395
|
db.transaction((tx) => {
|
|
16157
16396
|
tx.insert(agentMemory).values({
|
|
@@ -16166,15 +16405,15 @@ function writeCompactionNote(db, args) {
|
|
|
16166
16405
|
const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
|
|
16167
16406
|
const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
|
|
16168
16407
|
and14(
|
|
16169
|
-
|
|
16408
|
+
eq27(agentMemory.projectId, args.projectId),
|
|
16170
16409
|
like(agentMemory.key, `${sessionPrefix}%`)
|
|
16171
16410
|
)
|
|
16172
|
-
).orderBy(
|
|
16411
|
+
).orderBy(desc12(agentMemory.updatedAt)).all();
|
|
16173
16412
|
const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
|
|
16174
16413
|
if (stale.length > 0) {
|
|
16175
16414
|
tx.delete(agentMemory).where(sql9`${agentMemory.id} IN (${sql9.join(stale.map((s) => sql9`${s}`), sql9`, `)})`).run();
|
|
16176
16415
|
}
|
|
16177
|
-
const row = tx.select().from(agentMemory).where(and14(
|
|
16416
|
+
const row = tx.select().from(agentMemory).where(and14(eq27(agentMemory.projectId, args.projectId), eq27(agentMemory.key, key))).get();
|
|
16178
16417
|
if (row) inserted = rowToDto(row);
|
|
16179
16418
|
});
|
|
16180
16419
|
if (!inserted) throw new Error("compaction note write produced no row");
|
|
@@ -16802,7 +17041,7 @@ async function compactMessages(args) {
|
|
|
16802
17041
|
}
|
|
16803
17042
|
|
|
16804
17043
|
// src/agent/session-registry.ts
|
|
16805
|
-
var
|
|
17044
|
+
var log10 = createLogger("SessionRegistry");
|
|
16806
17045
|
var MAX_HYDRATE_NOTES = 20;
|
|
16807
17046
|
var MAX_HYDRATE_BYTES = 32 * 1024;
|
|
16808
17047
|
function escapeMemoryFragment(value) {
|
|
@@ -16851,7 +17090,7 @@ var SessionRegistry = class {
|
|
|
16851
17090
|
modelProvider: effectiveProvider,
|
|
16852
17091
|
modelId: effectiveModelId,
|
|
16853
17092
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16854
|
-
}).where(
|
|
17093
|
+
}).where(eq28(agentSessions.projectId, projectId)).run();
|
|
16855
17094
|
}
|
|
16856
17095
|
const agent2 = createAeroSession({
|
|
16857
17096
|
projectName,
|
|
@@ -17033,13 +17272,13 @@ ${lines.join("\n")}
|
|
|
17033
17272
|
agent.state.messages = result.messages;
|
|
17034
17273
|
agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, row.systemPrompt);
|
|
17035
17274
|
this.save(projectName);
|
|
17036
|
-
|
|
17275
|
+
log10.info("compaction.completed", {
|
|
17037
17276
|
projectName,
|
|
17038
17277
|
removedCount: result.removedCount,
|
|
17039
17278
|
summaryBytes: Buffer.byteLength(result.summary, "utf8")
|
|
17040
17279
|
});
|
|
17041
17280
|
} catch (err) {
|
|
17042
|
-
|
|
17281
|
+
log10.error("compaction.failed", {
|
|
17043
17282
|
projectName,
|
|
17044
17283
|
error: err instanceof Error ? err.message : String(err)
|
|
17045
17284
|
});
|
|
@@ -17069,7 +17308,7 @@ ${lines.join("\n")}
|
|
|
17069
17308
|
modelProvider: nextProvider,
|
|
17070
17309
|
modelId: nextModelId,
|
|
17071
17310
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
17072
|
-
}).where(
|
|
17311
|
+
}).where(eq28(agentSessions.projectId, projectId)).run();
|
|
17073
17312
|
}
|
|
17074
17313
|
/** Persist a session's transcript back to the DB. Call after any run settles. */
|
|
17075
17314
|
save(projectName) {
|
|
@@ -17136,7 +17375,7 @@ ${lines.join("\n")}
|
|
|
17136
17375
|
await agent.prompt(msgs);
|
|
17137
17376
|
this.save(projectName);
|
|
17138
17377
|
} catch (err) {
|
|
17139
|
-
|
|
17378
|
+
log10.error("drain.failed", {
|
|
17140
17379
|
projectName,
|
|
17141
17380
|
error: err instanceof Error ? err.message : String(err)
|
|
17142
17381
|
});
|
|
@@ -17231,17 +17470,17 @@ ${lines.join("\n")}
|
|
|
17231
17470
|
return id;
|
|
17232
17471
|
}
|
|
17233
17472
|
tryResolveProjectId(projectName) {
|
|
17234
|
-
const row = this.opts.db.select({ id: projects.id }).from(projects).where(
|
|
17473
|
+
const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq28(projects.name, projectName)).get();
|
|
17235
17474
|
return row?.id;
|
|
17236
17475
|
}
|
|
17237
17476
|
loadRow(projectId) {
|
|
17238
|
-
const row = this.opts.db.select().from(agentSessions).where(
|
|
17477
|
+
const row = this.opts.db.select().from(agentSessions).where(eq28(agentSessions.projectId, projectId)).get();
|
|
17239
17478
|
return row ?? null;
|
|
17240
17479
|
}
|
|
17241
17480
|
insertRow(params) {
|
|
17242
17481
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17243
17482
|
this.opts.db.insert(agentSessions).values({
|
|
17244
|
-
id:
|
|
17483
|
+
id: crypto27.randomUUID(),
|
|
17245
17484
|
projectId: params.projectId,
|
|
17246
17485
|
systemPrompt: params.systemPrompt,
|
|
17247
17486
|
modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
|
|
@@ -17254,14 +17493,14 @@ ${lines.join("\n")}
|
|
|
17254
17493
|
}
|
|
17255
17494
|
updateRow(projectId, patch) {
|
|
17256
17495
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17257
|
-
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(
|
|
17496
|
+
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq28(agentSessions.projectId, projectId)).run();
|
|
17258
17497
|
}
|
|
17259
17498
|
};
|
|
17260
17499
|
|
|
17261
17500
|
// src/agent/agent-routes.ts
|
|
17262
|
-
import { eq as
|
|
17501
|
+
import { eq as eq29 } from "drizzle-orm";
|
|
17263
17502
|
function resolveProject2(db, name) {
|
|
17264
|
-
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(
|
|
17503
|
+
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq29(projects.name, name)).get();
|
|
17265
17504
|
if (!row) throw notFound("project", name);
|
|
17266
17505
|
return row;
|
|
17267
17506
|
}
|
|
@@ -17270,7 +17509,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
17270
17509
|
"/projects/:name/agent/transcript",
|
|
17271
17510
|
async (request) => {
|
|
17272
17511
|
const project = resolveProject2(opts.db, request.params.name);
|
|
17273
|
-
const row = opts.db.select().from(agentSessions).where(
|
|
17512
|
+
const row = opts.db.select().from(agentSessions).where(eq29(agentSessions.projectId, project.id)).get();
|
|
17274
17513
|
if (!row) {
|
|
17275
17514
|
return { messages: [], modelProvider: null, modelId: null, updatedAt: null };
|
|
17276
17515
|
}
|
|
@@ -17294,7 +17533,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
17294
17533
|
async (request) => {
|
|
17295
17534
|
const project = resolveProject2(opts.db, request.params.name);
|
|
17296
17535
|
opts.sessionRegistry.reset(project.name);
|
|
17297
|
-
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
17536
|
+
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq29(agentSessions.projectId, project.id)).run();
|
|
17298
17537
|
return { status: "reset" };
|
|
17299
17538
|
}
|
|
17300
17539
|
);
|
|
@@ -17805,6 +18044,9 @@ var ApiClient = class {
|
|
|
17805
18044
|
async bingInspectUrl(project, url) {
|
|
17806
18045
|
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/inspect-url`, { url });
|
|
17807
18046
|
}
|
|
18047
|
+
async bingInspectSitemap(project, body) {
|
|
18048
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/inspect-sitemap`, body ?? {});
|
|
18049
|
+
}
|
|
17808
18050
|
async bingRequestIndexing(project, body) {
|
|
17809
18051
|
return this.request("POST", `/projects/${encodeURIComponent(project)}/bing/request-indexing`, body);
|
|
17810
18052
|
}
|
|
@@ -18111,7 +18353,7 @@ function formatAuditFactorScore(factor) {
|
|
|
18111
18353
|
}
|
|
18112
18354
|
|
|
18113
18355
|
// src/snapshot-service.ts
|
|
18114
|
-
var
|
|
18356
|
+
var log11 = createLogger("Snapshot");
|
|
18115
18357
|
var ANALYSIS_PROVIDER_PRIORITY = ["openai", "claude", "gemini", "perplexity", "local"];
|
|
18116
18358
|
var SNAPSHOT_QUERY_COUNT = 6;
|
|
18117
18359
|
var ProviderExecutionGate2 = class {
|
|
@@ -18254,7 +18496,7 @@ var SnapshotService = class {
|
|
|
18254
18496
|
return mapAuditReport(report);
|
|
18255
18497
|
} catch (err) {
|
|
18256
18498
|
const message = err instanceof Error ? err.message : String(err);
|
|
18257
|
-
|
|
18499
|
+
log11.warn("audit.failed", { homepageUrl, error: message });
|
|
18258
18500
|
return {
|
|
18259
18501
|
url: homepageUrl,
|
|
18260
18502
|
finalUrl: homepageUrl,
|
|
@@ -18284,7 +18526,7 @@ var SnapshotService = class {
|
|
|
18284
18526
|
phrases: parsedPhrases
|
|
18285
18527
|
};
|
|
18286
18528
|
} catch (err) {
|
|
18287
|
-
|
|
18529
|
+
log11.warn("profile.generation-failed", {
|
|
18288
18530
|
domain: ctx.domain,
|
|
18289
18531
|
provider: ctx.analysisProvider.adapter.name,
|
|
18290
18532
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -18426,7 +18668,7 @@ var SnapshotService = class {
|
|
|
18426
18668
|
recommendedActions: uniqueStrings(parsed.recommendedActions ?? []).slice(0, 4)
|
|
18427
18669
|
};
|
|
18428
18670
|
} catch (err) {
|
|
18429
|
-
|
|
18671
|
+
log11.warn("response.analysis-failed", {
|
|
18430
18672
|
provider: ctx.analysisProvider.adapter.name,
|
|
18431
18673
|
error: err instanceof Error ? err.message : String(err)
|
|
18432
18674
|
});
|
|
@@ -18711,7 +18953,7 @@ function clipText(value, length) {
|
|
|
18711
18953
|
// src/server.ts
|
|
18712
18954
|
var _require2 = createRequire3(import.meta.url);
|
|
18713
18955
|
var { version: PKG_VERSION } = _require2("../package.json");
|
|
18714
|
-
var
|
|
18956
|
+
var log12 = createLogger("Server");
|
|
18715
18957
|
var DEFAULT_QUOTA = {
|
|
18716
18958
|
maxConcurrency: 2,
|
|
18717
18959
|
maxRequestsPerMinute: 10,
|
|
@@ -18742,7 +18984,7 @@ function summarizeProviderConfig(provider, config) {
|
|
|
18742
18984
|
};
|
|
18743
18985
|
}
|
|
18744
18986
|
function hashApiKey(key) {
|
|
18745
|
-
return
|
|
18987
|
+
return crypto28.createHash("sha256").update(key).digest("hex");
|
|
18746
18988
|
}
|
|
18747
18989
|
function parseCookies2(header) {
|
|
18748
18990
|
if (!header) return {};
|
|
@@ -18798,7 +19040,7 @@ function applyLegacyCredentials(rows, config) {
|
|
|
18798
19040
|
}
|
|
18799
19041
|
if (migratedGoogle > 0) {
|
|
18800
19042
|
saveConfigPatch({ google: config.google });
|
|
18801
|
-
|
|
19043
|
+
log12.info("credentials.migrated", { type: "google", count: migratedGoogle });
|
|
18802
19044
|
}
|
|
18803
19045
|
let migratedGa4 = 0;
|
|
18804
19046
|
for (const row of rows.ga4) {
|
|
@@ -18816,7 +19058,7 @@ function applyLegacyCredentials(rows, config) {
|
|
|
18816
19058
|
}
|
|
18817
19059
|
if (migratedGa4 > 0) {
|
|
18818
19060
|
saveConfigPatch({ ga4: config.ga4 });
|
|
18819
|
-
|
|
19061
|
+
log12.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
|
|
18820
19062
|
}
|
|
18821
19063
|
}
|
|
18822
19064
|
async function createServer(opts) {
|
|
@@ -18848,11 +19090,11 @@ async function createServer(opts) {
|
|
|
18848
19090
|
applyLegacyCredentials(legacyRows, opts.config);
|
|
18849
19091
|
dropLegacyCredentialColumns(opts.db);
|
|
18850
19092
|
} catch (err) {
|
|
18851
|
-
|
|
19093
|
+
log12.warn("credentials.migration.failed", {
|
|
18852
19094
|
error: err instanceof Error ? err.message : String(err)
|
|
18853
19095
|
});
|
|
18854
19096
|
}
|
|
18855
|
-
|
|
19097
|
+
log12.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
|
|
18856
19098
|
const p = providers[k];
|
|
18857
19099
|
return p?.apiKey || p?.baseUrl || p?.vertexProject;
|
|
18858
19100
|
}) });
|
|
@@ -18900,7 +19142,7 @@ async function createServer(opts) {
|
|
|
18900
19142
|
intelligenceService,
|
|
18901
19143
|
(runId, projectId, result) => notifier.dispatchInsightWebhooks(runId, projectId, result),
|
|
18902
19144
|
async ({ runId, projectId, insightCount, criticalOrHigh }) => {
|
|
18903
|
-
const project = opts.db.select({ name: projects.name }).from(projects).where(
|
|
19145
|
+
const project = opts.db.select({ name: projects.name }).from(projects).where(eq30(projects.id, projectId)).get();
|
|
18904
19146
|
if (!project) return;
|
|
18905
19147
|
sessionRegistry.queueFollowUp(project.name, {
|
|
18906
19148
|
role: "user",
|
|
@@ -18994,7 +19236,7 @@ async function createServer(opts) {
|
|
|
18994
19236
|
return removed;
|
|
18995
19237
|
}
|
|
18996
19238
|
};
|
|
18997
|
-
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ??
|
|
19239
|
+
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto28.randomBytes(32).toString("hex");
|
|
18998
19240
|
const googleConnectionStore = {
|
|
18999
19241
|
listConnections: (domain) => listGoogleConnections(opts.config, domain),
|
|
19000
19242
|
getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
|
|
@@ -19040,11 +19282,11 @@ async function createServer(opts) {
|
|
|
19040
19282
|
const apiPrefix = basePath ? `${basePath}api/v1` : "/api/v1";
|
|
19041
19283
|
if (opts.config.apiKey) {
|
|
19042
19284
|
const keyHash = hashApiKey(opts.config.apiKey);
|
|
19043
|
-
const existing = opts.db.select().from(apiKeys).where(
|
|
19285
|
+
const existing = opts.db.select().from(apiKeys).where(eq30(apiKeys.keyHash, keyHash)).get();
|
|
19044
19286
|
if (!existing) {
|
|
19045
19287
|
const prefix = opts.config.apiKey.slice(0, 12);
|
|
19046
19288
|
opts.db.insert(apiKeys).values({
|
|
19047
|
-
id: `key_${
|
|
19289
|
+
id: `key_${crypto28.randomBytes(8).toString("hex")}`,
|
|
19048
19290
|
name: "default",
|
|
19049
19291
|
keyHash,
|
|
19050
19292
|
keyPrefix: prefix,
|
|
@@ -19068,7 +19310,7 @@ async function createServer(opts) {
|
|
|
19068
19310
|
};
|
|
19069
19311
|
const createSession = (apiKeyId) => {
|
|
19070
19312
|
pruneExpiredSessions();
|
|
19071
|
-
const sessionId =
|
|
19313
|
+
const sessionId = crypto28.randomBytes(32).toString("hex");
|
|
19072
19314
|
sessions.set(sessionId, {
|
|
19073
19315
|
apiKeyId,
|
|
19074
19316
|
expiresAt: Date.now() + SESSION_TTL_MS
|
|
@@ -19092,7 +19334,7 @@ async function createServer(opts) {
|
|
|
19092
19334
|
};
|
|
19093
19335
|
const getDefaultApiKey = () => {
|
|
19094
19336
|
if (!opts.config.apiKey) return void 0;
|
|
19095
|
-
return opts.db.select().from(apiKeys).where(
|
|
19337
|
+
return opts.db.select().from(apiKeys).where(eq30(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
|
|
19096
19338
|
};
|
|
19097
19339
|
const createPasswordSession = (reply) => {
|
|
19098
19340
|
const key = getDefaultApiKey();
|
|
@@ -19149,12 +19391,12 @@ async function createServer(opts) {
|
|
|
19149
19391
|
return reply.send({ authenticated: true });
|
|
19150
19392
|
}
|
|
19151
19393
|
if (apiKey) {
|
|
19152
|
-
const key = opts.db.select().from(apiKeys).where(
|
|
19394
|
+
const key = opts.db.select().from(apiKeys).where(eq30(apiKeys.keyHash, hashApiKey(apiKey))).get();
|
|
19153
19395
|
if (!key || key.revokedAt) {
|
|
19154
19396
|
const err2 = authInvalid();
|
|
19155
19397
|
return reply.status(err2.statusCode).send(err2.toJSON());
|
|
19156
19398
|
}
|
|
19157
|
-
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
19399
|
+
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq30(apiKeys.id, key.id)).run();
|
|
19158
19400
|
const sessionId = createSession(key.id);
|
|
19159
19401
|
reply.header("set-cookie", serializeSessionCookie({
|
|
19160
19402
|
name: SESSION_COOKIE_NAME,
|
|
@@ -19242,7 +19484,7 @@ async function createServer(opts) {
|
|
|
19242
19484
|
deps: {
|
|
19243
19485
|
enqueueAutoExtract: ({ projectId, release: r }) => {
|
|
19244
19486
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
19245
|
-
const runId =
|
|
19487
|
+
const runId = crypto28.randomUUID();
|
|
19246
19488
|
opts.db.insert(runs).values({
|
|
19247
19489
|
id: runId,
|
|
19248
19490
|
projectId,
|
|
@@ -19304,6 +19546,14 @@ async function createServer(opts) {
|
|
|
19304
19546
|
googleSettingsSummary,
|
|
19305
19547
|
bingSettingsSummary,
|
|
19306
19548
|
bingConnectionStore,
|
|
19549
|
+
onBingInspectSitemapRequested: (runId, projectId, inspectOpts) => {
|
|
19550
|
+
executeBingInspectSitemap(opts.db, runId, projectId, {
|
|
19551
|
+
...inspectOpts,
|
|
19552
|
+
config: opts.config
|
|
19553
|
+
}).catch((err) => {
|
|
19554
|
+
app.log.error({ runId, err }, "Bing inspect sitemap failed");
|
|
19555
|
+
});
|
|
19556
|
+
},
|
|
19307
19557
|
wordpressConnectionStore,
|
|
19308
19558
|
ga4CredentialStore,
|
|
19309
19559
|
onRunCreated: (runId, projectId, providers2, location) => {
|
|
@@ -19368,7 +19618,7 @@ async function createServer(opts) {
|
|
|
19368
19618
|
const targetProjectIds = affectedProjectIds.length > 0 ? affectedProjectIds : [null];
|
|
19369
19619
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
19370
19620
|
opts.db.insert(auditLog).values(targetProjectIds.map((projectId) => ({
|
|
19371
|
-
id:
|
|
19621
|
+
id: crypto28.randomUUID(),
|
|
19372
19622
|
projectId,
|
|
19373
19623
|
actor: "api",
|
|
19374
19624
|
action: existing ? "provider.updated" : "provider.created",
|