@canonry/canonry 4.148.8 → 4.148.11
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/dist/{chunk-LAJQRA4E.js → chunk-U2ETSHDL.js} +370 -338
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +9 -9
|
@@ -549,11 +549,11 @@ function checkLatestVersionForServer(opts) {
|
|
|
549
549
|
|
|
550
550
|
// src/server.ts
|
|
551
551
|
import { createRequire as createRequire3 } from "module";
|
|
552
|
-
import
|
|
552
|
+
import crypto23 from "crypto";
|
|
553
553
|
import fs8 from "fs";
|
|
554
554
|
import path9 from "path";
|
|
555
555
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
556
|
-
import { and as and15, eq as
|
|
556
|
+
import { and as and15, eq as eq22 } from "drizzle-orm";
|
|
557
557
|
import Fastify from "fastify";
|
|
558
558
|
import os5 from "os";
|
|
559
559
|
|
|
@@ -4225,8 +4225,83 @@ function maybeShowActivationNotice(io = {}) {
|
|
|
4225
4225
|
}
|
|
4226
4226
|
|
|
4227
4227
|
// src/gsc-sync.ts
|
|
4228
|
+
import crypto6 from "crypto";
|
|
4229
|
+
import { eq as eq4, and as and4, sql as sql3 } from "drizzle-orm";
|
|
4230
|
+
|
|
4231
|
+
// src/gsc-coverage-snapshot.ts
|
|
4228
4232
|
import crypto5 from "crypto";
|
|
4229
|
-
import {
|
|
4233
|
+
import { and as and3, desc, eq as eq3, gte } from "drizzle-orm";
|
|
4234
|
+
var COVERAGE_IMPRESSION_WINDOW_DAYS = 30;
|
|
4235
|
+
function pageKey(raw) {
|
|
4236
|
+
if (!raw) return null;
|
|
4237
|
+
try {
|
|
4238
|
+
return normalizeUrlPath(new URL(raw).pathname);
|
|
4239
|
+
} catch {
|
|
4240
|
+
return normalizeUrlPath(raw);
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
function isoDaysAgo(days) {
|
|
4244
|
+
const d = /* @__PURE__ */ new Date();
|
|
4245
|
+
d.setUTCDate(d.getUTCDate() - days);
|
|
4246
|
+
return d.toISOString().split("T")[0];
|
|
4247
|
+
}
|
|
4248
|
+
function writeCoverageSnapshot(db, projectId, runId, opts = {}) {
|
|
4249
|
+
const windowDays = opts.windowDays ?? COVERAGE_IMPRESSION_WINDOW_DAYS;
|
|
4250
|
+
const since = isoDaysAgo(windowDays);
|
|
4251
|
+
const pageRows = db.select({ page: gscSearchData.page, impressions: gscSearchData.impressions }).from(gscSearchData).where(and3(eq3(gscSearchData.projectId, projectId), gte(gscSearchData.date, since))).all();
|
|
4252
|
+
const allInspections = db.select().from(gscUrlInspections).where(eq3(gscUrlInspections.projectId, projectId)).all();
|
|
4253
|
+
const latestAudit = db.select({ runId: siteAuditSnapshots.runId }).from(siteAuditSnapshots).where(eq3(siteAuditSnapshots.projectId, projectId)).orderBy(desc(siteAuditSnapshots.createdAt)).limit(1).get();
|
|
4254
|
+
const sitemapUrls = latestAudit ? db.select({ url: siteAuditPages.url }).from(siteAuditPages).where(eq3(siteAuditPages.runId, latestAudit.runId)).all() : [];
|
|
4255
|
+
const impressionsByKey = /* @__PURE__ */ new Map();
|
|
4256
|
+
for (const row of pageRows) {
|
|
4257
|
+
const key = pageKey(row.page);
|
|
4258
|
+
if (key) impressionsByKey.set(key, (impressionsByKey.get(key) ?? 0) + row.impressions);
|
|
4259
|
+
}
|
|
4260
|
+
for (const row of sitemapUrls) {
|
|
4261
|
+
const key = pageKey(row.url);
|
|
4262
|
+
if (key && !impressionsByKey.has(key)) impressionsByKey.set(key, 0);
|
|
4263
|
+
}
|
|
4264
|
+
const latestByKey = /* @__PURE__ */ new Map();
|
|
4265
|
+
for (const row of allInspections) {
|
|
4266
|
+
const key = pageKey(row.url);
|
|
4267
|
+
if (!key) continue;
|
|
4268
|
+
const existing = latestByKey.get(key);
|
|
4269
|
+
if (!existing || row.inspectedAt > existing.inspectedAt) latestByKey.set(key, row);
|
|
4270
|
+
}
|
|
4271
|
+
const coverage = deriveIndexCoverage({
|
|
4272
|
+
pages: [...impressionsByKey].map(([page, impressions]) => ({ page, impressions })),
|
|
4273
|
+
inspections: [...latestByKey].map(([key, r]) => ({
|
|
4274
|
+
url: key,
|
|
4275
|
+
indexingState: r.indexingState,
|
|
4276
|
+
coverageState: r.coverageState
|
|
4277
|
+
}))
|
|
4278
|
+
});
|
|
4279
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
4280
|
+
const snapshotDate = now.toISOString().split("T")[0];
|
|
4281
|
+
db.delete(gscCoverageSnapshots).where(and3(eq3(gscCoverageSnapshots.projectId, projectId), eq3(gscCoverageSnapshots.date, snapshotDate))).run();
|
|
4282
|
+
db.insert(gscCoverageSnapshots).values({
|
|
4283
|
+
id: crypto5.randomUUID(),
|
|
4284
|
+
projectId,
|
|
4285
|
+
syncRunId: runId,
|
|
4286
|
+
date: snapshotDate,
|
|
4287
|
+
indexed: coverage.indexed,
|
|
4288
|
+
notIndexed: coverage.notIndexed,
|
|
4289
|
+
unknownPages: coverage.unknown,
|
|
4290
|
+
verifiedByInspection: coverage.verifiedByInspection,
|
|
4291
|
+
derivedFromImpressions: coverage.derivedFromImpressions,
|
|
4292
|
+
reasonBreakdown: coverage.reasonBreakdown,
|
|
4293
|
+
createdAt: now.toISOString()
|
|
4294
|
+
}).run();
|
|
4295
|
+
return {
|
|
4296
|
+
indexed: coverage.indexed,
|
|
4297
|
+
notIndexed: coverage.notIndexed,
|
|
4298
|
+
unknown: coverage.unknown,
|
|
4299
|
+
verifiedByInspection: coverage.verifiedByInspection,
|
|
4300
|
+
derivedFromImpressions: coverage.derivedFromImpressions
|
|
4301
|
+
};
|
|
4302
|
+
}
|
|
4303
|
+
|
|
4304
|
+
// src/gsc-sync.ts
|
|
4230
4305
|
var log2 = createLogger("GscSync");
|
|
4231
4306
|
function formatDate(d) {
|
|
4232
4307
|
return d.toISOString().split("T")[0];
|
|
@@ -4238,13 +4313,13 @@ function daysAgo(n) {
|
|
|
4238
4313
|
}
|
|
4239
4314
|
async function executeGscSync(db, runId, projectId, opts) {
|
|
4240
4315
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4241
|
-
db.update(runs).set({ status: "running", startedAt: now }).where(
|
|
4316
|
+
db.update(runs).set({ status: "running", startedAt: now }).where(eq4(runs.id, runId)).run();
|
|
4242
4317
|
try {
|
|
4243
4318
|
const { clientId: googleClientId, clientSecret: googleClientSecret } = getGoogleAuthConfig(opts.config);
|
|
4244
4319
|
if (!googleClientId || !googleClientSecret) {
|
|
4245
4320
|
throw new Error("Google OAuth is not configured in the local Canonry config");
|
|
4246
4321
|
}
|
|
4247
|
-
const project = db.select().from(projects).where(
|
|
4322
|
+
const project = db.select().from(projects).where(eq4(projects.id, projectId)).get();
|
|
4248
4323
|
if (!project) {
|
|
4249
4324
|
throw new Error(`Project not found: ${projectId}`);
|
|
4250
4325
|
}
|
|
@@ -4279,8 +4354,8 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
4279
4354
|
});
|
|
4280
4355
|
log2.info("fetch.complete", { runId, projectId, rowCount: rows.length });
|
|
4281
4356
|
db.delete(gscSearchData).where(
|
|
4282
|
-
|
|
4283
|
-
|
|
4357
|
+
and4(
|
|
4358
|
+
eq4(gscSearchData.projectId, projectId),
|
|
4284
4359
|
sql3`${gscSearchData.date} >= ${startDate}`,
|
|
4285
4360
|
sql3`${gscSearchData.date} <= ${endDate}`
|
|
4286
4361
|
)
|
|
@@ -4292,7 +4367,7 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
4292
4367
|
for (const row of batch) {
|
|
4293
4368
|
const [query, page, country, device, date] = row.keys;
|
|
4294
4369
|
db.insert(gscSearchData).values({
|
|
4295
|
-
id:
|
|
4370
|
+
id: crypto6.randomUUID(),
|
|
4296
4371
|
projectId,
|
|
4297
4372
|
syncRunId: runId,
|
|
4298
4373
|
date: date ?? "",
|
|
@@ -4314,8 +4389,8 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
4314
4389
|
dimensions: ["date"]
|
|
4315
4390
|
});
|
|
4316
4391
|
db.delete(gscDailyTotals).where(
|
|
4317
|
-
|
|
4318
|
-
|
|
4392
|
+
and4(
|
|
4393
|
+
eq4(gscDailyTotals.projectId, projectId),
|
|
4319
4394
|
sql3`${gscDailyTotals.date} >= ${startDate}`,
|
|
4320
4395
|
sql3`${gscDailyTotals.date} <= ${endDate}`
|
|
4321
4396
|
)
|
|
@@ -4324,7 +4399,7 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
4324
4399
|
for (const row of totalRows) {
|
|
4325
4400
|
const [date] = row.keys;
|
|
4326
4401
|
db.insert(gscDailyTotals).values({
|
|
4327
|
-
id:
|
|
4402
|
+
id: crypto6.randomUUID(),
|
|
4328
4403
|
projectId,
|
|
4329
4404
|
date: date ?? "",
|
|
4330
4405
|
clicks: row.clicks,
|
|
@@ -4340,8 +4415,8 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
4340
4415
|
dimensions: ["date", "query"]
|
|
4341
4416
|
});
|
|
4342
4417
|
db.delete(gscQueryDailyTotals).where(
|
|
4343
|
-
|
|
4344
|
-
|
|
4418
|
+
and4(
|
|
4419
|
+
eq4(gscQueryDailyTotals.projectId, projectId),
|
|
4345
4420
|
sql3`${gscQueryDailyTotals.date} >= ${startDate}`,
|
|
4346
4421
|
sql3`${gscQueryDailyTotals.date} <= ${endDate}`
|
|
4347
4422
|
)
|
|
@@ -4351,7 +4426,7 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
4351
4426
|
const [date, query] = row.keys;
|
|
4352
4427
|
if (!date || !query) continue;
|
|
4353
4428
|
db.insert(gscQueryDailyTotals).values({
|
|
4354
|
-
id:
|
|
4429
|
+
id: crypto6.randomUUID(),
|
|
4355
4430
|
projectId,
|
|
4356
4431
|
date,
|
|
4357
4432
|
query,
|
|
@@ -4364,50 +4439,20 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
4364
4439
|
}).run();
|
|
4365
4440
|
}
|
|
4366
4441
|
log2.info("query-totals.complete", { runId, projectId, rowCount: queryTotalRows.length });
|
|
4367
|
-
const
|
|
4368
|
-
|
|
4369
|
-
for (const row of allInspections) {
|
|
4370
|
-
const existing = latestByUrl.get(row.url);
|
|
4371
|
-
if (!existing || row.inspectedAt > existing.inspectedAt) {
|
|
4372
|
-
latestByUrl.set(row.url, row);
|
|
4373
|
-
}
|
|
4374
|
-
}
|
|
4375
|
-
const coverage = deriveIndexCoverage({
|
|
4376
|
-
pages: rows.map((row) => ({ page: row.keys[1] ?? "", impressions: row.impressions })),
|
|
4377
|
-
inspections: [...latestByUrl.values()].map((row) => ({
|
|
4378
|
-
url: row.url,
|
|
4379
|
-
indexingState: row.indexingState,
|
|
4380
|
-
coverageState: row.coverageState
|
|
4381
|
-
}))
|
|
4382
|
-
});
|
|
4383
|
-
const snapshotDate = formatDate(/* @__PURE__ */ new Date());
|
|
4384
|
-
db.delete(gscCoverageSnapshots).where(and3(eq3(gscCoverageSnapshots.projectId, projectId), eq3(gscCoverageSnapshots.date, snapshotDate))).run();
|
|
4385
|
-
db.insert(gscCoverageSnapshots).values({
|
|
4386
|
-
id: crypto5.randomUUID(),
|
|
4387
|
-
projectId,
|
|
4388
|
-
syncRunId: runId,
|
|
4389
|
-
date: snapshotDate,
|
|
4390
|
-
indexed: coverage.indexed,
|
|
4391
|
-
notIndexed: coverage.notIndexed,
|
|
4392
|
-
unknownPages: coverage.unknown,
|
|
4393
|
-
verifiedByInspection: coverage.verifiedByInspection,
|
|
4394
|
-
derivedFromImpressions: coverage.derivedFromImpressions,
|
|
4395
|
-
reasonBreakdown: coverage.reasonBreakdown,
|
|
4396
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4397
|
-
}).run();
|
|
4398
|
-
db.update(runs).set({ status: "completed", finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq3(runs.id, runId)).run();
|
|
4442
|
+
const coverage = writeCoverageSnapshot(db, projectId, runId);
|
|
4443
|
+
db.update(runs).set({ status: "completed", finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq4(runs.id, runId)).run();
|
|
4399
4444
|
log2.info("sync.completed", { runId, projectId, searchDataRows: rows.length, indexed: coverage.indexed, notIndexed: coverage.notIndexed, unknown: coverage.unknown, verifiedByInspection: coverage.verifiedByInspection });
|
|
4400
4445
|
} catch (err) {
|
|
4401
4446
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
4402
|
-
db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
4447
|
+
db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq4(runs.id, runId)).run();
|
|
4403
4448
|
log2.error("sync.failed", { runId, projectId, error: errorMsg });
|
|
4404
4449
|
throw err;
|
|
4405
4450
|
}
|
|
4406
4451
|
}
|
|
4407
4452
|
|
|
4408
4453
|
// src/gbp-sync.ts
|
|
4409
|
-
import
|
|
4410
|
-
import { eq as
|
|
4454
|
+
import crypto7 from "crypto";
|
|
4455
|
+
import { eq as eq5, and as and5, desc as desc2, inArray as inArray2, lt } from "drizzle-orm";
|
|
4411
4456
|
var MS_PER_DAY = 864e5;
|
|
4412
4457
|
var log3 = createLogger("GbpSync");
|
|
4413
4458
|
var LOCATION_CONCURRENCY = 4;
|
|
@@ -4428,13 +4473,13 @@ function monthMinus(n) {
|
|
|
4428
4473
|
}
|
|
4429
4474
|
async function executeGbpSync(db, runId, projectId, opts) {
|
|
4430
4475
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4431
|
-
db.update(runs).set({ status: "running", startedAt: now }).where(
|
|
4476
|
+
db.update(runs).set({ status: "running", startedAt: now }).where(eq5(runs.id, runId)).run();
|
|
4432
4477
|
try {
|
|
4433
4478
|
const { clientId, clientSecret } = getGoogleAuthConfig(opts.config);
|
|
4434
4479
|
if (!clientId || !clientSecret) {
|
|
4435
4480
|
throw new Error("Google OAuth is not configured in the local Canonry config");
|
|
4436
4481
|
}
|
|
4437
|
-
const project = db.select().from(projects).where(
|
|
4482
|
+
const project = db.select().from(projects).where(eq5(projects.id, projectId)).get();
|
|
4438
4483
|
if (!project) throw new Error(`Project not found: ${projectId}`);
|
|
4439
4484
|
const conn = getGoogleConnection(opts.config, project.canonicalDomain, "gbp");
|
|
4440
4485
|
if (!conn || !conn.refreshToken) {
|
|
@@ -4452,7 +4497,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4452
4497
|
});
|
|
4453
4498
|
saveConfigPatch(opts.config);
|
|
4454
4499
|
}
|
|
4455
|
-
let locationRows = db.select().from(gbpLocations).where(
|
|
4500
|
+
let locationRows = db.select().from(gbpLocations).where(and5(eq5(gbpLocations.projectId, projectId), eq5(gbpLocations.selected, true))).all();
|
|
4456
4501
|
if (opts.locationNames?.length) {
|
|
4457
4502
|
const wanted = new Set(opts.locationNames);
|
|
4458
4503
|
locationRows = locationRows.filter((l) => wanted.has(l.locationName));
|
|
@@ -4502,15 +4547,15 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4502
4547
|
})))
|
|
4503
4548
|
]);
|
|
4504
4549
|
const lodgingHash = lodging ? hashLodging(lodging) : null;
|
|
4505
|
-
const latestLodging = lodging ? db.select().from(gbpLodgingSnapshots).where(
|
|
4550
|
+
const latestLodging = lodging ? db.select().from(gbpLodgingSnapshots).where(and5(eq5(gbpLodgingSnapshots.projectId, projectId), eq5(gbpLodgingSnapshots.locationName, loc.locationName))).orderBy(desc2(gbpLodgingSnapshots.syncedAt)).limit(1).get() : void 0;
|
|
4506
4551
|
const lodgingChanged = lodging !== null && latestLodging?.contentHash !== lodgingHash;
|
|
4507
4552
|
const attributesHash = hashAttributes(attributes);
|
|
4508
|
-
const latestAttributes = db.select().from(gbpAttributesSnapshots).where(
|
|
4553
|
+
const latestAttributes = db.select().from(gbpAttributesSnapshots).where(and5(eq5(gbpAttributesSnapshots.projectId, projectId), eq5(gbpAttributesSnapshots.locationName, loc.locationName))).orderBy(desc2(gbpAttributesSnapshots.syncedAt)).limit(1).get();
|
|
4509
4554
|
const attributesChanged = latestAttributes?.contentHash !== attributesHash;
|
|
4510
4555
|
let placeToWrite = null;
|
|
4511
4556
|
let placeToTouch = null;
|
|
4512
4557
|
if (placesTier !== "off" && placesApiKey && lodging !== null && loc.placeId) {
|
|
4513
|
-
const latestPlace = db.select().from(gbpPlaceDetails).where(
|
|
4558
|
+
const latestPlace = db.select().from(gbpPlaceDetails).where(and5(eq5(gbpPlaceDetails.projectId, projectId), eq5(gbpPlaceDetails.locationName, loc.locationName))).orderBy(desc2(gbpPlaceDetails.syncedAt)).limit(1).get();
|
|
4514
4559
|
const ageDays = latestPlace ? (Date.now() - new Date(latestPlace.syncedAt).getTime()) / MS_PER_DAY : Infinity;
|
|
4515
4560
|
if (ageDays >= placesRefreshDays) {
|
|
4516
4561
|
try {
|
|
@@ -4528,10 +4573,10 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4528
4573
|
}
|
|
4529
4574
|
const insertNow = (/* @__PURE__ */ new Date()).toISOString();
|
|
4530
4575
|
db.transaction((tx) => {
|
|
4531
|
-
tx.delete(gbpDailyMetrics).where(
|
|
4576
|
+
tx.delete(gbpDailyMetrics).where(and5(eq5(gbpDailyMetrics.projectId, projectId), eq5(gbpDailyMetrics.locationName, loc.locationName))).run();
|
|
4532
4577
|
for (const row of metricRows) {
|
|
4533
4578
|
tx.insert(gbpDailyMetrics).values({
|
|
4534
|
-
id:
|
|
4579
|
+
id: crypto7.randomUUID(),
|
|
4535
4580
|
projectId,
|
|
4536
4581
|
locationName: loc.locationName,
|
|
4537
4582
|
date: row.date,
|
|
@@ -4540,10 +4585,10 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4540
4585
|
syncRunId: runId
|
|
4541
4586
|
}).run();
|
|
4542
4587
|
}
|
|
4543
|
-
tx.delete(gbpKeywordImpressions).where(
|
|
4588
|
+
tx.delete(gbpKeywordImpressions).where(and5(eq5(gbpKeywordImpressions.projectId, projectId), eq5(gbpKeywordImpressions.locationName, loc.locationName))).run();
|
|
4544
4589
|
for (const row of keywordRows) {
|
|
4545
4590
|
tx.insert(gbpKeywordImpressions).values({
|
|
4546
|
-
id:
|
|
4591
|
+
id: crypto7.randomUUID(),
|
|
4547
4592
|
projectId,
|
|
4548
4593
|
locationName: loc.locationName,
|
|
4549
4594
|
periodStart: monthKey(keywordsStart),
|
|
@@ -4556,15 +4601,15 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4556
4601
|
}
|
|
4557
4602
|
const fetchedMonths = monthlyKeywordResults.map((r) => r.month);
|
|
4558
4603
|
if (fetchedMonths.length > 0) {
|
|
4559
|
-
tx.delete(gbpKeywordMonthly).where(
|
|
4560
|
-
|
|
4561
|
-
|
|
4604
|
+
tx.delete(gbpKeywordMonthly).where(and5(
|
|
4605
|
+
eq5(gbpKeywordMonthly.projectId, projectId),
|
|
4606
|
+
eq5(gbpKeywordMonthly.locationName, loc.locationName),
|
|
4562
4607
|
inArray2(gbpKeywordMonthly.month, fetchedMonths)
|
|
4563
4608
|
)).run();
|
|
4564
4609
|
for (const { month, rows } of monthlyKeywordResults) {
|
|
4565
4610
|
for (const row of rows) {
|
|
4566
4611
|
tx.insert(gbpKeywordMonthly).values({
|
|
4567
|
-
id:
|
|
4612
|
+
id: crypto7.randomUUID(),
|
|
4568
4613
|
projectId,
|
|
4569
4614
|
locationName: loc.locationName,
|
|
4570
4615
|
month,
|
|
@@ -4576,16 +4621,16 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4576
4621
|
}).run();
|
|
4577
4622
|
}
|
|
4578
4623
|
}
|
|
4579
|
-
tx.delete(gbpKeywordMonthly).where(
|
|
4580
|
-
|
|
4581
|
-
|
|
4624
|
+
tx.delete(gbpKeywordMonthly).where(and5(
|
|
4625
|
+
eq5(gbpKeywordMonthly.projectId, projectId),
|
|
4626
|
+
eq5(gbpKeywordMonthly.locationName, loc.locationName),
|
|
4582
4627
|
lt(gbpKeywordMonthly.month, keywordRetentionCutoff)
|
|
4583
4628
|
)).run();
|
|
4584
4629
|
}
|
|
4585
|
-
tx.delete(gbpPlaceActions).where(
|
|
4630
|
+
tx.delete(gbpPlaceActions).where(and5(eq5(gbpPlaceActions.projectId, projectId), eq5(gbpPlaceActions.locationName, loc.locationName))).run();
|
|
4586
4631
|
for (const row of placeActionRows) {
|
|
4587
4632
|
tx.insert(gbpPlaceActions).values({
|
|
4588
|
-
id:
|
|
4633
|
+
id: crypto7.randomUUID(),
|
|
4589
4634
|
projectId,
|
|
4590
4635
|
locationName: loc.locationName,
|
|
4591
4636
|
placeActionLinkName: row.placeActionLinkName,
|
|
@@ -4598,7 +4643,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4598
4643
|
}
|
|
4599
4644
|
if (lodging !== null && lodgingChanged) {
|
|
4600
4645
|
tx.insert(gbpLodgingSnapshots).values({
|
|
4601
|
-
id:
|
|
4646
|
+
id: crypto7.randomUUID(),
|
|
4602
4647
|
projectId,
|
|
4603
4648
|
locationName: loc.locationName,
|
|
4604
4649
|
contentHash: lodgingHash,
|
|
@@ -4608,11 +4653,11 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4608
4653
|
syncRunId: runId
|
|
4609
4654
|
}).run();
|
|
4610
4655
|
} else if (lodging !== null && latestLodging) {
|
|
4611
|
-
tx.update(gbpLodgingSnapshots).set({ syncedAt: insertNow, syncRunId: runId }).where(
|
|
4656
|
+
tx.update(gbpLodgingSnapshots).set({ syncedAt: insertNow, syncRunId: runId }).where(eq5(gbpLodgingSnapshots.id, latestLodging.id)).run();
|
|
4612
4657
|
}
|
|
4613
4658
|
if (attributesChanged) {
|
|
4614
4659
|
tx.insert(gbpAttributesSnapshots).values({
|
|
4615
|
-
id:
|
|
4660
|
+
id: crypto7.randomUUID(),
|
|
4616
4661
|
projectId,
|
|
4617
4662
|
locationName: loc.locationName,
|
|
4618
4663
|
contentHash: attributesHash,
|
|
@@ -4622,11 +4667,11 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4622
4667
|
syncRunId: runId
|
|
4623
4668
|
}).run();
|
|
4624
4669
|
} else if (latestAttributes) {
|
|
4625
|
-
tx.update(gbpAttributesSnapshots).set({ syncedAt: insertNow, syncRunId: runId }).where(
|
|
4670
|
+
tx.update(gbpAttributesSnapshots).set({ syncedAt: insertNow, syncRunId: runId }).where(eq5(gbpAttributesSnapshots.id, latestAttributes.id)).run();
|
|
4626
4671
|
}
|
|
4627
4672
|
if (placeToWrite) {
|
|
4628
4673
|
tx.insert(gbpPlaceDetails).values({
|
|
4629
|
-
id:
|
|
4674
|
+
id: crypto7.randomUUID(),
|
|
4630
4675
|
projectId,
|
|
4631
4676
|
locationName: loc.locationName,
|
|
4632
4677
|
placeId: placeToWrite.placeId,
|
|
@@ -4637,9 +4682,9 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4637
4682
|
syncRunId: runId
|
|
4638
4683
|
}).run();
|
|
4639
4684
|
} else if (placeToTouch) {
|
|
4640
|
-
tx.update(gbpPlaceDetails).set({ syncedAt: insertNow, syncRunId: runId }).where(
|
|
4685
|
+
tx.update(gbpPlaceDetails).set({ syncedAt: insertNow, syncRunId: runId }).where(eq5(gbpPlaceDetails.id, placeToTouch)).run();
|
|
4641
4686
|
}
|
|
4642
|
-
tx.update(gbpLocations).set({ syncedAt: insertNow, updatedAt: insertNow }).where(
|
|
4687
|
+
tx.update(gbpLocations).set({ syncedAt: insertNow, updatedAt: insertNow }).where(eq5(gbpLocations.id, loc.id)).run();
|
|
4643
4688
|
});
|
|
4644
4689
|
okCount++;
|
|
4645
4690
|
} catch (err) {
|
|
@@ -4650,24 +4695,24 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4650
4695
|
}
|
|
4651
4696
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4652
4697
|
if (errors.size === 0) {
|
|
4653
|
-
db.update(runs).set({ status: "completed", finishedAt }).where(
|
|
4698
|
+
db.update(runs).set({ status: "completed", finishedAt }).where(eq5(runs.id, runId)).run();
|
|
4654
4699
|
} else if (okCount > 0) {
|
|
4655
4700
|
db.update(runs).set({
|
|
4656
4701
|
status: "partial",
|
|
4657
4702
|
error: serializeRunError(buildRunErrorFromMessages(errors)),
|
|
4658
4703
|
finishedAt
|
|
4659
|
-
}).where(
|
|
4704
|
+
}).where(eq5(runs.id, runId)).run();
|
|
4660
4705
|
} else {
|
|
4661
4706
|
db.update(runs).set({
|
|
4662
4707
|
status: "failed",
|
|
4663
4708
|
error: serializeRunError(buildRunErrorFromMessages(errors)),
|
|
4664
4709
|
finishedAt
|
|
4665
|
-
}).where(
|
|
4710
|
+
}).where(eq5(runs.id, runId)).run();
|
|
4666
4711
|
}
|
|
4667
4712
|
log3.info("sync.done", { runId, projectId, ok: okCount, failed: errors.size });
|
|
4668
4713
|
} catch (err) {
|
|
4669
4714
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
4670
|
-
db.update(runs).set({ status: "failed", error: serializeRunError({ message: errorMsg }), finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
4715
|
+
db.update(runs).set({ status: "failed", error: serializeRunError({ message: errorMsg }), finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq5(runs.id, runId)).run();
|
|
4671
4716
|
log3.error("sync.failed", { runId, projectId, error: errorMsg });
|
|
4672
4717
|
throw err;
|
|
4673
4718
|
}
|
|
@@ -4705,7 +4750,7 @@ async function refreshSelectedLocationProfiles(db, projectId, accessToken, locat
|
|
|
4705
4750
|
...profile,
|
|
4706
4751
|
updatedAt
|
|
4707
4752
|
};
|
|
4708
|
-
db.update(gbpLocations).set(update).where(
|
|
4753
|
+
db.update(gbpLocations).set(update).where(and5(eq5(gbpLocations.projectId, projectId), eq5(gbpLocations.id, row.id))).run();
|
|
4709
4754
|
refreshedRows.set(row.id, { ...row, ...update });
|
|
4710
4755
|
}
|
|
4711
4756
|
}
|
|
@@ -4713,8 +4758,8 @@ async function refreshSelectedLocationProfiles(db, projectId, accessToken, locat
|
|
|
4713
4758
|
}
|
|
4714
4759
|
|
|
4715
4760
|
// src/ads-sync.ts
|
|
4716
|
-
import
|
|
4717
|
-
import { eq as
|
|
4761
|
+
import crypto8 from "crypto";
|
|
4762
|
+
import { eq as eq6 } from "drizzle-orm";
|
|
4718
4763
|
|
|
4719
4764
|
// ../integration-openai-ads/src/constants.ts
|
|
4720
4765
|
var OPENAI_ADS_API_BASE = "https://api.ads.openai.com/v1";
|
|
@@ -5322,11 +5367,11 @@ function toDailyUpserts(level, entityId, read) {
|
|
|
5322
5367
|
}
|
|
5323
5368
|
async function executeAdsSync(db, runId, projectId, opts) {
|
|
5324
5369
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5325
|
-
db.update(runs).set({ status: "running", startedAt: now }).where(
|
|
5370
|
+
db.update(runs).set({ status: "running", startedAt: now }).where(eq6(runs.id, runId)).run();
|
|
5326
5371
|
try {
|
|
5327
|
-
const project = db.select().from(projects).where(
|
|
5372
|
+
const project = db.select().from(projects).where(eq6(projects.id, projectId)).get();
|
|
5328
5373
|
if (!project) throw new Error(`Project not found: ${projectId}`);
|
|
5329
|
-
const connRow = db.select().from(adsConnections).where(
|
|
5374
|
+
const connRow = db.select().from(adsConnections).where(eq6(adsConnections.projectId, projectId)).get();
|
|
5330
5375
|
if (!connRow) {
|
|
5331
5376
|
throw new Error('No ads connection found for this project. Run "canonry ads connect" first.');
|
|
5332
5377
|
}
|
|
@@ -5386,7 +5431,7 @@ async function executeAdsSync(db, runId, projectId, opts) {
|
|
|
5386
5431
|
);
|
|
5387
5432
|
const insertNow = (/* @__PURE__ */ new Date()).toISOString();
|
|
5388
5433
|
db.transaction((tx) => {
|
|
5389
|
-
tx.delete(adsCampaigns).where(
|
|
5434
|
+
tx.delete(adsCampaigns).where(eq6(adsCampaigns.projectId, projectId)).run();
|
|
5390
5435
|
for (const campaign of syncedCampaigns) {
|
|
5391
5436
|
tx.insert(adsCampaigns).values({
|
|
5392
5437
|
id: campaign.id,
|
|
@@ -5441,7 +5486,7 @@ async function executeAdsSync(db, runId, projectId, opts) {
|
|
|
5441
5486
|
}
|
|
5442
5487
|
for (const upsert of insightUpserts) {
|
|
5443
5488
|
tx.insert(adsInsightsDaily).values({
|
|
5444
|
-
id:
|
|
5489
|
+
id: crypto8.randomUUID(),
|
|
5445
5490
|
projectId,
|
|
5446
5491
|
level: upsert.level,
|
|
5447
5492
|
entityId: upsert.entityId,
|
|
@@ -5480,36 +5525,36 @@ async function executeAdsSync(db, runId, projectId, opts) {
|
|
|
5480
5525
|
conversionTrackingConfigured,
|
|
5481
5526
|
lastSyncedAt: insertNow,
|
|
5482
5527
|
updatedAt: insertNow
|
|
5483
|
-
}).where(
|
|
5528
|
+
}).where(eq6(adsConnections.projectId, projectId)).run();
|
|
5484
5529
|
});
|
|
5485
5530
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5486
5531
|
if (errors.size === 0) {
|
|
5487
|
-
db.update(runs).set({ status: "completed", finishedAt }).where(
|
|
5532
|
+
db.update(runs).set({ status: "completed", finishedAt }).where(eq6(runs.id, runId)).run();
|
|
5488
5533
|
} else if (syncedCampaigns.length > 0) {
|
|
5489
5534
|
db.update(runs).set({
|
|
5490
5535
|
status: "partial",
|
|
5491
5536
|
error: serializeRunError(buildRunErrorFromMessages(errors)),
|
|
5492
5537
|
finishedAt
|
|
5493
|
-
}).where(
|
|
5538
|
+
}).where(eq6(runs.id, runId)).run();
|
|
5494
5539
|
} else {
|
|
5495
5540
|
db.update(runs).set({
|
|
5496
5541
|
status: "failed",
|
|
5497
5542
|
error: serializeRunError(buildRunErrorFromMessages(errors)),
|
|
5498
5543
|
finishedAt
|
|
5499
|
-
}).where(
|
|
5544
|
+
}).where(eq6(runs.id, runId)).run();
|
|
5500
5545
|
}
|
|
5501
5546
|
log4.info("sync.done", { runId, projectId, campaigns: syncedCampaigns.length, insightRows: insightUpserts.length, failed: errors.size });
|
|
5502
5547
|
} catch (err) {
|
|
5503
5548
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
5504
|
-
db.update(runs).set({ status: "failed", error: serializeRunError({ message: errorMsg }), finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
5549
|
+
db.update(runs).set({ status: "failed", error: serializeRunError({ message: errorMsg }), finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq6(runs.id, runId)).run();
|
|
5505
5550
|
log4.error("sync.failed", { runId, projectId, error: errorMsg });
|
|
5506
5551
|
throw err;
|
|
5507
5552
|
}
|
|
5508
5553
|
}
|
|
5509
5554
|
|
|
5510
5555
|
// src/gsc-inspect-sitemap.ts
|
|
5511
|
-
import
|
|
5512
|
-
import { eq as
|
|
5556
|
+
import crypto9 from "crypto";
|
|
5557
|
+
import { eq as eq7 } from "drizzle-orm";
|
|
5513
5558
|
|
|
5514
5559
|
// src/sitemap-parser.ts
|
|
5515
5560
|
var log5 = createLogger("SitemapParser");
|
|
@@ -5610,6 +5655,8 @@ async function parseSitemapRecursive(url, urls, visited, depth, isChild) {
|
|
|
5610
5655
|
// src/gsc-inspect-paced.ts
|
|
5611
5656
|
var INSPECT_BASE_DELAY_MS = 1e3;
|
|
5612
5657
|
var INSPECT_MAX_CONCURRENCY = 5;
|
|
5658
|
+
var INSPECT_DAILY_QUOTA = 2e3;
|
|
5659
|
+
var INSPECT_SWEEP_MAX_URLS = 1500;
|
|
5613
5660
|
var INSPECT_PACING_JITTER_MS = 250;
|
|
5614
5661
|
var INSPECT_MAX_RETRIES = 3;
|
|
5615
5662
|
var INSPECT_MAX_BACKOFF_MS = 3e4;
|
|
@@ -5692,13 +5739,13 @@ async function inspectUrlsPaced(urls, cb, deps = {}) {
|
|
|
5692
5739
|
var log6 = createLogger("InspectSitemap");
|
|
5693
5740
|
async function executeInspectSitemap(db, runId, projectId, opts) {
|
|
5694
5741
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5695
|
-
db.update(runs).set({ status: "running", startedAt: now }).where(
|
|
5742
|
+
db.update(runs).set({ status: "running", startedAt: now }).where(eq7(runs.id, runId)).run();
|
|
5696
5743
|
try {
|
|
5697
5744
|
const { clientId: googleClientId, clientSecret: googleClientSecret } = getGoogleAuthConfig(opts.config);
|
|
5698
5745
|
if (!googleClientId || !googleClientSecret) {
|
|
5699
5746
|
throw new Error("Google OAuth is not configured in the local Canonry config");
|
|
5700
5747
|
}
|
|
5701
|
-
const project = db.select().from(projects).where(
|
|
5748
|
+
const project = db.select().from(projects).where(eq7(projects.id, projectId)).get();
|
|
5702
5749
|
if (!project) {
|
|
5703
5750
|
throw new Error(`Project not found: ${projectId}`);
|
|
5704
5751
|
}
|
|
@@ -5729,8 +5776,21 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
|
|
|
5729
5776
|
if (urls.length === 0) {
|
|
5730
5777
|
throw new Error("No URLs found in sitemap");
|
|
5731
5778
|
}
|
|
5779
|
+
const skipped = Math.max(0, urls.length - INSPECT_SWEEP_MAX_URLS);
|
|
5780
|
+
const targetUrls = skipped > 0 ? urls.slice(0, INSPECT_SWEEP_MAX_URLS) : urls;
|
|
5781
|
+
if (skipped > 0) {
|
|
5782
|
+
log6.warn("sitemap.over-budget", {
|
|
5783
|
+
runId,
|
|
5784
|
+
projectId,
|
|
5785
|
+
sitemapUrls: urls.length,
|
|
5786
|
+
inspecting: targetUrls.length,
|
|
5787
|
+
skipped,
|
|
5788
|
+
dailyQuota: INSPECT_DAILY_QUOTA,
|
|
5789
|
+
note: `Sitemap has ${urls.length} pages; Google allows ${INSPECT_DAILY_QUOTA} URL inspections per property per day. Inspecting the first ${targetUrls.length}; ${skipped} pages will not have a verdict from this run.`
|
|
5790
|
+
});
|
|
5791
|
+
}
|
|
5732
5792
|
const { inspected, errors, aborted, abortError } = await inspectUrlsPaced(
|
|
5733
|
-
|
|
5793
|
+
targetUrls,
|
|
5734
5794
|
{
|
|
5735
5795
|
inspectOne: (pageUrl) => inspectUrl(accessToken, pageUrl, propertyId),
|
|
5736
5796
|
onResult: (pageUrl, result, index) => {
|
|
@@ -5740,7 +5800,7 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
|
|
|
5740
5800
|
const rich = ir.richResultsResult;
|
|
5741
5801
|
const inspectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5742
5802
|
db.insert(gscUrlInspections).values({
|
|
5743
|
-
id:
|
|
5803
|
+
id: crypto9.randomUUID(),
|
|
5744
5804
|
projectId,
|
|
5745
5805
|
syncRunId: runId,
|
|
5746
5806
|
url: pageUrl,
|
|
@@ -5776,52 +5836,24 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
|
|
|
5776
5836
|
`URL inspection aborted after ${INSPECT_FAILFAST_THRESHOLD} consecutive rate/access failures (likely GSC URL Inspection quota exhaustion or property access loss). Last error: ${detail}`
|
|
5777
5837
|
);
|
|
5778
5838
|
}
|
|
5779
|
-
const
|
|
5780
|
-
const
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
}
|
|
5786
|
-
}
|
|
5787
|
-
let snapIndexed = 0;
|
|
5788
|
-
let snapNotIndexed = 0;
|
|
5789
|
-
const reasonCounts = {};
|
|
5790
|
-
for (const [, row] of latestByUrl) {
|
|
5791
|
-
if (row.indexingState === "INDEXING_ALLOWED") {
|
|
5792
|
-
snapIndexed++;
|
|
5793
|
-
} else {
|
|
5794
|
-
snapNotIndexed++;
|
|
5795
|
-
const reason = row.coverageState ?? "Unknown";
|
|
5796
|
-
reasonCounts[reason] = (reasonCounts[reason] ?? 0) + 1;
|
|
5797
|
-
}
|
|
5798
|
-
}
|
|
5799
|
-
const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
5800
|
-
db.delete(gscCoverageSnapshots).where(and5(eq6(gscCoverageSnapshots.projectId, projectId), eq6(gscCoverageSnapshots.date, snapshotDate))).run();
|
|
5801
|
-
db.insert(gscCoverageSnapshots).values({
|
|
5802
|
-
id: crypto8.randomUUID(),
|
|
5803
|
-
projectId,
|
|
5804
|
-
syncRunId: runId,
|
|
5805
|
-
date: snapshotDate,
|
|
5806
|
-
indexed: snapIndexed,
|
|
5807
|
-
notIndexed: snapNotIndexed,
|
|
5808
|
-
reasonBreakdown: reasonCounts,
|
|
5809
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5810
|
-
}).run();
|
|
5811
|
-
const status = errors > 0 && inspected > 0 ? "partial" : errors === urls.length ? "failed" : "completed";
|
|
5812
|
-
db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq6(runs.id, runId)).run();
|
|
5839
|
+
const coverage = writeCoverageSnapshot(db, projectId, runId);
|
|
5840
|
+
const snapIndexed = coverage.indexed;
|
|
5841
|
+
const snapNotIndexed = coverage.notIndexed;
|
|
5842
|
+
const attempted = targetUrls.length;
|
|
5843
|
+
const status = skipped > 0 || errors > 0 && inspected > 0 ? "partial" : errors === attempted ? "failed" : "completed";
|
|
5844
|
+
db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq7(runs.id, runId)).run();
|
|
5813
5845
|
log6.info("inspect.completed", { runId, projectId, inspected, errors, total: urls.length, indexed: snapIndexed, notIndexed: snapNotIndexed });
|
|
5814
5846
|
} catch (err) {
|
|
5815
5847
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
5816
|
-
db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
5848
|
+
db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq7(runs.id, runId)).run();
|
|
5817
5849
|
log6.error("inspect.failed", { runId, projectId, error: errorMsg });
|
|
5818
5850
|
throw err;
|
|
5819
5851
|
}
|
|
5820
5852
|
}
|
|
5821
5853
|
|
|
5822
5854
|
// src/bing-inspect-sitemap.ts
|
|
5823
|
-
import
|
|
5824
|
-
import { eq as
|
|
5855
|
+
import crypto10 from "crypto";
|
|
5856
|
+
import { eq as eq8, desc as desc3 } from "drizzle-orm";
|
|
5825
5857
|
var log7 = createLogger("BingInspectSitemap");
|
|
5826
5858
|
function parseBingDate(value) {
|
|
5827
5859
|
if (!value) return null;
|
|
@@ -5839,9 +5871,9 @@ function isBlockingIssueType(issueType) {
|
|
|
5839
5871
|
}
|
|
5840
5872
|
async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
5841
5873
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5842
|
-
db.update(runs).set({ status: RunStatuses.running, startedAt }).where(
|
|
5874
|
+
db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq8(runs.id, runId)).run();
|
|
5843
5875
|
try {
|
|
5844
|
-
const project = db.select().from(projects).where(
|
|
5876
|
+
const project = db.select().from(projects).where(eq8(projects.id, projectId)).get();
|
|
5845
5877
|
if (!project) {
|
|
5846
5878
|
throw new Error(`Project not found: ${projectId}`);
|
|
5847
5879
|
}
|
|
@@ -5859,7 +5891,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
5859
5891
|
if (sitemapUrls.length === 0) {
|
|
5860
5892
|
throw new Error("No URLs found in sitemap");
|
|
5861
5893
|
}
|
|
5862
|
-
const trackedRows = db.select({ url: bingUrlInspections.url }).from(bingUrlInspections).where(
|
|
5894
|
+
const trackedRows = db.select({ url: bingUrlInspections.url }).from(bingUrlInspections).where(eq8(bingUrlInspections.projectId, projectId)).all();
|
|
5863
5895
|
const trackedUrls = new Set(trackedRows.map((r) => r.url));
|
|
5864
5896
|
const discovered = sitemapUrls.filter((u) => !trackedUrls.has(u));
|
|
5865
5897
|
log7.info("sitemap.diff", {
|
|
@@ -5908,7 +5940,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
5908
5940
|
derivedInIndex = false;
|
|
5909
5941
|
}
|
|
5910
5942
|
db.insert(bingUrlInspections).values({
|
|
5911
|
-
id:
|
|
5943
|
+
id: crypto10.randomUUID(),
|
|
5912
5944
|
projectId,
|
|
5913
5945
|
url: pageUrl,
|
|
5914
5946
|
httpCode,
|
|
@@ -5942,7 +5974,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
5942
5974
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
5943
5975
|
}
|
|
5944
5976
|
}
|
|
5945
|
-
const allInspections = db.select().from(bingUrlInspections).where(
|
|
5977
|
+
const allInspections = db.select().from(bingUrlInspections).where(eq8(bingUrlInspections.projectId, projectId)).orderBy(desc3(bingUrlInspections.inspectedAt)).all();
|
|
5946
5978
|
const latestByUrl = /* @__PURE__ */ new Map();
|
|
5947
5979
|
const definitiveByUrl = /* @__PURE__ */ new Map();
|
|
5948
5980
|
for (const row of allInspections) {
|
|
@@ -5966,7 +5998,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
5966
5998
|
const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
5967
5999
|
const snapNow = (/* @__PURE__ */ new Date()).toISOString();
|
|
5968
6000
|
db.insert(bingCoverageSnapshots).values({
|
|
5969
|
-
id:
|
|
6001
|
+
id: crypto10.randomUUID(),
|
|
5970
6002
|
projectId,
|
|
5971
6003
|
syncRunId: runId,
|
|
5972
6004
|
date: snapshotDate,
|
|
@@ -5985,7 +6017,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
5985
6017
|
}
|
|
5986
6018
|
}).run();
|
|
5987
6019
|
const status = errors === sitemapUrls.length ? RunStatuses.failed : errors > 0 ? RunStatuses.partial : RunStatuses.completed;
|
|
5988
|
-
db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
6020
|
+
db.update(runs).set({ status, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq8(runs.id, runId)).run();
|
|
5989
6021
|
log7.info("inspect.completed", {
|
|
5990
6022
|
runId,
|
|
5991
6023
|
projectId,
|
|
@@ -5999,15 +6031,15 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
5999
6031
|
});
|
|
6000
6032
|
} catch (err) {
|
|
6001
6033
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
6002
|
-
db.update(runs).set({ status: RunStatuses.failed, error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
6034
|
+
db.update(runs).set({ status: RunStatuses.failed, error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq8(runs.id, runId)).run();
|
|
6003
6035
|
log7.error("inspect.failed", { runId, projectId, error: errorMsg });
|
|
6004
6036
|
throw err;
|
|
6005
6037
|
}
|
|
6006
6038
|
}
|
|
6007
6039
|
|
|
6008
6040
|
// src/coverage-refresh.ts
|
|
6009
|
-
import
|
|
6010
|
-
import { and as and6, desc as
|
|
6041
|
+
import crypto11 from "crypto";
|
|
6042
|
+
import { and as and6, desc as desc4, eq as eq9, inArray as inArray3 } from "drizzle-orm";
|
|
6011
6043
|
var log8 = createLogger("CoverageRefresh");
|
|
6012
6044
|
var COVERAGE_REFRESH_MIN_INTERVAL_MS = 60 * 60 * 1e3;
|
|
6013
6045
|
var ACTIVE_OR_DONE_STATUSES = [
|
|
@@ -6018,7 +6050,7 @@ var ACTIVE_OR_DONE_STATUSES = [
|
|
|
6018
6050
|
];
|
|
6019
6051
|
var defaultDeps = { executeInspectSitemap };
|
|
6020
6052
|
async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps, nowMs = Date.now()) {
|
|
6021
|
-
const project = db.select({ canonicalDomain: projects.canonicalDomain }).from(projects).where(
|
|
6053
|
+
const project = db.select({ canonicalDomain: projects.canonicalDomain }).from(projects).where(eq9(projects.id, projectId)).get();
|
|
6022
6054
|
if (!project) return null;
|
|
6023
6055
|
const { clientId, clientSecret } = getGoogleAuthConfig(config);
|
|
6024
6056
|
if (!clientId || !clientSecret) return null;
|
|
@@ -6026,11 +6058,11 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
|
|
|
6026
6058
|
if (!conn?.refreshToken || !conn.propertyId) return null;
|
|
6027
6059
|
const recent = db.select({ createdAt: runs.createdAt }).from(runs).where(
|
|
6028
6060
|
and6(
|
|
6029
|
-
|
|
6030
|
-
|
|
6061
|
+
eq9(runs.projectId, projectId),
|
|
6062
|
+
eq9(runs.kind, RunKinds["inspect-sitemap"]),
|
|
6031
6063
|
inArray3(runs.status, ACTIVE_OR_DONE_STATUSES)
|
|
6032
6064
|
)
|
|
6033
|
-
).orderBy(
|
|
6065
|
+
).orderBy(desc4(runs.createdAt)).limit(1).get();
|
|
6034
6066
|
if (recent) {
|
|
6035
6067
|
const ageMs = nowMs - Date.parse(recent.createdAt);
|
|
6036
6068
|
if (Number.isFinite(ageMs) && ageMs < COVERAGE_REFRESH_MIN_INTERVAL_MS) {
|
|
@@ -6038,7 +6070,7 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
|
|
|
6038
6070
|
return null;
|
|
6039
6071
|
}
|
|
6040
6072
|
}
|
|
6041
|
-
const runId =
|
|
6073
|
+
const runId = crypto11.randomUUID();
|
|
6042
6074
|
db.insert(runs).values({
|
|
6043
6075
|
id: runId,
|
|
6044
6076
|
projectId,
|
|
@@ -6061,9 +6093,9 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
|
|
|
6061
6093
|
}
|
|
6062
6094
|
|
|
6063
6095
|
// src/commoncrawl-sync.ts
|
|
6064
|
-
import
|
|
6096
|
+
import crypto12 from "crypto";
|
|
6065
6097
|
import path4 from "path";
|
|
6066
|
-
import { and as and7, eq as
|
|
6098
|
+
import { and as and7, eq as eq10, sql as sql4 } from "drizzle-orm";
|
|
6067
6099
|
var log9 = createLogger("CommonCrawlSync");
|
|
6068
6100
|
var INSERT_CHUNK_SIZE = 1e4;
|
|
6069
6101
|
function defaultDeps2() {
|
|
@@ -6089,7 +6121,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
6089
6121
|
phaseDetail: "downloading vertices + edges",
|
|
6090
6122
|
updatedAt: downloadStartedAt,
|
|
6091
6123
|
error: null
|
|
6092
|
-
}).where(
|
|
6124
|
+
}).where(eq10(ccReleaseSyncs.id, syncId)).run();
|
|
6093
6125
|
const paths = ccReleasePaths(release);
|
|
6094
6126
|
const releaseCacheDir = path4.join(deps.cacheDir, release);
|
|
6095
6127
|
const vertexPath = path4.join(releaseCacheDir, paths.vertexFilename);
|
|
@@ -6112,7 +6144,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
6112
6144
|
vertexSha256: vertex.sha256,
|
|
6113
6145
|
edgesSha256: edges.sha256,
|
|
6114
6146
|
updatedAt: downloadFinishedAt
|
|
6115
|
-
}).where(
|
|
6147
|
+
}).where(eq10(ccReleaseSyncs.id, syncId)).run();
|
|
6116
6148
|
const allProjects = db.select().from(projects).all();
|
|
6117
6149
|
const targets = Array.from(new Set(allProjects.map((p) => p.canonicalDomain)));
|
|
6118
6150
|
let rows = [];
|
|
@@ -6128,15 +6160,15 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
6128
6160
|
}
|
|
6129
6161
|
const queriedAt = deps.now().toISOString();
|
|
6130
6162
|
db.transaction((tx) => {
|
|
6131
|
-
tx.delete(backlinkDomains).where(
|
|
6132
|
-
tx.delete(backlinkSummaries).where(
|
|
6163
|
+
tx.delete(backlinkDomains).where(eq10(backlinkDomains.releaseSyncId, syncId)).run();
|
|
6164
|
+
tx.delete(backlinkSummaries).where(eq10(backlinkSummaries.releaseSyncId, syncId)).run();
|
|
6133
6165
|
const expanded = [];
|
|
6134
6166
|
for (const r of rows) {
|
|
6135
6167
|
const projectIds = projectsByDomain.get(r.targetDomain);
|
|
6136
6168
|
if (!projectIds) continue;
|
|
6137
6169
|
for (const projectId of projectIds) {
|
|
6138
6170
|
expanded.push({
|
|
6139
|
-
id:
|
|
6171
|
+
id: crypto12.randomUUID(),
|
|
6140
6172
|
projectId,
|
|
6141
6173
|
releaseSyncId: syncId,
|
|
6142
6174
|
release,
|
|
@@ -6156,7 +6188,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
6156
6188
|
const projectRows = rowsByProject.get(p.id) ?? [];
|
|
6157
6189
|
const summary = computeSummary(projectRows);
|
|
6158
6190
|
tx.insert(backlinkSummaries).values({
|
|
6159
|
-
id:
|
|
6191
|
+
id: crypto12.randomUUID(),
|
|
6160
6192
|
projectId: p.id,
|
|
6161
6193
|
releaseSyncId: syncId,
|
|
6162
6194
|
source: BacklinkSources.commoncrawl,
|
|
@@ -6189,7 +6221,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
6189
6221
|
domainsDiscovered: rows.length,
|
|
6190
6222
|
updatedAt: finishedAt,
|
|
6191
6223
|
error: null
|
|
6192
|
-
}).where(
|
|
6224
|
+
}).where(eq10(ccReleaseSyncs.id, syncId)).run();
|
|
6193
6225
|
log9.info("sync.completed", {
|
|
6194
6226
|
syncId,
|
|
6195
6227
|
release,
|
|
@@ -6219,7 +6251,7 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
6219
6251
|
error: errorMsg,
|
|
6220
6252
|
phaseDetail: null,
|
|
6221
6253
|
updatedAt: finishedAt
|
|
6222
|
-
}).where(
|
|
6254
|
+
}).where(eq10(ccReleaseSyncs.id, syncId)).run();
|
|
6223
6255
|
log9.error("sync.failed", { syncId, release, error: errorMsg });
|
|
6224
6256
|
throw err;
|
|
6225
6257
|
}
|
|
@@ -6253,9 +6285,9 @@ function computeSummary(rows) {
|
|
|
6253
6285
|
}
|
|
6254
6286
|
|
|
6255
6287
|
// src/backlink-extract.ts
|
|
6256
|
-
import
|
|
6288
|
+
import crypto13 from "crypto";
|
|
6257
6289
|
import fs3 from "fs";
|
|
6258
|
-
import { and as and8, desc as
|
|
6290
|
+
import { and as and8, desc as desc5, eq as eq11 } from "drizzle-orm";
|
|
6259
6291
|
var log10 = createLogger("BacklinkExtract");
|
|
6260
6292
|
function defaultDeps3() {
|
|
6261
6293
|
return {
|
|
@@ -6267,13 +6299,13 @@ function defaultDeps3() {
|
|
|
6267
6299
|
async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
6268
6300
|
const deps = { ...defaultDeps3(), ...opts.deps };
|
|
6269
6301
|
const startedAt = deps.now().toISOString();
|
|
6270
|
-
db.update(runs).set({ status: RunStatuses.running, startedAt }).where(
|
|
6302
|
+
db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq11(runs.id, runId)).run();
|
|
6271
6303
|
try {
|
|
6272
|
-
const project = db.select().from(projects).where(
|
|
6304
|
+
const project = db.select().from(projects).where(eq11(projects.id, projectId)).get();
|
|
6273
6305
|
if (!project) {
|
|
6274
6306
|
throw new Error(`Project not found: ${projectId}`);
|
|
6275
6307
|
}
|
|
6276
|
-
const sync = opts.release ? db.select().from(ccReleaseSyncs).where(
|
|
6308
|
+
const sync = opts.release ? db.select().from(ccReleaseSyncs).where(eq11(ccReleaseSyncs.release, opts.release)).get() : db.select().from(ccReleaseSyncs).where(eq11(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).orderBy(desc5(ccReleaseSyncs.createdAt)).limit(1).get();
|
|
6277
6309
|
if (!sync) {
|
|
6278
6310
|
throw new Error("No ready release sync available \u2014 run `canonry backlinks sync` first");
|
|
6279
6311
|
}
|
|
@@ -6302,14 +6334,14 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
6302
6334
|
db.transaction((tx) => {
|
|
6303
6335
|
tx.delete(backlinkDomains).where(
|
|
6304
6336
|
and8(
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6337
|
+
eq11(backlinkDomains.projectId, projectId),
|
|
6338
|
+
eq11(backlinkDomains.source, BacklinkSources.commoncrawl),
|
|
6339
|
+
eq11(backlinkDomains.release, release)
|
|
6308
6340
|
)
|
|
6309
6341
|
).run();
|
|
6310
6342
|
if (rows.length > 0) {
|
|
6311
6343
|
const values = rows.map((r) => ({
|
|
6312
|
-
id:
|
|
6344
|
+
id: crypto13.randomUUID(),
|
|
6313
6345
|
projectId,
|
|
6314
6346
|
releaseSyncId: syncId,
|
|
6315
6347
|
source: BacklinkSources.commoncrawl,
|
|
@@ -6323,7 +6355,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
6323
6355
|
}
|
|
6324
6356
|
const summary = computeSummary2(rows);
|
|
6325
6357
|
tx.insert(backlinkSummaries).values({
|
|
6326
|
-
id:
|
|
6358
|
+
id: crypto13.randomUUID(),
|
|
6327
6359
|
projectId,
|
|
6328
6360
|
releaseSyncId: syncId,
|
|
6329
6361
|
source: BacklinkSources.commoncrawl,
|
|
@@ -6347,7 +6379,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
6347
6379
|
}).run();
|
|
6348
6380
|
});
|
|
6349
6381
|
const finishedAt = deps.now().toISOString();
|
|
6350
|
-
db.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(
|
|
6382
|
+
db.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq11(runs.id, runId)).run();
|
|
6351
6383
|
log10.info("extract.completed", { runId, projectId, release, rows: rows.length });
|
|
6352
6384
|
} catch (err) {
|
|
6353
6385
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -6356,7 +6388,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
6356
6388
|
status: RunStatuses.failed,
|
|
6357
6389
|
error: errorMsg,
|
|
6358
6390
|
finishedAt
|
|
6359
|
-
}).where(
|
|
6391
|
+
}).where(eq11(runs.id, runId)).run();
|
|
6360
6392
|
log10.error("extract.failed", { runId, projectId, error: errorMsg });
|
|
6361
6393
|
throw err;
|
|
6362
6394
|
}
|
|
@@ -6366,8 +6398,8 @@ function computeSummary2(rows) {
|
|
|
6366
6398
|
}
|
|
6367
6399
|
|
|
6368
6400
|
// src/discovery-run.ts
|
|
6369
|
-
import
|
|
6370
|
-
import { and as and9, eq as
|
|
6401
|
+
import crypto14 from "crypto";
|
|
6402
|
+
import { and as and9, eq as eq12 } from "drizzle-orm";
|
|
6371
6403
|
var log11 = createLogger("DiscoveryRun");
|
|
6372
6404
|
var DEFAULT_SEED_COUNT = 30;
|
|
6373
6405
|
var EMBED_RETRY_MAX_RETRIES = 3;
|
|
@@ -6409,11 +6441,11 @@ async function embedWithRetry(fn, opts = {}) {
|
|
|
6409
6441
|
var QUERIES_PER_INTENT_BUCKET = 6;
|
|
6410
6442
|
async function executeDiscoveryRun(opts) {
|
|
6411
6443
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6412
|
-
opts.db.update(runs).set({ status: RunStatuses.running, startedAt }).where(
|
|
6444
|
+
opts.db.update(runs).set({ status: RunStatuses.running, startedAt }).where(eq12(runs.id, opts.runId)).run();
|
|
6413
6445
|
try {
|
|
6414
|
-
const projectRow = opts.db.select().from(projects).where(
|
|
6446
|
+
const projectRow = opts.db.select().from(projects).where(eq12(projects.id, opts.projectId)).get();
|
|
6415
6447
|
if (!projectRow) throw new Error(`Project ${opts.projectId} not found`);
|
|
6416
|
-
const projectCompetitors = opts.db.select({ domain: competitors.domain }).from(competitors).where(
|
|
6448
|
+
const projectCompetitors = opts.db.select({ domain: competitors.domain }).from(competitors).where(eq12(competitors.projectId, opts.projectId)).all().map((r) => r.domain.toLowerCase());
|
|
6417
6449
|
const canonicalDomains = effectiveDomains({
|
|
6418
6450
|
canonicalDomain: projectRow.canonicalDomain,
|
|
6419
6451
|
ownedDomains: projectRow.ownedDomains
|
|
@@ -6451,7 +6483,7 @@ async function executeDiscoveryRun(opts) {
|
|
|
6451
6483
|
seedProvider: result.seedProvider,
|
|
6452
6484
|
result
|
|
6453
6485
|
});
|
|
6454
|
-
opts.db.update(runs).set({ status: RunStatuses.completed, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
6486
|
+
opts.db.update(runs).set({ status: RunStatuses.completed, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq12(runs.id, opts.runId)).run();
|
|
6455
6487
|
log11.info("discovery.completed", {
|
|
6456
6488
|
runId: opts.runId,
|
|
6457
6489
|
sessionId: opts.sessionId,
|
|
@@ -6466,7 +6498,7 @@ async function executeDiscoveryRun(opts) {
|
|
|
6466
6498
|
status: RunStatuses.failed,
|
|
6467
6499
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6468
6500
|
error: errorMsg
|
|
6469
|
-
}).where(
|
|
6501
|
+
}).where(eq12(runs.id, opts.runId)).run();
|
|
6470
6502
|
}
|
|
6471
6503
|
}
|
|
6472
6504
|
function buildDefaultDeps(registry) {
|
|
@@ -6716,12 +6748,12 @@ function writeDiscoveryInsight(db, input) {
|
|
|
6716
6748
|
});
|
|
6717
6749
|
db.transaction((tx) => {
|
|
6718
6750
|
tx.update(insights).set({ dismissed: true }).where(and9(
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6751
|
+
eq12(insights.projectId, input.projectId),
|
|
6752
|
+
eq12(insights.type, "discovery.basket-divergence"),
|
|
6753
|
+
eq12(insights.dismissed, false)
|
|
6722
6754
|
)).run();
|
|
6723
6755
|
tx.insert(insights).values({
|
|
6724
|
-
id:
|
|
6756
|
+
id: crypto14.randomUUID(),
|
|
6725
6757
|
projectId: input.projectId,
|
|
6726
6758
|
runId: input.runId,
|
|
6727
6759
|
type: "discovery.basket-divergence",
|
|
@@ -6757,8 +6789,8 @@ function buildDiscoveryInsightTitle(input) {
|
|
|
6757
6789
|
}
|
|
6758
6790
|
|
|
6759
6791
|
// src/execute-site-audit.ts
|
|
6760
|
-
import
|
|
6761
|
-
import { eq as
|
|
6792
|
+
import crypto15 from "crypto";
|
|
6793
|
+
import { eq as eq13 } from "drizzle-orm";
|
|
6762
6794
|
import { runSitemapAudit } from "@ainyc/aeo-audit";
|
|
6763
6795
|
var log12 = createLogger("SiteAudit");
|
|
6764
6796
|
var SITE_AUDIT_DEFAULT_PAGE_LIMIT = 500;
|
|
@@ -6821,9 +6853,9 @@ function computeFactorAverages(pages) {
|
|
|
6821
6853
|
}
|
|
6822
6854
|
async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
6823
6855
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6824
|
-
db.update(runs).set({ status: "running", startedAt }).where(
|
|
6856
|
+
db.update(runs).set({ status: "running", startedAt }).where(eq13(runs.id, runId)).run();
|
|
6825
6857
|
try {
|
|
6826
|
-
const project = db.select().from(projects).where(
|
|
6858
|
+
const project = db.select().from(projects).where(eq13(projects.id, projectId)).get();
|
|
6827
6859
|
if (!project) {
|
|
6828
6860
|
throw new Error(`Project not found: ${projectId}`);
|
|
6829
6861
|
}
|
|
@@ -6856,7 +6888,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
6856
6888
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6857
6889
|
db.transaction((tx) => {
|
|
6858
6890
|
tx.insert(siteAuditSnapshots).values({
|
|
6859
|
-
id:
|
|
6891
|
+
id: crypto15.randomUUID(),
|
|
6860
6892
|
projectId,
|
|
6861
6893
|
runId,
|
|
6862
6894
|
sitemapUrl: report.sitemapUrl,
|
|
@@ -6885,7 +6917,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
6885
6917
|
}).run();
|
|
6886
6918
|
for (const page of report.pages) {
|
|
6887
6919
|
tx.insert(siteAuditPages).values({
|
|
6888
|
-
id:
|
|
6920
|
+
id: crypto15.randomUUID(),
|
|
6889
6921
|
projectId,
|
|
6890
6922
|
runId,
|
|
6891
6923
|
url: page.url,
|
|
@@ -6896,7 +6928,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
6896
6928
|
createdAt: finishedAt
|
|
6897
6929
|
}).run();
|
|
6898
6930
|
}
|
|
6899
|
-
tx.update(runs).set({ status, finishedAt }).where(
|
|
6931
|
+
tx.update(runs).set({ status, finishedAt }).where(eq13(runs.id, runId)).run();
|
|
6900
6932
|
});
|
|
6901
6933
|
log12.info("completed", {
|
|
6902
6934
|
runId,
|
|
@@ -6908,14 +6940,14 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
6908
6940
|
});
|
|
6909
6941
|
} catch (err) {
|
|
6910
6942
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
6911
|
-
db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
6943
|
+
db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq13(runs.id, runId)).run();
|
|
6912
6944
|
log12.error("failed", { runId, projectId, error: errorMsg });
|
|
6913
6945
|
throw err;
|
|
6914
6946
|
}
|
|
6915
6947
|
}
|
|
6916
6948
|
|
|
6917
6949
|
// src/commands/backfill.ts
|
|
6918
|
-
import { and as and10, eq as
|
|
6950
|
+
import { and as and10, eq as eq14, inArray as inArray4, isNull, sql as sql5 } from "drizzle-orm";
|
|
6919
6951
|
var SNAPSHOT_BATCH_SIZE = 500;
|
|
6920
6952
|
async function backfillAnswerVisibilityCommand(opts) {
|
|
6921
6953
|
const config = loadConfig();
|
|
@@ -6923,7 +6955,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
6923
6955
|
migrate(db);
|
|
6924
6956
|
const projectFilter = opts?.project?.trim();
|
|
6925
6957
|
const isDryRun = opts?.dryRun === true;
|
|
6926
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
6958
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq14(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
6927
6959
|
let examined = 0;
|
|
6928
6960
|
let updated = 0;
|
|
6929
6961
|
let wouldUpdate = 0;
|
|
@@ -6932,9 +6964,9 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
6932
6964
|
let providerErrors = 0;
|
|
6933
6965
|
if (scopedProjects.length > 0) {
|
|
6934
6966
|
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and10(
|
|
6935
|
-
|
|
6967
|
+
eq14(runs.kind, RunKinds["answer-visibility"]),
|
|
6936
6968
|
inArray4(runs.projectId, scopedProjects.map((project) => project.id))
|
|
6937
|
-
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(
|
|
6969
|
+
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq14(runs.kind, RunKinds["answer-visibility"])).all();
|
|
6938
6970
|
const runIdsByProject = /* @__PURE__ */ new Map();
|
|
6939
6971
|
for (const run of runRows) {
|
|
6940
6972
|
const existing = runIdsByProject.get(run.projectId);
|
|
@@ -6942,7 +6974,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
6942
6974
|
else runIdsByProject.set(run.projectId, [run.id]);
|
|
6943
6975
|
}
|
|
6944
6976
|
for (const project of scopedProjects) {
|
|
6945
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(
|
|
6977
|
+
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq14(competitors.projectId, project.id)).all().map((row) => row.domain);
|
|
6946
6978
|
const runIds = runIdsByProject.get(project.id) ?? [];
|
|
6947
6979
|
if (runIds.length === 0) continue;
|
|
6948
6980
|
const projectDomains = effectiveDomains({
|
|
@@ -7035,7 +7067,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
7035
7067
|
} else {
|
|
7036
7068
|
db.transaction((tx) => {
|
|
7037
7069
|
for (const update of pendingUpdates) {
|
|
7038
|
-
tx.update(querySnapshots).set(update.patch).where(
|
|
7070
|
+
tx.update(querySnapshots).set(update.patch).where(eq14(querySnapshots.id, update.id)).run();
|
|
7039
7071
|
}
|
|
7040
7072
|
});
|
|
7041
7073
|
updated += pendingUpdates.length;
|
|
@@ -7084,7 +7116,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
|
|
|
7084
7116
|
function backfillNormalizedPaths(db, opts) {
|
|
7085
7117
|
const baseConditions = [];
|
|
7086
7118
|
if (opts?.projectId) {
|
|
7087
|
-
baseConditions.push(
|
|
7119
|
+
baseConditions.push(eq14(gaTrafficSnapshots.projectId, opts.projectId));
|
|
7088
7120
|
}
|
|
7089
7121
|
const rows = db.select({
|
|
7090
7122
|
id: gaTrafficSnapshots.id,
|
|
@@ -7105,7 +7137,7 @@ function backfillNormalizedPaths(db, opts) {
|
|
|
7105
7137
|
unchanged++;
|
|
7106
7138
|
continue;
|
|
7107
7139
|
}
|
|
7108
|
-
tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(
|
|
7140
|
+
tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq14(gaTrafficSnapshots.id, row.id)).run();
|
|
7109
7141
|
updated++;
|
|
7110
7142
|
}
|
|
7111
7143
|
});
|
|
@@ -7119,7 +7151,7 @@ async function backfillNormalizedPathsCommand(opts) {
|
|
|
7119
7151
|
const projectFilter = opts?.project?.trim();
|
|
7120
7152
|
let projectId;
|
|
7121
7153
|
if (projectFilter) {
|
|
7122
|
-
const project = db.select({ id: projects.id }).from(projects).where(
|
|
7154
|
+
const project = db.select({ id: projects.id }).from(projects).where(eq14(projects.name, projectFilter)).get();
|
|
7123
7155
|
if (!project) {
|
|
7124
7156
|
const result2 = {
|
|
7125
7157
|
project: projectFilter,
|
|
@@ -7156,7 +7188,7 @@ async function backfillNormalizedPathsCommand(opts) {
|
|
|
7156
7188
|
function backfillAiReferralPaths(db, opts) {
|
|
7157
7189
|
const baseConditions = [];
|
|
7158
7190
|
if (opts?.projectId) {
|
|
7159
|
-
baseConditions.push(
|
|
7191
|
+
baseConditions.push(eq14(gaAiReferrals.projectId, opts.projectId));
|
|
7160
7192
|
}
|
|
7161
7193
|
const rows = db.select({
|
|
7162
7194
|
id: gaAiReferrals.id,
|
|
@@ -7177,7 +7209,7 @@ function backfillAiReferralPaths(db, opts) {
|
|
|
7177
7209
|
unchanged++;
|
|
7178
7210
|
continue;
|
|
7179
7211
|
}
|
|
7180
|
-
tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(
|
|
7212
|
+
tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(eq14(gaAiReferrals.id, row.id)).run();
|
|
7181
7213
|
updated++;
|
|
7182
7214
|
}
|
|
7183
7215
|
});
|
|
@@ -7191,7 +7223,7 @@ async function backfillAiReferralPathsCommand(opts) {
|
|
|
7191
7223
|
const projectFilter = opts?.project?.trim();
|
|
7192
7224
|
let projectId;
|
|
7193
7225
|
if (projectFilter) {
|
|
7194
|
-
const project = db.select({ id: projects.id }).from(projects).where(
|
|
7226
|
+
const project = db.select({ id: projects.id }).from(projects).where(eq14(projects.name, projectFilter)).get();
|
|
7195
7227
|
if (!project) {
|
|
7196
7228
|
const result2 = {
|
|
7197
7229
|
project: projectFilter,
|
|
@@ -7227,10 +7259,10 @@ async function backfillAiReferralPathsCommand(opts) {
|
|
|
7227
7259
|
}
|
|
7228
7260
|
function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
7229
7261
|
const isDryRun = opts?.dryRun === true;
|
|
7230
|
-
const project = db.select().from(projects).where(
|
|
7262
|
+
const project = db.select().from(projects).where(eq14(projects.id, projectId)).get();
|
|
7231
7263
|
if (!project) return { examined: 0, updated: 0, mentioned: 0 };
|
|
7232
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(
|
|
7233
|
-
const runRows = db.select({ id: runs.id }).from(runs).where(and10(
|
|
7264
|
+
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq14(competitors.projectId, projectId)).all().map((row) => row.domain);
|
|
7265
|
+
const runRows = db.select({ id: runs.id }).from(runs).where(and10(eq14(runs.kind, RunKinds["answer-visibility"]), eq14(runs.projectId, projectId))).all();
|
|
7234
7266
|
const runIds = runRows.map((r) => r.id);
|
|
7235
7267
|
let examined = 0;
|
|
7236
7268
|
let updated = 0;
|
|
@@ -7304,7 +7336,7 @@ function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
|
7304
7336
|
} else {
|
|
7305
7337
|
db.transaction((tx) => {
|
|
7306
7338
|
for (const update of pendingUpdates) {
|
|
7307
|
-
tx.update(querySnapshots).set(update.patch).where(
|
|
7339
|
+
tx.update(querySnapshots).set(update.patch).where(eq14(querySnapshots.id, update.id)).run();
|
|
7308
7340
|
}
|
|
7309
7341
|
});
|
|
7310
7342
|
updated += pendingUpdates.length;
|
|
@@ -7319,7 +7351,7 @@ async function backfillAnswerMentionsCommand(opts) {
|
|
|
7319
7351
|
migrate(db);
|
|
7320
7352
|
const projectFilter = opts?.project?.trim();
|
|
7321
7353
|
const isDryRun = opts?.dryRun === true;
|
|
7322
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
7354
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq14(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
7323
7355
|
let examined = 0;
|
|
7324
7356
|
let updated = 0;
|
|
7325
7357
|
let wouldUpdate = 0;
|
|
@@ -7538,7 +7570,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
7538
7570
|
const config = loadConfig();
|
|
7539
7571
|
const db = createClient(config.database);
|
|
7540
7572
|
migrate(db);
|
|
7541
|
-
const project = db.select().from(projects).where(
|
|
7573
|
+
const project = db.select().from(projects).where(eq14(projects.name, opts.project)).get();
|
|
7542
7574
|
if (!project) {
|
|
7543
7575
|
throw new Error(`Project "${opts.project}" not found`);
|
|
7544
7576
|
}
|
|
@@ -7550,7 +7582,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
7550
7582
|
`);
|
|
7551
7583
|
}
|
|
7552
7584
|
const events = db.select({ createdAt: auditLog.createdAt, action: auditLog.action, diff: auditLog.diff }).from(auditLog).where(and10(
|
|
7553
|
-
|
|
7585
|
+
eq14(auditLog.projectId, project.id),
|
|
7554
7586
|
inArray4(auditLog.action, ["keywords.appended", "keywords.deleted", "queries.appended", "queries.deleted", "queries.replaced"])
|
|
7555
7587
|
)).orderBy(auditLog.createdAt).all();
|
|
7556
7588
|
const history = replayQueryAuditLog(events);
|
|
@@ -7558,8 +7590,8 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
7558
7590
|
runId: runs.id,
|
|
7559
7591
|
createdAt: runs.createdAt,
|
|
7560
7592
|
location: runs.location
|
|
7561
|
-
}).from(runs).innerJoin(querySnapshots,
|
|
7562
|
-
|
|
7593
|
+
}).from(runs).innerJoin(querySnapshots, eq14(querySnapshots.runId, runs.id)).where(and10(
|
|
7594
|
+
eq14(runs.projectId, project.id),
|
|
7563
7595
|
isNull(querySnapshots.queryId),
|
|
7564
7596
|
isNull(querySnapshots.queryText)
|
|
7565
7597
|
)).groupBy(runs.id).orderBy(runs.createdAt).all();
|
|
@@ -7582,7 +7614,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
7582
7614
|
createdAt: querySnapshots.createdAt,
|
|
7583
7615
|
answerText: querySnapshots.answerText
|
|
7584
7616
|
}).from(querySnapshots).where(and10(
|
|
7585
|
-
|
|
7617
|
+
eq14(querySnapshots.runId, run.runId),
|
|
7586
7618
|
isNull(querySnapshots.queryId),
|
|
7587
7619
|
isNull(querySnapshots.queryText)
|
|
7588
7620
|
)).orderBy(querySnapshots.provider, querySnapshots.createdAt).all();
|
|
@@ -7648,7 +7680,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
7648
7680
|
if (!isDryRun && updates.length > 0) {
|
|
7649
7681
|
db.transaction((tx) => {
|
|
7650
7682
|
for (const u of updates) {
|
|
7651
|
-
tx.update(querySnapshots).set({ queryText: u.queryText }).where(
|
|
7683
|
+
tx.update(querySnapshots).set({ queryText: u.queryText }).where(eq14(querySnapshots.id, u.id)).run();
|
|
7652
7684
|
}
|
|
7653
7685
|
});
|
|
7654
7686
|
}
|
|
@@ -7722,7 +7754,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
7722
7754
|
const projectFilter = opts?.project?.trim();
|
|
7723
7755
|
const isDryRun = opts?.dryRun === true;
|
|
7724
7756
|
const isJson = isMachineFormat(opts?.format);
|
|
7725
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
7757
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq14(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
7726
7758
|
if (scopedProjects.length === 0) {
|
|
7727
7759
|
if (projectFilter && !isJson) {
|
|
7728
7760
|
process.stderr.write(`No project named "${projectFilter}".
|
|
@@ -7748,7 +7780,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
7748
7780
|
byBot: {}
|
|
7749
7781
|
};
|
|
7750
7782
|
const unknownCountRow = db.select({ n: sql5`count(*)` }).from(rawEventSamples).where(and10(
|
|
7751
|
-
|
|
7783
|
+
eq14(rawEventSamples.eventType, "unknown"),
|
|
7752
7784
|
inArray4(rawEventSamples.projectId, projectIds)
|
|
7753
7785
|
)).get();
|
|
7754
7786
|
result.unknownBefore = Number(unknownCountRow?.n ?? 0);
|
|
@@ -7761,7 +7793,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
7761
7793
|
pathNormalized: rawEventSamples.pathNormalized,
|
|
7762
7794
|
status: rawEventSamples.status
|
|
7763
7795
|
}).from(rawEventSamples).where(and10(
|
|
7764
|
-
|
|
7796
|
+
eq14(rawEventSamples.eventType, "unknown"),
|
|
7765
7797
|
inArray4(rawEventSamples.projectId, projectIds)
|
|
7766
7798
|
)).all();
|
|
7767
7799
|
result.examined = unknownSamples.length;
|
|
@@ -7800,7 +7832,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
7800
7832
|
result.reclassified++;
|
|
7801
7833
|
result.byBot[classified.botId] = (result.byBot[classified.botId] ?? 0) + 1;
|
|
7802
7834
|
if (isDryRun) continue;
|
|
7803
|
-
db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(
|
|
7835
|
+
db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(eq14(rawEventSamples.id, snap.id)).run();
|
|
7804
7836
|
const tsHour = new Date(snap.ts);
|
|
7805
7837
|
tsHour.setUTCMinutes(0, 0, 0);
|
|
7806
7838
|
if (userFetch) {
|
|
@@ -7865,7 +7897,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
7865
7897
|
}
|
|
7866
7898
|
if (!isDryRun) {
|
|
7867
7899
|
const afterRow = db.select({ n: sql5`count(*)` }).from(rawEventSamples).where(and10(
|
|
7868
|
-
|
|
7900
|
+
eq14(rawEventSamples.eventType, "unknown"),
|
|
7869
7901
|
inArray4(rawEventSamples.projectId, projectIds)
|
|
7870
7902
|
)).get();
|
|
7871
7903
|
result.unknownAfter = Number(afterRow?.n ?? 0);
|
|
@@ -7900,7 +7932,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
|
|
|
7900
7932
|
}
|
|
7901
7933
|
|
|
7902
7934
|
// src/commands/skills.ts
|
|
7903
|
-
import
|
|
7935
|
+
import crypto16 from "crypto";
|
|
7904
7936
|
import fs4 from "fs";
|
|
7905
7937
|
import os4 from "os";
|
|
7906
7938
|
import path5 from "path";
|
|
@@ -7955,7 +7987,7 @@ function walkRelative(dir, prefix = "") {
|
|
|
7955
7987
|
return out.sort();
|
|
7956
7988
|
}
|
|
7957
7989
|
function sha256File(filePath) {
|
|
7958
|
-
return
|
|
7990
|
+
return crypto16.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
|
|
7959
7991
|
}
|
|
7960
7992
|
function readSkillManifest(skillDir) {
|
|
7961
7993
|
try {
|
|
@@ -8297,9 +8329,9 @@ var ProviderRegistry = class {
|
|
|
8297
8329
|
};
|
|
8298
8330
|
|
|
8299
8331
|
// src/scheduler.ts
|
|
8300
|
-
import
|
|
8332
|
+
import crypto17 from "crypto";
|
|
8301
8333
|
import cron from "node-cron";
|
|
8302
|
-
import { and as and11, eq as
|
|
8334
|
+
import { and as and11, eq as eq15, inArray as inArray5, notExists, sql as sql6 } from "drizzle-orm";
|
|
8303
8335
|
var log13 = createLogger("Scheduler");
|
|
8304
8336
|
var DEFAULT_HEALTH_CRON = "0 */6 * * *";
|
|
8305
8337
|
function taskKey(projectId, kind) {
|
|
@@ -8332,15 +8364,15 @@ var Scheduler = class {
|
|
|
8332
8364
|
ensureHealthSchedules() {
|
|
8333
8365
|
const projectsWithoutHealth = this.db.select({ id: projects.id }).from(projects).where(notExists(
|
|
8334
8366
|
this.db.select({ one: sql6`1` }).from(schedules).where(and11(
|
|
8335
|
-
|
|
8336
|
-
|
|
8367
|
+
eq15(schedules.projectId, projects.id),
|
|
8368
|
+
eq15(schedules.kind, SchedulableRunKinds.doctor)
|
|
8337
8369
|
))
|
|
8338
8370
|
)).all();
|
|
8339
8371
|
if (projectsWithoutHealth.length === 0) return;
|
|
8340
8372
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8341
8373
|
for (const project of projectsWithoutHealth) {
|
|
8342
8374
|
this.db.insert(schedules).values({
|
|
8343
|
-
id:
|
|
8375
|
+
id: crypto17.randomUUID(),
|
|
8344
8376
|
projectId: project.id,
|
|
8345
8377
|
kind: SchedulableRunKinds.doctor,
|
|
8346
8378
|
cronExpr: DEFAULT_HEALTH_CRON,
|
|
@@ -8384,7 +8416,7 @@ var Scheduler = class {
|
|
|
8384
8416
|
start() {
|
|
8385
8417
|
this.ensureHealthSchedules();
|
|
8386
8418
|
this.ensureQueryBaskets();
|
|
8387
|
-
const allSchedules = this.db.select().from(schedules).where(
|
|
8419
|
+
const allSchedules = this.db.select().from(schedules).where(eq15(schedules.enabled, true)).all();
|
|
8388
8420
|
for (const schedule of allSchedules) {
|
|
8389
8421
|
const missedRunAt = schedule.nextRunAt;
|
|
8390
8422
|
this.registerCronTask(schedule);
|
|
@@ -8414,7 +8446,7 @@ var Scheduler = class {
|
|
|
8414
8446
|
this.stopTask(key, existing, "Stopped");
|
|
8415
8447
|
this.tasks.delete(key);
|
|
8416
8448
|
}
|
|
8417
|
-
const schedule = this.db.select().from(schedules).where(and11(
|
|
8449
|
+
const schedule = this.db.select().from(schedules).where(and11(eq15(schedules.projectId, projectId), eq15(schedules.kind, kind))).get();
|
|
8418
8450
|
if (schedule && schedule.enabled) {
|
|
8419
8451
|
this.registerCronTask(schedule);
|
|
8420
8452
|
}
|
|
@@ -8455,21 +8487,21 @@ var Scheduler = class {
|
|
|
8455
8487
|
this.db.update(schedules).set({
|
|
8456
8488
|
nextRunAt: nextRunFromCron(cronExpr, timezone),
|
|
8457
8489
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8458
|
-
}).where(
|
|
8490
|
+
}).where(eq15(schedules.id, scheduleId)).run();
|
|
8459
8491
|
const label = schedule.preset ?? cronExpr;
|
|
8460
8492
|
log13.info("cron.registered", { projectId, kind, schedule: label, timezone });
|
|
8461
8493
|
}
|
|
8462
8494
|
triggerRun(scheduleId, projectId, kind) {
|
|
8463
8495
|
try {
|
|
8464
8496
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8465
|
-
const currentSchedule = this.db.select().from(schedules).where(
|
|
8497
|
+
const currentSchedule = this.db.select().from(schedules).where(eq15(schedules.id, scheduleId)).get();
|
|
8466
8498
|
if (!currentSchedule || !currentSchedule.enabled) {
|
|
8467
8499
|
log13.warn("schedule.stale", { scheduleId, projectId, kind, msg: "schedule no longer exists or is disabled" });
|
|
8468
8500
|
this.remove(projectId, kind);
|
|
8469
8501
|
return;
|
|
8470
8502
|
}
|
|
8471
8503
|
const nextRunAt = nextRunFromCron(currentSchedule.cronExpr, currentSchedule.timezone);
|
|
8472
|
-
const project = this.db.select().from(projects).where(
|
|
8504
|
+
const project = this.db.select().from(projects).where(eq15(projects.id, projectId)).get();
|
|
8473
8505
|
if (!project) {
|
|
8474
8506
|
log13.error("project.not-found", { projectId, kind, msg: "skipping scheduled run" });
|
|
8475
8507
|
this.remove(projectId, kind);
|
|
@@ -8489,7 +8521,7 @@ var Scheduler = class {
|
|
|
8489
8521
|
lastRunAt: now,
|
|
8490
8522
|
nextRunAt,
|
|
8491
8523
|
updatedAt: now
|
|
8492
|
-
}).where(
|
|
8524
|
+
}).where(eq15(schedules.id, currentSchedule.id)).run();
|
|
8493
8525
|
log13.info("traffic-sync.triggered", { projectName: project.name, sourceId });
|
|
8494
8526
|
this.callbacks.onTrafficSyncRequested(project.name, sourceId);
|
|
8495
8527
|
return;
|
|
@@ -8499,7 +8531,7 @@ var Scheduler = class {
|
|
|
8499
8531
|
log13.warn("gbp-sync.no-callback", { scheduleId, projectId, msg: "host did not register onGbpSyncRequested" });
|
|
8500
8532
|
return;
|
|
8501
8533
|
}
|
|
8502
|
-
const runId2 =
|
|
8534
|
+
const runId2 = crypto17.randomUUID();
|
|
8503
8535
|
this.db.insert(runs).values({
|
|
8504
8536
|
id: runId2,
|
|
8505
8537
|
projectId,
|
|
@@ -8512,7 +8544,7 @@ var Scheduler = class {
|
|
|
8512
8544
|
lastRunAt: now,
|
|
8513
8545
|
nextRunAt,
|
|
8514
8546
|
updatedAt: now
|
|
8515
|
-
}).where(
|
|
8547
|
+
}).where(eq15(schedules.id, currentSchedule.id)).run();
|
|
8516
8548
|
log13.info("gbp-sync.triggered", { runId: runId2, projectName: project.name });
|
|
8517
8549
|
this.callbacks.onGbpSyncRequested(runId2, projectId);
|
|
8518
8550
|
return;
|
|
@@ -8523,16 +8555,16 @@ var Scheduler = class {
|
|
|
8523
8555
|
return;
|
|
8524
8556
|
}
|
|
8525
8557
|
const activeAdsRun = this.db.select({ id: runs.id }).from(runs).where(and11(
|
|
8526
|
-
|
|
8527
|
-
|
|
8558
|
+
eq15(runs.projectId, projectId),
|
|
8559
|
+
eq15(runs.kind, RunKinds["ads-sync"]),
|
|
8528
8560
|
inArray5(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
8529
8561
|
)).get();
|
|
8530
8562
|
if (activeAdsRun) {
|
|
8531
8563
|
log13.info("ads-sync.skipped-active", { projectName: project.name, activeRunId: activeAdsRun.id });
|
|
8532
|
-
this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(
|
|
8564
|
+
this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq15(schedules.id, currentSchedule.id)).run();
|
|
8533
8565
|
return;
|
|
8534
8566
|
}
|
|
8535
|
-
const runId2 =
|
|
8567
|
+
const runId2 = crypto17.randomUUID();
|
|
8536
8568
|
this.db.insert(runs).values({
|
|
8537
8569
|
id: runId2,
|
|
8538
8570
|
projectId,
|
|
@@ -8545,7 +8577,7 @@ var Scheduler = class {
|
|
|
8545
8577
|
lastRunAt: now,
|
|
8546
8578
|
nextRunAt,
|
|
8547
8579
|
updatedAt: now
|
|
8548
|
-
}).where(
|
|
8580
|
+
}).where(eq15(schedules.id, currentSchedule.id)).run();
|
|
8549
8581
|
log13.info("ads-sync.triggered", { runId: runId2, projectName: project.name });
|
|
8550
8582
|
this.callbacks.onAdsSyncRequested(runId2, projectId);
|
|
8551
8583
|
return;
|
|
@@ -8559,7 +8591,7 @@ var Scheduler = class {
|
|
|
8559
8591
|
lastRunAt: now,
|
|
8560
8592
|
nextRunAt,
|
|
8561
8593
|
updatedAt: now
|
|
8562
|
-
}).where(
|
|
8594
|
+
}).where(eq15(schedules.id, currentSchedule.id)).run();
|
|
8563
8595
|
log13.info("data-refresh.triggered", { projectName: project.name });
|
|
8564
8596
|
this.callbacks.onDataRefreshRequested(project.name);
|
|
8565
8597
|
return;
|
|
@@ -8573,7 +8605,7 @@ var Scheduler = class {
|
|
|
8573
8605
|
lastRunAt: now,
|
|
8574
8606
|
nextRunAt,
|
|
8575
8607
|
updatedAt: now
|
|
8576
|
-
}).where(
|
|
8608
|
+
}).where(eq15(schedules.id, currentSchedule.id)).run();
|
|
8577
8609
|
log13.info("doctor.triggered", { projectName: project.name });
|
|
8578
8610
|
this.callbacks.onDoctorRequested(project.name);
|
|
8579
8611
|
return;
|
|
@@ -8587,7 +8619,7 @@ var Scheduler = class {
|
|
|
8587
8619
|
lastRunAt: now,
|
|
8588
8620
|
nextRunAt,
|
|
8589
8621
|
updatedAt: now
|
|
8590
|
-
}).where(
|
|
8622
|
+
}).where(eq15(schedules.id, currentSchedule.id)).run();
|
|
8591
8623
|
log13.info("backlinks-sync.triggered", { projectName: project.name });
|
|
8592
8624
|
this.callbacks.onBacklinksSyncRequested(project.name);
|
|
8593
8625
|
return;
|
|
@@ -8598,16 +8630,16 @@ var Scheduler = class {
|
|
|
8598
8630
|
return;
|
|
8599
8631
|
}
|
|
8600
8632
|
const active = this.db.select({ id: runs.id }).from(runs).where(and11(
|
|
8601
|
-
|
|
8602
|
-
|
|
8633
|
+
eq15(runs.projectId, projectId),
|
|
8634
|
+
eq15(runs.kind, RunKinds["site-audit"]),
|
|
8603
8635
|
inArray5(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
8604
8636
|
)).get();
|
|
8605
8637
|
if (active) {
|
|
8606
8638
|
log13.info("site-audit.skipped-active", { projectName: project.name, activeRunId: active.id });
|
|
8607
|
-
this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(
|
|
8639
|
+
this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq15(schedules.id, currentSchedule.id)).run();
|
|
8608
8640
|
return;
|
|
8609
8641
|
}
|
|
8610
|
-
const runId2 =
|
|
8642
|
+
const runId2 = crypto17.randomUUID();
|
|
8611
8643
|
this.db.insert(runs).values({
|
|
8612
8644
|
id: runId2,
|
|
8613
8645
|
projectId,
|
|
@@ -8620,7 +8652,7 @@ var Scheduler = class {
|
|
|
8620
8652
|
lastRunAt: now,
|
|
8621
8653
|
nextRunAt,
|
|
8622
8654
|
updatedAt: now
|
|
8623
|
-
}).where(
|
|
8655
|
+
}).where(eq15(schedules.id, currentSchedule.id)).run();
|
|
8624
8656
|
log13.info("site-audit.triggered", { runId: runId2, projectName: project.name });
|
|
8625
8657
|
this.callbacks.onSiteAuditRequested(runId2, projectId);
|
|
8626
8658
|
return;
|
|
@@ -8653,7 +8685,7 @@ var Scheduler = class {
|
|
|
8653
8685
|
this.db.update(schedules).set({
|
|
8654
8686
|
nextRunAt,
|
|
8655
8687
|
updatedAt: now
|
|
8656
|
-
}).where(
|
|
8688
|
+
}).where(eq15(schedules.id, currentSchedule.id)).run();
|
|
8657
8689
|
return;
|
|
8658
8690
|
}
|
|
8659
8691
|
const runId = queueResult.runId;
|
|
@@ -8661,7 +8693,7 @@ var Scheduler = class {
|
|
|
8661
8693
|
lastRunAt: now,
|
|
8662
8694
|
nextRunAt,
|
|
8663
8695
|
updatedAt: now
|
|
8664
|
-
}).where(
|
|
8696
|
+
}).where(eq15(schedules.id, currentSchedule.id)).run();
|
|
8665
8697
|
log13.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
|
|
8666
8698
|
this.callbacks.onRunCreated(runId, projectId, providers, resolvedLocation);
|
|
8667
8699
|
} catch (err) {
|
|
@@ -8694,8 +8726,8 @@ async function refreshAllIntegrations(client, projectName) {
|
|
|
8694
8726
|
}
|
|
8695
8727
|
|
|
8696
8728
|
// src/notifier.ts
|
|
8697
|
-
import { eq as
|
|
8698
|
-
import
|
|
8729
|
+
import { eq as eq16, desc as desc6, and as and12, inArray as inArray6, or } from "drizzle-orm";
|
|
8730
|
+
import crypto18 from "crypto";
|
|
8699
8731
|
var log15 = createLogger("Notifier");
|
|
8700
8732
|
var Notifier = class {
|
|
8701
8733
|
db;
|
|
@@ -8707,18 +8739,18 @@ var Notifier = class {
|
|
|
8707
8739
|
/** Called after a run completes (success, partial, or failed). */
|
|
8708
8740
|
async onRunCompleted(runId, projectId) {
|
|
8709
8741
|
log15.info("run.completed", { runId, projectId });
|
|
8710
|
-
const notifs = this.db.select().from(notifications).where(
|
|
8742
|
+
const notifs = this.db.select().from(notifications).where(eq16(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
8711
8743
|
if (notifs.length === 0) {
|
|
8712
8744
|
log15.info("notifications.none-enabled", { projectId });
|
|
8713
8745
|
return;
|
|
8714
8746
|
}
|
|
8715
8747
|
log15.info("notifications.found", { projectId, count: notifs.length });
|
|
8716
|
-
const run = this.db.select().from(runs).where(
|
|
8748
|
+
const run = this.db.select().from(runs).where(eq16(runs.id, runId)).get();
|
|
8717
8749
|
if (!run) {
|
|
8718
8750
|
log15.error("run.not-found", { runId, msg: "skipping notification dispatch" });
|
|
8719
8751
|
return;
|
|
8720
8752
|
}
|
|
8721
|
-
const project = this.db.select().from(projects).where(
|
|
8753
|
+
const project = this.db.select().from(projects).where(eq16(projects.id, projectId)).get();
|
|
8722
8754
|
if (!project) {
|
|
8723
8755
|
log15.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
|
|
8724
8756
|
return;
|
|
@@ -8771,7 +8803,7 @@ var Notifier = class {
|
|
|
8771
8803
|
* inferred from the absence of a webhook.
|
|
8772
8804
|
*/
|
|
8773
8805
|
async onHealthChecked(projectId, report) {
|
|
8774
|
-
const project = this.db.select().from(projects).where(
|
|
8806
|
+
const project = this.db.select().from(projects).where(eq16(projects.id, projectId)).get();
|
|
8775
8807
|
if (!project) {
|
|
8776
8808
|
log15.error("project.not-found", { projectId, msg: "skipping health notification" });
|
|
8777
8809
|
return null;
|
|
@@ -8802,7 +8834,7 @@ var Notifier = class {
|
|
|
8802
8834
|
const status = noSignal ? "warn" : worst ? worst.status : "ok";
|
|
8803
8835
|
const code = noSignal ? "health.no-signal" : worst?.code ?? "health.ok";
|
|
8804
8836
|
const summary = noSignal ? `No health check produced a result (${report.checks.length} skipped) \u2014 health is unknown, not confirmed.` : worst?.summary ?? `All ${graded.length} health check(s) passing.`;
|
|
8805
|
-
const previous = this.db.select().from(doctorHealthState).where(
|
|
8837
|
+
const previous = this.db.select().from(doctorHealthState).where(eq16(doctorHealthState.projectId, projectId)).get();
|
|
8806
8838
|
const previousStatus = previous?.status ?? null;
|
|
8807
8839
|
let event = null;
|
|
8808
8840
|
if (previous === void 0) {
|
|
@@ -8817,13 +8849,13 @@ var Notifier = class {
|
|
|
8817
8849
|
if (previous === void 0) {
|
|
8818
8850
|
this.db.insert(doctorHealthState).values({ ...observation, notifiedAt: null }).run();
|
|
8819
8851
|
} else {
|
|
8820
|
-
this.db.update(doctorHealthState).set(observation).where(
|
|
8852
|
+
this.db.update(doctorHealthState).set(observation).where(eq16(doctorHealthState.projectId, projectId)).run();
|
|
8821
8853
|
}
|
|
8822
8854
|
if (!event) {
|
|
8823
8855
|
log15.info("health.unchanged", { projectId, status, code });
|
|
8824
8856
|
return null;
|
|
8825
8857
|
}
|
|
8826
|
-
const notifs = this.db.select().from(notifications).where(
|
|
8858
|
+
const notifs = this.db.select().from(notifications).where(eq16(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
8827
8859
|
const payload = {
|
|
8828
8860
|
source: "canonry",
|
|
8829
8861
|
event,
|
|
@@ -8847,7 +8879,7 @@ var Notifier = class {
|
|
|
8847
8879
|
delivered += 1;
|
|
8848
8880
|
}
|
|
8849
8881
|
if (delivered > 0) {
|
|
8850
|
-
this.db.update(doctorHealthState).set({ notifiedAt: now }).where(
|
|
8882
|
+
this.db.update(doctorHealthState).set({ notifiedAt: now }).where(eq16(doctorHealthState.projectId, projectId)).run();
|
|
8851
8883
|
}
|
|
8852
8884
|
log15.info("health.notified", { projectId, event, status, code, subscribers: notifs.length, delivered });
|
|
8853
8885
|
return event;
|
|
@@ -8860,11 +8892,11 @@ var Notifier = class {
|
|
|
8860
8892
|
if (criticalInsights.length > 0) insightEvents.push("insight.critical");
|
|
8861
8893
|
if (highInsights.length > 0) insightEvents.push("insight.high");
|
|
8862
8894
|
if (insightEvents.length === 0) return;
|
|
8863
|
-
const notifs = this.db.select().from(notifications).where(
|
|
8895
|
+
const notifs = this.db.select().from(notifications).where(eq16(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
8864
8896
|
if (notifs.length === 0) return;
|
|
8865
|
-
const run = this.db.select().from(runs).where(
|
|
8897
|
+
const run = this.db.select().from(runs).where(eq16(runs.id, runId)).get();
|
|
8866
8898
|
if (!run) return;
|
|
8867
|
-
const project = this.db.select().from(projects).where(
|
|
8899
|
+
const project = this.db.select().from(projects).where(eq16(projects.id, projectId)).get();
|
|
8868
8900
|
if (!project) return;
|
|
8869
8901
|
for (const notif of notifs) {
|
|
8870
8902
|
const config = notif.config;
|
|
@@ -8894,7 +8926,7 @@ var Notifier = class {
|
|
|
8894
8926
|
}
|
|
8895
8927
|
}
|
|
8896
8928
|
computeTransitions(runId, projectId) {
|
|
8897
|
-
const thisRun = this.db.select().from(runs).where(
|
|
8929
|
+
const thisRun = this.db.select().from(runs).where(eq16(runs.id, runId)).get();
|
|
8898
8930
|
if (!thisRun) return [];
|
|
8899
8931
|
const completeness = measurementRunCompleteness(this.db, runId);
|
|
8900
8932
|
if (completeness.planned && !completeness.complete) {
|
|
@@ -8906,9 +8938,9 @@ var Notifier = class {
|
|
|
8906
8938
|
return [];
|
|
8907
8939
|
}
|
|
8908
8940
|
const groupSiblings = this.db.select().from(runs).where(and12(
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8941
|
+
eq16(runs.projectId, projectId),
|
|
8942
|
+
eq16(runs.kind, thisRun.kind),
|
|
8943
|
+
eq16(runs.createdAt, thisRun.createdAt)
|
|
8912
8944
|
)).all();
|
|
8913
8945
|
const stillPending = groupSiblings.some((r) => r.status === "queued" || r.status === "running");
|
|
8914
8946
|
if (stillPending) return [];
|
|
@@ -8924,7 +8956,7 @@ var Notifier = class {
|
|
|
8924
8956
|
return candidate.id > best.id ? candidate : best;
|
|
8925
8957
|
});
|
|
8926
8958
|
if (winner.id !== runId) return [];
|
|
8927
|
-
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(
|
|
8959
|
+
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq16(projects.id, projectId)).get();
|
|
8928
8960
|
const locationCount = Math.max(
|
|
8929
8961
|
1,
|
|
8930
8962
|
(projectLocations?.locations ?? []).length
|
|
@@ -8932,11 +8964,11 @@ var Notifier = class {
|
|
|
8932
8964
|
const RECENT_FETCH_LIMIT = Math.max(8, locationCount * 4);
|
|
8933
8965
|
const recentRuns = this.db.select().from(runs).where(
|
|
8934
8966
|
and12(
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
or(
|
|
8967
|
+
eq16(runs.projectId, projectId),
|
|
8968
|
+
eq16(runs.kind, thisRun.kind),
|
|
8969
|
+
or(eq16(runs.status, "completed"), eq16(runs.status, "partial"))
|
|
8938
8970
|
)
|
|
8939
|
-
).orderBy(
|
|
8971
|
+
).orderBy(desc6(runs.createdAt), desc6(runs.id)).limit(RECENT_FETCH_LIMIT).all();
|
|
8940
8972
|
const groups = groupRunsByCreatedAt(recentRuns);
|
|
8941
8973
|
const currentGroupIdx = groups.findIndex((g) => g[0]?.createdAt === thisRun.createdAt);
|
|
8942
8974
|
if (currentGroupIdx < 0) return [];
|
|
@@ -8951,7 +8983,7 @@ var Notifier = class {
|
|
|
8951
8983
|
provider: querySnapshots.provider,
|
|
8952
8984
|
location: querySnapshots.location,
|
|
8953
8985
|
citationState: querySnapshots.citationState
|
|
8954
|
-
}).from(querySnapshots).leftJoin(queries,
|
|
8986
|
+
}).from(querySnapshots).leftJoin(queries, eq16(querySnapshots.queryId, queries.id)).where(inArray6(querySnapshots.runId, currentRunIds)).all();
|
|
8955
8987
|
const previousSnapshots = this.db.select({
|
|
8956
8988
|
queryId: querySnapshots.queryId,
|
|
8957
8989
|
provider: querySnapshots.provider,
|
|
@@ -9021,7 +9053,7 @@ var Notifier = class {
|
|
|
9021
9053
|
}
|
|
9022
9054
|
logDelivery(projectId, notificationId, event, status, error) {
|
|
9023
9055
|
this.db.insert(auditLog).values({
|
|
9024
|
-
id:
|
|
9056
|
+
id: crypto18.randomUUID(),
|
|
9025
9057
|
projectId,
|
|
9026
9058
|
actor: "scheduler",
|
|
9027
9059
|
action: `notification.${status}`,
|
|
@@ -9034,7 +9066,7 @@ var Notifier = class {
|
|
|
9034
9066
|
};
|
|
9035
9067
|
|
|
9036
9068
|
// src/run-coordinator.ts
|
|
9037
|
-
import { eq as
|
|
9069
|
+
import { eq as eq17 } from "drizzle-orm";
|
|
9038
9070
|
var log16 = createLogger("RunCoordinator");
|
|
9039
9071
|
var RunCoordinator = class {
|
|
9040
9072
|
constructor(db, notifier, intelligenceService, onInsightsGenerated, onAeroEvent) {
|
|
@@ -9050,7 +9082,7 @@ var RunCoordinator = class {
|
|
|
9050
9082
|
onInsightsGenerated;
|
|
9051
9083
|
onAeroEvent;
|
|
9052
9084
|
async onRunCompleted(runId, projectId) {
|
|
9053
|
-
const runRow = this.db.select().from(runs).where(
|
|
9085
|
+
const runRow = this.db.select().from(runs).where(eq17(runs.id, runId)).get();
|
|
9054
9086
|
const kind = runRow?.kind ?? RunKinds["answer-visibility"];
|
|
9055
9087
|
if (runRow?.trigger === RunTriggers.probe) {
|
|
9056
9088
|
log16.info("probe.skip-side-effects", { runId, projectId, kind });
|
|
@@ -9141,7 +9173,7 @@ var RunCoordinator = class {
|
|
|
9141
9173
|
* so the Aero queue is never starved of a follow-up.
|
|
9142
9174
|
*/
|
|
9143
9175
|
buildDiscoveryAeroContext(runId, projectId, status, error) {
|
|
9144
|
-
const session = this.db.select().from(discoverySessions).where(
|
|
9176
|
+
const session = this.db.select().from(discoverySessions).where(eq17(discoverySessions.runId, runId)).get();
|
|
9145
9177
|
const competitorMap = session ? session.competitorMap : [];
|
|
9146
9178
|
return {
|
|
9147
9179
|
kind: RunKinds["aeo-discover-probe"],
|
|
@@ -9183,8 +9215,8 @@ function analysisResultFromInsights(insights2) {
|
|
|
9183
9215
|
}
|
|
9184
9216
|
|
|
9185
9217
|
// src/agent/session-registry.ts
|
|
9186
|
-
import
|
|
9187
|
-
import { eq as
|
|
9218
|
+
import crypto22 from "crypto";
|
|
9219
|
+
import { eq as eq19 } from "drizzle-orm";
|
|
9188
9220
|
|
|
9189
9221
|
// src/agent/session.ts
|
|
9190
9222
|
import fs7 from "fs";
|
|
@@ -9927,7 +9959,7 @@ function buildAeroStateTools(ctx, opts = {}) {
|
|
|
9927
9959
|
}
|
|
9928
9960
|
|
|
9929
9961
|
// src/agent/llm-usage.ts
|
|
9930
|
-
import
|
|
9962
|
+
import crypto19 from "crypto";
|
|
9931
9963
|
var AeroLlmUsageFeatures = {
|
|
9932
9964
|
turn: "aero.turn"
|
|
9933
9965
|
};
|
|
@@ -9946,7 +9978,7 @@ function recordLlmUsageEvent(args) {
|
|
|
9946
9978
|
const usage = args.message.usage;
|
|
9947
9979
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9948
9980
|
args.db.insert(llmUsageEvents).values({
|
|
9949
|
-
id:
|
|
9981
|
+
id: crypto19.randomUUID(),
|
|
9950
9982
|
projectId: args.projectId,
|
|
9951
9983
|
runId: args.runId,
|
|
9952
9984
|
agentSessionId: args.agentSessionId,
|
|
@@ -10022,7 +10054,7 @@ function splitAeroAnthropicSystemCachePayload(payload, model) {
|
|
|
10022
10054
|
}
|
|
10023
10055
|
|
|
10024
10056
|
// src/agent/tool-usage.ts
|
|
10025
|
-
import
|
|
10057
|
+
import crypto20 from "crypto";
|
|
10026
10058
|
var AeroToolEventStatuses = {
|
|
10027
10059
|
success: "success",
|
|
10028
10060
|
error: "error"
|
|
@@ -10085,7 +10117,7 @@ function createAeroToolUsageHooks(args) {
|
|
|
10085
10117
|
function recordAgentToolEvent(args) {
|
|
10086
10118
|
try {
|
|
10087
10119
|
args.db.insert(agentToolEvents).values({
|
|
10088
|
-
id:
|
|
10120
|
+
id: crypto20.randomUUID(),
|
|
10089
10121
|
projectId: args.projectId,
|
|
10090
10122
|
agentSessionId: args.agentSessionId,
|
|
10091
10123
|
toolCallId: args.toolCallId,
|
|
@@ -10312,8 +10344,8 @@ async function loadExternalMcpTools(servers, opts = {}) {
|
|
|
10312
10344
|
}
|
|
10313
10345
|
|
|
10314
10346
|
// src/agent/memory-store.ts
|
|
10315
|
-
import
|
|
10316
|
-
import { and as and13, desc as
|
|
10347
|
+
import crypto21 from "crypto";
|
|
10348
|
+
import { and as and13, desc as desc7, eq as eq18, like, sql as sql7 } from "drizzle-orm";
|
|
10317
10349
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
10318
10350
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
10319
10351
|
function rowToDto(row) {
|
|
@@ -10327,7 +10359,7 @@ function rowToDto(row) {
|
|
|
10327
10359
|
};
|
|
10328
10360
|
}
|
|
10329
10361
|
function listMemoryEntries(db, projectId, opts = {}) {
|
|
10330
|
-
const query = db.select().from(agentMemory).where(
|
|
10362
|
+
const query = db.select().from(agentMemory).where(eq18(agentMemory.projectId, projectId)).orderBy(desc7(agentMemory.updatedAt));
|
|
10331
10363
|
const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
|
|
10332
10364
|
return rows.map(rowToDto);
|
|
10333
10365
|
}
|
|
@@ -10341,7 +10373,7 @@ function upsertMemoryEntry(db, args) {
|
|
|
10341
10373
|
throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
|
|
10342
10374
|
}
|
|
10343
10375
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10344
|
-
const id =
|
|
10376
|
+
const id = crypto21.randomUUID();
|
|
10345
10377
|
db.insert(agentMemory).values({
|
|
10346
10378
|
id,
|
|
10347
10379
|
projectId: args.projectId,
|
|
@@ -10358,12 +10390,12 @@ function upsertMemoryEntry(db, args) {
|
|
|
10358
10390
|
updatedAt: now
|
|
10359
10391
|
}
|
|
10360
10392
|
}).run();
|
|
10361
|
-
const row = db.select().from(agentMemory).where(and13(
|
|
10393
|
+
const row = db.select().from(agentMemory).where(and13(eq18(agentMemory.projectId, args.projectId), eq18(agentMemory.key, args.key))).get();
|
|
10362
10394
|
if (!row) throw new Error("memory upsert produced no row");
|
|
10363
10395
|
return rowToDto(row);
|
|
10364
10396
|
}
|
|
10365
10397
|
function deleteMemoryEntry(db, projectId, key) {
|
|
10366
|
-
const result = db.delete(agentMemory).where(and13(
|
|
10398
|
+
const result = db.delete(agentMemory).where(and13(eq18(agentMemory.projectId, projectId), eq18(agentMemory.key, key))).run();
|
|
10367
10399
|
const changes = result.changes ?? 0;
|
|
10368
10400
|
return changes > 0;
|
|
10369
10401
|
}
|
|
@@ -10378,7 +10410,7 @@ function writeCompactionNote(db, args) {
|
|
|
10378
10410
|
}
|
|
10379
10411
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10380
10412
|
const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
|
|
10381
|
-
const id =
|
|
10413
|
+
const id = crypto21.randomUUID();
|
|
10382
10414
|
let inserted;
|
|
10383
10415
|
db.transaction((tx) => {
|
|
10384
10416
|
tx.insert(agentMemory).values({
|
|
@@ -10393,15 +10425,15 @@ function writeCompactionNote(db, args) {
|
|
|
10393
10425
|
const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
|
|
10394
10426
|
const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
|
|
10395
10427
|
and13(
|
|
10396
|
-
|
|
10428
|
+
eq18(agentMemory.projectId, args.projectId),
|
|
10397
10429
|
like(agentMemory.key, `${sessionPrefix}%`)
|
|
10398
10430
|
)
|
|
10399
|
-
).orderBy(
|
|
10431
|
+
).orderBy(desc7(agentMemory.updatedAt)).all();
|
|
10400
10432
|
const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
|
|
10401
10433
|
if (stale.length > 0) {
|
|
10402
10434
|
tx.delete(agentMemory).where(sql7`${agentMemory.id} IN (${sql7.join(stale.map((s) => sql7`${s}`), sql7`, `)})`).run();
|
|
10403
10435
|
}
|
|
10404
|
-
const row = tx.select().from(agentMemory).where(and13(
|
|
10436
|
+
const row = tx.select().from(agentMemory).where(and13(eq18(agentMemory.projectId, args.projectId), eq18(agentMemory.key, key))).get();
|
|
10405
10437
|
if (row) inserted = rowToDto(row);
|
|
10406
10438
|
});
|
|
10407
10439
|
if (!inserted) throw new Error("compaction note write produced no row");
|
|
@@ -10628,7 +10660,7 @@ var SessionRegistry = class {
|
|
|
10628
10660
|
modelProvider: effectiveProvider,
|
|
10629
10661
|
modelId: effectiveModelId,
|
|
10630
10662
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10631
|
-
}).where(
|
|
10663
|
+
}).where(eq19(agentSessions.projectId, projectId)).run();
|
|
10632
10664
|
}
|
|
10633
10665
|
const agent2 = createAeroSession({
|
|
10634
10666
|
projectName,
|
|
@@ -10657,7 +10689,7 @@ var SessionRegistry = class {
|
|
|
10657
10689
|
}
|
|
10658
10690
|
const { provider, modelId } = resolveSessionProviderAndModel(this.opts.config, preferences);
|
|
10659
10691
|
const systemPrompt = loadAeroSystemPrompt();
|
|
10660
|
-
const sessionId =
|
|
10692
|
+
const sessionId = crypto22.randomUUID();
|
|
10661
10693
|
const agent = createAeroSession({
|
|
10662
10694
|
projectName,
|
|
10663
10695
|
client: this.opts.client,
|
|
@@ -10862,7 +10894,7 @@ ${lines.join("\n")}
|
|
|
10862
10894
|
modelProvider: nextProvider,
|
|
10863
10895
|
modelId: nextModelId,
|
|
10864
10896
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10865
|
-
}).where(
|
|
10897
|
+
}).where(eq19(agentSessions.projectId, projectId)).run();
|
|
10866
10898
|
}
|
|
10867
10899
|
/** Persist a session's transcript back to the DB. Call after any run settles. */
|
|
10868
10900
|
save(projectName) {
|
|
@@ -11026,17 +11058,17 @@ ${lines.join("\n")}
|
|
|
11026
11058
|
return id;
|
|
11027
11059
|
}
|
|
11028
11060
|
tryResolveProjectId(projectName) {
|
|
11029
|
-
const row = this.opts.db.select({ id: projects.id }).from(projects).where(
|
|
11061
|
+
const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq19(projects.name, projectName)).get();
|
|
11030
11062
|
return row?.id;
|
|
11031
11063
|
}
|
|
11032
11064
|
loadRow(projectId) {
|
|
11033
|
-
const row = this.opts.db.select().from(agentSessions).where(
|
|
11065
|
+
const row = this.opts.db.select().from(agentSessions).where(eq19(agentSessions.projectId, projectId)).get();
|
|
11034
11066
|
return row ?? null;
|
|
11035
11067
|
}
|
|
11036
11068
|
insertRow(params) {
|
|
11037
11069
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11038
11070
|
this.opts.db.insert(agentSessions).values({
|
|
11039
|
-
id: params.id ??
|
|
11071
|
+
id: params.id ?? crypto22.randomUUID(),
|
|
11040
11072
|
projectId: params.projectId,
|
|
11041
11073
|
systemPrompt: params.systemPrompt,
|
|
11042
11074
|
modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
|
|
@@ -11049,14 +11081,14 @@ ${lines.join("\n")}
|
|
|
11049
11081
|
}
|
|
11050
11082
|
updateRow(projectId, patch) {
|
|
11051
11083
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11052
|
-
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(
|
|
11084
|
+
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq19(agentSessions.projectId, projectId)).run();
|
|
11053
11085
|
}
|
|
11054
11086
|
};
|
|
11055
11087
|
|
|
11056
11088
|
// src/agent/agent-routes.ts
|
|
11057
|
-
import { eq as
|
|
11089
|
+
import { eq as eq20 } from "drizzle-orm";
|
|
11058
11090
|
function resolveProject(db, name) {
|
|
11059
|
-
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(
|
|
11091
|
+
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq20(projects.name, name)).get();
|
|
11060
11092
|
if (!row) throw notFound("project", name);
|
|
11061
11093
|
return row;
|
|
11062
11094
|
}
|
|
@@ -11065,7 +11097,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
11065
11097
|
"/projects/:name/agent/transcript",
|
|
11066
11098
|
async (request) => {
|
|
11067
11099
|
const project = resolveProject(opts.db, request.params.name);
|
|
11068
|
-
const row = opts.db.select().from(agentSessions).where(
|
|
11100
|
+
const row = opts.db.select().from(agentSessions).where(eq20(agentSessions.projectId, project.id)).get();
|
|
11069
11101
|
if (!row) {
|
|
11070
11102
|
return { messages: [], modelProvider: null, modelId: null, updatedAt: null };
|
|
11071
11103
|
}
|
|
@@ -11089,7 +11121,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
11089
11121
|
async (request) => {
|
|
11090
11122
|
const project = resolveProject(opts.db, request.params.name);
|
|
11091
11123
|
opts.sessionRegistry.reset(project.name);
|
|
11092
|
-
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
11124
|
+
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq20(agentSessions.projectId, project.id)).run();
|
|
11093
11125
|
return { status: "reset" };
|
|
11094
11126
|
}
|
|
11095
11127
|
);
|
|
@@ -12032,20 +12064,20 @@ function clipText(value, length) {
|
|
|
12032
12064
|
}
|
|
12033
12065
|
|
|
12034
12066
|
// src/research-runner.ts
|
|
12035
|
-
import { and as and14, eq as
|
|
12067
|
+
import { and as and14, eq as eq21, inArray as inArray7, sql as sql8 } from "drizzle-orm";
|
|
12036
12068
|
var unfinishedResearchQueryStatuses = [ResearchQueryStatuses.queued, ResearchQueryStatuses.running];
|
|
12037
12069
|
function finalResearchRunStatus(completed, failed) {
|
|
12038
12070
|
if (failed === 0) return ResearchRunStatuses.completed;
|
|
12039
12071
|
return completed > 0 ? ResearchRunStatuses.partial : ResearchRunStatuses.failed;
|
|
12040
12072
|
}
|
|
12041
12073
|
async function executeResearchRun(db, registry, runId, projectId) {
|
|
12042
|
-
const run = db.select().from(researchRuns).where(and14(
|
|
12074
|
+
const run = db.select().from(researchRuns).where(and14(eq21(researchRuns.id, runId), eq21(researchRuns.projectId, projectId))).get();
|
|
12043
12075
|
if (!run || run.status !== ResearchRunStatuses.queued) return;
|
|
12044
|
-
const project = db.select().from(projects).where(
|
|
12076
|
+
const project = db.select().from(projects).where(eq21(projects.id, projectId)).get();
|
|
12045
12077
|
if (!project) return;
|
|
12046
12078
|
const provider = registry.get(run.provider);
|
|
12047
12079
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12048
|
-
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(and14(
|
|
12080
|
+
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(and14(eq21(researchRuns.id, runId), eq21(researchRuns.status, ResearchRunStatuses.queued))).run();
|
|
12049
12081
|
if (claim.changes !== 1) return;
|
|
12050
12082
|
let reserved = 0;
|
|
12051
12083
|
let dispatched = 0;
|
|
@@ -12069,8 +12101,8 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
12069
12101
|
}
|
|
12070
12102
|
reserved = run.totalQueries;
|
|
12071
12103
|
reservation = { scope, period };
|
|
12072
|
-
const rows = db.select().from(researchRunQueries).where(
|
|
12073
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(
|
|
12104
|
+
const rows = db.select().from(researchRunQueries).where(eq21(researchRunQueries.researchRunId, runId)).orderBy(researchRunQueries.position).all();
|
|
12105
|
+
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq21(competitors.projectId, projectId)).all().map((row) => row.domain);
|
|
12074
12106
|
const domains = effectiveDomains(project);
|
|
12075
12107
|
const brands = effectiveBrandNames(project);
|
|
12076
12108
|
const config = { ...provider.config, model: run.resolvedModel };
|
|
@@ -12078,7 +12110,7 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
12078
12110
|
await mapWithConcurrency(rows, Math.max(1, provider.config.quotaPolicy.maxConcurrency), async (row) => {
|
|
12079
12111
|
try {
|
|
12080
12112
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12081
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(and14(
|
|
12113
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(and14(eq21(researchRunQueries.id, row.id), eq21(researchRunQueries.status, ResearchQueryStatuses.queued))).run();
|
|
12082
12114
|
const raw = await gate.run(async () => {
|
|
12083
12115
|
dispatched++;
|
|
12084
12116
|
return provider.adapter.executeTrackedQuery({ query: row.queryText, canonicalDomains: domains, competitorDomains, ...run.location ? { location: run.location } : {} }, config);
|
|
@@ -12105,7 +12137,7 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
12105
12137
|
citationState: determineCitationState(normalized, domains),
|
|
12106
12138
|
rawResponse: raw.rawResponse,
|
|
12107
12139
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12108
|
-
}).where(and14(
|
|
12140
|
+
}).where(and14(eq21(researchRunQueries.id, row.id), eq21(researchRunQueries.status, ResearchQueryStatuses.running))).run();
|
|
12109
12141
|
if (completed.changes === 1) incrementResearchProgress(db, runId, "completedQueries");
|
|
12110
12142
|
} catch (error) {
|
|
12111
12143
|
try {
|
|
@@ -12129,18 +12161,18 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
12129
12161
|
}
|
|
12130
12162
|
}
|
|
12131
12163
|
function incrementResearchProgress(db, runId, column) {
|
|
12132
|
-
db.update(researchRuns).set({ [column]: sql8`${researchRuns[column]} + 1` }).where(
|
|
12164
|
+
db.update(researchRuns).set({ [column]: sql8`${researchRuns[column]} + 1` }).where(eq21(researchRuns.id, runId)).run();
|
|
12133
12165
|
}
|
|
12134
12166
|
function markResearchQueryFailed(db, runId, queryId, error) {
|
|
12135
12167
|
const message = error instanceof Error ? error.message : String(error);
|
|
12136
|
-
const failed = db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error: message, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and14(
|
|
12168
|
+
const failed = db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error: message, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and14(eq21(researchRunQueries.id, queryId), inArray7(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
|
|
12137
12169
|
if (failed.changes === 1) incrementResearchProgress(db, runId, "failedQueries");
|
|
12138
12170
|
}
|
|
12139
12171
|
function markUnfinishedResearchQueriesFailed(db, runId, error) {
|
|
12140
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and14(
|
|
12172
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and14(eq21(researchRunQueries.researchRunId, runId), inArray7(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
|
|
12141
12173
|
}
|
|
12142
12174
|
function finalizeResearchRun(db, runId, fatalError) {
|
|
12143
|
-
const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(
|
|
12175
|
+
const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(eq21(researchRunQueries.researchRunId, runId)).all();
|
|
12144
12176
|
const completed = rows.filter((row) => row.status === ResearchQueryStatuses.completed).length;
|
|
12145
12177
|
const failed = rows.filter((row) => row.status === ResearchQueryStatuses.failed).length;
|
|
12146
12178
|
db.update(researchRuns).set({
|
|
@@ -12149,7 +12181,7 @@ function finalizeResearchRun(db, runId, fatalError) {
|
|
|
12149
12181
|
failedQueries: failed,
|
|
12150
12182
|
error: fatalError ?? (failed === rows.length ? "Every research query failed." : null),
|
|
12151
12183
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12152
|
-
}).where(
|
|
12184
|
+
}).where(eq21(researchRuns.id, runId)).run();
|
|
12153
12185
|
}
|
|
12154
12186
|
|
|
12155
12187
|
// src/server.ts
|
|
@@ -12212,14 +12244,14 @@ function summarizeProviderConfig(config) {
|
|
|
12212
12244
|
};
|
|
12213
12245
|
}
|
|
12214
12246
|
function hashApiKey(key) {
|
|
12215
|
-
return
|
|
12247
|
+
return crypto23.createHash("sha256").update(key).digest("hex");
|
|
12216
12248
|
}
|
|
12217
12249
|
var DASHBOARD_SCRYPT_KEYLEN = 64;
|
|
12218
12250
|
var DASHBOARD_SCRYPT_COST = 1 << 15;
|
|
12219
12251
|
var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
|
|
12220
12252
|
function hashDashboardPassword(password) {
|
|
12221
|
-
const salt =
|
|
12222
|
-
const derived =
|
|
12253
|
+
const salt = crypto23.randomBytes(16);
|
|
12254
|
+
const derived = crypto23.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
|
|
12223
12255
|
N: DASHBOARD_SCRYPT_COST,
|
|
12224
12256
|
maxmem: DASHBOARD_SCRYPT_MAXMEM
|
|
12225
12257
|
});
|
|
@@ -12240,14 +12272,14 @@ function verifyDashboardPassword(password, storedHash) {
|
|
|
12240
12272
|
} catch {
|
|
12241
12273
|
return { ok: false, needsRehash: false };
|
|
12242
12274
|
}
|
|
12243
|
-
const derived =
|
|
12275
|
+
const derived = crypto23.scryptSync(password, salt, expected.length, {
|
|
12244
12276
|
N: DASHBOARD_SCRYPT_COST,
|
|
12245
12277
|
maxmem: DASHBOARD_SCRYPT_MAXMEM
|
|
12246
12278
|
});
|
|
12247
12279
|
if (derived.length !== expected.length)
|
|
12248
12280
|
return { ok: false, needsRehash: false };
|
|
12249
12281
|
return {
|
|
12250
|
-
ok:
|
|
12282
|
+
ok: crypto23.timingSafeEqual(derived, expected),
|
|
12251
12283
|
needsRehash: false
|
|
12252
12284
|
};
|
|
12253
12285
|
}
|
|
@@ -12256,7 +12288,7 @@ function verifyDashboardPassword(password, storedHash) {
|
|
|
12256
12288
|
const expected = Buffer.from(storedHash, "hex");
|
|
12257
12289
|
if (candidate.length !== expected.length)
|
|
12258
12290
|
return { ok: false, needsRehash: false };
|
|
12259
|
-
const ok =
|
|
12291
|
+
const ok = crypto23.timingSafeEqual(candidate, expected);
|
|
12260
12292
|
return { ok, needsRehash: ok };
|
|
12261
12293
|
}
|
|
12262
12294
|
return { ok: false, needsRehash: false };
|
|
@@ -12464,7 +12496,7 @@ async function createServer(opts) {
|
|
|
12464
12496
|
(runId, projectId, result) => notifier.dispatchInsightWebhooks(runId, projectId, result),
|
|
12465
12497
|
async (ctx) => {
|
|
12466
12498
|
if (!sessionRegistry) return;
|
|
12467
|
-
const project = opts.db.select({ name: projects.name }).from(projects).where(
|
|
12499
|
+
const project = opts.db.select({ name: projects.name }).from(projects).where(eq22(projects.id, ctx.projectId)).get();
|
|
12468
12500
|
if (!project) return;
|
|
12469
12501
|
let content;
|
|
12470
12502
|
if (ctx.kind === RunKinds["aeo-discover-probe"]) {
|
|
@@ -12807,7 +12839,7 @@ async function createServer(opts) {
|
|
|
12807
12839
|
void (async () => {
|
|
12808
12840
|
try {
|
|
12809
12841
|
const report = await aeroClient.runDoctor({ project: projectName });
|
|
12810
|
-
const project = opts.db.select().from(projects).where(
|
|
12842
|
+
const project = opts.db.select().from(projects).where(eq22(projects.name, projectName)).get();
|
|
12811
12843
|
if (!project) {
|
|
12812
12844
|
app.log.warn({ projectName }, "doctor schedule fired for an unknown project");
|
|
12813
12845
|
return;
|
|
@@ -12836,8 +12868,8 @@ async function createServer(opts) {
|
|
|
12836
12868
|
if (!probed) return;
|
|
12837
12869
|
const alreadySynced = opts.db.select().from(ccReleaseSyncs).where(
|
|
12838
12870
|
and15(
|
|
12839
|
-
|
|
12840
|
-
|
|
12871
|
+
eq22(ccReleaseSyncs.release, probed.release),
|
|
12872
|
+
eq22(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
|
|
12841
12873
|
)
|
|
12842
12874
|
).limit(1).get();
|
|
12843
12875
|
if (alreadySynced) {
|
|
@@ -12992,7 +13024,7 @@ async function createServer(opts) {
|
|
|
12992
13024
|
return removed;
|
|
12993
13025
|
}
|
|
12994
13026
|
};
|
|
12995
|
-
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ??
|
|
13027
|
+
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto23.randomBytes(32).toString("hex");
|
|
12996
13028
|
const googleConnectionStore = {
|
|
12997
13029
|
listConnections: (domain) => listGoogleConnections(opts.config, domain),
|
|
12998
13030
|
getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
|
|
@@ -13067,11 +13099,11 @@ async function createServer(opts) {
|
|
|
13067
13099
|
const googlePublicUrl = resolveGooglePublicUrl(opts.config, basePath);
|
|
13068
13100
|
if (opts.config.apiKey) {
|
|
13069
13101
|
const keyHash = hashApiKey(opts.config.apiKey);
|
|
13070
|
-
const existing = opts.db.select().from(apiKeys).where(
|
|
13102
|
+
const existing = opts.db.select().from(apiKeys).where(eq22(apiKeys.keyHash, keyHash)).get();
|
|
13071
13103
|
if (!existing) {
|
|
13072
13104
|
const prefix = opts.config.apiKey.slice(0, 12);
|
|
13073
13105
|
opts.db.insert(apiKeys).values({
|
|
13074
|
-
id: `key_${
|
|
13106
|
+
id: `key_${crypto23.randomBytes(8).toString("hex")}`,
|
|
13075
13107
|
name: "default",
|
|
13076
13108
|
keyHash,
|
|
13077
13109
|
keyPrefix: prefix,
|
|
@@ -13095,7 +13127,7 @@ async function createServer(opts) {
|
|
|
13095
13127
|
};
|
|
13096
13128
|
const createSession = (apiKeyId) => {
|
|
13097
13129
|
pruneExpiredSessions();
|
|
13098
|
-
const sessionId =
|
|
13130
|
+
const sessionId = crypto23.randomBytes(32).toString("hex");
|
|
13099
13131
|
sessions.set(sessionId, {
|
|
13100
13132
|
apiKeyId,
|
|
13101
13133
|
expiresAt: Date.now() + SESSION_TTL_MS
|
|
@@ -13119,7 +13151,7 @@ async function createServer(opts) {
|
|
|
13119
13151
|
};
|
|
13120
13152
|
const getDefaultApiKey = () => {
|
|
13121
13153
|
if (!opts.config.apiKey) return void 0;
|
|
13122
|
-
return opts.db.select().from(apiKeys).where(
|
|
13154
|
+
return opts.db.select().from(apiKeys).where(eq22(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
|
|
13123
13155
|
};
|
|
13124
13156
|
const createPasswordSession = (reply) => {
|
|
13125
13157
|
const key = getDefaultApiKey();
|
|
@@ -13143,7 +13175,7 @@ async function createServer(opts) {
|
|
|
13143
13175
|
if (!header) return false;
|
|
13144
13176
|
const parts = header.split(" ");
|
|
13145
13177
|
if (parts.length !== 2 || parts[0] !== "Bearer") return false;
|
|
13146
|
-
const key = opts.db.select().from(apiKeys).where(
|
|
13178
|
+
const key = opts.db.select().from(apiKeys).where(eq22(apiKeys.keyHash, hashApiKey(parts[1]))).get();
|
|
13147
13179
|
return Boolean(key && !key.revokedAt);
|
|
13148
13180
|
};
|
|
13149
13181
|
const namedAccountsInUse = () => anyUsersExist(opts.db);
|
|
@@ -13233,12 +13265,12 @@ async function createServer(opts) {
|
|
|
13233
13265
|
return reply.send({ authenticated: true });
|
|
13234
13266
|
}
|
|
13235
13267
|
if (apiKey) {
|
|
13236
|
-
const key = opts.db.select().from(apiKeys).where(
|
|
13268
|
+
const key = opts.db.select().from(apiKeys).where(eq22(apiKeys.keyHash, hashApiKey(apiKey))).get();
|
|
13237
13269
|
if (!key || key.revokedAt) {
|
|
13238
13270
|
const err2 = authInvalid();
|
|
13239
13271
|
return reply.status(err2.statusCode).send(err2.toJSON());
|
|
13240
13272
|
}
|
|
13241
|
-
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
13273
|
+
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq22(apiKeys.id, key.id)).run();
|
|
13242
13274
|
const sessionId = createSession(key.id);
|
|
13243
13275
|
reply.header(
|
|
13244
13276
|
"set-cookie",
|
|
@@ -13451,7 +13483,7 @@ async function createServer(opts) {
|
|
|
13451
13483
|
deps: {
|
|
13452
13484
|
enqueueAutoExtract: ({ projectId, release: r }) => {
|
|
13453
13485
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13454
|
-
const runId =
|
|
13486
|
+
const runId = crypto23.randomUUID();
|
|
13455
13487
|
opts.db.insert(runs).values({
|
|
13456
13488
|
id: runId,
|
|
13457
13489
|
projectId,
|
|
@@ -13576,7 +13608,7 @@ async function createServer(opts) {
|
|
|
13576
13608
|
...inspectOpts,
|
|
13577
13609
|
config: opts.config
|
|
13578
13610
|
}).then(() => {
|
|
13579
|
-
const finished = opts.db.select({ status: runs.status }).from(runs).where(
|
|
13611
|
+
const finished = opts.db.select({ status: runs.status }).from(runs).where(eq22(runs.id, runId)).get();
|
|
13580
13612
|
if (finished?.status === RunStatuses.completed || finished?.status === RunStatuses.partial) {
|
|
13581
13613
|
return maybeRefreshGscCoverage(opts.db, opts.config, projectId);
|
|
13582
13614
|
}
|
|
@@ -13671,7 +13703,7 @@ async function createServer(opts) {
|
|
|
13671
13703
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13672
13704
|
opts.db.insert(auditLog).values(
|
|
13673
13705
|
targetProjectIds.map((projectId) => ({
|
|
13674
|
-
id:
|
|
13706
|
+
id: crypto23.randomUUID(),
|
|
13675
13707
|
projectId,
|
|
13676
13708
|
actor: "api",
|
|
13677
13709
|
action: existing ? "provider.updated" : "provider.created",
|
|
@@ -13939,7 +13971,7 @@ async function createServer(opts) {
|
|
|
13939
13971
|
}
|
|
13940
13972
|
checkLatestVersionForServer();
|
|
13941
13973
|
scheduler.start();
|
|
13942
|
-
for (const run of opts.db.select({ id: researchRuns.id, projectId: researchRuns.projectId }).from(researchRuns).where(
|
|
13974
|
+
for (const run of opts.db.select({ id: researchRuns.id, projectId: researchRuns.projectId }).from(researchRuns).where(eq22(researchRuns.status, ResearchRunStatuses.queued)).all()) {
|
|
13943
13975
|
dispatchResearchRun(run.id, run.projectId);
|
|
13944
13976
|
}
|
|
13945
13977
|
app.addHook("onClose", async () => {
|